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

Generated by  doxygen 1.6.2