Sat Apr 26 2014 22:01:40

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