Merged revisions 61683 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                 chan->monitor->filename_changed = 1;
380         } else {
381                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
382         }
383
384         UNLOCK_IF_NEEDED(chan, need_lock);
385
386         return 0;
387 }
388
389 static int start_monitor_exec(struct ast_channel *chan, void *data)
390 {
391         char *arg = NULL;
392         char *format = NULL;
393         char *fname_base = NULL;
394         char *options = NULL;
395         char *delay = NULL;
396         char *urlprefix = NULL;
397         char tmp[256];
398         int joinfiles = 0;
399         int waitforbridge = 0;
400         int res = 0;
401         
402         /* Parse arguments. */
403         if (!ast_strlen_zero((char*)data)) {
404                 arg = ast_strdupa((char*)data);
405                 format = arg;
406                 fname_base = strchr(arg, '|');
407                 if (fname_base) {
408                         *fname_base = 0;
409                         fname_base++;
410                         if ((options = strchr(fname_base, '|'))) {
411                                 *options = 0;
412                                 options++;
413                                 if (strchr(options, 'm'))
414                                         joinfiles = 1;
415                                 if (strchr(options, 'b'))
416                                         waitforbridge = 1;
417                         }
418                 }
419                 arg = strchr(format,':');
420                 if (arg) {
421                         *arg++ = 0;
422                         urlprefix = arg;
423                 }
424         }
425         if (urlprefix) {
426                 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
427                         ((strcmp(format,"gsm")) ? "wav" : "gsm"));
428                 if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
429                         return -1;
430                 ast_cdr_setuserfield(chan, tmp);
431         }
432         if (waitforbridge) {
433                 /* We must remove the "b" option if listed.  In principle none of
434                    the following could give NULL results, but we check just to
435                    be pedantic. Reconstructing with checks for 'm' option does not
436                    work if we end up adding more options than 'm' in the future. */
437                 delay = ast_strdupa(data);
438                 options = strrchr(delay, '|');
439                 if (options) {
440                         arg = strchr(options, 'b');
441                         if (arg) {
442                                 *arg = 'X';
443                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
444                         }
445                 }
446                 return 0;
447         }
448
449         res = ast_monitor_start(chan, format, fname_base, 1);
450         if (res < 0)
451                 res = ast_monitor_change_fname(chan, fname_base, 1);
452         ast_monitor_setjoinfiles(chan, joinfiles);
453
454         return res;
455 }
456
457 static int stop_monitor_exec(struct ast_channel *chan, void *data)
458 {
459         return ast_monitor_stop(chan, 1);
460 }
461
462 static int change_monitor_exec(struct ast_channel *chan, void *data)
463 {
464         return ast_monitor_change_fname(chan, (const char*)data, 1);
465 }
466
467 static char start_monitor_action_help[] =
468 "Description: The 'Monitor' action may be used to record the audio on a\n"
469 "  specified channel.  The following parameters may be used to control\n"
470 "  this:\n"
471 "  Channel     - Required.  Used to specify the channel to record.\n"
472 "  File        - Optional.  Is the name of the file created in the\n"
473 "                monitor spool directory.  Defaults to the same name\n"
474 "                as the channel (with slashes replaced with dashes).\n"
475 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
476 "                to \"wav\".\n"
477 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
478 "                the input and output channels together after the\n"
479 "                recording is finished.\n";
480
481 static int start_monitor_action(struct mansession *s, const struct message *m)
482 {
483         struct ast_channel *c = NULL;
484         const char *name = astman_get_header(m, "Channel");
485         const char *fname = astman_get_header(m, "File");
486         const char *format = astman_get_header(m, "Format");
487         const char *mix = astman_get_header(m, "Mix");
488         char *d;
489         
490         if (ast_strlen_zero(name)) {
491                 astman_send_error(s, m, "No channel specified");
492                 return 0;
493         }
494         c = ast_get_channel_by_name_locked(name);
495         if (!c) {
496                 astman_send_error(s, m, "No such channel");
497                 return 0;
498         }
499
500         if (ast_strlen_zero(fname)) {
501                 /* No filename base specified, default to channel name as per CLI */            
502                 if (!(fname = ast_strdup(c->name))) {
503                         astman_send_error(s, m, "Could not start monitoring channel");
504                         ast_channel_unlock(c);
505                         return 0;
506                 }
507                 /* Channels have the format technology/channel_name - have to replace that /  */
508                 if ((d = strchr(fname, '/'))) 
509                         *d = '-';
510         }
511         
512         if (ast_monitor_start(c, format, fname, 1)) {
513                 if (ast_monitor_change_fname(c, fname, 1)) {
514                         astman_send_error(s, m, "Could not start monitoring channel");
515                         ast_channel_unlock(c);
516                         return 0;
517                 }
518         }
519
520         if (ast_true(mix)) {
521                 ast_monitor_setjoinfiles(c, 1);
522         }
523
524         ast_channel_unlock(c);
525         astman_send_ack(s, m, "Started monitoring channel");
526         return 0;
527 }
528
529 static char stop_monitor_action_help[] =
530 "Description: The 'StopMonitor' action may be used to end a previously\n"
531 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
532 "  of the channel monitored.\n";
533
534 static int stop_monitor_action(struct mansession *s, const struct message *m)
535 {
536         struct ast_channel *c = NULL;
537         const char *name = astman_get_header(m, "Channel");
538         int res;
539         if (ast_strlen_zero(name)) {
540                 astman_send_error(s, m, "No channel specified");
541                 return 0;
542         }
543         c = ast_get_channel_by_name_locked(name);
544         if (!c) {
545                 astman_send_error(s, m, "No such channel");
546                 return 0;
547         }
548         res = ast_monitor_stop(c, 1);
549         ast_channel_unlock(c);
550         if (res) {
551                 astman_send_error(s, m, "Could not stop monitoring channel");
552                 return 0;
553         }
554         astman_send_ack(s, m, "Stopped monitoring channel");
555         return 0;
556 }
557
558 static char change_monitor_action_help[] =
559 "Description: The 'ChangeMonitor' action may be used to change the file\n"
560 "  started by a previous 'Monitor' action.  The following parameters may\n"
561 "  be used to control this:\n"
562 "  Channel     - Required.  Used to specify the channel to record.\n"
563 "  File        - Required.  Is the new name of the file created in the\n"
564 "                monitor spool directory.\n";
565
566 static int change_monitor_action(struct mansession *s, const struct message *m)
567 {
568         struct ast_channel *c = NULL;
569         const char *name = astman_get_header(m, "Channel");
570         const char *fname = astman_get_header(m, "File");
571         if (ast_strlen_zero(name)) {
572                 astman_send_error(s, m, "No channel specified");
573                 return 0;
574         }
575         if (ast_strlen_zero(fname)) {
576                 astman_send_error(s, m, "No filename specified");
577                 return 0;
578         }
579         c = ast_get_channel_by_name_locked(name);
580         if (!c) {
581                 astman_send_error(s, m, "No such channel");
582                 return 0;
583         }
584         if (ast_monitor_change_fname(c, fname, 1)) {
585                 astman_send_error(s, m, "Could not change monitored filename of channel");
586                 ast_channel_unlock(c);
587                 return 0;
588         }
589         ast_channel_unlock(c);
590         astman_send_ack(s, m, "Changed monitor filename");
591         return 0;
592 }
593
594 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
595 {
596         if (chan->monitor)
597                 chan->monitor->joinfiles = turnon;
598 }
599
600 enum MONITOR_PAUSING_ACTION
601 {
602         MONITOR_ACTION_PAUSE,
603         MONITOR_ACTION_UNPAUSE
604 };
605           
606 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
607 {
608         struct ast_channel *c = NULL;
609         const char *name = astman_get_header(m, "Channel");
610         
611         if (ast_strlen_zero(name)) {
612                 astman_send_error(s, m, "No channel specified");
613                 return -1;
614         }
615         
616         c = ast_get_channel_by_name_locked(name);
617         if (!c) {
618                 astman_send_error(s, m, "No such channel");
619                 return -1;
620         }
621
622         if (action == MONITOR_ACTION_PAUSE)
623                 ast_monitor_pause(c);
624         else
625                 ast_monitor_unpause(c);
626         
627         ast_channel_unlock(c);
628         astman_send_ack(s, m, "Paused monitoring of the channel");
629         return 0;       
630 }
631
632 static char pause_monitor_action_help[] =
633         "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
634         " recording of a channel.  The following parameters may\n"
635         " be used to control this:\n"
636         "  Channel     - Required.  Used to specify the channel to record.\n";
637
638 static int pause_monitor_action(struct mansession *s, const struct message *m)
639 {
640         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
641 }
642
643 static char unpause_monitor_action_help[] =
644         "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
645         "  of a channel after calling PauseMonitor.  The following parameters may\n"
646         "  be used to control this:\n"
647         "  Channel     - Required.  Used to specify the channel to record.\n";
648
649 static int unpause_monitor_action(struct mansession *s, const struct message *m)
650 {
651         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
652 }
653         
654
655 static int load_module(void)
656 {
657         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
658         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
659         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
660         ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
661         ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
662         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
663         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
664         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
665         ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
666         ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
667
668         return 0;
669 }
670
671 static int unload_module(void)
672 {
673         ast_unregister_application("Monitor");
674         ast_unregister_application("StopMonitor");
675         ast_unregister_application("ChangeMonitor");
676         ast_unregister_application("PauseMonitor");
677         ast_unregister_application("UnpauseMonitor");
678         ast_manager_unregister("Monitor");
679         ast_manager_unregister("StopMonitor");
680         ast_manager_unregister("ChangeMonitor");
681         ast_manager_unregister("PauseMonitor");
682         ast_manager_unregister("UnpauseMonitor");
683
684         return 0;
685 }
686
687 /* usecount semantics need to be defined */
688 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
689                 .load = load_module,
690                 .unload = unload_module,
691                 );