Mon Jun 18 2012 03:55:39

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