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