More formatting cleanups.
[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
178         if (need_lock) {
179                 if (ast_mutex_lock(&chan->lock)) {
180                         ast_log(LOG_WARNING, "Unable to lock channel\n");
181                         return -1;
182                 }
183         }
184
185         if (chan->monitor) {
186                 char filename[ FILENAME_MAX ];
187
188                 if (chan->monitor->read_stream) {
189                         ast_closestream( chan->monitor->read_stream );
190                 }
191                 if (chan->monitor->write_stream) {
192                         ast_closestream( chan->monitor->write_stream );
193                 }
194
195                 if (chan->monitor->filename_changed&&strlen(chan->monitor->filename_base)) {
196                         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0 ) {
197                                 snprintf(filename, FILENAME_MAX, "%s-in", 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, chan->monitor->format );
202                         } else {
203                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename );
204                         }
205
206                         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
207                                 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base );
208                                 if (ast_fileexists(filename, NULL, NULL) > 0 ) {
209                                         ast_filedelete(filename, NULL);
210                                 }
211                                 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format );
212                         } else {
213                                 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename );
214                         }
215                 }
216
217                 if (chan->monitor->joinfiles && strlen(chan->monitor->filename_base)) {
218                         char tmp[1024];
219                         char tmp2[1024];
220                         char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
221                         char *name = chan->monitor->filename_base;
222                         int directory = strchr(name, '/') ? 1 : 0;
223                         char *dir = directory ? "" : AST_MONITOR_DIR;
224
225                         /* Set the execute application */
226                         execute=pbx_builtin_getvar_helper(chan,"MONITOR_EXEC");
227                         if (!execute || ast_strlen_zero(execute)) { 
228                                 execute = "nice -n 19 soxmix"; 
229                                 soxmix = 1;
230                         }                       
231                         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);
232                         if (soxmix) {
233                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f %s/%s-* ) &",tmp, dir ,name); /* remove legs when done mixing */
234                                 strncpy(tmp, tmp2, sizeof(tmp) - 1);
235                         }
236                         ast_verbose("monitor executing %s\n",tmp);
237                         if (ast_safe_system(tmp) == -1)
238                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
239                 }
240                 
241                 free(chan->monitor->format);
242                 free(chan->monitor);
243                 chan->monitor = NULL;
244         }
245
246         if (need_lock)
247                 ast_mutex_unlock(&chan->lock);
248         return 0;
249 }
250
251 /* Change monitoring filename of a channel */
252 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
253 {
254         char tmp[256];
255         if ((!fname_base) || (!strlen(fname_base))) {
256                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null", chan->name );
257                 return -1;
258         }
259         
260         if (need_lock) {
261                 if (ast_mutex_lock(&chan->lock)) {
262                         ast_log(LOG_WARNING, "Unable to lock channel\n");
263                         return -1;
264                 }
265         }
266
267         if (chan->monitor) {
268                 int directory = strchr(fname_base, '/') ? 1 : 0;
269                 /* try creating the directory just in case it doesn't exist */
270                 if (directory) {
271                         char *name = strdup(fname_base);
272                         snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
273                         free(name);
274                         system(tmp);
275                 }
276
277                 snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : AST_MONITOR_DIR, fname_base );
278         } else {
279                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started", chan->name, fname_base );
280         }
281
282         if (need_lock)
283                 ast_mutex_unlock(&chan->lock);
284
285         return 0;
286 }
287
288 static int start_monitor_exec(struct ast_channel *chan, void *data)
289 {
290         char *arg = NULL;
291         char *format = NULL;
292         char *fname_base = NULL;
293         char *options = NULL;
294         int joinfiles = 0;
295         int res;
296         
297         /* Parse arguments. */
298         if (data && strlen((char*)data)) {
299                 arg = strdup((char*)data);
300                 format = arg;
301                 fname_base = strchr(arg, '|');
302                 if (fname_base) {
303                         *fname_base = 0;
304                         fname_base++;
305                         if ((options = strchr(fname_base, '|'))) {
306                                 *options = 0;
307                                 options++;
308                                 if (strchr(options, 'm'))
309                                         joinfiles = 1;
310                         }
311                 }
312                 
313         }
314
315         res = ast_monitor_start(chan, format, fname_base, 1);
316         if (res < 0)
317                 res = ast_monitor_change_fname( chan, fname_base, 1 );
318         ast_monitor_setjoinfiles(chan, joinfiles);
319
320         if (arg)
321                 free( arg );
322
323         return res;
324 }
325
326 static int stop_monitor_exec(struct ast_channel *chan, void *data)
327 {
328         return ast_monitor_stop(chan, 1);
329 }
330
331 static int change_monitor_exec(struct ast_channel *chan, void *data)
332 {
333         return ast_monitor_change_fname(chan, (const char*)data, 1);
334 }
335
336 static int start_monitor_action(struct mansession *s, struct message *m)
337 {
338         struct ast_channel *c = NULL;
339         char *name = astman_get_header(m, "Channel");
340         char *fname = astman_get_header(m, "File");
341         char *format = astman_get_header(m, "Format");
342         char *mix = astman_get_header(m, "Mix");
343         char *d;
344         
345         if ((!name)||(!strlen(name))) {
346                 astman_send_error(s, m, "No channel specified");
347                 return 0;
348         }
349         c = ast_channel_walk_locked(NULL);
350         while (c) {
351                 if (!strcasecmp(c->name, name)) {
352                         break;
353                 }
354                 ast_mutex_unlock(&c->lock);
355                 c = ast_channel_walk_locked(c);
356         }
357         if (!c) {
358                 astman_send_error(s, m, "No such channel");
359                 return 0;
360         }
361
362         if ((!fname) || (!strlen(fname))) {
363                 // No filename base specified, default to channel name as per CLI
364                 fname = malloc (FILENAME_MAX);
365                 memset( fname, 0, FILENAME_MAX);
366                 strncpy( fname, c->name, FILENAME_MAX-1);
367                 // Channels have the format technology/channel_name - have to replace that / 
368                 if( (d=strchr( fname, '/')) ) *d='-';
369         }
370         
371         if (ast_monitor_start( c, format, fname, 1)) {
372                 if (ast_monitor_change_fname(c, fname, 1)) {
373                         astman_send_error(s, m, "Could not start monitoring channel");
374                         ast_mutex_unlock(&c->lock);
375                         return 0;
376                 }
377         }
378
379         if (ast_true(mix)) {
380                 ast_monitor_setjoinfiles( c, 1);
381         }
382
383         ast_mutex_unlock(&c->lock);
384         astman_send_ack(s, m, "Started monitoring channel");
385         return 0;
386 }
387
388 static int stop_monitor_action(struct mansession *s, struct message *m)
389 {
390         struct ast_channel *c = NULL;
391         char *name = astman_get_header(m, "Channel");
392         int res;
393         if ((!name)||(!strlen(name))) {
394                 astman_send_error(s, m, "No channel specified");
395                 return 0;
396         }
397         c = ast_channel_walk_locked(NULL);
398         while(c) {
399                 if (!strcasecmp(c->name, name)) {
400                         break;
401                 }
402                 ast_mutex_unlock(&c->lock);
403                 c = ast_channel_walk_locked(c);
404         }
405         if (!c) {
406                 astman_send_error(s, m, "No such channel");
407                 return 0;
408         }
409         res = ast_monitor_stop(c, 1);
410         ast_mutex_unlock(&c->lock);
411         if (res) {
412                 astman_send_error(s, m, "Could not stop monitoring channel");
413                 return 0;
414         }
415         astman_send_ack(s, m, "Stopped monitoring channel");
416         return 0;
417 }
418
419 static int change_monitor_action(struct mansession *s, struct message *m)
420 {
421         struct ast_channel *c = NULL;
422         char *name = astman_get_header(m, "Channel");
423         char *fname = astman_get_header(m, "File");
424         if ((!name) || (!strlen(name))) {
425                 astman_send_error(s, m, "No channel specified");
426                 return 0;
427         }
428         if ((!fname)||(!strlen(fname))) {
429                 astman_send_error(s, m, "No filename specified");
430                 return 0;
431         }
432         c = ast_channel_walk_locked(NULL);
433         while(c) {
434                 if (!strcasecmp(c->name, name)) {
435                         break;
436                 }
437                 ast_mutex_unlock(&c->lock);
438                 c = ast_channel_walk_locked(c);
439         }
440         if (!c) {
441                 astman_send_error(s, m, "No such channel");
442                 return 0;
443         }
444         if (ast_monitor_change_fname(c, fname, 1)) {
445                 astman_send_error(s, m, "Could not change monitored filename of channel");
446                 ast_mutex_unlock(&c->lock);
447                 return 0;
448         }
449         ast_mutex_unlock(&c->lock);
450         astman_send_ack(s, m, "Stopped monitoring channel");
451         return 0;
452 }
453
454 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
455 {
456         if (chan->monitor)
457                 chan->monitor->joinfiles = turnon;
458 }
459
460 int load_module(void)
461 {
462         ast_register_application( "Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip );
463         ast_register_application( "StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip );
464         ast_register_application( "ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip );
465         ast_manager_register( "Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis );
466         ast_manager_register( "StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis );
467         ast_manager_register( "ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis );
468
469         return 0;
470 }
471
472 int unload_module(void)
473 {
474         ast_unregister_application( "Monitor" );
475         ast_unregister_application( "StopMonitor" );
476         return 0;
477 }
478
479 char *description(void)
480 {
481         return "Call Monitoring Resource";
482 }
483
484 int usecount(void)
485 {
486         /* Never allow monitor to be unloaded because it will
487            unresolve needed symbols in the channel */
488 #if 0
489         int res;
490         STANDARD_USECOUNT(res);
491         return res;
492 #else
493         return 1;
494 #endif
495 }
496
497 char *key()
498 {
499         return ASTERISK_GPL_KEY;
500 }