2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief PBX channel monitoring
23 * \author Mark Spencer <markster@digium.com>
27 <support_level>core</support_level>
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
38 #include "asterisk/lock.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/file.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/stasis.h"
46 #include "asterisk/stasis_channels.h"
47 #define AST_API_MODULE
48 #include "asterisk/monitor.h"
49 #include "asterisk/app.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/config.h"
52 #include "asterisk/options.h"
55 <application name="Monitor" language="en_US">
60 <parameter name="file_format" argsep=":">
61 <argument name="file_format" required="true">
62 <para>optional, if not set, defaults to <literal>wav</literal></para>
64 <argument name="urlbase" />
66 <parameter name="fname_base">
67 <para>if set, changes the filename used to the one specified.</para>
69 <parameter name="options">
72 <para>when the recording ends mix the two leg files into one and
73 delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
74 is set, the application referenced in it will be executed instead of
75 soxmix/sox and the raw leg files will NOT be deleted automatically.
76 soxmix/sox or <variable>MONITOR_EXEC</variable> is handed 3 arguments,
77 the two leg files and a target mixed file name which is the same as
78 the leg file names only without the in/out designator.</para>
79 <para>If <variable>MONITOR_EXEC_ARGS</variable> is set, the contents
80 will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
81 Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
82 administrator interface.</para>
85 <para>Don't begin recording unless a call is bridged to another channel.</para>
88 <para>Skip recording of input stream (disables <literal>m</literal> option).</para>
91 <para>Skip recording of output stream (disables <literal>m</literal> option).</para>
97 <para>Used to start monitoring a channel. The channel's input and output
98 voice packets are logged to files until the channel hangs up or
99 monitoring is stopped by the StopMonitor application.</para>
100 <para>By default, files are stored to <filename>/var/spool/asterisk/monitor/</filename>.
101 Returns <literal>-1</literal> if monitor files can't be opened or if the channel is
102 already monitored, otherwise <literal>0</literal>.</para>
105 <ref type="application">StopMonitor</ref>
108 <application name="StopMonitor" language="en_US">
110 Stop monitoring a channel.
114 <para>Stops monitoring a channel. Has no effect if the channel is not monitored.</para>
117 <application name="ChangeMonitor" language="en_US">
119 Change monitoring filename of a channel.
122 <parameter name="filename_base" required="true">
123 <para>The new filename base to use for monitoring this channel.</para>
127 <para>Changes monitoring filename of a channel. Has no effect if the
128 channel is not monitored.</para>
131 <application name="PauseMonitor" language="en_US">
133 Pause monitoring of a channel.
137 <para>Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.</para>
140 <ref type="application">UnpauseMonitor</ref>
143 <application name="UnpauseMonitor" language="en_US">
145 Unpause monitoring of a channel.
149 <para>Unpauses monitoring of a channel on which monitoring had
150 previously been paused with PauseMonitor.</para>
153 <ref type="application">PauseMonitor</ref>
156 <manager name="Monitor" language="en_US">
161 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
162 <parameter name="Channel" required="true">
163 <para>Used to specify the channel to record.</para>
165 <parameter name="File">
166 <para>Is the name of the file created in the monitor spool directory.
167 Defaults to the same name as the channel (with slashes replaced with dashes).</para>
169 <parameter name="Format">
170 <para>Is the audio recording format. Defaults to <literal>wav</literal>.</para>
172 <parameter name="Mix">
173 <para>Boolean parameter as to whether to mix the input and output channels
174 together after the recording is finished.</para>
178 <para>This action may be used to record the audio on a
179 specified channel.</para>
182 <manager name="StopMonitor" language="en_US">
184 Stop monitoring a channel.
187 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
188 <parameter name="Channel" required="true">
189 <para>The name of the channel monitored.</para>
193 <para>This action may be used to end a previously started 'Monitor' action.</para>
196 <manager name="ChangeMonitor" language="en_US">
198 Change monitoring filename of a channel.
201 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
202 <parameter name="Channel" required="true">
203 <para>Used to specify the channel to record.</para>
205 <parameter name="File" required="true">
206 <para>Is the new name of the file created in the
207 monitor spool directory.</para>
211 <para>This action may be used to change the file
212 started by a previous 'Monitor' action.</para>
215 <manager name="PauseMonitor" language="en_US">
217 Pause monitoring of a channel.
220 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
221 <parameter name="Channel" required="true">
222 <para>Used to specify the channel to record.</para>
226 <para>This action may be used to temporarily stop the
227 recording of a channel.</para>
230 <manager name="UnpauseMonitor" language="en_US">
232 Unpause monitoring of a channel.
235 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
236 <parameter name="Channel" required="true">
237 <para>Used to specify the channel to record.</para>
241 <para>This action may be used to re-enable recording
242 of a channel after calling PauseMonitor.</para>
248 AST_MUTEX_DEFINE_STATIC(monitorlock);
250 #define LOCK_IF_NEEDED(lock, needed) do { \
252 ast_channel_lock(lock); \
255 #define UNLOCK_IF_NEEDED(lock, needed) do { \
257 ast_channel_unlock(lock); \
260 static unsigned long seq = 0;
263 * \brief Change state of monitored channel
265 * \param state monitor state
266 * \retval 0 on success.
267 * \retval -1 on failure.
269 static int ast_monitor_set_state(struct ast_channel *chan, int state)
271 LOCK_IF_NEEDED(chan, 1);
272 if (!ast_channel_monitor(chan)) {
273 UNLOCK_IF_NEEDED(chan, 1);
276 ast_channel_monitor(chan)->state = state;
277 UNLOCK_IF_NEEDED(chan, 1);
281 /*! \brief Start monitoring a channel
282 * \param chan ast_channel struct to record
283 * \param format_spec file format to use for recording
284 * \param fname_base filename base to record to
285 * \param need_lock whether to lock the channel mutex
286 * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used
287 * Creates the file to record, if no format is specified it assumes WAV
288 * It also sets channel variable __MONITORED=yes
289 * \retval 0 on success
290 * \retval -1 on failure
292 int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
293 const char *fname_base, int need_lock, int stream_action)
296 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
298 LOCK_IF_NEEDED(chan, need_lock);
300 if (!(ast_channel_monitor(chan))) {
301 struct ast_channel_monitor *monitor;
302 char *channel_name, *p;
304 /* Create monitoring directory if needed */
305 ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
307 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
308 UNLOCK_IF_NEEDED(chan, need_lock);
312 /* Determine file names */
313 if (!ast_strlen_zero(fname_base)) {
314 int directory = strchr(fname_base, '/') ? 1 : 0;
315 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
316 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
318 snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
319 absolute, absolute_suffix, fname_base);
320 snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
321 absolute, absolute_suffix, fname_base);
322 snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
323 absolute, absolute_suffix, fname_base);
325 /* try creating the directory just in case it doesn't exist */
327 char *name = ast_strdupa(monitor->filename_base);
328 ast_mkdir(dirname(name), 0777);
331 ast_mutex_lock(&monitorlock);
332 snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
333 ast_config_AST_MONITOR_DIR, seq);
334 snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
335 ast_config_AST_MONITOR_DIR, seq);
337 ast_mutex_unlock(&monitorlock);
339 /* Replace all '/' chars from the channel name with '-' chars. */
340 channel_name = ast_strdupa(ast_channel_name(chan));
341 for (p = channel_name; (p = strchr(p, '/')); ) {
345 snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
346 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
347 monitor->filename_changed = 1;
350 monitor->stop = ast_monitor_stop;
352 /* Determine file format */
353 if (!ast_strlen_zero(format_spec)) {
354 monitor->format = ast_strdup(format_spec);
356 monitor->format = ast_strdup("wav");
360 if (stream_action & X_REC_IN) {
361 if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
362 ast_filedelete(monitor->read_filename, NULL);
363 if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
364 monitor->format, NULL,
365 O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
366 ast_log(LOG_WARNING, "Could not create file %s\n",
367 monitor->read_filename);
369 UNLOCK_IF_NEEDED(chan, need_lock);
373 monitor->read_stream = NULL;
375 if (stream_action & X_REC_OUT) {
376 if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
377 ast_filedelete(monitor->write_filename, NULL);
379 if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
380 monitor->format, NULL,
381 O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
382 ast_log(LOG_WARNING, "Could not create file %s\n",
383 monitor->write_filename);
384 if (monitor->read_stream) {
385 ast_closestream(monitor->read_stream);
388 UNLOCK_IF_NEEDED(chan, need_lock);
392 monitor->write_stream = NULL;
394 ast_channel_monitor_set(chan, monitor);
395 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
396 /* so we know this call has been monitored in case we need to bill for it or something */
397 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
399 message = ast_channel_cached_blob_create(chan,
400 ast_channel_monitor_start_type(),
403 stasis_publish(ast_channel_topic(chan), message);
406 ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
410 UNLOCK_IF_NEEDED(chan, need_lock);
416 * \brief Get audio format.
417 * \param format recording format.
418 * The file format extensions that Asterisk uses are not all the same as that
419 * which soxmix expects. This function ensures that the format used as the
420 * extension on the filename is something soxmix will understand.
422 static const char *get_soxmix_format(const char *format)
424 const char *res = format;
426 if (!strcasecmp(format,"ulaw"))
428 if (!strcasecmp(format,"alaw"))
435 * \brief Stop monitoring channel
438 * Stop the recording, close any open streams, mix in/out channels if required
441 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
444 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
446 LOCK_IF_NEEDED(chan, need_lock);
448 if (ast_channel_monitor(chan)) {
449 char filename[ FILENAME_MAX ];
451 if (ast_channel_monitor(chan)->read_stream) {
452 ast_closestream(ast_channel_monitor(chan)->read_stream);
454 if (ast_channel_monitor(chan)->write_stream) {
455 ast_closestream(ast_channel_monitor(chan)->write_stream);
458 if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
459 if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
460 snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
461 if (ast_fileexists(filename, NULL, NULL) > 0) {
462 ast_filedelete(filename, NULL);
464 ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
466 ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
469 if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
470 snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
471 if (ast_fileexists(filename, NULL, NULL) > 0) {
472 ast_filedelete(filename, NULL);
474 ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
476 ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
480 if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
483 const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
484 char *fname_base = ast_channel_monitor(chan)->filename_base;
485 const char *execute, *execute_args;
486 /* at this point, fname_base really is the full path */
488 /* Set the execute application */
489 execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
490 if (ast_strlen_zero(execute)) {
492 execute = "nice -n 19 soxmix";
494 execute = "nice -n 19 sox -m";
496 format = get_soxmix_format(format);
499 execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
500 if (ast_strlen_zero(execute_args)) {
504 snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
505 execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
507 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
508 ast_copy_string(tmp, tmp2, sizeof(tmp));
510 ast_debug(1,"monitor executing %s\n",tmp);
511 if (ast_safe_system(tmp) == -1)
512 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
515 ast_free(ast_channel_monitor(chan)->format);
516 ast_free(ast_channel_monitor(chan));
517 ast_channel_monitor_set(chan, NULL);
519 message = ast_channel_cached_blob_create(chan,
520 ast_channel_monitor_stop_type(),
523 stasis_publish(ast_channel_topic(chan), message);
525 pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
527 pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
529 UNLOCK_IF_NEEDED(chan, need_lock);
535 /*! \brief Pause monitoring of channel */
536 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
538 return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
541 /*! \brief Unpause monitoring of channel */
542 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
544 return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
547 /*! \brief Wrapper for ast_monitor_pause */
548 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
550 return ast_monitor_pause(chan);
553 /*! \brief Wrapper for ast_monitor_unpause */
554 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
556 return ast_monitor_unpause(chan);
560 * \brief Change monitored filename of channel
562 * \param fname_base new filename
564 * \retval 0 on success.
565 * \retval -1 on failure.
567 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
569 if (ast_strlen_zero(fname_base)) {
570 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", ast_channel_name(chan));
574 LOCK_IF_NEEDED(chan, need_lock);
576 if (ast_channel_monitor(chan)) {
577 int directory = strchr(fname_base, '/') ? 1 : 0;
578 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
579 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
580 char tmpstring[sizeof(ast_channel_monitor(chan)->filename_base)] = "";
581 int i, fd[2] = { -1, -1 }, doexit = 0;
583 /* before continuing, see if we're trying to rename the file to itself... */
584 snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
586 /* try creating the directory just in case it doesn't exist */
588 char *name = ast_strdupa(tmpstring);
589 ast_mkdir(dirname(name), 0777);
593 * \note We cannot just compare filenames, due to symlinks, relative
594 * paths, and other possible filesystem issues. We could use
595 * realpath(3), but its use is discouraged. However, if we try to
596 * create the same file from two different paths, the second will
597 * fail, and so we have our notification that the filenames point to
600 * Remember, also, that we're using the basename of the file (i.e.
601 * the file without the format suffix), so it does not already exist
602 * and we aren't interfering with the recording itself.
604 ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, ast_channel_monitor(chan)->filename_base);
606 if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
607 (fd[1] = open(ast_channel_monitor(chan)->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
609 ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
611 ast_debug(2, "No need to rename monitor filename to itself\n");
616 /* Cleanup temporary files */
617 for (i = 0; i < 2; i++) {
619 while (close(fd[i]) < 0 && errno == EINTR);
623 /* if previous monitor file existed in a subdirectory, the directory will not be removed */
624 unlink(ast_channel_monitor(chan)->filename_base);
627 UNLOCK_IF_NEEDED(chan, need_lock);
631 ast_copy_string(ast_channel_monitor(chan)->filename_base, tmpstring, sizeof(ast_channel_monitor(chan)->filename_base));
632 ast_channel_monitor(chan)->filename_changed = 1;
634 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", ast_channel_name(chan), fname_base);
637 UNLOCK_IF_NEEDED(chan, need_lock);
644 * \brief Start monitor
646 * \param data arguments passed fname|options
647 * \retval 0 on success.
648 * \retval -1 on failure.
650 static int start_monitor_exec(struct ast_channel *chan, const char *data)
655 char *urlprefix = NULL;
657 int stream_action = X_REC_IN | X_REC_OUT;
659 int waitforbridge = 0;
662 AST_DECLARE_APP_ARGS(args,
664 AST_APP_ARG(fname_base);
665 AST_APP_ARG(options);
668 /* Parse arguments. */
669 if (ast_strlen_zero(data)) {
670 ast_log(LOG_ERROR, "Monitor requires an argument\n");
674 parse = ast_strdupa(data);
675 AST_STANDARD_APP_ARGS(args, parse);
677 if (!ast_strlen_zero(args.options)) {
678 if (strchr(args.options, 'm'))
679 stream_action |= X_JOIN;
680 if (strchr(args.options, 'b'))
682 if (strchr(args.options, 'i'))
683 stream_action &= ~X_REC_IN;
684 if (strchr(args.options, 'o'))
685 stream_action &= ~X_REC_OUT;
688 arg = strchr(args.format, ':');
694 if (!ast_strlen_zero(urlprefix) && !ast_strlen_zero(args.fname_base)) {
695 struct ast_cdr *chan_cdr;
696 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
697 ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
698 ast_channel_lock(chan);
699 if (!ast_channel_cdr(chan)) {
700 if (!(chan_cdr = ast_cdr_alloc())) {
701 ast_channel_unlock(chan);
704 ast_channel_cdr_set(chan, chan_cdr);
706 ast_cdr_setuserfield(chan, tmp);
707 ast_channel_unlock(chan);
710 /* We must remove the "b" option if listed. In principle none of
711 the following could give NULL results, but we check just to
712 be pedantic. Reconstructing with checks for 'm' option does not
713 work if we end up adding more options than 'm' in the future. */
714 delay = ast_strdupa(data);
715 options = strrchr(delay, ',');
717 arg = strchr(options, 'b');
720 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
726 res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
728 res = ast_monitor_change_fname(chan, args.fname_base, 1);
730 if (stream_action & X_JOIN) {
731 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
734 ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
736 ast_monitor_setjoinfiles(chan, joinfiles);
741 /*! \brief Wrapper function \see ast_monitor_stop */
742 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
744 return ast_monitor_stop(chan, 1);
747 /*! \brief Wrapper function \see ast_monitor_change_fname */
748 static int change_monitor_exec(struct ast_channel *chan, const char *data)
750 return ast_monitor_change_fname(chan, data, 1);
753 /*! \brief Start monitoring a channel by manager connection */
754 static int start_monitor_action(struct mansession *s, const struct message *m)
756 struct ast_channel *c = NULL;
757 const char *name = astman_get_header(m, "Channel");
758 const char *fname = astman_get_header(m, "File");
759 const char *format = astman_get_header(m, "Format");
760 const char *mix = astman_get_header(m, "Mix");
763 if (ast_strlen_zero(name)) {
764 astman_send_error(s, m, "No channel specified");
768 if (!(c = ast_channel_get_by_name(name))) {
769 astman_send_error(s, m, "No such channel");
773 if (ast_strlen_zero(fname)) {
774 /* No filename specified, default to the channel name. */
776 fname = ast_strdupa(ast_channel_name(c));
777 ast_channel_unlock(c);
779 /* Replace all '/' chars from the channel name with '-' chars. */
780 for (d = (char *) fname; (d = strchr(d, '/')); ) {
785 if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
786 if (ast_monitor_change_fname(c, fname, 1)) {
787 astman_send_error(s, m, "Could not start monitoring channel");
788 c = ast_channel_unref(c);
795 ast_monitor_setjoinfiles(c, 1);
796 ast_channel_unlock(c);
799 c = ast_channel_unref(c);
801 astman_send_ack(s, m, "Started monitoring channel");
806 /*! \brief Stop monitoring a channel by manager connection */
807 static int stop_monitor_action(struct mansession *s, const struct message *m)
809 struct ast_channel *c = NULL;
810 const char *name = astman_get_header(m, "Channel");
813 if (ast_strlen_zero(name)) {
814 astman_send_error(s, m, "No channel specified");
818 if (!(c = ast_channel_get_by_name(name))) {
819 astman_send_error(s, m, "No such channel");
823 res = ast_monitor_stop(c, 1);
825 c = ast_channel_unref(c);
828 astman_send_error(s, m, "Could not stop monitoring channel");
832 astman_send_ack(s, m, "Stopped monitoring channel");
837 /*! \brief Change filename of a monitored channel by manager connection */
838 static int change_monitor_action(struct mansession *s, const struct message *m)
840 struct ast_channel *c = NULL;
841 const char *name = astman_get_header(m, "Channel");
842 const char *fname = astman_get_header(m, "File");
844 if (ast_strlen_zero(name)) {
845 astman_send_error(s, m, "No channel specified");
849 if (ast_strlen_zero(fname)) {
850 astman_send_error(s, m, "No filename specified");
854 if (!(c = ast_channel_get_by_name(name))) {
855 astman_send_error(s, m, "No such channel");
859 if (ast_monitor_change_fname(c, fname, 1)) {
860 c = ast_channel_unref(c);
861 astman_send_error(s, m, "Could not change monitored filename of channel");
865 c = ast_channel_unref(c);
867 astman_send_ack(s, m, "Changed monitor filename");
872 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
874 if (ast_channel_monitor(chan))
875 ast_channel_monitor(chan)->joinfiles = turnon;
878 enum MONITOR_PAUSING_ACTION
880 MONITOR_ACTION_PAUSE,
881 MONITOR_ACTION_UNPAUSE
884 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
886 struct ast_channel *c = NULL;
887 const char *name = astman_get_header(m, "Channel");
889 if (ast_strlen_zero(name)) {
890 astman_send_error(s, m, "No channel specified");
894 if (!(c = ast_channel_get_by_name(name))) {
895 astman_send_error(s, m, "No such channel");
899 if (action == MONITOR_ACTION_PAUSE) {
900 ast_monitor_pause(c);
902 ast_monitor_unpause(c);
905 c = ast_channel_unref(c);
907 astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
912 static int pause_monitor_action(struct mansession *s, const struct message *m)
914 return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
917 static int unpause_monitor_action(struct mansession *s, const struct message *m)
919 return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
922 static int load_module(void)
924 ast_register_application_xml("Monitor", start_monitor_exec);
925 ast_register_application_xml("StopMonitor", stop_monitor_exec);
926 ast_register_application_xml("ChangeMonitor", change_monitor_exec);
927 ast_register_application_xml("PauseMonitor", pause_monitor_exec);
928 ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
929 ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
930 ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
931 ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
932 ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
933 ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
935 return AST_MODULE_LOAD_SUCCESS;
938 static int unload_module(void)
940 ast_unregister_application("Monitor");
941 ast_unregister_application("StopMonitor");
942 ast_unregister_application("ChangeMonitor");
943 ast_unregister_application("PauseMonitor");
944 ast_unregister_application("UnpauseMonitor");
945 ast_manager_unregister("Monitor");
946 ast_manager_unregister("StopMonitor");
947 ast_manager_unregister("ChangeMonitor");
948 ast_manager_unregister("PauseMonitor");
949 ast_manager_unregister("UnpauseMonitor");
954 /* usecount semantics need to be defined */
955 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Call Monitoring Resource",
957 .unload = unload_module,
958 .load_pri = AST_MODPRI_CHANNEL_DEPEND,