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