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

Generated on Wed Aug 18 22:33:55 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7