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

Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1