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