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