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