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