Thu Apr 28 2011 17:15:25

Asterisk developer's documentation


res_monitor.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief PBX channel monitoring
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  */
00025  
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 258779 $")
00029 
00030 #include <sys/stat.h>
00031 #include <libgen.h>
00032 
00033 #include "asterisk/paths.h"   /* use ast_config_AST_MONITOR_DIR */
00034 #include "asterisk/lock.h"
00035 #include "asterisk/channel.h"
00036 #include "asterisk/file.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/manager.h"
00040 #include "asterisk/cli.h"
00041 #define AST_API_MODULE
00042 #include "asterisk/monitor.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/config.h"
00046 #include "asterisk/options.h"
00047 
00048 AST_MUTEX_DEFINE_STATIC(monitorlock);
00049 
00050 #define LOCK_IF_NEEDED(lock, needed) do { \
00051    if (needed) \
00052       ast_channel_lock(lock); \
00053    } while(0)
00054 
00055 #define UNLOCK_IF_NEEDED(lock, needed) do { \
00056    if (needed) \
00057       ast_channel_unlock(lock); \
00058    } while (0)
00059 
00060 static unsigned long seq = 0;
00061 
00062 static char *monitor_synopsis = "Monitor a channel";
00063 
00064 static char *monitor_descrip = "  Monitor([file_format[:urlbase],[fname_base],[options]]):\n"
00065 "Used to start monitoring a channel. The channel's input and output\n"
00066 "voice packets are logged to files until the channel hangs up or\n"
00067 "monitoring is stopped by the StopMonitor application.\n"
00068 "  file_format    optional, if not set, defaults to \"wav\"\n"
00069 "  fname_base     if set, changes the filename used to the one specified.\n"
00070 "  options:\n"
00071 "    m   - when the recording ends mix the two leg files into one and\n"
00072 "          delete the two leg files.  If the variable MONITOR_EXEC is set, the\n"
00073 "          application referenced in it will be executed instead of\n"
00074 #ifdef HAVE_SOXMIX
00075 "          soxmix and the raw leg files will NOT be deleted automatically.\n"
00076 "          soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
00077 #else
00078 "          sox and the raw leg files will NOT be deleted automatically.\n"
00079 "          sox or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
00080 #endif
00081 "          and a target mixed file name which is the same as the leg file names\n"
00082 "          only without the in/out designator.\n"
00083 "          If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
00084 "          additional arguments to MONITOR_EXEC\n"
00085 "          Both MONITOR_EXEC and the Mix flag can be set from the\n"
00086 "          administrator interface\n"
00087 "\n"
00088 "    b   - Don't begin recording unless a call is bridged to another channel\n"
00089 "    i   - Skip recording of input stream (disables m option)\n"
00090 "    o   - Skip recording of output stream (disables m option)\n"
00091 "\nBy default, files are stored to /var/spool/asterisk/monitor/.\n"
00092 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
00093 "monitored, otherwise 0.\n"
00094 ;
00095 
00096 static char *stopmonitor_synopsis = "Stop monitoring a channel";
00097 
00098 static char *stopmonitor_descrip = "  StopMonitor():\n"
00099    "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
00100 
00101 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
00102 
00103 static char *changemonitor_descrip = "  ChangeMonitor(filename_base):\n"
00104    "Changes monitoring filename of a channel. Has no effect if the channel is not monitored.\n"
00105    "The argument is the new filename base to use for monitoring this channel.\n";
00106 
00107 static char *pausemonitor_synopsis = "Pause monitoring of a channel";
00108 
00109 static char *pausemonitor_descrip = "  PauseMonitor():\n"
00110    "Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.\n";
00111 
00112 static char *unpausemonitor_synopsis = "Unpause monitoring of a channel";
00113 
00114 static char *unpausemonitor_descrip = "  UnpauseMonitor():\n"
00115    "Unpauses monitoring of a channel on which monitoring had\n"
00116    "previously been paused with PauseMonitor.\n";
00117 
00118 /*! 
00119  * \brief Change state of monitored channel 
00120  * \param chan 
00121  * \param state monitor state
00122  * \retval 0 on success.
00123  * \retval -1 on failure.
00124 */
00125 static int ast_monitor_set_state(struct ast_channel *chan, int state)
00126 {
00127    LOCK_IF_NEEDED(chan, 1);
00128    if (!chan->monitor) {
00129       UNLOCK_IF_NEEDED(chan, 1);
00130       return -1;
00131    }
00132    chan->monitor->state = state;
00133    UNLOCK_IF_NEEDED(chan, 1);
00134    return 0;
00135 }
00136 
00137 /*! \brief Start monitoring a channel
00138  * \param chan ast_channel struct to record
00139  * \param format_spec file format to use for recording
00140  * \param fname_base filename base to record to
00141  * \param need_lock whether to lock the channel mutex
00142  * \param stream_action whether to record the input and/or output streams.  X_REC_IN | X_REC_OUT is most often used
00143  * Creates the file to record, if no format is specified it assumes WAV
00144  * It also sets channel variable __MONITORED=yes
00145  * \retval 0 on success
00146  * \retval -1 on failure
00147  */
00148 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
00149       const char *fname_base, int need_lock, int stream_action)
00150 {
00151    int res = 0;
00152 
00153    LOCK_IF_NEEDED(chan, need_lock);
00154 
00155    if (!(chan->monitor)) {
00156       struct ast_channel_monitor *monitor;
00157       char *channel_name, *p;
00158 
00159       /* Create monitoring directory if needed */
00160       ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
00161 
00162       if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
00163          UNLOCK_IF_NEEDED(chan, need_lock);
00164          return -1;
00165       }
00166 
00167       /* Determine file names */
00168       if (!ast_strlen_zero(fname_base)) {
00169          int directory = strchr(fname_base, '/') ? 1 : 0;
00170          const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
00171          const char *absolute_suffix = *fname_base == '/' ? "" : "/";
00172 
00173          snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
00174                   absolute, absolute_suffix, fname_base);
00175          snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
00176                   absolute, absolute_suffix, fname_base);
00177          snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
00178                   absolute, absolute_suffix, fname_base);
00179 
00180          /* try creating the directory just in case it doesn't exist */
00181          if (directory) {
00182             char *name = ast_strdupa(monitor->filename_base);
00183             ast_mkdir(dirname(name), 0777);
00184          }
00185       } else {
00186          ast_mutex_lock(&monitorlock);
00187          snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
00188                   ast_config_AST_MONITOR_DIR, seq);
00189          snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
00190                   ast_config_AST_MONITOR_DIR, seq);
00191          seq++;
00192          ast_mutex_unlock(&monitorlock);
00193 
00194          channel_name = ast_strdupa(chan->name);
00195          while ((p = strchr(channel_name, '/'))) {
00196             *p = '-';
00197          }
00198          snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
00199                 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
00200          monitor->filename_changed = 1;
00201       }
00202 
00203       monitor->stop = ast_monitor_stop;
00204 
00205       /* Determine file format */
00206       if (!ast_strlen_zero(format_spec)) {
00207          monitor->format = ast_strdup(format_spec);
00208       } else {
00209          monitor->format = ast_strdup("wav");
00210       }
00211       
00212       /* open files */
00213       if (stream_action & X_REC_IN) {
00214          if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
00215             ast_filedelete(monitor->read_filename, NULL);
00216          if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
00217                      monitor->format, NULL,
00218                      O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
00219             ast_log(LOG_WARNING, "Could not create file %s\n",
00220                      monitor->read_filename);
00221             ast_free(monitor);
00222             UNLOCK_IF_NEEDED(chan, need_lock);
00223             return -1;
00224          }
00225       } else
00226          monitor->read_stream = NULL;
00227 
00228       if (stream_action & X_REC_OUT) {
00229          if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
00230             ast_filedelete(monitor->write_filename, NULL);
00231          }
00232          if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
00233                      monitor->format, NULL,
00234                      O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
00235             ast_log(LOG_WARNING, "Could not create file %s\n",
00236                      monitor->write_filename);
00237             ast_closestream(monitor->read_stream);
00238             ast_free(monitor);
00239             UNLOCK_IF_NEEDED(chan, need_lock);
00240             return -1;
00241          }
00242       } else
00243          monitor->write_stream = NULL;
00244 
00245       chan->monitor = monitor;
00246       ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
00247       /* so we know this call has been monitored in case we need to bill for it or something */
00248       pbx_builtin_setvar_helper(chan, "__MONITORED","true");
00249 
00250       manager_event(EVENT_FLAG_CALL, "MonitorStart",
00251                          "Channel: %s\r\n"
00252                           "Uniqueid: %s\r\n",                        
00253                            chan->name,
00254                          chan->uniqueid                        
00255                           );
00256    } else {
00257       ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
00258       res = -1;
00259    }
00260 
00261    UNLOCK_IF_NEEDED(chan, need_lock);
00262 
00263    return res;
00264 }
00265 
00266 /*!
00267  * \brief Get audio format.
00268  * \param format recording format.
00269  * The file format extensions that Asterisk uses are not all the same as that
00270  * which soxmix expects.  This function ensures that the format used as the
00271  * extension on the filename is something soxmix will understand.
00272  */
00273 static const char *get_soxmix_format(const char *format)
00274 {
00275    const char *res = format;
00276 
00277    if (!strcasecmp(format,"ulaw"))
00278       res = "ul";
00279    if (!strcasecmp(format,"alaw"))
00280       res = "al";
00281    
00282    return res;
00283 }
00284 
00285 /*! 
00286  * \brief Stop monitoring channel 
00287  * \param chan 
00288  * \param need_lock
00289  * Stop the recording, close any open streams, mix in/out channels if required
00290  * \return Always 0
00291 */
00292 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
00293 {
00294    int delfiles = 0;
00295 
00296    LOCK_IF_NEEDED(chan, need_lock);
00297 
00298    if (chan->monitor) {
00299       char filename[ FILENAME_MAX ];
00300 
00301       if (chan->monitor->read_stream) {
00302          ast_closestream(chan->monitor->read_stream);
00303       }
00304       if (chan->monitor->write_stream) {
00305          ast_closestream(chan->monitor->write_stream);
00306       }
00307 
00308       if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
00309          if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
00310             snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
00311             if (ast_fileexists(filename, NULL, NULL) > 0) {
00312                ast_filedelete(filename, NULL);
00313             }
00314             ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
00315          } else {
00316             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
00317          }
00318 
00319          if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
00320             snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
00321             if (ast_fileexists(filename, NULL, NULL) > 0) {
00322                ast_filedelete(filename, NULL);
00323             }
00324             ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
00325          } else {
00326             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
00327          }
00328       }
00329 
00330       if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
00331          char tmp[1024];
00332          char tmp2[1024];
00333          const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
00334          char *fname_base = chan->monitor->filename_base;
00335          const char *execute, *execute_args;
00336          /* at this point, fname_base really is the full path */
00337 
00338          /* Set the execute application */
00339          execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
00340          if (ast_strlen_zero(execute)) {
00341 #ifdef HAVE_SOXMIX
00342             execute = "nice -n 19 soxmix";
00343 #else
00344             execute = "nice -n 19 sox -m";
00345 #endif
00346             format = get_soxmix_format(format);
00347             delfiles = 1;
00348          } 
00349          execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
00350          if (ast_strlen_zero(execute_args)) {
00351             execute_args = "";
00352          }
00353          
00354          snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
00355             execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
00356          if (delfiles) {
00357             snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
00358             ast_copy_string(tmp, tmp2, sizeof(tmp));
00359          }
00360          ast_debug(1,"monitor executing %s\n",tmp);
00361          if (ast_safe_system(tmp) == -1)
00362             ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
00363       }
00364       
00365       ast_free(chan->monitor->format);
00366       ast_free(chan->monitor);
00367       chan->monitor = NULL;
00368 
00369       manager_event(EVENT_FLAG_CALL, "MonitorStop",
00370                          "Channel: %s\r\n"
00371                            "Uniqueid: %s\r\n",
00372                            chan->name,
00373                            chan->uniqueid
00374                            );
00375       pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
00376    }
00377    pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
00378 
00379    UNLOCK_IF_NEEDED(chan, need_lock);
00380 
00381    return 0;
00382 }
00383 
00384 
00385 /*! \brief Pause monitoring of channel */
00386 int ast_monitor_pause(struct ast_channel *chan)
00387 {
00388    return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
00389 }
00390 
00391 /*! \brief Unpause monitoring of channel */
00392 int ast_monitor_unpause(struct ast_channel *chan)
00393 {
00394    return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
00395 }
00396 
00397 /*! \brief Wrapper for ast_monitor_pause */
00398 static int pause_monitor_exec(struct ast_channel *chan, void *data)
00399 {
00400    return ast_monitor_pause(chan);
00401 }
00402 
00403 /*! \brief Wrapper for ast_monitor_unpause */
00404 static int unpause_monitor_exec(struct ast_channel *chan, void *data)
00405 {
00406    return ast_monitor_unpause(chan);
00407 }
00408 
00409 /*! 
00410  * \brief Change monitored filename of channel 
00411  * \param chan
00412  * \param fname_base new filename
00413  * \param need_lock
00414  * \retval 0 on success.
00415  * \retval -1 on failure.
00416 */
00417 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
00418 {
00419    if (ast_strlen_zero(fname_base)) {
00420       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
00421       return -1;
00422    }
00423 
00424    LOCK_IF_NEEDED(chan, need_lock);
00425 
00426    if (chan->monitor) {
00427       int directory = strchr(fname_base, '/') ? 1 : 0;
00428       const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
00429       const char *absolute_suffix = *fname_base == '/' ? "" : "/";
00430       char tmpstring[sizeof(chan->monitor->filename_base)] = "";
00431       int i, fd[2] = { -1, -1 }, doexit = 0;
00432 
00433       /* before continuing, see if we're trying to rename the file to itself... */
00434       snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
00435 
00436       /* try creating the directory just in case it doesn't exist */
00437       if (directory) {
00438          char *name = ast_strdupa(tmpstring);
00439          ast_mkdir(dirname(name), 0777);
00440       }
00441 
00442       /*!\note We cannot just compare filenames, due to symlinks, relative
00443        * paths, and other possible filesystem issues.  We could use
00444        * realpath(3), but its use is discouraged.  However, if we try to
00445        * create the same file from two different paths, the second will
00446        * fail, and so we have our notification that the filenames point to
00447        * the same path.
00448        *
00449        * Remember, also, that we're using the basename of the file (i.e.
00450        * the file without the format suffix), so it does not already exist
00451        * and we aren't interfering with the recording itself.
00452        */
00453       ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
00454       
00455       if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
00456          (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
00457          if (fd[0] < 0) {
00458             ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
00459          } else {
00460             ast_debug(2, "No need to rename monitor filename to itself\n");
00461          }
00462          doexit = 1;
00463       }
00464 
00465       /* Cleanup temporary files */
00466       for (i = 0; i < 2; i++) {
00467          if (fd[i] >= 0) {
00468             while (close(fd[i]) < 0 && errno == EINTR);
00469          }
00470       }
00471       unlink(tmpstring);
00472       /* if previous monitor file existed in a subdirectory, the directory will not be removed */
00473       unlink(chan->monitor->filename_base);
00474 
00475       if (doexit) {
00476          UNLOCK_IF_NEEDED(chan, need_lock);
00477          return 0;
00478       }
00479 
00480       ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
00481       chan->monitor->filename_changed = 1;
00482    } else {
00483       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
00484    }
00485 
00486    UNLOCK_IF_NEEDED(chan, need_lock);
00487 
00488    return 0;
00489 }
00490 
00491  
00492 /*!
00493  * \brief Start monitor
00494  * \param chan
00495  * \param data arguments passed fname|options
00496  * \retval 0 on success.
00497  * \retval -1 on failure.
00498 */
00499 static int start_monitor_exec(struct ast_channel *chan, void *data)
00500 {
00501    char *arg = NULL;
00502    char *options = NULL;
00503    char *delay = NULL;
00504    char *urlprefix = NULL;
00505    char tmp[256];
00506    int stream_action = X_REC_IN | X_REC_OUT;
00507    int joinfiles = 0;
00508    int waitforbridge = 0;
00509    int res = 0;
00510    char *parse;
00511    AST_DECLARE_APP_ARGS(args,
00512       AST_APP_ARG(format);
00513       AST_APP_ARG(fname_base);
00514       AST_APP_ARG(options);
00515    );
00516    
00517    /* Parse arguments. */
00518    if (ast_strlen_zero((char*)data)) {
00519       ast_log(LOG_ERROR, "Monitor requires an argument\n");
00520       return 0;
00521    }
00522 
00523    parse = ast_strdupa((char*)data);
00524    AST_STANDARD_APP_ARGS(args, parse);
00525 
00526    if (!ast_strlen_zero(args.options)) {
00527       if (strchr(args.options, 'm'))
00528          stream_action |= X_JOIN;
00529       if (strchr(args.options, 'b'))
00530          waitforbridge = 1;
00531       if (strchr(args.options, 'i'))
00532          stream_action &= ~X_REC_IN;
00533       if (strchr(args.options, 'o'))
00534          stream_action &= ~X_REC_OUT;
00535    }
00536 
00537    arg = strchr(args.format, ':');
00538    if (arg) {
00539       *arg++ = 0;
00540       urlprefix = arg;
00541    }
00542 
00543    if (urlprefix) {
00544       snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
00545          ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
00546       if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
00547          return -1;
00548       ast_cdr_setuserfield(chan, tmp);
00549    }
00550    if (waitforbridge) {
00551       /* We must remove the "b" option if listed.  In principle none of
00552          the following could give NULL results, but we check just to
00553          be pedantic. Reconstructing with checks for 'm' option does not
00554          work if we end up adding more options than 'm' in the future. */
00555       delay = ast_strdupa(data);
00556       options = strrchr(delay, ',');
00557       if (options) {
00558          arg = strchr(options, 'b');
00559          if (arg) {
00560             *arg = 'X';
00561             pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
00562          }
00563       }
00564       return 0;
00565    }
00566 
00567    res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
00568    if (res < 0)
00569       res = ast_monitor_change_fname(chan, args.fname_base, 1);
00570 
00571    if (stream_action & X_JOIN) {
00572       if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
00573          joinfiles = 1;
00574       else
00575          ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
00576    }
00577    ast_monitor_setjoinfiles(chan, joinfiles);
00578 
00579    return res;
00580 }
00581 
00582 /*! \brief Wrapper function \see ast_monitor_stop */
00583 static int stop_monitor_exec(struct ast_channel *chan, void *data)
00584 {
00585    return ast_monitor_stop(chan, 1);
00586 }
00587 
00588 /*! \brief Wrapper function \see ast_monitor_change_fname */
00589 static int change_monitor_exec(struct ast_channel *chan, void *data)
00590 {
00591    return ast_monitor_change_fname(chan, (const char*)data, 1);
00592 }
00593 
00594 static char start_monitor_action_help[] =
00595 "Description: The 'Monitor' action may be used to record the audio on a\n"
00596 "  specified channel.  The following parameters may be used to control\n"
00597 "  this:\n"
00598 "  Channel     - Required.  Used to specify the channel to record.\n"
00599 "  File        - Optional.  Is the name of the file created in the\n"
00600 "                monitor spool directory.  Defaults to the same name\n"
00601 "                as the channel (with slashes replaced with dashes).\n"
00602 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
00603 "                to \"wav\".\n"
00604 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
00605 "                the input and output channels together after the\n"
00606 "                recording is finished.\n";
00607 
00608 /*! \brief Start monitoring a channel by manager connection */
00609 static int start_monitor_action(struct mansession *s, const struct message *m)
00610 {
00611    struct ast_channel *c = NULL;
00612    const char *name = astman_get_header(m, "Channel");
00613    const char *fname = astman_get_header(m, "File");
00614    const char *format = astman_get_header(m, "Format");
00615    const char *mix = astman_get_header(m, "Mix");
00616    char *d;
00617 
00618    if (ast_strlen_zero(name)) {
00619       astman_send_error(s, m, "No channel specified");
00620       return 0;
00621    }
00622    c = ast_get_channel_by_name_locked(name);
00623    if (!c) {
00624       astman_send_error(s, m, "No such channel");
00625       return 0;
00626    }
00627 
00628    if (ast_strlen_zero(fname)) {
00629       /* No filename base specified, default to channel name as per CLI */    
00630       fname = ast_strdupa(c->name);
00631       /* Channels have the format technology/channel_name - have to replace that /  */
00632       if ((d = strchr(fname, '/'))) 
00633          *d = '-';
00634    }
00635 
00636    if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
00637       if (ast_monitor_change_fname(c, fname, 1)) {
00638          astman_send_error(s, m, "Could not start monitoring channel");
00639          ast_channel_unlock(c);
00640          return 0;
00641       }
00642    }
00643 
00644    if (ast_true(mix)) {
00645       ast_monitor_setjoinfiles(c, 1);
00646    }
00647 
00648    ast_channel_unlock(c);
00649    astman_send_ack(s, m, "Started monitoring channel");
00650    return 0;
00651 }
00652 
00653 static char stop_monitor_action_help[] =
00654 "Description: The 'StopMonitor' action may be used to end a previously\n"
00655 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
00656 "  of the channel monitored.\n";
00657 
00658 /*! \brief Stop monitoring a channel by manager connection */
00659 static int stop_monitor_action(struct mansession *s, const struct message *m)
00660 {
00661    struct ast_channel *c = NULL;
00662    const char *name = astman_get_header(m, "Channel");
00663    int res;
00664    if (ast_strlen_zero(name)) {
00665       astman_send_error(s, m, "No channel specified");
00666       return 0;
00667    }
00668    c = ast_get_channel_by_name_locked(name);
00669    if (!c) {
00670       astman_send_error(s, m, "No such channel");
00671       return 0;
00672    }
00673    res = ast_monitor_stop(c, 1);
00674    ast_channel_unlock(c);
00675    if (res) {
00676       astman_send_error(s, m, "Could not stop monitoring channel");
00677       return 0;
00678    }
00679    astman_send_ack(s, m, "Stopped monitoring channel");
00680    return 0;
00681 }
00682 
00683 static char change_monitor_action_help[] =
00684 "Description: The 'ChangeMonitor' action may be used to change the file\n"
00685 "  started by a previous 'Monitor' action.  The following parameters may\n"
00686 "  be used to control this:\n"
00687 "  Channel     - Required.  Used to specify the channel to record.\n"
00688 "  File        - Required.  Is the new name of the file created in the\n"
00689 "                monitor spool directory.\n";
00690 
00691 /*! \brief Change filename of a monitored channel by manager connection */
00692 static int change_monitor_action(struct mansession *s, const struct message *m)
00693 {
00694    struct ast_channel *c = NULL;
00695    const char *name = astman_get_header(m, "Channel");
00696    const char *fname = astman_get_header(m, "File");
00697    if (ast_strlen_zero(name)) {
00698       astman_send_error(s, m, "No channel specified");
00699       return 0;
00700    }
00701    if (ast_strlen_zero(fname)) {
00702       astman_send_error(s, m, "No filename specified");
00703       return 0;
00704    }
00705    c = ast_get_channel_by_name_locked(name);
00706    if (!c) {
00707       astman_send_error(s, m, "No such channel");
00708       return 0;
00709    }
00710    if (ast_monitor_change_fname(c, fname, 1)) {
00711       astman_send_error(s, m, "Could not change monitored filename of channel");
00712       ast_channel_unlock(c);
00713       return 0;
00714    }
00715    ast_channel_unlock(c);
00716    astman_send_ack(s, m, "Changed monitor filename");
00717    return 0;
00718 }
00719 
00720 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
00721 {
00722    if (chan->monitor)
00723       chan->monitor->joinfiles = turnon;
00724 }
00725 
00726 enum MONITOR_PAUSING_ACTION
00727 {
00728    MONITOR_ACTION_PAUSE,
00729    MONITOR_ACTION_UNPAUSE
00730 };
00731      
00732 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
00733 {
00734    struct ast_channel *c = NULL;
00735    const char *name = astman_get_header(m, "Channel");
00736    
00737    if (ast_strlen_zero(name)) {
00738       astman_send_error(s, m, "No channel specified");
00739       return -1;
00740    }
00741    
00742    c = ast_get_channel_by_name_locked(name);
00743    if (!c) {
00744       astman_send_error(s, m, "No such channel");
00745       return -1;
00746    }
00747 
00748    if (action == MONITOR_ACTION_PAUSE)
00749       ast_monitor_pause(c);
00750    else
00751       ast_monitor_unpause(c);
00752    
00753    ast_channel_unlock(c);
00754    astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
00755    return 0;   
00756 }
00757 
00758 static char pause_monitor_action_help[] =
00759    "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
00760    " recording of a channel.  The following parameters may\n"
00761    " be used to control this:\n"
00762    "  Channel     - Required.  Used to specify the channel to record.\n";
00763 
00764 static int pause_monitor_action(struct mansession *s, const struct message *m)
00765 {
00766    return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
00767 }
00768 
00769 static char unpause_monitor_action_help[] =
00770    "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
00771    "  of a channel after calling PauseMonitor.  The following parameters may\n"
00772    "  be used to control this:\n"
00773    "  Channel     - Required.  Used to specify the channel to record.\n";
00774 
00775 static int unpause_monitor_action(struct mansession *s, const struct message *m)
00776 {
00777    return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
00778 }
00779    
00780 
00781 static int load_module(void)
00782 {
00783    ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
00784    ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
00785    ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
00786    ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
00787    ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
00788    ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
00789    ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
00790    ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
00791    ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
00792    ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
00793 
00794    return AST_MODULE_LOAD_SUCCESS;
00795 }
00796 
00797 static int unload_module(void)
00798 {
00799    ast_unregister_application("Monitor");
00800    ast_unregister_application("StopMonitor");
00801    ast_unregister_application("ChangeMonitor");
00802    ast_unregister_application("PauseMonitor");
00803    ast_unregister_application("UnpauseMonitor");
00804    ast_manager_unregister("Monitor");
00805    ast_manager_unregister("StopMonitor");
00806    ast_manager_unregister("ChangeMonitor");
00807    ast_manager_unregister("PauseMonitor");
00808    ast_manager_unregister("UnpauseMonitor");
00809 
00810    return 0;
00811 }
00812 
00813 /* usecount semantics need to be defined */
00814 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
00815       .load = load_module,
00816       .unload = unload_module,
00817       );