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