Mon Mar 19 11:30:29 2012

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: 352959 $")
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          channel_name = ast_strdupa(chan->name);
00337          while ((p = strchr(channel_name, '/'))) {
00338             *p = '-';
00339          }
00340          snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
00341                 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
00342          monitor->filename_changed = 1;
00343       }
00344 
00345       monitor->stop = ast_monitor_stop;
00346 
00347       /* Determine file format */
00348       if (!ast_strlen_zero(format_spec)) {
00349          monitor->format = ast_strdup(format_spec);
00350       } else {
00351          monitor->format = ast_strdup("wav");
00352       }
00353       
00354       /* open files */
00355       if (stream_action & X_REC_IN) {
00356          if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
00357             ast_filedelete(monitor->read_filename, NULL);
00358          if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
00359                      monitor->format, NULL,
00360                      O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
00361             ast_log(LOG_WARNING, "Could not create file %s\n",
00362                      monitor->read_filename);
00363             ast_free(monitor);
00364             UNLOCK_IF_NEEDED(chan, need_lock);
00365             return -1;
00366          }
00367       } else
00368          monitor->read_stream = NULL;
00369 
00370       if (stream_action & X_REC_OUT) {
00371          if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
00372             ast_filedelete(monitor->write_filename, NULL);
00373          }
00374          if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
00375                      monitor->format, NULL,
00376                      O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
00377             ast_log(LOG_WARNING, "Could not create file %s\n",
00378                      monitor->write_filename);
00379             ast_closestream(monitor->read_stream);
00380             ast_free(monitor);
00381             UNLOCK_IF_NEEDED(chan, need_lock);
00382             return -1;
00383          }
00384       } else
00385          monitor->write_stream = NULL;
00386 
00387       chan->monitor = monitor;
00388       ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
00389       /* so we know this call has been monitored in case we need to bill for it or something */
00390       pbx_builtin_setvar_helper(chan, "__MONITORED","true");
00391 
00392       ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart",
00393                          "Channel: %s\r\n"
00394                        "Uniqueid: %s\r\n",
00395                            chan->name,
00396                          chan->uniqueid);
00397    } else {
00398       ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
00399       res = -1;
00400    }
00401 
00402    UNLOCK_IF_NEEDED(chan, need_lock);
00403 
00404    return res;
00405 }
00406 
00407 /*!
00408  * \brief Get audio format.
00409  * \param format recording format.
00410  * The file format extensions that Asterisk uses are not all the same as that
00411  * which soxmix expects.  This function ensures that the format used as the
00412  * extension on the filename is something soxmix will understand.
00413  */
00414 static const char *get_soxmix_format(const char *format)
00415 {
00416    const char *res = format;
00417 
00418    if (!strcasecmp(format,"ulaw"))
00419       res = "ul";
00420    if (!strcasecmp(format,"alaw"))
00421       res = "al";
00422    
00423    return res;
00424 }
00425 
00426 /*! 
00427  * \brief Stop monitoring channel 
00428  * \param chan 
00429  * \param need_lock
00430  * Stop the recording, close any open streams, mix in/out channels if required
00431  * \return Always 0
00432 */
00433 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
00434 {
00435    int delfiles = 0;
00436 
00437    LOCK_IF_NEEDED(chan, need_lock);
00438 
00439    if (chan->monitor) {
00440       char filename[ FILENAME_MAX ];
00441 
00442       if (chan->monitor->read_stream) {
00443          ast_closestream(chan->monitor->read_stream);
00444       }
00445       if (chan->monitor->write_stream) {
00446          ast_closestream(chan->monitor->write_stream);
00447       }
00448 
00449       if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
00450          if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
00451             snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
00452             if (ast_fileexists(filename, NULL, NULL) > 0) {
00453                ast_filedelete(filename, NULL);
00454             }
00455             ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
00456          } else {
00457             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
00458          }
00459 
00460          if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
00461             snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
00462             if (ast_fileexists(filename, NULL, NULL) > 0) {
00463                ast_filedelete(filename, NULL);
00464             }
00465             ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
00466          } else {
00467             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
00468          }
00469       }
00470 
00471       if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
00472          char tmp[1024];
00473          char tmp2[1024];
00474          const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
00475          char *fname_base = chan->monitor->filename_base;
00476          const char *execute, *execute_args;
00477          /* at this point, fname_base really is the full path */
00478 
00479          /* Set the execute application */
00480          execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
00481          if (ast_strlen_zero(execute)) {
00482 #ifdef HAVE_SOXMIX
00483             execute = "nice -n 19 soxmix";
00484 #else
00485             execute = "nice -n 19 sox -m";
00486 #endif
00487             format = get_soxmix_format(format);
00488             delfiles = 1;
00489          } 
00490          execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
00491          if (ast_strlen_zero(execute_args)) {
00492             execute_args = "";
00493          }
00494          
00495          snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
00496             execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
00497          if (delfiles) {
00498             snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
00499             ast_copy_string(tmp, tmp2, sizeof(tmp));
00500          }
00501          ast_debug(1,"monitor executing %s\n",tmp);
00502          if (ast_safe_system(tmp) == -1)
00503             ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
00504       }
00505       
00506       ast_free(chan->monitor->format);
00507       ast_free(chan->monitor);
00508       chan->monitor = NULL;
00509 
00510       ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop",
00511                          "Channel: %s\r\n"
00512                            "Uniqueid: %s\r\n",
00513                            chan->name,
00514                            chan->uniqueid
00515                            );
00516       pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
00517    }
00518    pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
00519 
00520    UNLOCK_IF_NEEDED(chan, need_lock);
00521 
00522    return 0;
00523 }
00524 
00525 
00526 /*! \brief Pause monitoring of channel */
00527 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
00528 {
00529    return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
00530 }
00531 
00532 /*! \brief Unpause monitoring of channel */
00533 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
00534 {
00535    return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
00536 }
00537 
00538 /*! \brief Wrapper for ast_monitor_pause */
00539 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
00540 {
00541    return ast_monitor_pause(chan);
00542 }
00543 
00544 /*! \brief Wrapper for ast_monitor_unpause */
00545 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
00546 {
00547    return ast_monitor_unpause(chan);
00548 }
00549 
00550 /*! 
00551  * \brief Change monitored filename of channel 
00552  * \param chan
00553  * \param fname_base new filename
00554  * \param need_lock
00555  * \retval 0 on success.
00556  * \retval -1 on failure.
00557 */
00558 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
00559 {
00560    if (ast_strlen_zero(fname_base)) {
00561       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
00562       return -1;
00563    }
00564 
00565    LOCK_IF_NEEDED(chan, need_lock);
00566 
00567    if (chan->monitor) {
00568       int directory = strchr(fname_base, '/') ? 1 : 0;
00569       const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
00570       const char *absolute_suffix = *fname_base == '/' ? "" : "/";
00571       char tmpstring[sizeof(chan->monitor->filename_base)] = "";
00572       int i, fd[2] = { -1, -1 }, doexit = 0;
00573 
00574       /* before continuing, see if we're trying to rename the file to itself... */
00575       snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
00576 
00577       /* try creating the directory just in case it doesn't exist */
00578       if (directory) {
00579          char *name = ast_strdupa(tmpstring);
00580          ast_mkdir(dirname(name), 0777);
00581       }
00582 
00583       /*!\note We cannot just compare filenames, due to symlinks, relative
00584        * paths, and other possible filesystem issues.  We could use
00585        * realpath(3), but its use is discouraged.  However, if we try to
00586        * create the same file from two different paths, the second will
00587        * fail, and so we have our notification that the filenames point to
00588        * the same path.
00589        *
00590        * Remember, also, that we're using the basename of the file (i.e.
00591        * the file without the format suffix), so it does not already exist
00592        * and we aren't interfering with the recording itself.
00593        */
00594       ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
00595       
00596       if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
00597          (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
00598          if (fd[0] < 0) {
00599             ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
00600          } else {
00601             ast_debug(2, "No need to rename monitor filename to itself\n");
00602          }
00603          doexit = 1;
00604       }
00605 
00606       /* Cleanup temporary files */
00607       for (i = 0; i < 2; i++) {
00608          if (fd[i] >= 0) {
00609             while (close(fd[i]) < 0 && errno == EINTR);
00610          }
00611       }
00612       unlink(tmpstring);
00613       /* if previous monitor file existed in a subdirectory, the directory will not be removed */
00614       unlink(chan->monitor->filename_base);
00615 
00616       if (doexit) {
00617          UNLOCK_IF_NEEDED(chan, need_lock);
00618          return 0;
00619       }
00620 
00621       ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
00622       chan->monitor->filename_changed = 1;
00623    } else {
00624       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
00625    }
00626 
00627    UNLOCK_IF_NEEDED(chan, need_lock);
00628 
00629    return 0;
00630 }
00631 
00632  
00633 /*!
00634  * \brief Start monitor
00635  * \param chan
00636  * \param data arguments passed fname|options
00637  * \retval 0 on success.
00638  * \retval -1 on failure.
00639 */
00640 static int start_monitor_exec(struct ast_channel *chan, const char *data)
00641 {
00642    char *arg = NULL;
00643    char *options = NULL;
00644    char *delay = NULL;
00645    char *urlprefix = NULL;
00646    char tmp[256];
00647    int stream_action = X_REC_IN | X_REC_OUT;
00648    int joinfiles = 0;
00649    int waitforbridge = 0;
00650    int res = 0;
00651    char *parse;
00652    AST_DECLARE_APP_ARGS(args,
00653       AST_APP_ARG(format);
00654       AST_APP_ARG(fname_base);
00655       AST_APP_ARG(options);
00656    );
00657    
00658    /* Parse arguments. */
00659    if (ast_strlen_zero(data)) {
00660       ast_log(LOG_ERROR, "Monitor requires an argument\n");
00661       return 0;
00662    }
00663 
00664    parse = ast_strdupa(data);
00665    AST_STANDARD_APP_ARGS(args, parse);
00666 
00667    if (!ast_strlen_zero(args.options)) {
00668       if (strchr(args.options, 'm'))
00669          stream_action |= X_JOIN;
00670       if (strchr(args.options, 'b'))
00671          waitforbridge = 1;
00672       if (strchr(args.options, 'i'))
00673          stream_action &= ~X_REC_IN;
00674       if (strchr(args.options, 'o'))
00675          stream_action &= ~X_REC_OUT;
00676    }
00677 
00678    arg = strchr(args.format, ':');
00679    if (arg) {
00680       *arg++ = 0;
00681       urlprefix = arg;
00682    }
00683 
00684    if (urlprefix) {
00685       snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
00686          ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
00687       ast_channel_lock(chan);
00688       if (!chan->cdr && !(chan->cdr = ast_cdr_alloc())) {
00689          ast_channel_unlock(chan);
00690          return -1;
00691       }
00692       ast_cdr_setuserfield(chan, tmp);
00693       ast_channel_unlock(chan);
00694    }
00695    if (waitforbridge) {
00696       /* We must remove the "b" option if listed.  In principle none of
00697          the following could give NULL results, but we check just to
00698          be pedantic. Reconstructing with checks for 'm' option does not
00699          work if we end up adding more options than 'm' in the future. */
00700       delay = ast_strdupa(data);
00701       options = strrchr(delay, ',');
00702       if (options) {
00703          arg = strchr(options, 'b');
00704          if (arg) {
00705             *arg = 'X';
00706             pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
00707          }
00708       }
00709       return 0;
00710    }
00711 
00712    res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
00713    if (res < 0)
00714       res = ast_monitor_change_fname(chan, args.fname_base, 1);
00715 
00716    if (stream_action & X_JOIN) {
00717       if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
00718          joinfiles = 1;
00719       else
00720          ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
00721    }
00722    ast_monitor_setjoinfiles(chan, joinfiles);
00723 
00724    return res;
00725 }
00726 
00727 /*! \brief Wrapper function \see ast_monitor_stop */
00728 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
00729 {
00730    return ast_monitor_stop(chan, 1);
00731 }
00732 
00733 /*! \brief Wrapper function \see ast_monitor_change_fname */
00734 static int change_monitor_exec(struct ast_channel *chan, const char *data)
00735 {
00736    return ast_monitor_change_fname(chan, data, 1);
00737 }
00738 
00739 /*! \brief Start monitoring a channel by manager connection */
00740 static int start_monitor_action(struct mansession *s, const struct message *m)
00741 {
00742    struct ast_channel *c = NULL;
00743    const char *name = astman_get_header(m, "Channel");
00744    const char *fname = astman_get_header(m, "File");
00745    const char *format = astman_get_header(m, "Format");
00746    const char *mix = astman_get_header(m, "Mix");
00747    char *d;
00748 
00749    if (ast_strlen_zero(name)) {
00750       astman_send_error(s, m, "No channel specified");
00751       return AMI_SUCCESS;
00752    }
00753 
00754    if (!(c = ast_channel_get_by_name(name))) {
00755       astman_send_error(s, m, "No such channel");
00756       return AMI_SUCCESS;
00757    }
00758 
00759    if (ast_strlen_zero(fname)) {
00760       /* No filename base specified, default to channel name as per CLI */
00761       ast_channel_lock(c);
00762       fname = ast_strdupa(c->name);
00763       ast_channel_unlock(c);
00764       /* Channels have the format technology/channel_name - have to replace that /  */
00765       if ((d = strchr(fname, '/'))) {
00766          *d = '-';
00767       }
00768    }
00769 
00770    if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
00771       if (ast_monitor_change_fname(c, fname, 1)) {
00772          astman_send_error(s, m, "Could not start monitoring channel");
00773          c = ast_channel_unref(c);
00774          return AMI_SUCCESS;
00775       }
00776    }
00777 
00778    if (ast_true(mix)) {
00779       ast_channel_lock(c);
00780       ast_monitor_setjoinfiles(c, 1);
00781       ast_channel_unlock(c);
00782    }
00783 
00784    c = ast_channel_unref(c);
00785 
00786    astman_send_ack(s, m, "Started monitoring channel");
00787 
00788    return AMI_SUCCESS;
00789 }
00790 
00791 /*! \brief Stop monitoring a channel by manager connection */
00792 static int stop_monitor_action(struct mansession *s, const struct message *m)
00793 {
00794    struct ast_channel *c = NULL;
00795    const char *name = astman_get_header(m, "Channel");
00796    int res;
00797 
00798    if (ast_strlen_zero(name)) {
00799       astman_send_error(s, m, "No channel specified");
00800       return AMI_SUCCESS;
00801    }
00802 
00803    if (!(c = ast_channel_get_by_name(name))) {
00804       astman_send_error(s, m, "No such channel");
00805       return AMI_SUCCESS;
00806    }
00807 
00808    res = ast_monitor_stop(c, 1);
00809 
00810    c = ast_channel_unref(c);
00811 
00812    if (res) {
00813       astman_send_error(s, m, "Could not stop monitoring channel");
00814       return AMI_SUCCESS;
00815    }
00816 
00817    astman_send_ack(s, m, "Stopped monitoring channel");
00818 
00819    return AMI_SUCCESS;
00820 }
00821 
00822 /*! \brief Change filename of a monitored channel by manager connection */
00823 static int change_monitor_action(struct mansession *s, const struct message *m)
00824 {
00825    struct ast_channel *c = NULL;
00826    const char *name = astman_get_header(m, "Channel");
00827    const char *fname = astman_get_header(m, "File");
00828 
00829    if (ast_strlen_zero(name)) {
00830       astman_send_error(s, m, "No channel specified");
00831       return AMI_SUCCESS;
00832    }
00833 
00834    if (ast_strlen_zero(fname)) {
00835       astman_send_error(s, m, "No filename specified");
00836       return AMI_SUCCESS;
00837    }
00838 
00839    if (!(c = ast_channel_get_by_name(name))) {
00840       astman_send_error(s, m, "No such channel");
00841       return AMI_SUCCESS;
00842    }
00843 
00844    if (ast_monitor_change_fname(c, fname, 1)) {
00845       c = ast_channel_unref(c);
00846       astman_send_error(s, m, "Could not change monitored filename of channel");
00847       return AMI_SUCCESS;
00848    }
00849 
00850    c = ast_channel_unref(c);
00851 
00852    astman_send_ack(s, m, "Changed monitor filename");
00853 
00854    return AMI_SUCCESS;
00855 }
00856 
00857 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
00858 {
00859    if (chan->monitor)
00860       chan->monitor->joinfiles = turnon;
00861 }
00862 
00863 enum MONITOR_PAUSING_ACTION
00864 {
00865    MONITOR_ACTION_PAUSE,
00866    MONITOR_ACTION_UNPAUSE
00867 };
00868 
00869 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
00870 {
00871    struct ast_channel *c = NULL;
00872    const char *name = astman_get_header(m, "Channel");
00873 
00874    if (ast_strlen_zero(name)) {
00875       astman_send_error(s, m, "No channel specified");
00876       return AMI_SUCCESS;
00877    }
00878 
00879    if (!(c = ast_channel_get_by_name(name))) {
00880       astman_send_error(s, m, "No such channel");
00881       return AMI_SUCCESS;
00882    }
00883 
00884    if (action == MONITOR_ACTION_PAUSE) {
00885       ast_monitor_pause(c);
00886    } else {
00887       ast_monitor_unpause(c);
00888    }
00889 
00890    c = ast_channel_unref(c);
00891 
00892    astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
00893 
00894    return AMI_SUCCESS;
00895 }
00896 
00897 static int pause_monitor_action(struct mansession *s, const struct message *m)
00898 {
00899    return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
00900 }
00901 
00902 static int unpause_monitor_action(struct mansession *s, const struct message *m)
00903 {
00904    return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
00905 }
00906 
00907 static int load_module(void)
00908 {
00909    ast_register_application_xml("Monitor", start_monitor_exec);
00910    ast_register_application_xml("StopMonitor", stop_monitor_exec);
00911    ast_register_application_xml("ChangeMonitor", change_monitor_exec);
00912    ast_register_application_xml("PauseMonitor", pause_monitor_exec);
00913    ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
00914    ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
00915    ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
00916    ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
00917    ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
00918    ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
00919 
00920    return AST_MODULE_LOAD_SUCCESS;
00921 }
00922 
00923 static int unload_module(void)
00924 {
00925    ast_unregister_application("Monitor");
00926    ast_unregister_application("StopMonitor");
00927    ast_unregister_application("ChangeMonitor");
00928    ast_unregister_application("PauseMonitor");
00929    ast_unregister_application("UnpauseMonitor");
00930    ast_manager_unregister("Monitor");
00931    ast_manager_unregister("StopMonitor");
00932    ast_manager_unregister("ChangeMonitor");
00933    ast_manager_unregister("PauseMonitor");
00934    ast_manager_unregister("UnpauseMonitor");
00935 
00936    return 0;
00937 }
00938 
00939 /* usecount semantics need to be defined */
00940 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Call Monitoring Resource",
00941       .load = load_module,
00942       .unload = unload_module,
00943       .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00944       );

Generated on Mon Mar 19 11:30:29 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7