Thu Apr 28 2011 16:57:12

Asterisk developer's documentation


monitor.h File Reference

Channel monitoring. More...

#include "asterisk/channel.h"
Include dependency graph for monitor.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ast_channel_monitor

Defines

#define X_JOIN   4
#define X_REC_IN   1
#define X_REC_OUT   2

Enumerations

enum  AST_MONITORING_STATE { AST_MONITOR_RUNNING, AST_MONITOR_PAUSED }

Functions

int ast_monitor_change_fname (struct ast_channel *chan, const char *fname_base, int need_lock) attribute_weak
 Change monitored filename of channel.
int ast_monitor_pause (struct ast_channel *chan) attribute_weak
 Pause monitoring of channel.
void ast_monitor_setjoinfiles (struct ast_channel *chan, int turnon) attribute_weak
int ast_monitor_start (struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action) attribute_weak
 Start monitoring a channel.
int ast_monitor_stop (struct ast_channel *chan, int need_lock) attribute_weak
 Stop monitoring channel.
int ast_monitor_unpause (struct ast_channel *chan) attribute_weak
 Unpause monitoring of channel.

Detailed Description

Channel monitoring.

Definition in file monitor.h.


Define Documentation

#define X_JOIN   4

Definition at line 36 of file monitor.h.

Referenced by start_monitor_exec().

#define X_REC_IN   1
#define X_REC_OUT   2

Enumeration Type Documentation

Enumerator:
AST_MONITOR_RUNNING 
AST_MONITOR_PAUSED 

Definition at line 28 of file monitor.h.


Function Documentation

int ast_monitor_change_fname ( struct ast_channel chan,
const char *  fname_base,
int  need_lock 
)

Change monitored filename of channel.

Parameters:
chan
fname_basenew filename
need_lock
Return values:
0on success.
-1on failure.

Note:
We cannot just compare filenames, due to symlinks, relative paths, and other possible filesystem issues. We could use realpath(3), but its use is discouraged. However, if we try to create the same file from two different paths, the second will fail, and so we have our notification that the filenames point to the same path.

Remember, also, that we're using the basename of the file (i.e. the file without the format suffix), so it does not already exist and we aren't interfering with the recording itself.

Definition at line 417 of file res_monitor.c.

References ast_config_AST_MONITOR_DIR, ast_copy_string(), ast_debug, ast_log(), ast_mkdir(), ast_strdupa, ast_strlen_zero(), errno, ast_channel_monitor::filename_base, ast_channel_monitor::filename_changed, LOCK_IF_NEEDED, LOG_ERROR, LOG_WARNING, ast_channel::monitor, name, ast_channel::name, and UNLOCK_IF_NEEDED.

Referenced by change_monitor_action(), change_monitor_exec(), start_monitor_action(), and start_monitor_exec().

{
   if (ast_strlen_zero(fname_base)) {
      ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
      return -1;
   }

   LOCK_IF_NEEDED(chan, need_lock);

   if (chan->monitor) {
      int directory = strchr(fname_base, '/') ? 1 : 0;
      const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
      const char *absolute_suffix = *fname_base == '/' ? "" : "/";
      char tmpstring[sizeof(chan->monitor->filename_base)] = "";
      int i, fd[2] = { -1, -1 }, doexit = 0;

      /* before continuing, see if we're trying to rename the file to itself... */
      snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);

      /* try creating the directory just in case it doesn't exist */
      if (directory) {
         char *name = ast_strdupa(tmpstring);
         ast_mkdir(dirname(name), 0777);
      }

      /*!\note We cannot just compare filenames, due to symlinks, relative
       * paths, and other possible filesystem issues.  We could use
       * realpath(3), but its use is discouraged.  However, if we try to
       * create the same file from two different paths, the second will
       * fail, and so we have our notification that the filenames point to
       * the same path.
       *
       * Remember, also, that we're using the basename of the file (i.e.
       * the file without the format suffix), so it does not already exist
       * and we aren't interfering with the recording itself.
       */
      ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
      
      if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
         (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
         if (fd[0] < 0) {
            ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
         } else {
            ast_debug(2, "No need to rename monitor filename to itself\n");
         }
         doexit = 1;
      }

      /* Cleanup temporary files */
      for (i = 0; i < 2; i++) {
         if (fd[i] >= 0) {
            while (close(fd[i]) < 0 && errno == EINTR);
         }
      }
      unlink(tmpstring);
      /* if previous monitor file existed in a subdirectory, the directory will not be removed */
      unlink(chan->monitor->filename_base);

      if (doexit) {
         UNLOCK_IF_NEEDED(chan, need_lock);
         return 0;
      }

      ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
      chan->monitor->filename_changed = 1;
   } else {
      ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
   }

   UNLOCK_IF_NEEDED(chan, need_lock);

   return 0;
}
int ast_monitor_pause ( struct ast_channel chan)

