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