Merge my applicationmap_fixup branch to address the issues described in this
[asterisk/asterisk.git] / res / res_monitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief PBX channel monitoring
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25  
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
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>
37
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
51 AST_MUTEX_DEFINE_STATIC(monitorlock);
52
53 #define LOCK_IF_NEEDED(lock, needed) do { \
54         if (needed) \
55                 ast_channel_lock(lock); \
56         } while(0)
57
58 #define UNLOCK_IF_NEEDED(lock, needed) do { \
59         if (needed) \
60                 ast_channel_unlock(lock); \
61         } while (0)
62
63 static unsigned long seq = 0;
64
65 static char *monitor_synopsis = "Monitor a channel";
66
67 static char *monitor_descrip = "Monitor([file_format[:urlbase]|[fname_base]|[options]]):\n"
68 "Used to start monitoring a channel. The channel's input and output\n"
69 "voice packets are logged to files until the channel hangs up or\n"
70 "monitoring is stopped by the StopMonitor application.\n"
71 "  file_format          optional, if not set, defaults to \"wav\"\n"
72 "  fname_base           if set, changes the filename used to the one specified.\n"
73 "  options:\n"
74 "    m   - when the recording ends mix the two leg files into one and\n"
75 "          delete the two leg files.  If the variable MONITOR_EXEC is set, the\n"
76 "          application referenced in it will be executed instead of\n"
77 "          soxmix and the raw leg files will NOT be deleted automatically.\n"
78 "          soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
79 "          and a target mixed file name which is the same as the leg file names\n"
80 "          only without the in/out designator.\n"
81 "          If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
82 "          additional arguements to MONITOR_EXEC\n"
83 "          Both MONITOR_EXEC and the Mix flag can be set from the\n"
84 "          administrator interface\n"
85 "\n"
86 "    b   - Don't begin recording unless a call is bridged to another channel\n"
87 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
88 "monitored, otherwise 0.\n"
89 ;
90
91 static char *stopmonitor_synopsis = "Stop monitoring a channel";
92
93 static char *stopmonitor_descrip = "StopMonitor\n"
94         "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
95
96 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
97
98 static char *changemonitor_descrip = "ChangeMonitor(filename_base)\n"
99         "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
100         "The argument is the new filename base to use for monitoring this channel.\n";
101
102 static char *pausemonitor_synopsis = "Pause monitoring of a channel";
103
104 static char *pausemonitor_descrip = "PauseMonitor\n"
105         "Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.\n";
106
107 static char *unpausemonitor_synopsis = "Unpause monitoring of a channel";
108
109 static char *unpausemonitor_descrip = "UnpauseMonitor\n"
110         "Unpauses monitoring of a channel on which monitoring had\n"
111         "previously been paused with PauseMonitor.\n";
112
113 static int ast_monitor_set_state(struct ast_channel *chan, int state)
114 {
115         LOCK_IF_NEEDED(chan, 1);
116         if (!chan->monitor) {
117                 UNLOCK_IF_NEEDED(chan, 1);
118                 return -1;
119         }
120         chan->monitor->state = state;
121         UNLOCK_IF_NEEDED(chan, 1);
122         return 0;
123 }
124
125 /* Start monitoring a channel */
126 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
127                 const char *fname_base, int need_lock)
128 {
129         int res = 0;
130         char tmp[256];
131
132         LOCK_IF_NEEDED(chan, need_lock);
133
134         if (!(chan->monitor)) {
135                 struct ast_channel_monitor *monitor;
136                 char *channel_name, *p;
137
138                 /* Create monitoring directory if needed */
139                 if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) {
140                         if (errno != EEXIST) {
141                                 ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
142                                         strerror(errno));
143                         }
144                 }
145
146                 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
147                         UNLOCK_IF_NEEDED(chan, need_lock);
148                         return -1;
149                 }
150
151                 /* Determine file names */
152                 if (!ast_strlen_zero(fname_base)) {
153                         int directory = strchr(fname_base, '/') ? 1 : 0;
154                         /* try creating the directory just in case it doesn't exist */
155                         if (directory) {
156                                 char *name = strdup(fname_base);
157                                 snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name));
158                                 free(name);
159                                 ast_safe_system(tmp);
160                         }
161                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
162                                                 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
163                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
164                                                 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
165                         ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
166                 } else {
167                         ast_mutex_lock(&monitorlock);
168                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
169                                                 ast_config_AST_MONITOR_DIR, seq);
170                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
171                                                 ast_config_AST_MONITOR_DIR, seq);
172                         seq++;
173                         ast_mutex_unlock(&monitorlock);
174
175                         channel_name = ast_strdupa(chan->name);
176                         while ((p = strchr(channel_name, '/'))) {
177                                 *p = '-';
178                         }
179                         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
180                                          ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
181                         monitor->filename_changed = 1;
182                 }
183
184                 monitor->stop = ast_monitor_stop;
185
186                 /* Determine file format */
187                 if (!ast_strlen_zero(format_spec)) {
188                         monitor->format = strdup(format_spec);
189                 } else {
190                         monitor->format = strdup("wav");
191                 }
192                 
193                 /* open files */
194                 if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) {
195                         ast_filedelete(monitor->read_filename, NULL);
196                 }
197                 if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
198                                                 monitor->format, NULL,
199                                                 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
200                         ast_log(LOG_WARNING, "Could not create file %s\n",
201                                                 monitor->read_filename);
202                         free(monitor);
203                         UNLOCK_IF_NEEDED(chan, need_lock);
204                         return -1;
205                 }
206                 if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
207                         ast_filedelete(monitor->write_filename, NULL);
208                 }
209                 if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
210                                                 monitor->format, NULL,
211                                                 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
212                         ast_log(LOG_WARNING, "Could not create file %s\n",
213                                                 monitor->write_filename);
214                         ast_closestream(monitor->read_stream);
215                         free(monitor);
216                         UNLOCK_IF_NEEDED(chan, need_lock);
217                         return -1;
218                 }
219                 chan->monitor = monitor;
220                 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
221                 /* so we know this call has been monitored in case we need to bill for it or something */
222                 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
223         } else {
224                 ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
225                                         chan->name);
226                 res = -1;
227         }
228
229         UNLOCK_IF_NEEDED(chan, need_lock);
230
231         return res;
232 }
233
234 /* Stop monitoring a channel */
235 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
236 {
237         int delfiles = 0;
238
239         LOCK_IF_NEEDED(chan, need_lock);
240
241         if (chan->monitor) {
242                 char filename[ FILENAME_MAX ];
243
244                 if (chan->monitor->read_stream) {
245                         ast_closestream(chan->monitor->read_stream);
246                 }
247                 if (chan->monitor->write_stream) {
248                         ast_closestream(chan->monitor->write_stream);
249                 }
250
251                 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
252                         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
253                                 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
254                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
255                                         ast_filedelete(filename, NULL);
256                                 }
257                                 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
258                         } else {
259                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
260                         }
261
262                         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
263                                 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
264                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
265                                         ast_filedelete(filename, NULL);
266                                 }
267                                 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
268                         } else {
269                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
270                         }
271                 }
272
273                 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
274                         char tmp[1024];
275                         char tmp2[1024];
276                         char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
277                         char *name = chan->monitor->filename_base;
278                         int directory = strchr(name, '/') ? 1 : 0;
279                         char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
280                         const char *execute, *execute_args;
281
282                         /* Set the execute application */
283                         execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
284                         if (ast_strlen_zero(execute)) { 
285                                 execute = "nice -n 19 soxmix";
286                                 delfiles = 1;
287                         } 
288                         execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
289                         if (ast_strlen_zero(execute_args)) {
290                                 execute_args = "";
291                         }
292                         
293                         snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
294                         if (delfiles) {
295                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
296                                 ast_copy_string(tmp, tmp2, sizeof(tmp));
297                         }
298                         ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
299                         if (ast_safe_system(tmp) == -1)
300                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
301                 }
302                 
303                 free(chan->monitor->format);
304                 free(chan->monitor);
305                 chan->monitor = NULL;
306         }
307
308         UNLOCK_IF_NEEDED(chan, need_lock);
309
310         return 0;
311 }
312
313
314 /* Pause monitoring of a channel */
315 int ast_monitor_pause(struct ast_channel *chan)
316 {
317         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
318 }
319
320 /* Unpause monitoring of a channel */
321 int ast_monitor_unpause(struct ast_channel *chan)
322 {
323         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
324 }
325
326 int pause_monitor_exec(struct ast_channel *chan, void *data)
327 {
328         return ast_monitor_pause(chan);
329 }
330
331 int unpause_monitor_exec(struct ast_channel *chan, void *data)
332 {
333         return ast_monitor_unpause(chan);
334 }
335
336 /* Change monitoring filename of a channel */
337 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
338 {
339         char tmp[256];
340         if (ast_strlen_zero(fname_base)) {
341                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null", chan->name);
342                 return -1;
343         }
344
345         LOCK_IF_NEEDED(chan, need_lock);
346
347         if (chan->monitor) {
348                 int directory = strchr(fname_base, '/') ? 1 : 0;
349                 /* try creating the directory just in case it doesn't exist */
350                 if (directory) {
351                         char *name = strdup(fname_base);
352                         snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
353                         free(name);
354                         ast_safe_system(tmp);
355                 }
356
357                 snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
358         } else {
359                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
360         }
361
362         UNLOCK_IF_NEEDED(chan, need_lock);
363
364         return 0;
365 }
366
367 static int start_monitor_exec(struct ast_channel *chan, void *data)
368 {
369         char *arg = NULL;
370         char *format = NULL;
371         char *fname_base = NULL;
372         char *options = NULL;
373         char *delay = NULL;
374         char *urlprefix = NULL;
375         char tmp[256];
376         int joinfiles = 0;
377         int waitforbridge = 0;
378         int res = 0;
379         
380         /* Parse arguments. */
381         if (!ast_strlen_zero((char*)data)) {
382                 arg = ast_strdupa((char*)data);
383                 format = arg;
384                 fname_base = strchr(arg, '|');
385                 if (fname_base) {
386                         *fname_base = 0;
387                         fname_base++;
388                         if ((options = strchr(fname_base, '|'))) {
389                                 *options = 0;
390                                 options++;
391                                 if (strchr(options, 'm'))
392                                         joinfiles = 1;
393                                 if (strchr(options, 'b'))
394                                         waitforbridge = 1;
395                         }
396                 }
397                 arg = strchr(format,':');
398                 if (arg) {
399                         *arg++ = 0;
400                         urlprefix = arg;
401                 }
402         }
403         if (urlprefix) {
404                 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
405                         ((strcmp(format,"gsm")) ? "wav" : "gsm"));
406                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
407                         return -1;
408                 ast_cdr_setuserfield(chan, tmp);
409         }
410         if (waitforbridge) {
411                 /* We must remove the "b" option if listed.  In principle none of
412                    the following could give NULL results, but we check just to
413                    be pedantic. Reconstructing with checks for 'm' option does not
414                    work if we end up adding more options than 'm' in the future. */
415                 delay = ast_strdupa(data);
416                 options = strrchr(delay, '|');
417                 if (options) {
418                         arg = strchr(options, 'b');
419                         if (arg) {
420                                 *arg = 'X';
421                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
422                         }
423                 }
424                 return 0;
425         }
426
427         res = ast_monitor_start(chan, format, fname_base, 1);
428         if (res < 0)
429                 res = ast_monitor_change_fname(chan, fname_base, 1);
430         ast_monitor_setjoinfiles(chan, joinfiles);
431
432         return res;
433 }
434
435 static int stop_monitor_exec(struct ast_channel *chan, void *data)
436 {
437         return ast_monitor_stop(chan, 1);
438 }
439
440 static int change_monitor_exec(struct ast_channel *chan, void *data)
441 {
442         return ast_monitor_change_fname(chan, (const char*)data, 1);
443 }
444
445 static char start_monitor_action_help[] =
446 "Description: The 'Monitor' action may be used to record the audio on a\n"
447 "  specified channel.  The following parameters may be used to control\n"
448 "  this:\n"
449 "  Channel     - Required.  Used to specify the channel to record.\n"
450 "  File        - Optional.  Is the name of the file created in the\n"
451 "                monitor spool directory.  Defaults to the same name\n"
452 "                as the channel (with slashes replaced with dashes).\n"
453 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
454 "                to \"wav\".\n"
455 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
456 "                the input and output channels together after the\n"
457 "                recording is finished.\n";
458
459 static int start_monitor_action(struct mansession *s, struct message *m)
460 {
461         struct ast_channel *c = NULL;
462         char *name = astman_get_header(m, "Channel");
463         char *fname = astman_get_header(m, "File");
464         char *format = astman_get_header(m, "Format");
465         char *mix = astman_get_header(m, "Mix");
466         char *d;
467         
468         if (ast_strlen_zero(name)) {
469                 astman_send_error(s, m, "No channel specified");
470                 return 0;
471         }
472         c = ast_get_channel_by_name_locked(name);
473         if (!c) {
474                 astman_send_error(s, m, "No such channel");
475                 return 0;
476         }
477
478         if (ast_strlen_zero(fname)) {
479                 /* No filename base specified, default to channel name as per CLI */            
480                 if (!(fname = ast_strdup(c->name))) {
481                         astman_send_error(s, m, "Could not start monitoring channel");
482                         ast_channel_unlock(c);
483                         return 0;
484                 }
485                 /* Channels have the format technology/channel_name - have to replace that /  */
486                 if ((d = strchr(fname, '/'))) 
487                         *d = '-';
488         }
489         
490         if (ast_monitor_start(c, format, fname, 1)) {
491                 if (ast_monitor_change_fname(c, fname, 1)) {
492                         astman_send_error(s, m, "Could not start monitoring channel");
493                         ast_channel_unlock(c);
494                         return 0;
495                 }
496         }
497
498         if (ast_true(mix)) {
499                 ast_monitor_setjoinfiles(c, 1);
500         }
501
502         ast_channel_unlock(c);
503         astman_send_ack(s, m, "Started monitoring channel");
504         return 0;
505 }
506
507 static char stop_monitor_action_help[] =
508 "Description: The 'StopMonitor' action may be used to end a previously\n"
509 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
510 "  of the channel monitored.\n";
511
512 static int stop_monitor_action(struct mansession *s, struct message *m)
513 {
514         struct ast_channel *c = NULL;
515         char *name = astman_get_header(m, "Channel");
516         int res;
517         if (ast_strlen_zero(name)) {
518                 astman_send_error(s, m, "No channel specified");
519                 return 0;
520         }
521         c = ast_get_channel_by_name_locked(name);
522         if (!c) {
523                 astman_send_error(s, m, "No such channel");
524                 return 0;
525         }
526         res = ast_monitor_stop(c, 1);
527         ast_channel_unlock(c);
528         if (res) {
529                 astman_send_error(s, m, "Could not stop monitoring channel");
530                 return 0;
531         }
532         astman_send_ack(s, m, "Stopped monitoring channel");
533         return 0;
534 }
535
536 static char change_monitor_action_help[] =
537 "Description: The 'ChangeMonitor' action may be used to change the file\n"
538 "  started by a previous 'Monitor' action.  The following parameters may\n"
539 "  be used to control this:\n"
540 "  Channel     - Required.  Used to specify the channel to record.\n"
541 "  File        - Required.  Is the new name of the file created in the\n"
542 "                monitor spool directory.\n";
543
544 static int change_monitor_action(struct mansession *s, struct message *m)
545 {
546         struct ast_channel *c = NULL;
547         char *name = astman_get_header(m, "Channel");
548         char *fname = astman_get_header(m, "File");
549         if (ast_strlen_zero(name)) {
550                 astman_send_error(s, m, "No channel specified");
551                 return 0;
552         }
553         if (ast_strlen_zero(fname)) {
554                 astman_send_error(s, m, "No filename specified");
555                 return 0;
556         }
557         c = ast_get_channel_by_name_locked(name);
558         if (!c) {
559                 astman_send_error(s, m, "No such channel");
560                 return 0;
561         }
562         if (ast_monitor_change_fname(c, fname, 1)) {
563                 astman_send_error(s, m, "Could not change monitored filename of channel");
564                 ast_channel_unlock(c);
565                 return 0;
566         }
567         ast_channel_unlock(c);
568         astman_send_ack(s, m, "Changed monitor filename");
569         return 0;
570 }
571
572 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
573 {
574         if (chan->monitor)
575                 chan->monitor->joinfiles = turnon;
576 }
577
578 #define IS_NULL_STRING(string) ((!(string)) || (ast_strlen_zero((string))))
579
580 enum MONITOR_PAUSING_ACTION
581 {
582         MONITOR_ACTION_PAUSE,
583         MONITOR_ACTION_UNPAUSE
584 };
585           
586 static int do_pause_or_unpause(struct mansession *s, struct message *m, int action)
587 {
588         struct ast_channel *c = NULL;
589         char *name = astman_get_header(m, "Channel");
590         
591         if (IS_NULL_STRING(name)) {
592                 astman_send_error(s, m, "No channel specified");
593                 return -1;
594         }
595         
596         c = ast_get_channel_by_name_locked(name);
597         if (!c) {
598                 astman_send_error(s, m, "No such channel");
599                 return -1;
600         }
601
602         if (action == MONITOR_ACTION_PAUSE)
603                 ast_monitor_pause(c);
604         else
605                 ast_monitor_unpause(c);
606         
607         ast_channel_unlock(c);
608         astman_send_ack(s, m, "Paused monitoring of the channel");
609         return 0;       
610 }
611
612 static char pause_monitor_action_help[] =
613         "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
614         " recording of a channel.  The following parameters may\n"
615         " be used to control this:\n"
616         "  Channel     - Required.  Used to specify the channel to record.\n";
617
618 static int pause_monitor_action(struct mansession *s, struct message *m)
619 {
620         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
621 }
622
623 static char unpause_monitor_action_help[] =
624         "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
625         "  of a channel after calling PauseMonitor.  The following parameters may\n"
626         "  be used to control this:\n"
627         "  Channel     - Required.  Used to specify the channel to record.\n";
628
629 static int unpause_monitor_action(struct mansession *s, struct message *m)
630 {
631         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
632 }
633         
634
635 static int load_module(void *mod)
636 {
637         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
638         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
639         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
640         ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
641         ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
642         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
643         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
644         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
645         ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
646         ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
647
648         return 0;
649 }
650
651 static int unload_module(void *mod)
652 {
653         ast_unregister_application("Monitor");
654         ast_unregister_application("StopMonitor");
655         ast_unregister_application("ChangeMonitor");
656         ast_unregister_application("PauseMonitor");
657         ast_unregister_application("UnpauseMonitor");
658         ast_manager_unregister("Monitor");
659         ast_manager_unregister("StopMonitor");
660         ast_manager_unregister("ChangeMonitor");
661         ast_unregister_application("PauseMonitor");
662         ast_unregister_application("UnpauseMonitor");
663
664         return 0;
665 }
666
667 static const char *description(void)
668 {
669         return "Call Monitoring Resource";
670 }
671
672 static const char *key(void)
673 {
674         return ASTERISK_GPL_KEY;
675 }
676
677 STD_MOD(MOD_0 | NO_USECOUNT | NO_UNLOAD, NULL, NULL, NULL);     /* MOD_0 because it exports some symbols */