Thu Apr 8 01:21:01 2010

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

Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1