Make monitor merge application settable via variable, allow setting of variables...
[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.h"
21 #include "../astconf.h"
22
23 #define AST_MONITOR_DIR AST_SPOOL_DIR "/monitor"
24
25 static ast_mutex_t monitorlock = AST_MUTEX_INITIALIZER;
26
27 static unsigned long seq = 0;
28
29 static char *monitor_synopsis = "Monitor a channel";
30
31 static char *monitor_descrip = "Monitor([file_format|[fname_base]|[options]]):\n"
32 "Used to start monitoring a channel. The channel's input and output\n"
33 "voice packets are logged to files until the channel hangs up or\n"
34 "monitoring is stopped by the StopMonitor application.\n"
35 "      file_format -- optional, if not set, defaults to \"wav\"\n"
36 "      fname_base -- if set, changes the filename used to the one specified.\n"
37 "      options:\n"
38 "              'm' - when the recording ends mix the two leg files into one and\n"
39 "                    delete the two leg files.  If MONITOR_EXEC is set, the\n"
40 "                    application refernced in it will be executed instead of\n"
41 "                    soxmix and the raw leg files will NOT be deleted automatically.\n"
42 "                    soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
43 "                    and a target mixed file name which is the same as the leg file names\n"
44 "                    only without the in/out designator.\n\n"
45 "                    Both MONITOR_EXEC and the Mix flag can be set from the\n"
46 "                    administrator interface\n";
47
48 static char *stopmonitor_synopsis = "Stop monitoring a channel";
49
50 static char *stopmonitor_descrip = "StopMonitor\n"
51         "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
52
53 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
54
55 static char *changemonitor_descrip = "ChangeMonitor\n"
56         "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
57         "The option string may contain the following:\n"
58         "       filename_base -- if set, changes the filename used to the one specified.\n";
59
60 /* Start monitoring a channel */
61 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
62                                                 const char *fname_base, int need_lock )
63 {
64         int res = 0;
65         char tmp[256];
66
67         if( need_lock ) {
68                 if (ast_mutex_lock(&chan->lock)) {
69                         ast_log(LOG_WARNING, "Unable to lock channel\n");
70                         return -1;
71                 }
72         }
73
74         if( !(chan->monitor) ) {
75                 struct ast_channel_monitor *monitor;
76                 char *channel_name, *p;
77
78                 /* Create monitoring directory if needed */
79                 if( mkdir( AST_MONITOR_DIR, 0770 ) < 0 ) {
80                         if( errno != EEXIST ) {
81                                 ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
82                                                 strerror( errno ) );
83                         }
84                 }
85
86                 monitor = malloc( sizeof( struct ast_channel_monitor ) );
87                 memset( monitor, 0, sizeof( struct ast_channel_monitor ) );
88
89                 /* Determine file names */
90                 if( fname_base && strlen( fname_base ) ) {
91                         int directory = strchr(fname_base, '/') ? 1 : 0;
92                         /* try creating the directory just in case it doesn't exist */
93                         if (directory) {
94                                 char *name = strdup(fname_base);
95                                 snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
96                                 free(name);
97                                 system(tmp);
98                         }
99                         snprintf(       monitor->read_filename, FILENAME_MAX, "%s/%s-in",
100                                                 directory ? "" : AST_MONITOR_DIR, fname_base );
101                         snprintf(       monitor->write_filename, FILENAME_MAX, "%s/%s-out",
102                                                 directory ? "" : AST_MONITOR_DIR, fname_base );
103                         strncpy(monitor->filename_base, fname_base, sizeof(monitor->filename_base) - 1);
104                 } else {
105                         ast_mutex_lock( &monitorlock );
106                         snprintf(       monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
107                                                 AST_MONITOR_DIR, seq );
108                         snprintf(       monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
109                                                 AST_MONITOR_DIR, seq );
110                         seq++;
111                         ast_mutex_unlock( &monitorlock );
112
113                         channel_name = strdup( chan->name );
114                         while( ( p = strchr( channel_name, '/' ) ) ) {
115                                 *p = '-';
116                         }
117                         snprintf(       monitor->filename_base, FILENAME_MAX, "%s/%s",
118                                                 AST_MONITOR_DIR, channel_name );
119                         monitor->filename_changed = 1;
120                         free( channel_name );
121                 }
122
123                 monitor->stop = ast_monitor_stop;
124
125                 // Determine file format
126                 if( format_spec && strlen( format_spec ) ) {
127                         monitor->format = strdup( format_spec );
128                 } else {
129                         monitor->format = strdup( "wav" );
130                 }
131                 
132                 // open files
133                 if( ast_fileexists( monitor->read_filename, NULL, NULL ) > 0 ) {
134                         ast_filedelete( monitor->read_filename, NULL );
135                 }
136                 if( !(monitor->read_stream = ast_writefile(     monitor->read_filename,
137                                                 monitor->format, NULL,
138                                                 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644 ) ) ) {
139                         ast_log(        LOG_WARNING, "Could not create file %s\n",
140                                                 monitor->read_filename );
141                         free( monitor );
142                         ast_mutex_unlock(&chan->lock);
143                         return -1;
144                 }
145                 if( ast_fileexists( monitor->write_filename, NULL, NULL ) > 0 ) {
146                         ast_filedelete( monitor->write_filename, NULL );
147                 }
148                 if( !(monitor->write_stream = ast_writefile( monitor->write_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->write_filename );
153                         ast_closestream( monitor->read_stream );
154                         free( monitor );
155                         ast_mutex_unlock(&chan->lock);
156                         return -1;
157                 }
158                 chan->monitor = monitor;
159         } else {
160                 ast_log(        LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
161                                         chan->name );
162                 res = -1;
163         }
164
165         if( need_lock ) {
166                 ast_mutex_unlock(&chan->lock);
167         }
168         return res;
169 }
170
171 /* Stop monitoring a channel */
172 int ast_monitor_stop( struct ast_channel *chan, int need_lock )
173 {
174   char *execute=NULL;
175   int soxmix =0;
176         if(need_lock) {
177                 if(ast_mutex_lock(&chan->lock)) {
178                         ast_log(LOG_WARNING, "Unable to lock channel\n");
179                         return -1;
180                 }
181         }
182
183         if(chan->monitor) {
184                 char filename[ FILENAME_MAX ];
185
186                 if(chan->monitor->read_stream) {
187                         ast_closestream( chan->monitor->read_stream );
188                 }
189                 if(chan->monitor->write_stream) {
190                         ast_closestream( chan->monitor->write_stream );
191                 }
192
193                 if(chan->monitor->filename_changed&&strlen(chan->monitor->filename_base)) {
194                         if( ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0 ) {
195                                 snprintf(       filename, FILENAME_MAX, "%s-in",
196                                                         chan->monitor->filename_base );
197                                 if(ast_fileexists( filename, NULL, NULL ) > 0) {
198                                         ast_filedelete( filename, NULL );
199                                 }
200                                 ast_filerename( chan->monitor->read_filename, filename,
201                                                                 chan->monitor->format );
202                         } else {
203                                 ast_log(        LOG_WARNING, "File %s not found\n",
204                                                         chan->monitor->read_filename );
205                         }
206
207                         if(ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
208                                 snprintf(       filename, FILENAME_MAX, "%s-out",
209                                                         chan->monitor->filename_base );
210                                 if( ast_fileexists( filename, NULL, NULL ) > 0 ) {
211                                         ast_filedelete( filename, NULL );
212                                 }
213                                 ast_filerename( chan->monitor->write_filename, filename,
214                                                                 chan->monitor->format );
215                         } else {
216                                 ast_log(        LOG_WARNING, "File %s not found\n",
217                                                         chan->monitor->write_filename );
218                         }
219                 }
220
221                 if (chan->monitor->joinfiles && !ast_strlen_zero(execute) && strlen(chan->monitor->filename_base)) {
222                         char tmp[1024];
223                         char tmp2[1024];
224                         char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
225                         char *name = chan->monitor->filename_base;
226                         int directory = strchr(name, '/') ? 1 : 0;
227                         char *dir = directory ? "" : AST_MONITOR_DIR;
228
229                         /* Set the execute application */
230                         execute=pbx_builtin_getvar_helper(chan,"MONITOR_EXEC");
231                         if (!execute || ast_strlen_zero(execute)) { 
232                           execute = "nice -n 19 soxmix"; 
233                           soxmix = 1;
234                         }                       
235                         snprintf(tmp, sizeof(tmp), "%s %s/%s-in.%s %s/%s-out.%s %s/%s.%s &", execute, dir, name, format, dir, name, format, dir, name, format);
236                         if (soxmix) 
237                           snprintf(tmp2,sizeof(tmp2), "%s& rm -f %s/%s-* &",tmp, dir ,name); /* remove legs when done mixing */
238                         ast_verbose("monitor executing %s\n",tmp);
239                         if (ast_safe_system(tmp) == -1)
240                           ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
241                 }
242                 
243                 free( chan->monitor->format );
244                 free( chan->monitor );
245                 chan->monitor = NULL;
246         }
247
248         if( need_lock ) {
249                 ast_mutex_unlock(&chan->lock);
250         }
251         return 0;
252 }
253
254 /* Change monitoring filename of a channel */
255 int ast_monitor_change_fname(   struct ast_channel *chan,
256                                                                 const char *fname_base, int need_lock )
257 {
258         char tmp[256];
259         if( (!fname_base) || (!strlen(fname_base)) ) {
260                 ast_log(        LOG_WARNING,
261                                         "Cannot change monitor filename of channel %s to null",
262                                         chan->name );
263                 return -1;
264         }
265         
266         if( need_lock ) {
267                 if (ast_mutex_lock(&chan->lock)) {
268                         ast_log(LOG_WARNING, "Unable to lock channel\n");
269                         return -1;
270                 }
271         }
272
273         if( chan->monitor ) {
274                 int directory = strchr(fname_base, '/') ? 1 : 0;
275                 /* try creating the directory just in case it doesn't exist */
276                 if (directory) {
277                         char *name = strdup(fname_base);
278                         snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
279                         free(name);
280                         system(tmp);
281                 }
282
283                 snprintf(       chan->monitor->filename_base, FILENAME_MAX, "%s/%s",
284                                         directory ? "" : AST_MONITOR_DIR, fname_base );
285         } else {
286                 ast_log(        LOG_WARNING,
287                                         "Cannot change monitor filename of channel %s to %s, monitoring not started",
288                                         chan->name, fname_base );
289                                 
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         int joinfiles = 0;
305         int res;
306         
307         /* Parse arguments. */
308         if( data && strlen((char*)data) ) {
309                 arg = strdup( (char*)data );
310                 format = arg;
311                 fname_base = strchr( arg, '|' );
312                 if( fname_base ) {
313                         *fname_base = 0;
314                         fname_base++;
315                         if ((options = strchr(fname_base, '|'))) {
316                                 *options = 0;
317                                 options++;
318                                 if (strchr(options, 'm'))
319                                         joinfiles = 1;
320                         }
321                 }
322                 
323         }
324
325         res = ast_monitor_start( chan, format, fname_base, 1 );
326         if( res < 0 ) {
327                 res = ast_monitor_change_fname( chan, fname_base, 1 );
328         }
329         ast_monitor_setjoinfiles(chan, joinfiles);
330
331         if( arg ) {
332                 free( arg );
333         }
334
335         return res;
336 }
337
338 static int stop_monitor_exec(struct ast_channel *chan, void *data)
339 {
340         return ast_monitor_stop( chan, 1 );
341 }
342
343 static int change_monitor_exec(struct ast_channel *chan, void *data)
344 {
345         return ast_monitor_change_fname( chan, (const char*)data, 1 );
346 }
347
348 static int start_monitor_action(struct mansession *s, struct message *m)
349 {
350         struct ast_channel *c = NULL;
351         char *name = astman_get_header(m, "Channel");
352         char *fname = astman_get_header(m, "File");
353         char *format = astman_get_header(m, "Format");
354         char *mix = astman_get_header(m, "Mix");
355         char *d;
356         
357         if((!name)||(!strlen(name))) {
358                 astman_send_error(s, m, "No channel specified");
359                 return 0;
360         }
361         c = ast_channel_walk_locked(NULL);
362         while(c) {
363                 if (!strcasecmp(c->name, name)) {
364                         break;
365                 }
366                 ast_mutex_unlock(&c->lock);
367                 c = ast_channel_walk_locked(c);
368         }
369         if (!c) {
370                 astman_send_error(s, m, "No such channel");
371                 return 0;
372         }
373      
374         if( (!fname) || (!strlen(fname)) ) {
375                 // No filename base specified, default to channel name as per CLI
376                 fname = malloc (FILENAME_MAX);
377                 memset( fname, 0, FILENAME_MAX);
378                 strncpy( fname, c->name, FILENAME_MAX-1);
379                 // Channels have the format technology/channel_name - have to replace that / 
380                 if( (d=strchr( fname, '/')) ) *d='-';
381         }
382         
383         if( ast_monitor_start( c, format, fname, 1 ) ) {
384                 if( ast_monitor_change_fname( c, fname, 1 ) ) {
385                         astman_send_error(s, m, "Could not start monitoring channel");
386                         return 0;
387                 }
388         }
389
390         if(ast_true(mix)) {
391           ast_monitor_setjoinfiles( c, 1);
392         }
393
394         ast_mutex_unlock(&c->lock);
395         astman_send_ack(s, m, "Started monitoring channel");
396         return 0;
397 }
398
399 static int stop_monitor_action(struct mansession *s, struct message *m)
400 {
401         struct ast_channel *c = NULL;
402         char *name = astman_get_header(m, "Channel");
403         int res;
404         if((!name)||(!strlen(name))) {
405                 astman_send_error(s, m, "No channel specified");
406                 return 0;
407         }
408         c = ast_channel_walk_locked(NULL);
409         while(c) {
410                 if (!strcasecmp(c->name, name)) {
411                         break;
412                 }
413                 ast_mutex_unlock(&c->lock);
414                 c = ast_channel_walk_locked(c);
415         }
416         if (!c) {
417                 astman_send_error(s, m, "No such channel");
418                 return 0;
419         }
420         res = ast_monitor_stop( c, 1 );
421         ast_mutex_unlock(&c->lock);
422         if( res ) {
423                 astman_send_error(s, m, "Could not stop monitoring channel");
424                 return 0;
425         }
426         astman_send_ack(s, m, "Stopped monitoring channel");
427         return 0;
428 }
429
430 static int change_monitor_action(struct mansession *s, struct message *m)
431 {
432         struct ast_channel *c = NULL;
433         char *name = astman_get_header(m, "Channel");
434         char *fname = astman_get_header(m, "File");
435         if((!name) || (!strlen(name))) {
436                 astman_send_error(s, m, "No channel specified");
437                 return 0;
438         }
439         if ((!fname)||(!strlen(fname))) {
440                 astman_send_error(s, m, "No filename specified");
441                 return 0;
442         }
443         c = ast_channel_walk_locked(NULL);
444         while(c) {
445                 if (!strcasecmp(c->name, name)) {
446                         break;
447                 }
448                 ast_mutex_unlock(&c->lock);
449                 c = ast_channel_walk_locked(c);
450         }
451         if (!c) {
452                 astman_send_error(s, m, "No such channel");
453                 return 0;
454         }
455         if( ast_monitor_change_fname( c, fname, 1 ) ) {
456                 astman_send_error(s, m, "Could not change monitored filename of channel");
457                 return 0;
458         }
459         astman_send_ack(s, m, "Stopped monitoring channel");
460         return 0;
461 }
462
463 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
464 {
465         if (chan->monitor)
466                 chan->monitor->joinfiles = turnon;
467 }
468
469 int load_module(void)
470 {
471         ast_register_application( "Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip );
472         ast_register_application( "StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip );
473         ast_register_application( "ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip );
474         ast_manager_register( "Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis );
475         ast_manager_register( "StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis );
476         ast_manager_register( "ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis );
477
478         return 0;
479 }
480
481 int unload_module(void)
482 {
483         ast_unregister_application( "Monitor" );
484         ast_unregister_application( "StopMonitor" );
485         return 0;
486 }
487
488 char *description(void)
489 {
490         return "Call Monitoring Resource";
491 }
492
493 int usecount(void)
494 {
495         /* Never allow monitor to be unloaded because it will
496            unresolve needed symbols in the channel */
497 #if 0
498         int res;
499         STANDARD_USECOUNT(res);
500         return res;
501 #else
502         return 1;
503 #endif
504 }
505
506 char *key()
507 {
508         return ASTERISK_GPL_KEY;
509 }