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