Pause monitoring of channel.

Definition at line 386 of file res_monitor.c.

References AST_MONITOR_PAUSED, and ast_monitor_set_state().

Referenced by do_pause_or_unpause(), and pause_monitor_exec().

void ast_monitor_setjoinfiles ( struct ast_channel chan,
int  turnon 
)
int ast_monitor_start ( struct ast_channel chan,
const char *  format_spec,
const char *  fname_base,
int  need_lock,
int  stream_action 
)

Start monitoring a channel.

Parameters:
chanast_channel struct to record
format_specfile format to use for recording
fname_basefilename base to record to
need_lockwhether to lock the channel mutex
stream_actionwhether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used Creates the file to record, if no format is specified it assumes WAV It also sets channel variable __MONITORED=yes
Return values:
0on success
-1on failure

Definition at line 148 of file res_monitor.c.

References ast_calloc, ast_closestream(), ast_config_AST_MONITOR_DIR, ast_debug, AST_FILE_MODE, ast_filedelete(), ast_fileexists(), ast_free, ast_log(), ast_mkdir(), AST_MONITOR_RUNNING, ast_monitor_set_state(), ast_monitor_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_strdup, ast_strdupa, ast_strlen_zero(), ast_writefile(), EVENT_FLAG_CALL, ast_channel_monitor::filename_base, ast_channel_monitor::filename_changed, FILENAME_MAX, ast_channel_monitor::format, LOCK_IF_NEEDED, LOG_WARNING, manager_event, monitor, ast_channel::monitor, monitorlock, ast_channel::name, name, pbx_builtin_setvar_helper(), ast_channel_monitor::read_filename, ast_channel_monitor::read_stream, ast_channel_monitor::stop, ast_channel::uniqueid, UNLOCK_IF_NEEDED, ast_channel_monitor::write_filename, ast_channel_monitor::write_stream, X_REC_IN, and X_REC_OUT.

Referenced by __agent_start_monitoring(), start_monitor_action(), start_monitor_exec(), and try_calling().

{
   int res = 0;

   LOCK_IF_NEEDED(chan, need_lock);

   if (!(chan->monitor)) {
      struct ast_channel_monitor *monitor;
      char *channel_name, *p;

      /* Create monitoring directory if needed */
      ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);

      if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
         UNLOCK_IF_NEEDED(chan, need_lock);
         return -1;
      }

      /* Determine file names */
      if (!ast_strlen_zero(fname_base)) {
         int directory = strchr(fname_base, '/') ? 1 : 0;
         const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
         const char *absolute_suffix = *fname_base == '/' ? "" : "/";

         snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
                  absolute, absolute_suffix, fname_base);
         snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
                  absolute, absolute_suffix, fname_base);
         snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
                  absolute, absolute_suffix, fname_base);

         /* try creating the directory just in case it doesn't exist */
         if (directory) {
            char *name = ast_strdupa(monitor->filename_base);
            ast_mkdir(dirname(name), 0777);
         }
      } else {
         ast_mutex_lock(&monitorlock);
         snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
                  ast_config_AST_MONITOR_DIR, seq);
         snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
                  ast_config_AST_MONITOR_DIR, seq);
         seq++;
         ast_mutex_unlock(&monitorlock);

         channel_name = ast_strdupa(chan->name);
         while ((p = strchr(channel_name, '/'))) {
            *p = '-';
         }
         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
                ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
         monitor->filename_changed = 1;
      }

      monitor->stop = ast_monitor_stop;

      /* Determine file format */
      if (!ast_strlen_zero(format_spec)) {
         monitor->format = ast_strdup(format_spec);
      } else {
         monitor->format = ast_strdup("wav");
      }
      
      /* open files */
      if (stream_action & X_REC_IN) {
         if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
            ast_filedelete(monitor->read_filename, NULL);
         if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
                     monitor->format, NULL,
                     O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
            ast_log(LOG_WARNING, "Could not create file %s\n",
                     monitor->read_filename);
            ast_free(monitor);
            UNLOCK_IF_NEEDED(chan, need_lock);
            return -1;
         }
      } else
         monitor->read_stream = NULL;

      if (stream_action & X_REC_OUT) {
         if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
            ast_filedelete(monitor->write_filename, NULL);
         }
         if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
                     monitor->format, NULL,
                     O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
            ast_log(LOG_WARNING, "Could not create file %s\n",
                     monitor->write_filename);
            ast_closestream(monitor->read_stream);
            ast_free(monitor);
            UNLOCK_IF_NEEDED(chan, need_lock);
            return -1;
         }
      } else
         monitor->write_stream = NULL;

      chan->monitor = monitor;
      ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
      /* so we know this call has been monitored in case we need to bill for it or something */
      pbx_builtin_setvar_helper(chan, "__MONITORED","true");

      manager_event(EVENT_FLAG_CALL, "MonitorStart",
                         "Channel: %s\r\n"
                          "Uniqueid: %s\r\n",                        
                           chan->name,
                         chan->uniqueid                        
                          );
   } else {
      ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
      res = -1;
   }

   UNLOCK_IF_NEEDED(chan, need_lock);

   return res;
}
int ast_monitor_stop ( struct ast_channel chan,
int  need_lock 
)

