Let's also include aclocal.m4
[asterisk-bristuff.git] / res / res_monitor.c
blob4baa1580debb93a2d103133b19ca3456ddca876e
1 /*
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.
19 /*! \file
21 * \brief PBX channel monitoring
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <libgen.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 { \
55 if (needed) \
56 ast_channel_lock(lock); \
57 } while(0)
59 #define UNLOCK_IF_NEEDED(lock, needed) do { \
60 if (needed) \
61 ast_channel_unlock(lock); \
62 } while (0)
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"
74 " options:\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"
78 #ifdef HAVE_SOXMIX
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"
81 #else
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"
84 #endif
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"
91 "\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);
124 return -1;
126 chan->monitor->state = state;
127 UNLOCK_IF_NEEDED(chan, 1);
128 return 0;
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)
135 int res = 0;
136 char tmp[256];
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",
148 strerror(errno));
152 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
153 UNLOCK_IF_NEEDED(chan, need_lock);
154 return -1;
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 */
162 if (directory) {
163 char *name = strdup(fname_base);
164 snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name));
165 free(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));
173 } else {
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);
179 seq++;
180 ast_mutex_unlock(&monitorlock);
182 channel_name = ast_strdupa(chan->name);
183 while ((p = strchr(channel_name, '/'))) {
184 *p = '-';
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);
196 } else {
197 monitor->format = strdup("wav");
200 /* open files */
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);
209 free(monitor);
210 UNLOCK_IF_NEEDED(chan, need_lock);
211 return -1;
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);
222 free(monitor);
223 UNLOCK_IF_NEEDED(chan, need_lock);
224 return -1;
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");
230 } else {
231 ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
232 chan->name);
233 res = -1;
236 UNLOCK_IF_NEEDED(chan, need_lock);
238 return res;
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"))
251 res = "ul";
252 if (!strcasecmp(format,"alaw"))
253 res = "al";
255 return res;
258 /* Stop monitoring a channel */
259 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
261 int delfiles = 0;
263 LOCK_IF_NEEDED(chan, need_lock);
265 if (chan->monitor) {
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);
282 } else {
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);
292 } else {
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)) {
298 char tmp[1024];
299 char tmp2[1024];
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)) {
310 #ifdef HAVE_SOXMIX
311 execute = "nice -n 19 soxmix";
312 #else
313 execute = "nice -n 19 sox -m";
314 #endif
315 format = get_soxmix_format(format);
316 delfiles = 1;
318 execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
319 if (ast_strlen_zero(execute_args)) {
320 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);
324 if (delfiles) {
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);
334 free(chan->monitor);
335 chan->monitor = NULL;
338 UNLOCK_IF_NEEDED(chan, need_lock);
340 return 0;
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)
369 char tmp[256];
370 if (ast_strlen_zero(fname_base)) {
371 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
372 return -1;
375 LOCK_IF_NEEDED(chan, need_lock);
377 if (chan->monitor) {
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);
388 return 0;
391 /* try creating the directory just in case it doesn't exist */
392 if (directory) {
393 char *name = strdup(fname_base);
394 snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
395 free(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;
401 } else {
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);
407 return 0;
410 static int start_monitor_exec(struct ast_channel *chan, void *data)
412 char *arg = NULL;
413 char *format = NULL;
414 char *fname_base = NULL;
415 char *options = NULL;
416 char *delay = NULL;
417 char *urlprefix = NULL;
418 char tmp[256];
419 int joinfiles = 0;
420 int waitforbridge = 0;
421 int res = 0;
423 /* Parse arguments. */
424 if (!ast_strlen_zero((char*)data)) {
425 arg = ast_strdupa((char*)data);
426 format = arg;
427 fname_base = strchr(arg, '|');
428 if (fname_base) {
429 *fname_base = 0;
430 fname_base++;
431 if ((options = strchr(fname_base, '|'))) {
432 *options = 0;
433 options++;
434 if (strchr(options, 'm'))
435 joinfiles = 1;
436 if (strchr(options, 'b'))
437 waitforbridge = 1;
440 arg = strchr(format,':');
441 if (arg) {
442 *arg++ = 0;
443 urlprefix = arg;
446 if (urlprefix) {
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()))
450 return -1;
451 ast_cdr_setuserfield(chan, tmp);
453 if (waitforbridge) {
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, '|');
460 if (options) {
461 arg = strchr(options, 'b');
462 if (arg) {
463 *arg = 'X';
464 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
467 return 0;
470 res = ast_monitor_start(chan, format, fname_base, 1);
471 if (res < 0)
472 res = ast_monitor_change_fname(chan, fname_base, 1);
473 ast_monitor_setjoinfiles(chan, joinfiles);
475 return res;
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"
491 " this:\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"
497 " to \"wav\".\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");
509 char *d;
511 if (ast_strlen_zero(name)) {
512 astman_send_error(s, m, "No channel specified");
513 return 0;
515 c = ast_get_channel_by_name_locked(name);
516 if (!c) {
517 astman_send_error(s, m, "No such channel");
518 return 0;
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);
526 return 0;
528 /* Channels have the format technology/channel_name - have to replace that / */
529 if ((d = strchr(fname, '/')))
530 *d = '-';
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);
537 return 0;
541 if (ast_true(mix)) {
542 ast_monitor_setjoinfiles(c, 1);
545 ast_channel_unlock(c);
546 astman_send_ack(s, m, "Started monitoring channel");
547 return 0;
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");
559 int res;
560 if (ast_strlen_zero(name)) {
561 astman_send_error(s, m, "No channel specified");
562 return 0;
564 c = ast_get_channel_by_name_locked(name);
565 if (!c) {
566 astman_send_error(s, m, "No such channel");
567 return 0;
569 res = ast_monitor_stop(c, 1);
570 ast_channel_unlock(c);
571 if (res) {
572 astman_send_error(s, m, "Could not stop monitoring channel");
573 return 0;
575 astman_send_ack(s, m, "Stopped monitoring channel");
576 return 0;
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");
594 return 0;
596 if (ast_strlen_zero(fname)) {
597 astman_send_error(s, m, "No filename specified");
598 return 0;
600 c = ast_get_channel_by_name_locked(name);
601 if (!c) {
602 astman_send_error(s, m, "No such channel");
603 return 0;
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);
608 return 0;
610 ast_channel_unlock(c);
611 astman_send_ack(s, m, "Changed monitor filename");
612 return 0;
615 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
617 if (chan->monitor)
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");
636 return -1;
639 c = ast_get_channel_by_name_locked(name);
640 if (!c) {
641 astman_send_error(s, m, "No such channel");
642 return -1;
645 if (action == MONITOR_ACTION_PAUSE)
646 ast_monitor_pause(c);
647 else
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"));
652 return 0;
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);
691 return 0;
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");
707 return 0;
710 /* usecount semantics need to be defined */
711 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
712 .load = load_module,
713 .unload = unload_module,