00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <errno.h>
00028 #include <string.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <libgen.h>
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 43924 $")
00036
00037 #include "asterisk/lock.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/module.h"
00043 #include "asterisk/manager.h"
00044 #include "asterisk/cli.h"
00045 #include "asterisk/monitor.h"
00046 #include "asterisk/app.h"
00047 #include "asterisk/utils.h"
00048 #include "asterisk/config.h"
00049
00050 AST_MUTEX_DEFINE_STATIC(monitorlock);
00051
00052 static unsigned long seq = 0;
00053
00054 static char *monitor_synopsis = "Monitor a channel";
00055
00056 static char *monitor_descrip = "Monitor([file_format[:urlbase]|[fname_base]|[options]]):\n"
00057 "Used to start monitoring a channel. The channel's input and output\n"
00058 "voice packets are logged to files until the channel hangs up or\n"
00059 "monitoring is stopped by the StopMonitor application.\n"
00060 " file_format optional, if not set, defaults to \"wav\"\n"
00061 " fname_base if set, changes the filename used to the one specified.\n"
00062 " options:\n"
00063 " m - when the recording ends mix the two leg files into one and\n"
00064 " delete the two leg files. If the variable MONITOR_EXEC is set, the\n"
00065 " application referenced in it will be executed instead of\n"
00066 " soxmix and the raw leg files will NOT be deleted automatically.\n"
00067 " soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
00068 " and a target mixed file name which is the same as the leg file names\n"
00069 " only without the in/out designator.\n"
00070 " If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
00071 " additional arguements to MONITOR_EXEC\n"
00072 " Both MONITOR_EXEC and the Mix flag can be set from the\n"
00073 " administrator interface\n"
00074 "\n"
00075 " b - Don't begin recording unless a call is bridged to another channel\n"
00076 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
00077 "monitored, otherwise 0.\n"
00078 ;
00079
00080 static char *stopmonitor_synopsis = "Stop monitoring a channel";
00081
00082 static char *stopmonitor_descrip = "StopMonitor\n"
00083 "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
00084
00085 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
00086
00087 static char *changemonitor_descrip = "ChangeMonitor(filename_base)\n"
00088 "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
00089 "The argument is the new filename base to use for monitoring this channel.\n";
00090
00091
00092 int ast_monitor_start( struct ast_channel *chan, const char *format_spec,
00093 const char *fname_base, int need_lock)
00094 {
00095 int res = 0;
00096 char tmp[256];
00097
00098 if (need_lock) {
00099 if (ast_mutex_lock(&chan->lock)) {
00100 ast_log(LOG_WARNING, "Unable to lock channel\n");
00101 return -1;
00102 }
00103 }
00104
00105 if (!(chan->monitor)) {
00106 struct ast_channel_monitor *monitor;
00107 char *channel_name, *p;
00108
00109
00110 if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) {
00111 if (errno != EEXIST) {
00112 ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
00113 strerror(errno));
00114 }
00115 }
00116
00117 monitor = malloc(sizeof(struct ast_channel_monitor));
00118 if (!monitor) {
00119 if (need_lock)
00120 ast_mutex_unlock(&chan->lock);
00121 return -1;
00122 }
00123 memset(monitor, 0, sizeof(struct ast_channel_monitor));
00124
00125
00126 if (!ast_strlen_zero(fname_base)) {
00127 int directory = strchr(fname_base, '/') ? 1 : 0;
00128
00129 if (directory) {
00130 char *name = strdup(fname_base);
00131 snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name));
00132 free(name);
00133 ast_safe_system(tmp);
00134 }
00135 snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
00136 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
00137 snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
00138 directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
00139 ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
00140 } else {
00141 ast_mutex_lock(&monitorlock);
00142 snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
00143 ast_config_AST_MONITOR_DIR, seq);
00144 snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
00145 ast_config_AST_MONITOR_DIR, seq);
00146 seq++;
00147 ast_mutex_unlock(&monitorlock);
00148
00149 if((channel_name = ast_strdupa(chan->name))) {
00150 while((p = strchr(channel_name, '/'))) {
00151 *p = '-';
00152 }
00153 snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
00154 ast_config_AST_MONITOR_DIR, (int)time(NULL),channel_name);
00155 monitor->filename_changed = 1;
00156 } else {
00157 ast_log(LOG_ERROR,"Failed to allocate Memory\n");
00158 return -1;
00159 }
00160 }
00161
00162 monitor->stop = ast_monitor_stop;
00163
00164
00165 if (!ast_strlen_zero(format_spec)) {
00166 monitor->format = strdup(format_spec);
00167 } else {
00168 monitor->format = strdup("wav");
00169 }
00170
00171
00172 if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) {
00173 ast_filedelete(monitor->read_filename, NULL);
00174 }
00175 if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
00176 monitor->format, NULL,
00177 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
00178 ast_log(LOG_WARNING, "Could not create file %s\n",
00179 monitor->read_filename);
00180 free(monitor);
00181 ast_mutex_unlock(&chan->lock);
00182 return -1;
00183 }
00184 if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
00185 ast_filedelete(monitor->write_filename, NULL);
00186 }
00187 if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
00188 monitor->format, NULL,
00189 O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
00190 ast_log(LOG_WARNING, "Could not create file %s\n",
00191 monitor->write_filename);
00192 ast_closestream(monitor->read_stream);
00193 free(monitor);
00194 ast_mutex_unlock(&chan->lock);
00195 return -1;
00196 }
00197 chan->monitor = monitor;
00198
00199 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
00200 } else {
00201 ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
00202 chan->name);
00203 res = -1;
00204 }
00205
00206 if (need_lock) {
00207 ast_mutex_unlock(&chan->lock);
00208 }
00209 return res;
00210 }
00211
00212
00213 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
00214 {
00215 char *execute, *execute_args;
00216 int delfiles = 0;
00217
00218 if (need_lock) {
00219 if (ast_mutex_lock(&chan->lock)) {
00220 ast_log(LOG_WARNING, "Unable to lock channel\n");
00221 return -1;
00222 }
00223 }
00224
00225 if (chan->monitor) {
00226 char filename[ FILENAME_MAX ];
00227
00228 if (chan->monitor->read_stream) {
00229 ast_closestream(chan->monitor->read_stream);
00230 }
00231 if (chan->monitor->write_stream) {
00232 ast_closestream(chan->monitor->write_stream);
00233 }
00234
00235 if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
00236 if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
00237 snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
00238 if (ast_fileexists(filename, NULL, NULL) > 0) {
00239 ast_filedelete(filename, NULL);
00240 }
00241 ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
00242 } else {
00243 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
00244 }
00245
00246 if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
00247 snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
00248 if (ast_fileexists(filename, NULL, NULL) > 0) {
00249 ast_filedelete(filename, NULL);
00250 }
00251 ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
00252 } else {
00253 ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
00254 }
00255 }
00256
00257 if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
00258 char tmp[1024];
00259 char tmp2[1024];
00260 char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
00261 char *name = chan->monitor->filename_base;
00262 int directory = strchr(name, '/') ? 1 : 0;
00263 char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
00264
00265
00266 execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
00267 if (ast_strlen_zero(execute)) {
00268 execute = "nice -n 19 soxmix";
00269 delfiles = 1;
00270 }
00271 execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
00272 if (ast_strlen_zero(execute_args)) {
00273 execute_args = "";
00274 }
00275
00276 snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
00277 if (delfiles) {
00278 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name);
00279 ast_copy_string(tmp, tmp2, sizeof(tmp));
00280 }
00281 ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
00282 if (ast_safe_system(tmp) == -1)
00283 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
00284 }
00285
00286 free(chan->monitor->format);
00287 free(chan->monitor);
00288 chan->monitor = NULL;
00289 }
00290
00291 if (need_lock)
00292 ast_mutex_unlock(&chan->lock);
00293 return 0;
00294 }
00295
00296
00297 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
00298 {
00299 char tmp[256];
00300 if (ast_strlen_zero(fname_base)) {
00301 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
00302 return -1;
00303 }
00304
00305 if (need_lock) {
00306 if (ast_mutex_lock(&chan->lock)) {
00307 ast_log(LOG_WARNING, "Unable to lock channel\n");
00308 return -1;
00309 }
00310 }
00311
00312 if (chan->monitor) {
00313 int directory = strchr(fname_base, '/') ? 1 : 0;
00314
00315 if (directory) {
00316 char *name = strdup(fname_base);
00317 snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
00318 free(name);
00319 ast_safe_system(tmp);
00320 }
00321
00322 snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
00323 } else {
00324 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
00325 }
00326
00327 if (need_lock)
00328 ast_mutex_unlock(&chan->lock);
00329
00330 return 0;
00331 }
00332
00333 static int start_monitor_exec(struct ast_channel *chan, void *data)
00334 {
00335 char *arg = NULL;
00336 char *format = NULL;
00337 char *fname_base = NULL;
00338 char *options = NULL;
00339 char *delay = NULL;
00340 char *urlprefix = NULL;
00341 char tmp[256];
00342 int joinfiles = 0;
00343 int waitforbridge = 0;
00344 int res = 0;
00345
00346
00347 if (!ast_strlen_zero((char*)data)) {
00348 arg = ast_strdupa((char*)data);
00349 format = arg;
00350 fname_base = strchr(arg, '|');
00351 if (fname_base) {
00352 *fname_base = 0;
00353 fname_base++;
00354 if ((options = strchr(fname_base, '|'))) {
00355 *options = 0;
00356 options++;
00357 if (strchr(options, 'm'))
00358 joinfiles = 1;
00359 if (strchr(options, 'b'))
00360 waitforbridge = 1;
00361 }
00362 }
00363 arg = strchr(format,':');
00364 if (arg) {
00365 *arg++ = 0;
00366 urlprefix = arg;
00367 }
00368 }
00369 if (urlprefix) {
00370 snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
00371 ((strcmp(format,"gsm")) ? "wav" : "gsm"));
00372 if (!chan->cdr)
00373 chan->cdr = ast_cdr_alloc();
00374 ast_cdr_setuserfield(chan, tmp);
00375 }
00376 if (waitforbridge) {
00377
00378
00379
00380
00381 delay = ast_strdupa((char*)data);
00382 if (delay) {
00383 options = strrchr(delay, '|');
00384 if (options) {
00385 arg = strchr(options, 'b');
00386 if (arg) {
00387 *arg = 'X';
00388 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
00389 }
00390 }
00391 }
00392 return 0;
00393 }
00394
00395 res = ast_monitor_start(chan, format, fname_base, 1);
00396 if (res < 0)
00397 res = ast_monitor_change_fname(chan, fname_base, 1);
00398 ast_monitor_setjoinfiles(chan, joinfiles);
00399
00400 return res;
00401 }
00402
00403 static int stop_monitor_exec(struct ast_channel *chan, void *data)
00404 {
00405 return ast_monitor_stop(chan, 1);
00406 }
00407
00408 static int change_monitor_exec(struct ast_channel *chan, void *data)
00409 {
00410 return ast_monitor_change_fname(chan, (const char*)data, 1);
00411 }
00412
00413 static char start_monitor_action_help[] =
00414 "Description: The 'Monitor' action may be used to record the audio on a\n"
00415 " specified channel. The following parameters may be used to control\n"
00416 " this:\n"
00417 " Channel - Required. Used to specify the channel to record.\n"
00418 " File - Optional. Is the name of the file created in the\n"
00419 " monitor spool directory. Defaults to the same name\n"
00420 " as the channel (with slashes replaced with dashes).\n"
00421 " Format - Optional. Is the audio recording format. Defaults\n"
00422 " to \"wav\".\n"
00423 " Mix - Optional. Boolean parameter as to whether to mix\n"
00424 " the input and output channels together after the\n"
00425 " recording is finished.\n";
00426
00427 static int start_monitor_action(struct mansession *s, struct message *m)
00428 {
00429 struct ast_channel *c = NULL;
00430 char *name = astman_get_header(m, "Channel");
00431 char *fname = astman_get_header(m, "File");
00432 char *format = astman_get_header(m, "Format");
00433 char *mix = astman_get_header(m, "Mix");
00434 char *d;
00435
00436 if (ast_strlen_zero(name)) {
00437 astman_send_error(s, m, "No channel specified");
00438 return 0;
00439 }
00440 c = ast_get_channel_by_name_locked(name);
00441 if (!c) {
00442 astman_send_error(s, m, "No such channel");
00443 return 0;
00444 }
00445
00446 if (ast_strlen_zero(fname)) {
00447
00448 fname = malloc (FILENAME_MAX);
00449 if (!fname) {
00450 astman_send_error(s, m, "Could not start monitoring channel");
00451 ast_mutex_unlock(&c->lock);
00452 return 0;
00453 }
00454 memset(fname, 0, FILENAME_MAX);
00455 ast_copy_string(fname, c->name, FILENAME_MAX);
00456
00457 if ((d=strchr(fname, '/'))) *d='-';
00458 }
00459
00460 if (ast_monitor_start(c, format, fname, 1)) {
00461 if (ast_monitor_change_fname(c, fname, 1)) {
00462 astman_send_error(s, m, "Could not start monitoring channel");
00463 ast_mutex_unlock(&c->lock);
00464 return 0;
00465 }
00466 }
00467
00468 if (ast_true(mix)) {
00469 ast_monitor_setjoinfiles(c, 1);
00470 }
00471
00472 ast_mutex_unlock(&c->lock);
00473 astman_send_ack(s, m, "Started monitoring channel");
00474 return 0;
00475 }
00476
00477 static char stop_monitor_action_help[] =
00478 "Description: The 'StopMonitor' action may be used to end a previously\n"
00479 " started 'Monitor' action. The only parameter is 'Channel', the name\n"
00480 " of the channel monitored.\n";
00481
00482 static int stop_monitor_action(struct mansession *s, struct message *m)
00483 {
00484 struct ast_channel *c = NULL;
00485 char *name = astman_get_header(m, "Channel");
00486 int res;
00487 if (ast_strlen_zero(name)) {
00488 astman_send_error(s, m, "No channel specified");
00489 return 0;
00490 }
00491 c = ast_get_channel_by_name_locked(name);
00492 if (!c) {
00493 astman_send_error(s, m, "No such channel");
00494 return 0;
00495 }
00496 res = ast_monitor_stop(c, 1);
00497 ast_mutex_unlock(&c->lock);
00498 if (res) {
00499 astman_send_error(s, m, "Could not stop monitoring channel");
00500 return 0;
00501 }
00502 astman_send_ack(s, m, "Stopped monitoring channel");
00503 return 0;
00504 }
00505
00506 static char change_monitor_action_help[] =
00507 "Description: The 'ChangeMonitor' action may be used to change the file\n"
00508 " started by a previous 'Monitor' action. The following parameters may\n"
00509 " be used to control this:\n"
00510 " Channel - Required. Used to specify the channel to record.\n"
00511 " File - Required. Is the new name of the file created in the\n"
00512 " monitor spool directory.\n";
00513
00514 static int change_monitor_action(struct mansession *s, struct message *m)
00515 {
00516 struct ast_channel *c = NULL;
00517 char *name = astman_get_header(m, "Channel");
00518 char *fname = astman_get_header(m, "File");
00519 if (ast_strlen_zero(name)) {
00520 astman_send_error(s, m, "No channel specified");
00521 return 0;
00522 }
00523 if (ast_strlen_zero(fname)) {
00524 astman_send_error(s, m, "No filename specified");
00525 return 0;
00526 }
00527 c = ast_get_channel_by_name_locked(name);
00528 if (!c) {
00529 astman_send_error(s, m, "No such channel");
00530 return 0;
00531 }
00532 if (ast_monitor_change_fname(c, fname, 1)) {
00533 astman_send_error(s, m, "Could not change monitored filename of channel");
00534 ast_mutex_unlock(&c->lock);
00535 return 0;
00536 }
00537 ast_mutex_unlock(&c->lock);
00538 astman_send_ack(s, m, "Changed monitor filename");
00539 return 0;
00540 }
00541
00542 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
00543 {
00544 if (chan->monitor)
00545 chan->monitor->joinfiles = turnon;
00546 }
00547
00548 int load_module(void)
00549 {
00550 ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
00551 ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
00552 ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
00553 ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
00554 ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
00555 ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
00556
00557 return 0;
00558 }
00559
00560 int unload_module(void)
00561 {
00562 ast_unregister_application("Monitor");
00563 ast_unregister_application("StopMonitor");
00564 ast_unregister_application("ChangeMonitor");
00565 ast_manager_unregister("Monitor");
00566 ast_manager_unregister("StopMonitor");
00567 ast_manager_unregister("ChangeMonitor");
00568 return 0;
00569 }
00570
00571 char *description(void)
00572 {
00573 return "Call Monitoring Resource";
00574 }
00575
00576 int usecount(void)
00577 {
00578
00579
00580 #if 0
00581 int res;
00582 STANDARD_USECOUNT(res);
00583 return res;
00584 #else
00585 return 1;
00586 #endif
00587 }
00588
00589 char *key()
00590 {
00591 return ASTERISK_GPL_KEY;
00592 }