Stop monitoring channel.

Parameters:
chan
need_lockStop the recording, close any open streams, mix in/out channels if required
Returns:
Always 0

Definition at line 292 of file res_monitor.c.

References ast_closestream(), ast_copy_string(), ast_debug, ast_filedelete(), ast_fileexists(), ast_filerename(), ast_free, ast_log(), ast_safe_system(), ast_strlen_zero(), EVENT_FLAG_CALL, ast_channel_monitor::filename_base, ast_channel_monitor::filename_changed, FILENAME_MAX, format, ast_channel_monitor::format, get_soxmix_format(), ast_channel_monitor::joinfiles, LOCK_IF_NEEDED, LOG_WARNING, manager_event, ast_channel::monitor, ast_channel::name, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel_monitor::read_filename, ast_channel_monitor::read_stream, ast_channel::uniqueid, UNLOCK_IF_NEEDED, ast_channel_monitor::write_filename, and ast_channel_monitor::write_stream.

Referenced by ast_monitor_start(), stop_monitor_action(), and stop_monitor_exec().

{
   int delfiles = 0;

   LOCK_IF_NEEDED(chan, need_lock);

   if (chan->monitor) {
      char filename[ FILENAME_MAX ];

      if (chan->monitor->read_stream) {
         ast_closestream(chan->monitor->read_stream);
      }
      if (chan->monitor->write_stream) {
         ast_closestream(chan->monitor->write_stream);
      }

      if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
         if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
            snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
            if (ast_fileexists(filename, NULL, NULL) > 0) {
               ast_filedelete(filename, NULL);
            }
            ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
         } else {
            ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
         }

         if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
            snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
            if (ast_fileexists(filename, NULL, NULL) > 0) {
               ast_filedelete(filename, NULL);
            }
            ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
         } else {
            ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
         }
      }

      if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
         char tmp[1024];
         char tmp2[1024];
         const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
         char *fname_base = chan->monitor->filename_base;
         const char *execute, *execute_args;
         /* at this point, fname_base really is the full path */

         /* Set the execute application */
         execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
         if (ast_strlen_zero(execute)) {
#ifdef HAVE_SOXMIX
            execute = "nice -n 19 soxmix";
#else
            execute = "nice -n 19 sox -m";
#endif
            format = get_soxmix_format(format);
            delfiles = 1;
         } 
         execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
         if (ast_strlen_zero(execute_args)) {
            execute_args = "";
         }
         
         snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
            execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
         if (delfiles) {
            snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
            ast_copy_string(tmp, tmp2, sizeof(tmp));
         }
         ast_debug(1,"monitor executing %s\n",tmp);
         if (ast_safe_system(tmp) == -1)
            ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
      }
      
      ast_free(chan->monitor->format);
      ast_free(chan->monitor);
      chan->monitor = NULL;

      manager_event(EVENT_FLAG_CALL, "MonitorStop",
                         "Channel: %s\r\n"
                           "Uniqueid: %s\r\n",
                           chan->name,
                           chan->uniqueid
                           );
      pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
   }
   pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);

   UNLOCK_IF_NEEDED(chan, need_lock);

   return 0;
}
int ast_monitor_unpause ( struct ast_channel chan)

Unpause monitoring of channel.

Definition at line 392 of file res_monitor.c.

References AST_MONITOR_RUNNING, and ast_monitor_set_state().

Referenced by do_pause_or_unpause(), and unpause_monitor_exec().