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>
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
34 #include <sys/types.h>
38 #include "asterisk/lock.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/file.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/cli.h"
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"
52 AST_MUTEX_DEFINE_STATIC(monitorlock
);
54 #define LOCK_IF_NEEDED(lock, needed) do { \
56 ast_channel_lock(lock); \
59 #define UNLOCK_IF_NEEDED(lock, needed) do { \
61 ast_channel_unlock(lock); \
64 static unsigned long seq
= 0;
66 static char *monitor_synopsis
= "Monitor a channel";
68 static char *monitor_descrip
= "Monitor([file_format[:urlbase]|[fname_base]|[options]]):\n"
69 "Used to start monitoring a channel. The channel's input and output\n"
70 "voice packets are logged to files until the channel hangs up or\n"
71 "monitoring is stopped by the StopMonitor application.\n"
72 " file_format optional, if not set, defaults to \"wav\"\n"
73 " fname_base if set, changes the filename used to the one specified.\n"
75 " m - when the recording ends mix the two leg files into one and\n"
76 " delete the two leg files. If the variable MONITOR_EXEC is set, the\n"
77 " application referenced in it will be executed instead of\n"
79 " soxmix and the raw leg files will NOT be deleted automatically.\n"
80 " soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
82 " sox and the raw leg files will NOT be deleted automatically.\n"
83 " sox or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
85 " and a target mixed file name which is the same as the leg file names\n"
86 " only without the in/out designator.\n"
87 " If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
88 " additional arguements to MONITOR_EXEC\n"
89 " Both MONITOR_EXEC and the Mix flag can be set from the\n"
90 " administrator interface\n"
92 " b - Don't begin recording unless a call is bridged to another channel\n"
93 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
94 "monitored, otherwise 0.\n"
97 static char *stopmonitor_synopsis
= "Stop monitoring a channel";
99 static char *stopmonitor_descrip
= "StopMonitor\n"
100 "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
102 static char *changemonitor_synopsis
= "Change monitoring filename of a channel";
104 static char *changemonitor_descrip
= "ChangeMonitor(filename_base)\n"
105 "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
106 "The argument is the new filename base to use for monitoring this channel.\n";
108 static char *pausemonitor_synopsis
= "Pause monitoring of a channel";
110 static char *pausemonitor_descrip
= "PauseMonitor\n"
111 "Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.\n";
113 static char *unpausemonitor_synopsis
= "Unpause monitoring of a channel";
115 static char *unpausemonitor_descrip
= "UnpauseMonitor\n"
116 "Unpauses monitoring of a channel on which monitoring had\n"
117 "previously been paused with PauseMonitor.\n";
119 static int ast_monitor_set_state(struct ast_channel
*chan
, int state
)
121 LOCK_IF_NEEDED(chan
, 1);
122 if (!chan
->monitor
) {
123 UNLOCK_IF_NEEDED(chan
, 1);
126 chan
->monitor
->state
= state
;
127 UNLOCK_IF_NEEDED(chan
, 1);
131 /* Start monitoring a channel */
132 int ast_monitor_start( struct ast_channel
*chan
, const char *format_spec
,
133 const char *fname_base
, int need_lock
)
138 LOCK_IF_NEEDED(chan
, need_lock
);
140 if (!(chan
->monitor
)) {
141 struct ast_channel_monitor
*monitor
;
142 char *channel_name
, *p
;
144 /* Create monitoring directory if needed */
145 if (mkdir(ast_config_AST_MONITOR_DIR
, 0770) < 0) {
146 if (errno
!= EEXIST
) {
147 ast_log(LOG_WARNING
, "Unable to create audio monitor directory: %s\n",
152 if (!(monitor
= ast_calloc(1, sizeof(*monitor
)))) {
153 UNLOCK_IF_NEEDED(chan
, need_lock
);
157 /* Determine file names */
158 if (!ast_strlen_zero(fname_base
)) {
159 int directory
= strchr(fname_base
, '/') ? 1 : 0;
160 const char *absolute
= *fname_base
== '/' ? "" : "/";
161 /* try creating the directory just in case it doesn't exist */
163 char *name
= strdup(fname_base
);
164 snprintf(tmp
, sizeof(tmp
), "mkdir -p \"%s\"",dirname(name
));
166 ast_safe_system(tmp
);
168 snprintf(monitor
->read_filename
, FILENAME_MAX
, "%s%s%s-in",
169 directory
? "" : ast_config_AST_MONITOR_DIR
, absolute
, fname_base
);
170 snprintf(monitor
->write_filename
, FILENAME_MAX
, "%s%s%s-out",
171 directory
? "" : ast_config_AST_MONITOR_DIR
, absolute
, fname_base
);
172 ast_copy_string(monitor
->filename_base
, fname_base
, sizeof(monitor
->filename_base
));
174 ast_mutex_lock(&monitorlock
);
175 snprintf(monitor
->read_filename
, FILENAME_MAX
, "%s/audio-in-%ld",
176 ast_config_AST_MONITOR_DIR
, seq
);
177 snprintf(monitor
->write_filename
, FILENAME_MAX
, "%s/audio-out-%ld",
178 ast_config_AST_MONITOR_DIR
, seq
);
180 ast_mutex_unlock(&monitorlock
);
182 channel_name
= ast_strdupa(chan
->name
);
183 while ((p
= strchr(channel_name
, '/'))) {
186 snprintf(monitor
->filename_base
, FILENAME_MAX
, "%s/%d-%s",
187 ast_config_AST_MONITOR_DIR
, (int)time(NULL
), channel_name
);
188 monitor
->filename_changed
= 1;
191 monitor
->stop
= ast_monitor_stop
;
193 /* Determine file format */
194 if (!ast_strlen_zero(format_spec
)) {
195 monitor
->format
= strdup(format_spec
);
197 monitor
->format
= strdup("wav");
201 if (ast_fileexists(monitor
->read_filename
, NULL
, NULL
) > 0) {
202 ast_filedelete(monitor
->read_filename
, NULL
);
204 if (!(monitor
->read_stream
= ast_writefile(monitor
->read_filename
,
205 monitor
->format
, NULL
,
206 O_CREAT
|O_TRUNC
|O_WRONLY
, 0, 0644))) {
207 ast_log(LOG_WARNING
, "Could not create file %s\n",
208 monitor
->read_filename
);
210 UNLOCK_IF_NEEDED(chan
, need_lock
);
213 if (ast_fileexists(monitor
->write_filename
, NULL
, NULL
) > 0) {
214 ast_filedelete(monitor
->write_filename
, NULL
);
216 if (!(monitor
->write_stream
= ast_writefile(monitor
->write_filename
,
217 monitor
->format
, NULL
,
218 O_CREAT
|O_TRUNC
|O_WRONLY
, 0, 0644))) {
219 ast_log(LOG_WARNING
, "Could not create file %s\n",
220 monitor
->write_filename
);
221 ast_closestream(monitor
->read_stream
);
223 UNLOCK_IF_NEEDED(chan
, need_lock
);
226 chan
->monitor
= monitor
;
227 ast_monitor_set_state(chan
, AST_MONITOR_RUNNING
);
228 /* so we know this call has been monitored in case we need to bill for it or something */
229 pbx_builtin_setvar_helper(chan
, "__MONITORED","true");
231 ast_log(LOG_DEBUG
,"Cannot start monitoring %s, already monitored\n",
236 UNLOCK_IF_NEEDED(chan
, need_lock
);
242 * The file format extensions that Asterisk uses are not all the same as that
243 * which soxmix expects. This function ensures that the format used as the
244 * extension on the filename is something soxmix will understand.
246 static const char *get_soxmix_format(const char *format
)
248 const char *res
= format
;
250 if (!strcasecmp(format
,"ulaw"))
252 if (!strcasecmp(format
,"alaw"))
258 /* Stop monitoring a channel */
259 int ast_monitor_stop(struct ast_channel
*chan
, int need_lock
)
263 LOCK_IF_NEEDED(chan
, need_lock
);
266 char filename
[ FILENAME_MAX
];
268 if (chan
->monitor
->read_stream
) {
269 ast_closestream(chan
->monitor
->read_stream
);
271 if (chan
->monitor
->write_stream
) {
272 ast_closestream(chan
->monitor
->write_stream
);
275 if (chan
->monitor
->filename_changed
&& !ast_strlen_zero(chan
->monitor
->filename_base
)) {
276 if (ast_fileexists(chan
->monitor
->read_filename
,NULL
,NULL
) > 0) {
277 snprintf(filename
, FILENAME_MAX
, "%s-in", chan
->monitor
->filename_base
);
278 if (ast_fileexists(filename
, NULL
, NULL
) > 0) {
279 ast_filedelete(filename
, NULL
);
281 ast_filerename(chan
->monitor
->read_filename
, filename
, chan
->monitor
->format
);
283 ast_log(LOG_WARNING
, "File %s not found\n", chan
->monitor
->read_filename
);
286 if (ast_fileexists(chan
->monitor
->write_filename
,NULL
,NULL
) > 0) {
287 snprintf(filename
, FILENAME_MAX
, "%s-out", chan
->monitor
->filename_base
);
288 if (ast_fileexists(filename
, NULL
, NULL
) > 0) {
289 ast_filedelete(filename
, NULL
);
291 ast_filerename(chan
->monitor
->write_filename
, filename
, chan
->monitor
->format
);
293 ast_log(LOG_WARNING
, "File %s not found\n", chan
->monitor
->write_filename
);
297 if (chan
->monitor
->joinfiles
&& !ast_strlen_zero(chan
->monitor
->filename_base
)) {
300 const char *format
= !strcasecmp(chan
->monitor
->format
,"wav49") ? "WAV" : chan
->monitor
->format
;
301 char *name
= chan
->monitor
->filename_base
;
302 int directory
= strchr(name
, '/') ? 1 : 0;
303 char *dir
= directory
? "" : ast_config_AST_MONITOR_DIR
;
304 const char *execute
, *execute_args
;
305 const char *absolute
= *name
== '/' ? "" : "/";
307 /* Set the execute application */
308 execute
= pbx_builtin_getvar_helper(chan
, "MONITOR_EXEC");
309 if (ast_strlen_zero(execute
)) {
311 execute
= "nice -n 19 soxmix";
313 execute
= "nice -n 19 sox -m";
315 format
= get_soxmix_format(format
);
318 execute_args
= pbx_builtin_getvar_helper(chan
, "MONITOR_EXEC_ARGS");
319 if (ast_strlen_zero(execute_args
)) {
323 snprintf(tmp
, sizeof(tmp
), "%s \"%s%s%s-in.%s\" \"%s%s%s-out.%s\" \"%s%s%s.%s\" %s &", execute
, dir
, absolute
, name
, format
, dir
, absolute
, name
, format
, dir
, absolute
, name
, format
,execute_args
);
325 snprintf(tmp2
,sizeof(tmp2
), "( %s& rm -f \"%s%s%s-\"* ) &",tmp
, dir
, absolute
, name
); /* remove legs when done mixing */
326 ast_copy_string(tmp
, tmp2
, sizeof(tmp
));
328 ast_log(LOG_DEBUG
,"monitor executing %s\n",tmp
);
329 if (ast_safe_system(tmp
) == -1)
330 ast_log(LOG_WARNING
, "Execute of %s failed.\n",tmp
);
333 free(chan
->monitor
->format
);
335 chan
->monitor
= NULL
;
338 UNLOCK_IF_NEEDED(chan
, need_lock
);
344 /* Pause monitoring of a channel */
345 int ast_monitor_pause(struct ast_channel
*chan
)
347 return ast_monitor_set_state(chan
, AST_MONITOR_PAUSED
);
350 /* Unpause monitoring of a channel */
351 int ast_monitor_unpause(struct ast_channel
*chan
)
353 return ast_monitor_set_state(chan
, AST_MONITOR_RUNNING
);
356 static int pause_monitor_exec(struct ast_channel
*chan
, void *data
)
358 return ast_monitor_pause(chan
);
361 static int unpause_monitor_exec(struct ast_channel
*chan
, void *data
)
363 return ast_monitor_unpause(chan
);
366 /* Change monitoring filename of a channel */
367 int ast_monitor_change_fname(struct ast_channel
*chan
, const char *fname_base
, int need_lock
)
370 if (ast_strlen_zero(fname_base
)) {
371 ast_log(LOG_WARNING
, "Cannot change monitor filename of channel %s to null\n", chan
->name
);
375 LOCK_IF_NEEDED(chan
, need_lock
);
378 int directory
= strchr(fname_base
, '/') ? 1 : 0;
379 const char *absolute
= *fname_base
== '/' ? "" : "/";
380 char tmpstring
[sizeof(chan
->monitor
->filename_base
)] = "";
382 /* before continuing, see if we're trying to rename the file to itself... */
383 snprintf(tmpstring
, sizeof(tmpstring
), "%s%s%s", directory
? "" : ast_config_AST_MONITOR_DIR
, absolute
, fname_base
);
384 if (!strcmp(tmpstring
, chan
->monitor
->filename_base
)) {
385 if (option_debug
> 2)
386 ast_log(LOG_DEBUG
, "No need to rename monitor filename to itself\n");
387 UNLOCK_IF_NEEDED(chan
, need_lock
);
391 /* try creating the directory just in case it doesn't exist */
393 char *name
= strdup(fname_base
);
394 snprintf(tmp
, sizeof(tmp
), "mkdir -p %s",dirname(name
));
396 ast_safe_system(tmp
);
399 ast_copy_string(chan
->monitor
->filename_base
, tmpstring
, sizeof(chan
->monitor
->filename_base
));
400 chan
->monitor
->filename_changed
= 1;
402 ast_log(LOG_WARNING
, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan
->name
, fname_base
);
405 UNLOCK_IF_NEEDED(chan
, need_lock
);
410 static int start_monitor_exec(struct ast_channel
*chan
, void *data
)
414 char *fname_base
= NULL
;
415 char *options
= NULL
;
417 char *urlprefix
= NULL
;
420 int waitforbridge
= 0;
423 /* Parse arguments. */
424 if (!ast_strlen_zero((char*)data
)) {
425 arg
= ast_strdupa((char*)data
);
427 fname_base
= strchr(arg
, '|');
431 if ((options
= strchr(fname_base
, '|'))) {
434 if (strchr(options
, 'm'))
436 if (strchr(options
, 'b'))
440 arg
= strchr(format
,':');
447 snprintf(tmp
,sizeof(tmp
) - 1,"%s/%s.%s",urlprefix
,fname_base
,
448 ((strcmp(format
,"gsm")) ? "wav" : "gsm"));
449 if (!chan
->cdr
&& !(chan
->cdr
= ast_cdr_alloc()))
451 ast_cdr_setuserfield(chan
, tmp
);
454 /* We must remove the "b" option if listed. In principle none of
455 the following could give NULL results, but we check just to
456 be pedantic. Reconstructing with checks for 'm' option does not
457 work if we end up adding more options than 'm' in the future. */
458 delay
= ast_strdupa(data
);
459 options
= strrchr(delay
, '|');
461 arg
= strchr(options
, 'b');
464 pbx_builtin_setvar_helper(chan
,"AUTO_MONITOR",delay
);
470 res
= ast_monitor_start(chan
, format
, fname_base
, 1);
472 res
= ast_monitor_change_fname(chan
, fname_base
, 1);
473 ast_monitor_setjoinfiles(chan
, joinfiles
);
478 static int stop_monitor_exec(struct ast_channel
*chan
, void *data
)
480 return ast_monitor_stop(chan
, 1);
483 static int change_monitor_exec(struct ast_channel
*chan
, void *data
)
485 return ast_monitor_change_fname(chan
, (const char*)data
, 1);
488 static char start_monitor_action_help
[] =
489 "Description: The 'Monitor' action may be used to record the audio on a\n"
490 " specified channel. The following parameters may be used to control\n"
492 " Channel - Required. Used to specify the channel to record.\n"
493 " File - Optional. Is the name of the file created in the\n"
494 " monitor spool directory. Defaults to the same name\n"
495 " as the channel (with slashes replaced with dashes).\n"
496 " Format - Optional. Is the audio recording format. Defaults\n"
498 " Mix - Optional. Boolean parameter as to whether to mix\n"
499 " the input and output channels together after the\n"
500 " recording is finished.\n";
502 static int start_monitor_action(struct mansession
*s
, const struct message
*m
)
504 struct ast_channel
*c
= NULL
;
505 const char *name
= astman_get_header(m
, "Channel");
506 const char *fname
= astman_get_header(m
, "File");
507 const char *format
= astman_get_header(m
, "Format");
508 const char *mix
= astman_get_header(m
, "Mix");
511 if (ast_strlen_zero(name
)) {
512 astman_send_error(s
, m
, "No channel specified");
515 c
= ast_get_channel_by_name_locked(name
);
517 astman_send_error(s
, m
, "No such channel");
521 if (ast_strlen_zero(fname
)) {
522 /* No filename base specified, default to channel name as per CLI */
523 if (!(fname
= ast_strdup(c
->name
))) {
524 astman_send_error(s
, m
, "Could not start monitoring channel");
525 ast_channel_unlock(c
);
528 /* Channels have the format technology/channel_name - have to replace that / */
529 if ((d
= strchr(fname
, '/')))
533 if (ast_monitor_start(c
, format
, fname
, 1)) {
534 if (ast_monitor_change_fname(c
, fname
, 1)) {
535 astman_send_error(s
, m
, "Could not start monitoring channel");
536 ast_channel_unlock(c
);
542 ast_monitor_setjoinfiles(c
, 1);
545 ast_channel_unlock(c
);
546 astman_send_ack(s
, m
, "Started monitoring channel");
550 static char stop_monitor_action_help
[] =
551 "Description: The 'StopMonitor' action may be used to end a previously\n"
552 " started 'Monitor' action. The only parameter is 'Channel', the name\n"
553 " of the channel monitored.\n";
555 static int stop_monitor_action(struct mansession
*s
, const struct message
*m
)
557 struct ast_channel
*c
= NULL
;
558 const char *name
= astman_get_header(m
, "Channel");
560 if (ast_strlen_zero(name
)) {
561 astman_send_error(s
, m
, "No channel specified");
564 c
= ast_get_channel_by_name_locked(name
);
566 astman_send_error(s
, m
, "No such channel");
569 res
= ast_monitor_stop(c
, 1);
570 ast_channel_unlock(c
);
572 astman_send_error(s
, m
, "Could not stop monitoring channel");
575 astman_send_ack(s
, m
, "Stopped monitoring channel");
579 static char change_monitor_action_help
[] =
580 "Description: The 'ChangeMonitor' action may be used to change the file\n"
581 " started by a previous 'Monitor' action. The following parameters may\n"
582 " be used to control this:\n"
583 " Channel - Required. Used to specify the channel to record.\n"
584 " File - Required. Is the new name of the file created in the\n"
585 " monitor spool directory.\n";
587 static int change_monitor_action(struct mansession
*s
, const struct message
*m
)
589 struct ast_channel
*c
= NULL
;
590 const char *name
= astman_get_header(m
, "Channel");
591 const char *fname
= astman_get_header(m
, "File");
592 if (ast_strlen_zero(name
)) {
593 astman_send_error(s
, m
, "No channel specified");
596 if (ast_strlen_zero(fname
)) {
597 astman_send_error(s
, m
, "No filename specified");
600 c
= ast_get_channel_by_name_locked(name
);
602 astman_send_error(s
, m
, "No such channel");
605 if (ast_monitor_change_fname(c
, fname
, 1)) {
606 astman_send_error(s
, m
, "Could not change monitored filename of channel");
607 ast_channel_unlock(c
);
610 ast_channel_unlock(c
);
611 astman_send_ack(s
, m
, "Changed monitor filename");
615 void ast_monitor_setjoinfiles(struct ast_channel
*chan
, int turnon
)
618 chan
->monitor
->joinfiles
= turnon
;
621 #define IS_NULL_STRING(string) ((!(string)) || (ast_strlen_zero((string))))
623 enum MONITOR_PAUSING_ACTION
625 MONITOR_ACTION_PAUSE
,
626 MONITOR_ACTION_UNPAUSE
629 static int do_pause_or_unpause(struct mansession
*s
, const struct message
*m
, int action
)
631 struct ast_channel
*c
= NULL
;
632 const char *name
= astman_get_header(m
, "Channel");
634 if (IS_NULL_STRING(name
)) {
635 astman_send_error(s
, m
, "No channel specified");
639 c
= ast_get_channel_by_name_locked(name
);
641 astman_send_error(s
, m
, "No such channel");
645 if (action
== MONITOR_ACTION_PAUSE
)
646 ast_monitor_pause(c
);
648 ast_monitor_unpause(c
);
650 ast_channel_unlock(c
);
651 astman_send_ack(s
, m
, (action
== MONITOR_ACTION_PAUSE
? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
655 static char pause_monitor_action_help
[] =
656 "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
657 " recording of a channel. The following parameters may\n"
658 " be used to control this:\n"
659 " Channel - Required. Used to specify the channel to record.\n";
661 static int pause_monitor_action(struct mansession
*s
, const struct message
*m
)
663 return do_pause_or_unpause(s
, m
, MONITOR_ACTION_PAUSE
);
666 static char unpause_monitor_action_help
[] =
667 "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
668 " of a channel after calling PauseMonitor. The following parameters may\n"
669 " be used to control this:\n"
670 " Channel - Required. Used to specify the channel to record.\n";
672 static int unpause_monitor_action(struct mansession
*s
, const struct message
*m
)
674 return do_pause_or_unpause(s
, m
, MONITOR_ACTION_UNPAUSE
);
678 static int load_module(void)
680 ast_register_application("Monitor", start_monitor_exec
, monitor_synopsis
, monitor_descrip
);
681 ast_register_application("StopMonitor", stop_monitor_exec
, stopmonitor_synopsis
, stopmonitor_descrip
);
682 ast_register_application("ChangeMonitor", change_monitor_exec
, changemonitor_synopsis
, changemonitor_descrip
);
683 ast_register_application("PauseMonitor", pause_monitor_exec
, pausemonitor_synopsis
, pausemonitor_descrip
);
684 ast_register_application("UnpauseMonitor", unpause_monitor_exec
, unpausemonitor_synopsis
, unpausemonitor_descrip
);
685 ast_manager_register2("Monitor", EVENT_FLAG_CALL
, start_monitor_action
, monitor_synopsis
, start_monitor_action_help
);
686 ast_manager_register2("StopMonitor", EVENT_FLAG_CALL
, stop_monitor_action
, stopmonitor_synopsis
, stop_monitor_action_help
);
687 ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL
, change_monitor_action
, changemonitor_synopsis
, change_monitor_action_help
);
688 ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL
, pause_monitor_action
, pausemonitor_synopsis
, pause_monitor_action_help
);
689 ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL
, unpause_monitor_action
, unpausemonitor_synopsis
, unpause_monitor_action_help
);
694 static int unload_module(void)
696 ast_unregister_application("Monitor");
697 ast_unregister_application("StopMonitor");
698 ast_unregister_application("ChangeMonitor");
699 ast_unregister_application("PauseMonitor");
700 ast_unregister_application("UnpauseMonitor");
701 ast_manager_unregister("Monitor");
702 ast_manager_unregister("StopMonitor");
703 ast_manager_unregister("ChangeMonitor");
704 ast_manager_unregister("PauseMonitor");
705 ast_manager_unregister("UnpauseMonitor");
710 /* usecount semantics need to be defined */
711 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Call Monitoring Resource",
713 .unload
= unload_module
,