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