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