Merge Mahmut's recording patches
[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 pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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_pthread_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 char *get_header(struct message *m, char *var)
279 {
280         char cmp[80];
281         int x;
282         snprintf(cmp, sizeof(cmp), "%s: ", var);
283         for (x=0;x<m->hdrcount;x++)
284                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
285                         return m->headers[x] + strlen(cmp);
286         return NULL;
287 }
288
289 static void send_error(struct mansession *s, char *error)
290 {
291         ast_pthread_mutex_lock(&s->lock);
292         ast_cli(s->fd, "Response: Error\r\n");
293         ast_cli(s->fd, "Message: %s\r\n\r\n", error);
294         ast_pthread_mutex_unlock(&s->lock);
295 }
296
297 static void send_response(struct mansession *s, char *resp, char *msg)
298 {
299         ast_pthread_mutex_lock(&s->lock);
300         ast_cli(s->fd, "Response: %s\r\n", resp);
301         if (msg)
302                 ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
303         else
304                 ast_cli(s->fd, "\r\n");
305         ast_pthread_mutex_unlock(&s->lock);
306 }
307
308 static void send_ack(struct mansession *s, char *msg)
309 {
310         send_response(s, "Success", msg);
311 }
312
313 static int start_monitor_action(struct mansession *s, struct message *m)
314 {
315         struct ast_channel *c = NULL;
316         char *name = get_header(m, "Channel");
317         char *fname = get_header(m, "File");
318         char *format = get_header(m, "Format");
319         if((!name)||(!strlen(name))) {
320                 send_error(s, "No channel specified");
321                 return 0;
322         }
323         c = ast_channel_walk(NULL);
324         while(c) {
325                 if (!strcasecmp(c->name, name)) {
326                         break;
327                 }
328                 c = ast_channel_walk(c);
329         }
330         if (!c) {
331                 send_error(s, "No such channel");
332                 return 0;
333         }
334         if( ast_monitor_start( c, format, fname, 1 ) ) {
335                 if( ast_monitor_change_fname( c, fname, 1 ) ) {
336                         send_error(s, "Could not start monitoring channel");
337                         return 0;
338                 }
339         }
340         send_ack(s, "Started monitoring channel");
341         return 0;
342 }
343
344 static int stop_monitor_action(struct mansession *s, struct message *m)
345 {
346         struct ast_channel *c = NULL;
347         char *name = get_header(m, "Channel");
348         if((!name)||(!strlen(name))) {
349                 send_error(s, "No channel specified");
350                 return 0;
351         }
352         c = ast_channel_walk(NULL);
353         while(c) {
354                 if (!strcasecmp(c->name, name)) {
355                         break;
356                 }
357                 c = ast_channel_walk(c);
358         }
359         if (!c) {
360                 send_error(s, "No such channel");
361                 return 0;
362         }
363         if( ast_monitor_stop( c, 1 ) ) {
364                 send_error(s, "Could not stop monitoring channel");
365                 return 0;
366         }
367         send_ack(s, "Stopped monitoring channel");
368         return 0;
369 }
370
371 static int change_monitor_action(struct mansession *s, struct message *m)
372 {
373         struct ast_channel *c = NULL;
374         char *name = get_header(m, "Channel");
375         char *fname = get_header(m, "File");
376         if((!name) || (!strlen(name))) {
377                 send_error(s, "No channel specified");
378                 return 0;
379         }
380         if ((!fname)||(!strlen(fname))) {
381                 send_error(s, "No filename specified");
382                 return 0;
383         }
384         c = ast_channel_walk(NULL);
385         while(c) {
386                 if (!strcasecmp(c->name, name)) {
387                         break;
388                 }
389                 c = ast_channel_walk(c);
390         }
391         if (!c) {
392                 send_error(s, "No such channel");
393                 return 0;
394         }
395         if( ast_monitor_change_fname( c, fname, 1 ) ) {
396                 send_error(s, "Could not change monitored filename of channel");
397                 return 0;
398         }
399         send_ack(s, "Stopped monitoring channel");
400         return 0;
401 }
402
403 int load_module(void)
404 {
405         ast_register_application( "Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip );
406         ast_register_application( "StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip );
407         ast_register_application( "ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip );
408         ast_manager_register( "Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis );
409         ast_manager_register( "StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis );
410         ast_manager_register( "ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis );
411
412         return 0;
413 }
414
415 int unload_module(void)
416 {
417         ast_unregister_application( "Monitor" );
418         ast_unregister_application( "StopMonitor" );
419         return 0;
420 }
421
422 char *description(void)
423 {
424         return "Call Monitoring Resource";
425 }
426
427 int usecount(void)
428 {
429         /* Never allow monitor to be unloaded because it will
430            unresolve needed symbols in the channel */
431 #if 0
432         int res;
433         STANDARD_USECOUNT(res);
434         return res;
435 #else
436         return 1;
437 #endif
438 }
439
440 char *key()
441 {
442         return ASTERISK_GPL_KEY;
443 }