Wed Apr 6 11:29:47 2011

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

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7