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