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