62d35dd1deb98507562cc5290940c9c2a9517466
[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\n", 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                 fname_base = strchr(arg, '|');
327                 if (fname_base) {
328                         *fname_base = 0;
329                         fname_base++;
330                         if ((options = strchr(fname_base, '|'))) {
331                                 *options = 0;
332                                 options++;
333                                 if (strchr(options, 'm'))
334                                         joinfiles = 1;
335                                 if (strchr(options, 'b'))
336                                         waitforbridge = 1;
337                         }
338                 }
339                 arg = strchr(format,':');
340                 if (arg) {
341                         *arg++ = 0;
342                         urlprefix = arg;
343                 }
344         }
345         if (urlprefix) {
346                 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
347                         ((strcmp(format,"gsm")) ? "wav" : "gsm"));
348                 if (!chan->cdr)
349                         chan->cdr = ast_cdr_alloc();
350                 ast_cdr_setuserfield(chan, tmp);
351         }
352         if (waitforbridge) {
353                 /* We must remove the "b" option if listed.  In principle none of
354                    the following could give NULL results, but we check just to
355                    be pedantic. Reconstructing with checks for 'm' option does not
356                    work if we end up adding more options than 'm' in the future. */
357                 delay = ast_strdupa((char*)data);
358                 if (delay) {
359                         options = strrchr(delay, '|');
360                         if (options) {
361                                 arg = strchr(options, 'b');
362                                 if (arg) {
363                                         *arg = 'X';
364                                         pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
365                                 }
366                         }
367                 }
368                 return 0;
369         }
370
371         res = ast_monitor_start(chan, format, fname_base, 1);
372         if (res < 0)
373                 res = ast_monitor_change_fname(chan, fname_base, 1);
374         ast_monitor_setjoinfiles(chan, joinfiles);
375
376         return res;
377 }
378
379 static int stop_monitor_exec(struct ast_channel *chan, void *data)
380 {
381         return ast_monitor_stop(chan, 1);
382 }
383
384 static int change_monitor_exec(struct ast_channel *chan, void *data)
385 {
386         return ast_monitor_change_fname(chan, (const char*)data, 1);
387 }
388
389 static char start_monitor_action_help[] =
390 "Description: The 'Monitor' action may be used to record the audio on a\n"
391 "  specified channel.  The following parameters may be used to control\n"
392 "  this:\n"
393 "  Channel     - Required.  Used to specify the channel to record.\n"
394 "  File        - Optional.  Is the name of the file created in the\n"
395 "                monitor spool directory.  Defaults to the same name\n"
396 "                as the channel (with slashes replaced with dashes).\n"
397 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
398 "                to \"wav\".\n"
399 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
400 "                the input and output channels together after the\n"
401 "                recording is finished.\n";
402
403 static int start_monitor_action(struct mansession *s, struct message *m)
404 {
405         struct ast_channel *c = NULL;
406         char *name = astman_get_header(m, "Channel");
407         char *fname = astman_get_header(m, "File");
408         char *format = astman_get_header(m, "Format");
409         char *mix = astman_get_header(m, "Mix");
410         char *d;
411         
412         if ((!name) || (ast_strlen_zero(name))) {
413                 astman_send_error(s, m, "No channel specified");
414                 return 0;
415         }
416         c = ast_get_channel_by_name_locked(name);
417         if (!c) {
418                 astman_send_error(s, m, "No such channel");
419                 return 0;
420         }
421
422         if ((!fname) || (ast_strlen_zero(fname))) {
423                 /* No filename base specified, default to channel name as per CLI */
424                 fname = malloc (FILENAME_MAX);
425                 if (!fname) {
426                         astman_send_error(s, m, "Could not start monitoring channel");
427                         ast_mutex_unlock(&c->lock);
428                         return 0;
429                 }
430                 memset(fname, 0, FILENAME_MAX);
431                 strncpy(fname, c->name, FILENAME_MAX-1);
432                 /* Channels have the format technology/channel_name - have to replace that /  */
433                 if ((d=strchr(fname, '/'))) *d='-';
434         }
435         
436         if (ast_monitor_start(c, format, fname, 1)) {
437                 if (ast_monitor_change_fname(c, fname, 1)) {
438                         astman_send_error(s, m, "Could not start monitoring channel");
439                         ast_mutex_unlock(&c->lock);
440                         return 0;
441                 }
442         }
443
444         if (ast_true(mix)) {
445                 ast_monitor_setjoinfiles(c, 1);
446         }
447
448         ast_mutex_unlock(&c->lock);
449         astman_send_ack(s, m, "Started monitoring channel");
450         return 0;
451 }
452
453 static char stop_monitor_action_help[] =
454 "Description: The 'StopMonitor' action may be used to end a previously\n"
455 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
456 "  of the channel monitored.\n";
457
458 static int stop_monitor_action(struct mansession *s, struct message *m)
459 {
460         struct ast_channel *c = NULL;
461         char *name = astman_get_header(m, "Channel");
462         int res;
463         if ((!name) || (ast_strlen_zero(name))) {
464                 astman_send_error(s, m, "No channel specified");
465                 return 0;
466         }
467         c = ast_get_channel_by_name_locked(name);
468         if (!c) {
469                 astman_send_error(s, m, "No such channel");
470                 return 0;
471         }
472         res = ast_monitor_stop(c, 1);
473         ast_mutex_unlock(&c->lock);
474         if (res) {
475                 astman_send_error(s, m, "Could not stop monitoring channel");
476                 return 0;
477         }
478         astman_send_ack(s, m, "Stopped monitoring channel");
479         return 0;
480 }
481
482 static char change_monitor_action_help[] =
483 "Description: The 'ChangeMonitor' action may be used to change the file\n"
484 "  started by a previous 'Monitor' action.  The following parameters may\n"
485 "  be used to control this:\n"
486 "  Channel     - Required.  Used to specify the channel to record.\n"
487 "  File        - Required.  Is the new name of the file created in the\n"
488 "                monitor spool directory.\n";
489
490 static int change_monitor_action(struct mansession *s, struct message *m)
491 {
492         struct ast_channel *c = NULL;
493         char *name = astman_get_header(m, "Channel");
494         char *fname = astman_get_header(m, "File");
495         if ((!name) || (ast_strlen_zero(name))) {
496                 astman_send_error(s, m, "No channel specified");
497                 return 0;
498         }
499         if ((!fname)||(ast_strlen_zero(fname))) {
500                 astman_send_error(s, m, "No filename specified");
501                 return 0;
502         }
503         c = ast_get_channel_by_name_locked(name);
504         if (!c) {
505                 astman_send_error(s, m, "No such channel");
506                 return 0;
507         }
508         if (ast_monitor_change_fname(c, fname, 1)) {
509                 astman_send_error(s, m, "Could not change monitored filename of channel");
510                 ast_mutex_unlock(&c->lock);
511                 return 0;
512         }
513         ast_mutex_unlock(&c->lock);
514         astman_send_ack(s, m, "Stopped monitoring channel");
515         return 0;
516 }
517
518 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
519 {
520         if (chan->monitor)
521                 chan->monitor->joinfiles = turnon;
522 }
523
524 int load_module(void)
525 {
526         ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
527         ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
528         ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
529         ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
530         ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
531         ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
532
533         return 0;
534 }
535
536 int unload_module(void)
537 {
538         ast_unregister_application("Monitor");
539         ast_unregister_application("StopMonitor");
540         ast_unregister_application("ChangeMonitor");
541         ast_manager_unregister("Monitor");
542         ast_manager_unregister("StopMonitor");
543         ast_manager_unregister("ChangeMonitor");
544         return 0;
545 }
546
547 char *description(void)
548 {
549         return "Call Monitoring Resource";
550 }
551
552 int usecount(void)
553 {
554         /* Never allow monitor to be unloaded because it will
555            unresolve needed symbols in the channel */
556 #if 0
557         int res;
558         STANDARD_USECOUNT(res);
559         return res;
560 #else
561         return 1;
562 #endif
563 }
564
565 char *key()
566 {
567         return ASTERISK_GPL_KEY;
568 }