use a default file name for res_monitor, if none was given. Bug #704
[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
8 #include <asterisk/lock.h>
9 #include <asterisk/channel.h>
10 #include <asterisk/logger.h>
11 #include <asterisk/file.h>
12 #include <asterisk/pbx.h>
13 #include <asterisk/module.h>
14 #include <asterisk/manager.h>
15 #include <asterisk/cli.h>
16 #include <asterisk/monitor.h>
17
18 #define AST_MONITOR_DIR INSTALL_PREFIX "/var/spool/asterisk/monitor"
19
20 static ast_mutex_t monitorlock = AST_MUTEX_INITIALIZER;
21
22 static unsigned long seq = 0;
23
24 static char *monitor_synopsis = "Monitor a channel";
25
26 static char *monitor_descrip = "Monitor\n"
27         "Used to start monitoring a channel. The channel's input and output\n"
28         "voice packets are logged to files until the channel hangs up or\n"
29         "monitoring is stopped by the StopMonitor application.\n"
30         "The option string may contain the following arguments: [file_format|[fname_base]]\n"
31         "       file_format -- optional, if not set, defaults to \"wav\"\n"
32         "       fname_base -- if set, changes the filename used to the one specified.\n";
33
34 static char *stopmonitor_synopsis = "Stop monitoring a channel";
35
36 static char *stopmonitor_descrip = "StopMonitor\n"
37         "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
38
39 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
40
41 static char *changemonitor_descrip = "ChangeMonitor\n"
42         "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
43         "The option string may contain the following:\n"
44         "       filename_base -- if set, changes the filename used to the one specified.\n";
45
46 /* Start monitoring a channel */
47 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
48                                                 const char *fname_base, int need_lock )
49 {
50         int res = 0;
51
52         if( need_lock ) {
53                 if (ast_mutex_lock(&chan->lock)) {
54                         ast_log(LOG_WARNING, "Unable to lock channel\n");
55                         return -1;
56                 }
57         }
58
59         if( !(chan->monitor) ) {
60                 struct ast_channel_monitor *monitor;
61                 char *channel_name, *p;
62
63                 /* Create monitoring directory if needed */
64                 if( mkdir( AST_MONITOR_DIR, 0770 ) < 0 ) {
65                         if( errno != EEXIST ) {
66                                 ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
67                                                 strerror( errno ) );
68                         }
69                 }
70
71                 monitor = malloc( sizeof( struct ast_channel_monitor ) );
72                 memset( monitor, 0, sizeof( struct ast_channel_monitor ) );
73
74                 /* Determine file names */
75                 if( fname_base && strlen( fname_base ) ) {
76                         snprintf(       monitor->read_filename, FILENAME_MAX, "%s/%s-in",
77                                                 AST_MONITOR_DIR, fname_base );
78                         snprintf(       monitor->write_filename, FILENAME_MAX, "%s/%s-out",
79                                                 AST_MONITOR_DIR, fname_base );
80                         *monitor->filename_base = 0;
81                 } else {
82                         ast_mutex_lock( &monitorlock );
83                         snprintf(       monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
84                                                 AST_MONITOR_DIR, seq );
85                         snprintf(       monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
86                                                 AST_MONITOR_DIR, seq );
87                         seq++;
88                         ast_mutex_unlock( &monitorlock );
89
90                         channel_name = strdup( chan->name );
91                         while( ( p = strchr( channel_name, '/' ) ) ) {
92                                 *p = '-';
93                         }
94                         snprintf(       monitor->filename_base, FILENAME_MAX, "%s/%s",
95                                                 AST_MONITOR_DIR, channel_name );
96                         free( channel_name );
97                 }
98
99                 monitor->stop = ast_monitor_stop;
100
101                 // Determine file format
102                 if( format_spec && strlen( format_spec ) ) {
103                         monitor->format = strdup( format_spec );
104                 } else {
105                         monitor->format = strdup( "wav" );
106                 }
107                 
108                 // open files
109                 if( ast_fileexists( monitor->read_filename, NULL, NULL ) > 0 ) {
110                         ast_filedelete( monitor->read_filename, NULL );
111                 }
112                 if( !(monitor->read_stream = ast_writefile(     monitor->read_filename,
113                                                 monitor->format, NULL,
114                                                 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644 ) ) ) {
115                         ast_log(        LOG_WARNING, "Could not create file %s\n",
116                                                 monitor->read_filename );
117                         free( monitor );
118                         ast_mutex_unlock(&chan->lock);
119                         return -1;
120                 }
121                 if( ast_fileexists( monitor->write_filename, NULL, NULL ) > 0 ) {
122                         ast_filedelete( monitor->write_filename, NULL );
123                 }
124                 if( !(monitor->write_stream = ast_writefile( monitor->write_filename,
125                                                 monitor->format, NULL,
126                                                 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644 ) ) ) {
127                         ast_log(        LOG_WARNING, "Could not create file %s\n",
128                                                 monitor->write_filename );
129                         ast_closestream( monitor->read_stream );
130                         free( monitor );
131                         ast_mutex_unlock(&chan->lock);
132                         return -1;
133                 }
134                 chan->monitor = monitor;
135         } else {
136                 ast_log(        LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
137                                         chan->name );
138                 res = -1;
139         }
140
141         if( need_lock ) {
142                 ast_mutex_unlock(&chan->lock);
143         }
144         return res;
145 }
146
147 /* Stop monitoring a channel */
148 int ast_monitor_stop( struct ast_channel *chan, int need_lock )
149 {
150         if(need_lock) {
151                 if(ast_mutex_lock(&chan->lock)) {
152                         ast_log(LOG_WARNING, "Unable to lock channel\n");
153                         return -1;
154                 }
155         }
156
157         if(chan->monitor) {
158                 char filename[ FILENAME_MAX ];
159
160                 if(chan->monitor->read_stream) {
161                         ast_closestream( chan->monitor->read_stream );
162                 }
163                 if(chan->monitor->write_stream) {
164                         ast_closestream( chan->monitor->write_stream );
165                 }
166
167                 if(chan->monitor->filename_base&&strlen(chan->monitor->filename_base)) {
168                         if( ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0 ) {
169                                 snprintf(       filename, FILENAME_MAX, "%s-in",
170                                                         chan->monitor->filename_base );
171                                 if(ast_fileexists( filename, NULL, NULL ) > 0) {
172                                         ast_filedelete( filename, NULL );
173                                 }
174                                 ast_filerename( chan->monitor->read_filename, filename,
175                                                                 chan->monitor->format );
176                         } else {
177                                 ast_log(        LOG_WARNING, "File %s not found\n",
178                                                         chan->monitor->read_filename );
179                         }
180
181                         if(ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
182                                 snprintf(       filename, FILENAME_MAX, "%s-out",
183                                                         chan->monitor->filename_base );
184                                 if( ast_fileexists( filename, NULL, NULL ) > 0 ) {
185                                         ast_filedelete( filename, NULL );
186                                 }
187                                 ast_filerename( chan->monitor->write_filename, filename,
188                                                                 chan->monitor->format );
189                         } else {
190                                 ast_log(        LOG_WARNING, "File %s not found\n",
191                                                         chan->monitor->write_filename );
192                         }
193                 }
194
195                 free( chan->monitor->format );
196                 free( chan->monitor );
197                 chan->monitor = NULL;
198         }
199
200         if( need_lock ) {
201                 ast_mutex_unlock(&chan->lock);
202         }
203         return 0;
204 }
205
206 /* Change monitoring filename of a channel */
207 int ast_monitor_change_fname(   struct ast_channel *chan,
208                                                                 const char *fname_base, int need_lock )
209 {
210         if( (!fname_base) || (!strlen(fname_base)) ) {
211                 ast_log(        LOG_WARNING,
212                                         "Cannot change monitor filename of channel %s to null",
213                                         chan->name );
214                 return -1;
215         }
216         
217         if( need_lock ) {
218                 if (ast_mutex_lock(&chan->lock)) {
219                         ast_log(LOG_WARNING, "Unable to lock channel\n");
220                         return -1;
221                 }
222         }
223
224         if( chan->monitor ) {
225                 snprintf(       chan->monitor->filename_base, FILENAME_MAX, "%s/%s",
226                                         AST_MONITOR_DIR, fname_base );
227         } else {
228                 ast_log(        LOG_WARNING,
229                                         "Cannot change monitor filename of channel %s to %s, monitoring not started",
230                                         chan->name, fname_base );
231                                 
232         }
233
234         if( need_lock ) {
235                 ast_mutex_unlock(&chan->lock);
236         }
237         return 0;
238 }
239
240 static int start_monitor_exec(struct ast_channel *chan, void *data)
241 {
242         char *arg = NULL;
243         char *format = NULL;
244         char *fname_base = NULL;
245         int res;
246         
247         /* Parse arguments. */
248         if( data && strlen((char*)data) ) {
249                 arg = strdup( (char*)data );
250                 format = arg;
251                 fname_base = strchr( arg, '|' );
252                 if( fname_base ) {
253                         *fname_base = 0;
254                         fname_base++;
255                 }
256         }
257
258         res = ast_monitor_start( chan, format, fname_base, 1 );
259         if( res < 0 ) {
260                 res = ast_monitor_change_fname( chan, fname_base, 1 );
261         }
262
263         if( arg ) {
264                 free( arg );
265         }
266
267         return res;
268 }
269
270 static int stop_monitor_exec(struct ast_channel *chan, void *data)
271 {
272         return ast_monitor_stop( chan, 1 );
273 }
274
275 static int change_monitor_exec(struct ast_channel *chan, void *data)
276 {
277         return ast_monitor_change_fname( chan, (const char*)data, 1 );
278 }
279
280 static int start_monitor_action(struct mansession *s, struct message *m)
281 {
282         struct ast_channel *c = NULL;
283         char *name = astman_get_header(m, "Channel");
284         char *fname = astman_get_header(m, "File");
285         char *format = astman_get_header(m, "Format");
286         char *d;
287         
288         if((!name)||(!strlen(name))) {
289                 astman_send_error(s, m, "No channel specified");
290                 return 0;
291         }
292         c = ast_channel_walk(NULL);
293         while(c) {
294                 if (!strcasecmp(c->name, name)) {
295                         break;
296                 }
297                 c = ast_channel_walk(c);
298         }
299         if (!c) {
300                 astman_send_error(s, m, "No such channel");
301                 return 0;
302         }
303      
304         if( (!fname) || (!strlen(fname)) ) {
305                 // No filename base specified, default to channel name as per CLI
306                 fname = malloc (FILENAME_MAX);
307                 memset( fname, 0, FILENAME_MAX);
308                 strncpy( fname, c->name, FILENAME_MAX-1);
309                 // Channels have the format technology/channel_name - have to replace that / 
310                 if( (d=strchr( fname, '/')) ) *d='-';
311         }
312         
313         if( ast_monitor_start( c, format, fname, 1 ) ) {
314                 if( ast_monitor_change_fname( c, fname, 1 ) ) {
315                         astman_send_error(s, m, "Could not start monitoring channel");
316                         return 0;
317                 }
318         }
319         astman_send_ack(s, m, "Started monitoring channel");
320         return 0;
321 }
322
323 static int stop_monitor_action(struct mansession *s, struct message *m)
324 {
325         struct ast_channel *c = NULL;
326         char *name = astman_get_header(m, "Channel");
327         if((!name)||(!strlen(name))) {
328                 astman_send_error(s, m, "No channel specified");
329                 return 0;
330         }
331         c = ast_channel_walk(NULL);
332         while(c) {
333                 if (!strcasecmp(c->name, name)) {
334                         break;
335                 }
336                 c = ast_channel_walk(c);
337         }
338         if (!c) {
339                 astman_send_error(s, m, "No such channel");
340                 return 0;
341         }
342         if( ast_monitor_stop( c, 1 ) ) {
343                 astman_send_error(s, m, "Could not stop monitoring channel");
344                 return 0;
345         }
346         astman_send_ack(s, m, "Stopped monitoring channel");
347         return 0;
348 }
349
350 static int change_monitor_action(struct mansession *s, struct message *m)
351 {
352         struct ast_channel *c = NULL;
353         char *name = astman_get_header(m, "Channel");
354         char *fname = astman_get_header(m, "File");
355         if((!name) || (!strlen(name))) {
356                 astman_send_error(s, m, "No channel specified");
357                 return 0;
358         }
359         if ((!fname)||(!strlen(fname))) {
360                 astman_send_error(s, m, "No filename specified");
361                 return 0;
362         }
363         c = ast_channel_walk(NULL);
364         while(c) {
365                 if (!strcasecmp(c->name, name)) {
366                         break;
367                 }
368                 c = ast_channel_walk(c);
369         }
370         if (!c) {
371                 astman_send_error(s, m, "No such channel");
372                 return 0;
373         }
374         if( ast_monitor_change_fname( c, fname, 1 ) ) {
375                 astman_send_error(s, m, "Could not change monitored filename of channel");
376                 return 0;
377         }
378         astman_send_ack(s, m, "Stopped monitoring channel");
379         return 0;
380 }
381
382 int load_module(void)
383 {
384         ast_register_application( "Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip );
385         ast_register_application( "StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip );
386         ast_register_application( "ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip );
387         ast_manager_register( "Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis );
388         ast_manager_register( "StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis );
389         ast_manager_register( "ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis );
390
391         return 0;
392 }
393
394 int unload_module(void)
395 {
396         ast_unregister_application( "Monitor" );
397         ast_unregister_application( "StopMonitor" );
398         return 0;
399 }
400
401 char *description(void)
402 {
403         return "Call Monitoring Resource";
404 }
405
406 int usecount(void)
407 {
408         /* Never allow monitor to be unloaded because it will
409            unresolve needed symbols in the channel */
410 #if 0
411         int res;
412         STANDARD_USECOUNT(res);
413         return res;
414 #else
415         return 1;
416 #endif
417 }
418
419 char *key()
420 {
421         return ASTERISK_GPL_KEY;
422 }