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/manager.h"
44 #include "asterisk/cli.h"
45 #define AST_API_MODULE
46 #include "asterisk/monitor.h"
47 #include "asterisk/app.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/config.h"
50 #include "asterisk/options.h"
53 <application name="Monitor" language="en_US">
58 <parameter name="file_format" argsep=":">
59 <argument name="file_format" required="true">
60 <para>optional, if not set, defaults to <literal>wav</literal></para>
62 <argument name="urlbase" />
64 <parameter name="fname_base">
65 <para>if set, changes the filename used to the one specified.</para>
67 <parameter name="options">
70 <para>when the recording ends mix the two leg files into one and
71 delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
72 is set, the application referenced in it will be executed instead of
73 soxmix/sox and the raw leg files will NOT be deleted automatically.
74 soxmix/sox or <variable>MONITOR_EXEC</variable> is handed 3 arguments,
75 the two leg files and a target mixed file name which is the same as
76 the leg file names only without the in/out designator.</para>
77 <para>If <variable>MONITOR_EXEC_ARGS</variable> is set, the contents
78 will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
79 Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
80 administrator interface.</para>
83 <para>Don't begin recording unless a call is bridged to another channel.</para>
86 <para>Skip recording of input stream (disables <literal>m</literal> option).</para>
89 <para>Skip recording of output stream (disables <literal>m</literal> option).</para>
95 <para>Used to start monitoring a channel. The channel's input and output
96 voice packets are logged to files until the channel hangs up or
97 monitoring is stopped by the StopMonitor application.</para>
98 <para>By default, files are stored to <filename>/var/spool/asterisk/monitor/</filename>.
99 Returns <literal>-1</literal> if monitor files can't be opened or if the channel is
100 already monitored, otherwise <literal>0</literal>.</para>
103 <ref type="application">StopMonitor</ref>
106 <application name="StopMonitor" language="en_US">
108 Stop monitoring a channel.
112 <para>Stops monitoring a channel. Has no effect if the channel is not monitored.</para>
115 <application name="ChangeMonitor" language="en_US">
117 Change monitoring filename of a channel.
120 <parameter name="filename_base" required="true">
121 <para>The new filename base to use for monitoring this channel.</para>
125 <para>Changes monitoring filename of a channel. Has no effect if the
126 channel is not monitored.</para>
129 <application name="PauseMonitor" language="en_US">
131 Pause monitoring of a channel.
135 <para>Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.</para>
138 <ref type="application">UnpauseMonitor</ref>
141 <application name="UnpauseMonitor" language="en_US">
143 Unpause monitoring of a channel.
147 <para>Unpauses monitoring of a channel on which monitoring had
148 previously been paused with PauseMonitor.</para>
151 <ref type="application">PauseMonitor</ref>
154 <manager name="Monitor" language="en_US">
159 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
160 <parameter name="Channel" required="true">
161 <para>Used to specify the channel to record.</para>
163 <parameter name="File">
164 <para>Is the name of the file created in the monitor spool directory.
165 Defaults to the same name as the channel (with slashes replaced with dashes).</para>
167 <parameter name="Format">
168 <para>Is the audio recording format. Defaults to <literal>wav</literal>.</para>
170 <parameter name="Mix">
171 <para>Boolean parameter as to whether to mix the input and output channels
172 together after the recording is finished.</para>
176 <para>This action may be used to record the audio on a
177 specified channel.</para>
180 <manager name="StopMonitor" language="en_US">
182 Stop monitoring a channel.
185 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
186 <parameter name="Channel" required="true">
187 <para>The name of the channel monitored.</para>
191 <para>This action may be used to end a previously started 'Monitor' action.</para>
194 <manager name="ChangeMonitor" language="en_US">
196 Change monitoring filename of a channel.
199 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
200 <parameter name="Channel" required="true">
201 <para>Used to specify the channel to record.</para>
203 <parameter name="File" required="true">
204 <para>Is the new name of the file created in the
205 monitor spool directory.</para>
209 <para>This action may be used to change the file
210 started by a previous 'Monitor' action.</para>
213 <manager name="PauseMonitor" language="en_US">
215 Pause monitoring of a channel.
218 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
219 <parameter name="Channel" required="true">
220 <para>Used to specify the channel to record.</para>
224 <para>This action may be used to temporarily stop the
225 recording of a channel.</para>
228 <manager name="UnpauseMonitor" language="en_US">
230 Unpause monitoring of a channel.
233 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
234 <parameter name="Channel" required="true">
235 <para>Used to specify the channel to record.</para>
239 <para>This action may be used to re-enable recording
240 of a channel after calling PauseMonitor.</para>
246 AST_MUTEX_DEFINE_STATIC(monitorlock);
248 #define LOCK_IF_NEEDED(lock, needed) do { \
250 ast_channel_lock(lock); \
253 #define UNLOCK_IF_NEEDED(lock, needed) do { \
255 ast_channel_unlock(lock); \
258 static unsigned long seq = 0;
261 * \brief Change state of monitored channel
263 * \param state monitor state
264 * \retval 0 on success.
265 * \retval -1 on failure.
267 static int ast_monitor_set_state(struct ast_channel *chan, int state)
269 LOCK_IF_NEEDED(chan, 1);
270 if (!chan->monitor) {
271 UNLOCK_IF_NEEDED(chan, 1);
274 chan->monitor->state = state;
275 UNLOCK_IF_NEEDED(chan, 1);
279 /*! \brief Start monitoring a channel
280 * \param chan ast_channel struct to record
281 * \param format_spec file format to use for recording
282 * \param fname_base filename base to record to
283 * \param need_lock whether to lock the channel mutex
284 * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used
285 * Creates the file to record, if no format is specified it assumes WAV
286 * It also sets channel variable __MONITORED=yes
287 * \retval 0 on success
288 * \retval -1 on failure
290 int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
291 const char *fname_base, int need_lock, int stream_action)
295 LOCK_IF_NEEDED(chan, need_lock);
297 if (!(chan->monitor)) {
298 struct ast_channel_monitor *monitor;
299 char *channel_name, *p;
301 /* Create monitoring directory if needed */
302 ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
304 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
305 UNLOCK_IF_NEEDED(chan, need_lock);
309 /* Determine file names */
310 if (!ast_strlen_zero(fname_base)) {
311 int directory = strchr(fname_base, '/') ? 1 : 0;
312 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
313 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
315 snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
316 absolute, absolute_suffix, fname_base);
317 snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
318 absolute, absolute_suffix, fname_base);
319 snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
320 absolute, absolute_suffix, fname_base);
322 /* try creating the directory just in case it doesn't exist */
324 char *name = ast_strdupa(monitor->filename_base);
325 ast_mkdir(dirname(name), 0777);
328 ast_mutex_lock(&monitorlock);
329 snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
330 ast_config_AST_MONITOR_DIR, seq);
331 snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
332 ast_config_AST_MONITOR_DIR, seq);
334 ast_mutex_unlock(&monitorlock);
336 channel_name = ast_strdupa(ast_channel_name(chan));
337 while ((p = strchr(channel_name, '/'))) {
340 snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
341 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
342 monitor->filename_changed = 1;
345 monitor->stop = ast_monitor_stop;
347 /* Determine file format */
348 if (!ast_strlen_zero(format_spec)) {
349 monitor->format = ast_strdup(format_spec);
351 monitor->format = ast_strdup("wav");
355 if (stream_action & X_REC_IN) {
356 if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
357 ast_filedelete(monitor->read_filename, NULL);
358 if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
359 monitor->format, NULL,
360 O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
361 ast_log(LOG_WARNING, "Could not create file %s\n",
362 monitor->read_filename);
364 UNLOCK_IF_NEEDED(chan, need_lock);
368 monitor->read_stream = NULL;
370 if (stream_action & X_REC_OUT) {
371 if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
372 ast_filedelete(monitor->write_filename, NULL);
374 if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
375 monitor->format, NULL,
376 O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
377 ast_log(LOG_WARNING, "Could not create file %s\n",
378 monitor->write_filename);
379 ast_closestream(monitor->read_stream);
381 UNLOCK_IF_NEEDED(chan, need_lock);
385 monitor->write_stream = NULL;
387 chan->monitor = monitor;
388 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
389 /* so we know this call has been monitored in case we need to bill for it or something */
390 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
392 ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart",
395 ast_channel_name(chan),
396 ast_channel_uniqueid(chan));
398 ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
402 UNLOCK_IF_NEEDED(chan, need_lock);
408 * \brief Get audio format.
409 * \param format recording format.
410 * The file format extensions that Asterisk uses are not all the same as that
411 * which soxmix expects. This function ensures that the format used as the
412 * extension on the filename is something soxmix will understand.
414 static const char *get_soxmix_format(const char *format)
416 const char *res = format;
418 if (!strcasecmp(format,"ulaw"))
420 if (!strcasecmp(format,"alaw"))
427 * \brief Stop monitoring channel
430 * Stop the recording, close any open streams, mix in/out channels if required
433 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
437 LOCK_IF_NEEDED(chan, need_lock);
440 char filename[ FILENAME_MAX ];
442 if (chan->monitor->read_stream) {
443 ast_closestream(chan->monitor->read_stream);
445 if (chan->monitor->write_stream) {
446 ast_closestream(chan->monitor->write_stream);
449 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
450 if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
451 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
452 if (ast_fileexists(filename, NULL, NULL) > 0) {
453 ast_filedelete(filename, NULL);
455 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
457 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
460 if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
461 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
462 if (ast_fileexists(filename, NULL, NULL) > 0) {
463 ast_filedelete(filename, NULL);
465 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
467 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
471 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
474 const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
475 char *fname_base = chan->monitor->filename_base;
476 const char *execute, *execute_args;
477 /* at this point, fname_base really is the full path */
479 /* Set the execute application */
480 execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
481 if (ast_strlen_zero(execute)) {
483 execute = "nice -n 19 soxmix";
485 execute = "nice -n 19 sox -m";
487 format = get_soxmix_format(format);
490 execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
491 if (ast_strlen_zero(execute_args)) {
495 snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
496 execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
498 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
499 ast_copy_string(tmp, tmp2, sizeof(tmp));
501 ast_debug(1,"monitor executing %s\n",tmp);
502 if (ast_safe_system(tmp) == -1)
503 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
506 ast_free(chan->monitor->format);
507 ast_free(chan->monitor);
508 chan->monitor = NULL;
510 ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop",
513 ast_channel_name(chan),
514 ast_channel_uniqueid(chan)
516 pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
518 pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
520 UNLOCK_IF_NEEDED(chan, need_lock);
526 /*! \brief Pause monitoring of channel */
527 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
529 return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
532 /*! \brief Unpause monitoring of channel */
533 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
535 return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
538 /*! \brief Wrapper for ast_monitor_pause */
539 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
541 return ast_monitor_pause(chan);
544 /*! \brief Wrapper for ast_monitor_unpause */
545 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
547 return ast_monitor_unpause(chan);
551 * \brief Change monitored filename of channel
553 * \param fname_base new filename
555 * \retval 0 on success.
556 * \retval -1 on failure.
558 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
560 if (ast_strlen_zero(fname_base)) {
561 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", ast_channel_name(chan));
565 LOCK_IF_NEEDED(chan, need_lock);
568 int directory = strchr(fname_base, '/') ? 1 : 0;
569 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
570 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
571 char tmpstring[sizeof(chan->monitor->filename_base)] = "";
572 int i, fd[2] = { -1, -1 }, doexit = 0;
574 /* before continuing, see if we're trying to rename the file to itself... */
575 snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
577 /* try creating the directory just in case it doesn't exist */
579 char *name = ast_strdupa(tmpstring);
580 ast_mkdir(dirname(name), 0777);
583 /*!\note We cannot just compare filenames, due to symlinks, relative
584 * paths, and other possible filesystem issues. We could use
585 * realpath(3), but its use is discouraged. However, if we try to
586 * create the same file from two different paths, the second will
587 * fail, and so we have our notification that the filenames point to
590 * Remember, also, that we're using the basename of the file (i.e.
591 * the file without the format suffix), so it does not already exist
592 * and we aren't interfering with the recording itself.
594 ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
596 if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
597 (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
599 ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
601 ast_debug(2, "No need to rename monitor filename to itself\n");
606 /* Cleanup temporary files */
607 for (i = 0; i < 2; i++) {
609 while (close(fd[i]) < 0 && errno == EINTR);
613 /* if previous monitor file existed in a subdirectory, the directory will not be removed */
614 unlink(chan->monitor->filename_base);
617 UNLOCK_IF_NEEDED(chan, need_lock);
621 ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
622 chan->monitor->filename_changed = 1;
624 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", ast_channel_name(chan), fname_base);
627 UNLOCK_IF_NEEDED(chan, need_lock);
634 * \brief Start monitor
636 * \param data arguments passed fname|options
637 * \retval 0 on success.
638 * \retval -1 on failure.
640 static int start_monitor_exec(struct ast_channel *chan, const char *data)
643 char *options = NULL;
645 char *urlprefix = NULL;
647 int stream_action = X_REC_IN | X_REC_OUT;
649 int waitforbridge = 0;
652 AST_DECLARE_APP_ARGS(args,
654 AST_APP_ARG(fname_base);
655 AST_APP_ARG(options);
658 /* Parse arguments. */
659 if (ast_strlen_zero(data)) {
660 ast_log(LOG_ERROR, "Monitor requires an argument\n");
664 parse = ast_strdupa(data);
665 AST_STANDARD_APP_ARGS(args, parse);
667 if (!ast_strlen_zero(args.options)) {
668 if (strchr(args.options, 'm'))
669 stream_action |= X_JOIN;
670 if (strchr(args.options, 'b'))
672 if (strchr(args.options, 'i'))
673 stream_action &= ~X_REC_IN;
674 if (strchr(args.options, 'o'))
675 stream_action &= ~X_REC_OUT;
678 arg = strchr(args.format, ':');
685 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
686 ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
687 ast_channel_lock(chan);
688 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc())) {
689 ast_channel_unlock(chan);
692 ast_cdr_setuserfield(chan, tmp);
693 ast_channel_unlock(chan);
696 /* We must remove the "b" option if listed. In principle none of
697 the following could give NULL results, but we check just to
698 be pedantic. Reconstructing with checks for 'm' option does not
699 work if we end up adding more options than 'm' in the future. */
700 delay = ast_strdupa(data);
701 options = strrchr(delay, ',');
703 arg = strchr(options, 'b');
706 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
712 res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
714 res = ast_monitor_change_fname(chan, args.fname_base, 1);
716 if (stream_action & X_JOIN) {
717 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
720 ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
722 ast_monitor_setjoinfiles(chan, joinfiles);
727 /*! \brief Wrapper function \see ast_monitor_stop */
728 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
730 return ast_monitor_stop(chan, 1);
733 /*! \brief Wrapper function \see ast_monitor_change_fname */
734 static int change_monitor_exec(struct ast_channel *chan, const char *data)
736 return ast_monitor_change_fname(chan, data, 1);
739 /*! \brief Start monitoring a channel by manager connection */
740 static int start_monitor_action(struct mansession *s, const struct message *m)
742 struct ast_channel *c = NULL;
743 const char *name = astman_get_header(m, "Channel");
744 const char *fname = astman_get_header(m, "File");
745 const char *format = astman_get_header(m, "Format");
746 const char *mix = astman_get_header(m, "Mix");
749 if (ast_strlen_zero(name)) {
750 astman_send_error(s, m, "No channel specified");
754 if (!(c = ast_channel_get_by_name(name))) {
755 astman_send_error(s, m, "No such channel");
759 if (ast_strlen_zero(fname)) {
760 /* No filename base specified, default to channel name as per CLI */
762 fname = ast_strdupa(ast_channel_name(c));
763 ast_channel_unlock(c);
764 /* Channels have the format technology/channel_name - have to replace that / */
765 if ((d = strchr(fname, '/'))) {
770 if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
771 if (ast_monitor_change_fname(c, fname, 1)) {
772 astman_send_error(s, m, "Could not start monitoring channel");
773 c = ast_channel_unref(c);
780 ast_monitor_setjoinfiles(c, 1);
781 ast_channel_unlock(c);
784 c = ast_channel_unref(c);
786 astman_send_ack(s, m, "Started monitoring channel");
791 /*! \brief Stop monitoring a channel by manager connection */
792 static int stop_monitor_action(struct mansession *s, const struct message *m)
794 struct ast_channel *c = NULL;
795 const char *name = astman_get_header(m, "Channel");
798 if (ast_strlen_zero(name)) {
799 astman_send_error(s, m, "No channel specified");
803 if (!(c = ast_channel_get_by_name(name))) {
804 astman_send_error(s, m, "No such channel");
808 res = ast_monitor_stop(c, 1);
810 c = ast_channel_unref(c);
813 astman_send_error(s, m, "Could not stop monitoring channel");
817 astman_send_ack(s, m, "Stopped monitoring channel");
822 /*! \brief Change filename of a monitored channel by manager connection */
823 static int change_monitor_action(struct mansession *s, const struct message *m)
825 struct ast_channel *c = NULL;
826 const char *name = astman_get_header(m, "Channel");
827 const char *fname = astman_get_header(m, "File");
829 if (ast_strlen_zero(name)) {
830 astman_send_error(s, m, "No channel specified");
834 if (ast_strlen_zero(fname)) {
835 astman_send_error(s, m, "No filename specified");
839 if (!(c = ast_channel_get_by_name(name))) {
840 astman_send_error(s, m, "No such channel");
844 if (ast_monitor_change_fname(c, fname, 1)) {
845 c = ast_channel_unref(c);
846 astman_send_error(s, m, "Could not change monitored filename of channel");
850 c = ast_channel_unref(c);
852 astman_send_ack(s, m, "Changed monitor filename");
857 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
860 chan->monitor->joinfiles = turnon;
863 enum MONITOR_PAUSING_ACTION
865 MONITOR_ACTION_PAUSE,
866 MONITOR_ACTION_UNPAUSE
869 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
871 struct ast_channel *c = NULL;
872 const char *name = astman_get_header(m, "Channel");
874 if (ast_strlen_zero(name)) {
875 astman_send_error(s, m, "No channel specified");
879 if (!(c = ast_channel_get_by_name(name))) {
880 astman_send_error(s, m, "No such channel");
884 if (action == MONITOR_ACTION_PAUSE) {
885 ast_monitor_pause(c);
887 ast_monitor_unpause(c);
890 c = ast_channel_unref(c);
892 astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
897 static int pause_monitor_action(struct mansession *s, const struct message *m)
899 return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
902 static int unpause_monitor_action(struct mansession *s, const struct message *m)
904 return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
907 static int load_module(void)
909 ast_register_application_xml("Monitor", start_monitor_exec);
910 ast_register_application_xml("StopMonitor", stop_monitor_exec);
911 ast_register_application_xml("ChangeMonitor", change_monitor_exec);
912 ast_register_application_xml("PauseMonitor", pause_monitor_exec);
913 ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
914 ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
915 ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
916 ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
917 ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
918 ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
920 return AST_MODULE_LOAD_SUCCESS;
923 static int unload_module(void)
925 ast_unregister_application("Monitor");
926 ast_unregister_application("StopMonitor");
927 ast_unregister_application("ChangeMonitor");
928 ast_unregister_application("PauseMonitor");
929 ast_unregister_application("UnpauseMonitor");
930 ast_manager_unregister("Monitor");
931 ast_manager_unregister("StopMonitor");
932 ast_manager_unregister("ChangeMonitor");
933 ast_manager_unregister("PauseMonitor");
934 ast_manager_unregister("UnpauseMonitor");
939 /* usecount semantics need to be defined */
940 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Call Monitoring Resource",
942 .unload = unload_module,
943 .load_pri = AST_MODPRI_CHANNEL_DEPEND,