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