Merged revisions 49742 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, AST_FILE_MODE))) {
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, AST_FILE_MODE))) {
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 /*
237  * The file format extensions that Asterisk uses are not all the same as that
238  * which soxmix expects.  This function ensures that the format used as the
239  * extension on the filename is something soxmix will understand.
240  */
241 static const char *get_soxmix_format(const char *format)
242 {
243         const char *res = format;
244
245         if (!strcasecmp(format,"ulaw"))
246                 res = "ul";
247         if (!strcasecmp(format,"alaw"))
248                 res = "al";
249         
250         return res;
251 }
252
253 /* Stop monitoring a channel */
254 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
255 {
256         int delfiles = 0;
257
258         LOCK_IF_NEEDED(chan, need_lock);
259
260         if (chan->monitor) {
261                 char filename[ FILENAME_MAX ];
262
263                 if (chan->monitor->read_stream) {
264                         ast_closestream(chan->monitor->read_stream);
265                 }
266                 if (chan->monitor->write_stream) {
267                         ast_closestream(chan->monitor->write_stream);
268                 }
269
270                 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
271                         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
272                                 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
273                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
274                                         ast_filedelete(filename, NULL);
275                                 }
276                                 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
277                         } else {
278                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
279                         }
280
281                         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
282                                 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
283                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
284                                         ast_filedelete(filename, NULL);
285                                 }
286                                 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
287                         } else {
288                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
289                         }
290                 }
291
292                 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
293                         char tmp[1024];
294                         char tmp2[1024];
295                         const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
296                         char *name = chan->monitor->filename_base;
297                         int directory = strchr(name, '/') ? 1 : 0;
298                         char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
299                         const char *execute, *execute_args;
300
301                         /* Set the execute application */
302                         execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
303                         if (ast_strlen_zero(execute)) { 
304                                 execute = "nice -n 19 soxmix";
305                                 format = get_soxmix_format(format);
306                                 delfiles = 1;
307                         } 
308                         execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
309                         if (ast_strlen_zero(execute_args)) {
310                                 execute_args = "";
311                         }
312                         
313                         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);
314                         if (delfiles) {
315                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
316                                 ast_copy_string(tmp, tmp2, sizeof(tmp));
317                         }
318                         if (option_debug)
319                                 ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
320                         if (ast_safe_system(tmp) == -1)
321                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
322                 }
323                 
324                 free(chan->monitor->format);
325                 free(chan->monitor);
326                 chan->monitor = NULL;
327         }
328
329         UNLOCK_IF_NEEDED(chan, need_lock);
330
331         return 0;
332 }
333
334
335 /* Pause monitoring of a channel */
336 int ast_monitor_pause(struct ast_channel *chan)
337 {
338         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
339 }
340
341 /* Unpause monitoring of a channel */
342 int ast_monitor_unpause(struct ast_channel *chan)
343 {
344         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
345 }
346
347 static int pause_monitor_exec(struct ast_channel *chan, void *data)
348 {
349         return ast_monitor_pause(chan);
350 }
351
352 static int unpause_monitor_exec(struct ast_channel *chan, void *data)
353 {
354         return ast_monitor_unpause(chan);
355 }
356
357 /* Change monitoring filename of a channel */
358 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
359 {
360         char tmp[256];
361         if (ast_strlen_zero(fname_base)) {
362                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
363                 return -1;
364         }
365
366         LOCK_IF_NEEDED(chan, need_lock);
367
368         if (chan->monitor) {
369                 int directory = strchr(fname_base, '/') ? 1 : 0;
370                 /* try creating the directory just in case it doesn't exist */
371                 if (directory) {
372                         char *name = strdup(fname_base);
373                         snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
374                         free(name);
375                         ast_safe_system(tmp);
376                 }
377
378                 snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
379         } else {
380                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
381         }
382
383         UNLOCK_IF_NEEDED(chan, need_lock);
384
385         return 0;
386 }
387
388 static int start_monitor_exec(struct ast_channel *chan, void *data)
389 {
390         char *arg = NULL;
391         char *format = NULL;
392         char *fname_base = NULL;
393         char *options = NULL;
394         char *delay = NULL;
395         char *urlprefix = NULL;
396         char tmp[256];
397         int joinfiles = 0;
398         int waitforbridge = 0;
399         int res = 0;
400         
401         /* Parse arguments. */
402         if (!ast_strlen_zero((char*)data)) {
403                 arg = ast_strdupa((char*)data);
404                 format = arg;
405                 fname_base = strchr(arg, '|');
406                 if (fname_base) {
407                         *fname_base = 0;
408                         fname_base++;
409                         if ((options = strchr(fname_base, '|'))) {
410                                 *options = 0;
411                                 options++;
412                                 if (strchr(options, 'm'))
413                                         joinfiles = 1;
414                                 if (strchr(options, 'b'))
415                                         waitforbridge = 1;
416                         }
417                 }
418                 arg = strchr(format,':');
419                 if (arg) {
420                         *arg++ = 0;
421                         urlprefix = arg;
422                 }
423         }
424         if (urlprefix) {
425                 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
426                         ((strcmp(format,"gsm")) ? "wav" : "gsm"));
427                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
428                         return -1;
429                 ast_cdr_setuserfield(chan, tmp);
430         }
431         if (waitforbridge) {
432                 /* We must remove the "b" option if listed.  In principle none of
433                    the following could give NULL results, but we check just to
434                    be pedantic. Reconstructing with checks for 'm' option does not
435                    work if we end up adding more options than 'm' in the future. */
436                 delay = ast_strdupa(data);
437                 options = strrchr(delay, '|');
438                 if (options) {
439                         arg = strchr(options, 'b');
440                         if (arg) {
441                                 *arg = 'X';
442                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
443                         }
444                 }
445                 return 0;
446         }
447
448         res = ast_monitor_start(chan, format, fname_base, 1);
449         if (res < 0)
450                 res = ast_monitor_change_fname(chan, fname_base, 1);
451         ast_monitor_setjoinfiles(chan, joinfiles);
452
453         return res;
454 }
455
456 static int stop_monitor_exec(struct ast_channel *chan, void *data)
457 {
458         return ast_monitor_stop(chan, 1);
459 }
460
461 static int change_monitor_exec(struct ast_channel *chan, void *data)
462 {
463         return ast_monitor_change_fname(chan, (const char*)data, 1);
464 }
465
466 static char start_monitor_action_help[] =
467 "Description: The 'Monitor' action may be used to record the audio on a\n"
468 "  specified channel.  The following parameters may be used to control\n"
469 "  this:\n"
470 "  Channel     - Required.  Used to specify the channel to record.\n"
471 "  File        - Optional.  Is the name of the file created in the\n"
472 "                monitor spool directory.  Defaults to the same name\n"
473 "                as the channel (with slashes replaced with dashes).\n"
474 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
475 "                to \"wav\".\n"
476 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
477 "                the input and output channels together after the\n"
478 "                recording is finished.\n";
479
480 static int start_monitor_action(struct mansession *s, const struct message *m)
481 {
482         struct ast_channel *c = NULL;
483         const char *name = astman_get_header(m, "Channel");
484         const char *fname = astman_get_header(m, "File");
485         const char *format = astman_get_header(m, "Format");
486         const char *mix = astman_get_header(m, "Mix");
487         char *d;
488         
489         if (ast_strlen_zero(name)) {
490                 astman_send_error(s, m, "No channel specified");
491                 return 0;
492         }
493         c = ast_get_channel_by_name_locked(name);
494         if (!c) {
495                 astman_send_error(s, m, "No such channel");
496                 return 0;
497         }
498
499         if (ast_strlen_zero(fname)) {
500                 /* No filename base specified, default to channel name as per CLI */            
501                 if (!(fname = ast_strdup(c->name))) {
502                         astman_send_error(s, m, "Could not start monitoring channel");
503                         ast_channel_unlock(c);
504                         return 0;
505                 }
506                 /* Channels have the format technology/channel_name - have to replace that /  */
507                 if ((d = strchr(fname, '/'))) 
508                         *d = '-';
509         }
510         
511         if (ast_monitor_start(c, format, fname, 1)) {
512                 if (ast_monitor_change_fname(c, fname, 1)) {
513                         astman_send_error(s, m, "Could not start monitoring channel");
514                         ast_channel_unlock(c);
515                         return 0;
516                 }
517         }
518
519         if (ast_true(mix)) {
520                 ast_monitor_setjoinfiles(c, 1);
521         }
522
523         ast_channel_unlock(c);
524         astman_send_ack(s, m, "Started monitoring channel");
525         return 0;
526 }
527
528 static char stop_monitor_action_help[] =
529 "Description: The 'StopMonitor' action may be used to end a previously\n"
530 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
531 "  of the channel monitored.\n";
532
533 static int stop_monitor_action(struct mansession *s, const struct message *m)
534 {
535         struct ast_channel *c = NULL;
536         const char *name = astman_get_header(m, "Channel");
537         int res;
538         if (ast_strlen_zero(name)) {
539                 astman_send_error(s, m, "No channel specified");
540                 return 0;
541         }
542         c = ast_get_channel_by_name_locked(name);
543         if (!c) {
544                 astman_send_error(s, m, "No such channel");
545                 return 0;
546         }
547         res = ast_monitor_stop(c, 1);
548         ast_channel_unlock(c);
549         if (res) {
550                 astman_send_error(s, m, "Could not stop monitoring channel");
551                 return 0;
552         }
553         astman_send_ack(s, m, "Stopped monitoring channel");
554         return 0;
555 }
556
557 static char change_monitor_action_help[] =
558 "Description: The 'ChangeMonitor' action may be used to change the file\n"
559 "  started by a previous 'Monitor' action.  The following parameters may\n"
560 "  be used to control this:\n"
561 "  Channel     - Required.  Used to specify the channel to record.\n"
562 "  File        - Required.  Is the new name of the file created in the\n"
563 "                monitor spool directory.\n";
564
565 static int change_monitor_action(struct mansession *s, const struct message *m)
566 {
567         struct ast_channel *c = NULL;
568         const char *name = astman_get_header(m, "Channel");
569         const char *fname = astman_get_header(m, "File");
570         if (ast_strlen_zero(name)) {
571                 astman_send_error(s, m, "No channel specified");
572                 return 0;
573         }
574         if (ast_strlen_zero(fname)) {
575                 astman_send_error(s, m, "No filename specified");
576                 return 0;
577         }
578         c = ast_get_channel_by_name_locked(name);
579         if (!c) {
580                 astman_send_error(s, m, "No such channel");
581                 return 0;
582         }
583         if (ast_monitor_change_fname(c, fname, 1)) {
584                 astman_send_error(s, m, "Could not change monitored filename of channel");
585                 ast_channel_unlock(c);
586                 return 0;
587         }
588         ast_channel_unlock(c);
589         astman_send_ack(s, m, "Changed monitor filename");
590         return 0;
591 }
592
593 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
594 {
595         if (chan->monitor)
596                 chan->monitor->joinfiles = turnon;
597 }
598
599 enum MONITOR_PAUSING_ACTION
600 {
601         MONITOR_ACTION_PAUSE,
602         MONITOR_ACTION_UNPAUSE
603 };
604           
605 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
606 {
607         struct ast_channel *c = NULL;
608         const char *name = astman_get_header(m, "Channel");
609         
610         if (ast_strlen_zero(name)) {
611                 astman_send_error(s, m, "No channel specified");
612                 return -1;
613         }
614         
615         c = ast_get_channel_by_name_locked(name);
616         if (!c) {
617                 astman_send_error(s, m, "No such channel");
618                 return -1;
619         }
620
621         if (action == MONITOR_ACTION_PAUSE)
622                 ast_monitor_pause(c);
623         else
624                 ast_monitor_unpause(c);
625         
626         ast_channel_unlock(c);
627         astman_send_ack(s, m, "Paused monitoring of the channel");
628         return 0;       
629 }
630
631 static char pause_monitor_action_help[] =
632         "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
633         " recording of a channel.  The following parameters may\n"
634         " be used to control this:\n"
635         "  Channel     - Required.  Used to specify the channel to record.\n";
636
637 static int pause_monitor_action(struct mansession *s, const struct message *m)
638 {
639         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
640 }
641
642 static char unpause_monitor_action_help[] =
643         "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
644         "  of a channel after calling PauseMonitor.  The following parameters may\n"
645         "  be used to control this:\n"
646         "  Channel     - Required.  Used to specify the channel to record.\n";
647
648 static int unpause_monitor_action(struct mansession *s, const struct message *m)
649 {
650         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
651 }
652         
653
654 static int load_module(void)
655 {
656         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
657         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
658         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
659         ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
660         ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
661         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
662         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
663         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
664         ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
665         ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
666
667         return 0;
668 }
669
670 static int unload_module(void)
671 {
672         ast_unregister_application("Monitor");
673         ast_unregister_application("StopMonitor");
674         ast_unregister_application("ChangeMonitor");
675         ast_unregister_application("PauseMonitor");
676         ast_unregister_application("UnpauseMonitor");
677         ast_manager_unregister("Monitor");
678         ast_manager_unregister("StopMonitor");
679         ast_manager_unregister("ChangeMonitor");
680         ast_manager_unregister("PauseMonitor");
681         ast_manager_unregister("UnpauseMonitor");
682
683         return 0;
684 }
685
686 /* usecount semantics need to be defined */
687 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
688                 .load = load_module,
689                 .unload = unload_module,
690                 );