Thu Apr 28 2011 17:16:18

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/manager.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
Include dependency graph for res_musiconhold.c:

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

#define DONT_UNREF   0
#define get_mohbyname(a, b, c)   _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
#define HANDLE_REF   1
#define INITIAL_NUM_FILES   8
#define LOCAL_MPG_123   "/usr/local/bin/mpg123"
#define MAX_MP3S   256
#define MOH_CACHERTCLASSES   (1 << 5)
#define moh_class_malloc()   _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100
#define MOH_NOTDELETED   (1 << 30)
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define moh_register(a, b, c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
#define MOH_SINGLE   (1 << 1)
#define MOH_SORTALPHA   (1 << 4)
#define mohclass_ref(class, string)   (ao2_t_ref((class), +1, (string)), class)
#define mohclass_unref(class, string)   (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
#define MPG_123   "/usr/bin/mpg123"

Functions

static void __reg_module (void)
static void __unreg_module (void)
static struct mohclass_get_mohbyname (const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
static struct mohclass_moh_class_malloc (const char *file, int line, const char *funcname)
static int _moh_register (struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
static void ast_moh_destroy (void)
static int ast_moh_files_next (struct ast_channel *chan)
static struct mohclassget_mohbydigit (char digit)
static char * handle_cli_moh_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_moh_show_classes (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_moh_show_files (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int init_app_class (struct mohclass *class)
static int init_files_class (struct mohclass *class)
static int load_module (void)
static int load_moh_classes (int reload)
static void local_ast_moh_cleanup (struct ast_channel *chan)
static int local_ast_moh_start (struct ast_channel *chan, const char *mclass, const char *interpclass)
static void local_ast_moh_stop (struct ast_channel *chan)
static int moh_add_file (struct mohclass *class, const char *filepath)
static void * moh_alloc (struct ast_channel *chan, void *params)
static int moh_class_cmp (void *obj, void *arg, int flags)
static void moh_class_destructor (void *obj)
static int moh_class_hash (const void *obj, const int flags)
static int moh_class_inuse (void *obj, void *arg, int flags)
static int moh_class_mark (void *obj, void *arg, int flags)
static int moh_classes_delete_marked (void *obj, void *arg, int flags)
static int moh_diff (struct mohclass *old, struct mohclass *new)
static int moh_digit_match (void *obj, void *arg, int flags)
static void * moh_files_alloc (struct ast_channel *chan, void *params)
static int moh_files_generator (struct ast_channel *chan, void *data, int len, int samples)
static struct ast_framemoh_files_readframe (struct ast_channel *chan)
static void moh_files_release (struct ast_channel *chan, void *data)
static int moh_generate (struct ast_channel *chan, void *data, int len, int samples)
static void moh_handle_digit (struct ast_channel *chan, char digit)
static void moh_release (struct ast_channel *chan, void *data)
static void moh_rescan_files (void)
static int moh_scan_files (struct mohclass *class)
static int moh_sort_compare (const void *i1, const void *i2)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int play_moh_exec (struct ast_channel *chan, void *data)
static int reload (void)
static int set_moh_exec (struct ast_channel *chan, void *data)
static int spawn_mp3 (struct mohclass *class)
static int start_moh_exec (struct ast_channel *chan, void *data)
static int stop_moh_exec (struct ast_channel *chan, void *data)
static int unload_module (void)
static int wait_moh_exec (struct ast_channel *chan, void *data)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Music On Hold Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_moh []
static struct ast_flags global_flags [1] = {{0}}
static struct ast_generator moh_file_stream
static struct ao2_containermohclasses
static struct ast_generator mohgen
static char * play_moh = "MusicOnHold"
static char * play_moh_desc = "Returns 0 when done, -1 on hangup.\n"
static char * play_moh_syn = "Play Music On Hold indefinitely"
static int respawn_time = 20
static char * set_moh = "SetMusicOnHold"
static char * set_moh_desc = " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n"
static char * set_moh_syn = "Set default Music On Hold class"
static char * start_moh = "StartMusicOnHold"
static char * start_moh_desc = "music source for the channel will be used. Always returns 0.\n"
static char * start_moh_syn = "Play Music On Hold"
static char * stop_moh = "StopMusicOnHold"
static char * stop_moh_desc = "Stops playing music on hold.\n"
static char * stop_moh_syn = "Stop Playing Music On Hold"
static char * wait_moh = "WaitMusicOnHold"
static char * wait_moh_desc = " !!! DEPRECATED. Use MusicOnHold instead !!!\n"
static char * wait_moh_syn = "Wait, playing Music On Hold"

Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define DONT_UNREF   0

Definition at line 77 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define get_mohbyname (   a,
  b,
 
)    _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)

Definition at line 795 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 76 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 75 of file res_musiconhold.c.

Referenced by moh_add_file().

#define LOCAL_MPG_123   "/usr/local/bin/mpg123"

Definition at line 194 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MAX_MP3S   256

Definition at line 196 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CACHERTCLASSES   (1 << 5)

Should we use a separate instance of MOH for each user or not

Definition at line 147 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define moh_class_malloc ( )    _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)

Definition at line 1240 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 150 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 141 of file res_musiconhold.c.

Referenced by init_app_class(), local_ast_moh_start(), and spawn_mp3().

#define MOH_RANDOMIZE   (1 << 3)
#define moh_register (   a,
  b,
 
)    _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
Note:
This function owns the reference it gets to moh if unref is true

Definition at line 1170 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

Definition at line 142 of file res_musiconhold.c.

Referenced by init_app_class(), local_ast_moh_start(), and spawn_mp3().

#define MOH_SORTALPHA   (1 << 4)

Definition at line 145 of file res_musiconhold.c.

Referenced by load_moh_classes(), local_ast_moh_start(), and moh_scan_files().

#define mohclass_ref (   class,
  string 
)    (ao2_t_ref((class), +1, (string)), class)

Definition at line 200 of file res_musiconhold.c.

Referenced by moh_alloc(), moh_files_alloc(), and mohalloc().

#define mohclass_unref (   class,
  string 
)    (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
#define MPG_123   "/usr/bin/mpg123"

Definition at line 195 of file res_musiconhold.c.

Referenced by spawn_mp3().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 1914 of file res_musiconhold.c.

static void __unreg_module ( void  ) [static]

Definition at line 1914 of file res_musiconhold.c.

static struct mohclass* _get_mohbyname ( const char *  name,
int  warn,
int  flags,
const char *  file,
int  lineno,
const char *  funcname 
) [static, read]

Definition at line 797 of file res_musiconhold.c.

References _ao2_find(), _ao2_find_debug(), ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, and mohclass::name.

Referenced by _moh_register().

{
   struct mohclass *moh = NULL;
   struct mohclass tmp_class = {
      .flags = 0,
   };

   ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));

#ifdef REF_DEBUG
   moh = _ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
#else
   moh = _ao2_find(mohclasses, &tmp_class, flags);
#endif

   if (!moh && warn) {
      ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
   }

   return moh;
}
static struct mohclass* _moh_class_malloc ( const char *  file,
int  line,
const char *  funcname 
) [static, read]

Definition at line 1242 of file res_musiconhold.c.

References __AST_DEBUG_MALLOC, _ao2_alloc_debug(), ao2_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().

{
   struct mohclass *class;

   if ((class =
#ifdef REF_DEBUG
         _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
#elif defined(__AST_DEBUG_MALLOC)
         _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
#else
         ao2_alloc(sizeof(*class), moh_class_destructor)
#endif
      )) {
      class->format = AST_FORMAT_SLINEAR;
      class->srcfd = -1;
      class->pseudofd = -1;
   }

   return class;
}
static int _moh_register ( struct mohclass moh,
int  reload,
int  unref,
const char *  file,
int  line,
const char *  funcname 
) [static]

Definition at line 1171 of file res_musiconhold.c.

References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, mohclass::mode, moh_diff(), MOH_NOTDELETED, mohclass_unref, mohclass::name, respawn_time, and mohclass::start.

{
   struct mohclass *mohclass = NULL;

   if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
      ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
      mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
      if (unref) {
         moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
      }
      return -1;
   } else if (mohclass) {
      /* Found a class, but it's different from the one being registered */
      mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
   }

   time(&moh->start);
   moh->start -= respawn_time;
   
   if (!strcasecmp(moh->mode, "files")) {
      if (init_files_class(moh)) {
         if (unref) {
            moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
         }
         return -1;
      }
   } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
         !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
         !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
      if (init_app_class(moh)) {
         if (unref) {
            moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
         }
         return -1;
      }
   } else {
      ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
      if (unref) {
         moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
      }
      return -1;
   }

   ao2_t_link(mohclasses, moh, "Adding class to container");

   if (unref) {
      moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
   }
   
   return 0;
}
static void ast_moh_destroy ( void  ) [static]

Definition at line 1707 of file res_musiconhold.c.

References ao2_t_callback, ast_verb, OBJ_MULTIPLE, OBJ_NODATA, and OBJ_UNLINK.

Referenced by load_module(), and unload_module().

{
   ast_verb(2, "Destroying musiconhold processes\n");
   ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
}
static int ast_moh_files_next ( struct ast_channel chan) [static]

Definition at line 256 of file res_musiconhold.c.

References ast_closestream(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, ast_channel::name, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

{
   struct moh_files_state *state = chan->music_state;
   int tries;

   /* Discontinue a stream if it is running already */
   if (chan->stream) {
      ast_closestream(chan->stream);
      chan->stream = NULL;
   }

   if (!state->class->total_files) {
      ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
      return -1;
   }

   if (state->pos == 0 && state->save_pos_filename == NULL) {
      /* First time so lets play the file. */
      state->save_pos = -1;
   } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
      /* If a specific file has been saved confirm it still exists and that it is still valid */
      state->pos = state->save_pos;
      state->save_pos = -1;
   } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
      /* Get a random file and ensure we can open it */
      for (tries = 0; tries < 20; tries++) {
         state->pos = ast_random() % state->class->total_files;
         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
            break;
         }
      }
      state->save_pos = -1;
      state->samples = 0;
   } else {
      /* This is easy, just increment our position and make sure we don't exceed the total file count */
      state->pos++;
      state->pos %= state->class->total_files;
      state->save_pos = -1;
      state->samples = 0;
   }

   for (tries = 0; tries < state->class->total_files; ++tries) {
      if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
         break;
      }

      ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
      state->pos++;
      state->pos %= state->class->total_files;
   }

   if (tries == state->class->total_files) {
      return -1;
   }

   /* Record the pointer to the filename for position resuming later */
   state->save_pos_filename = state->class->filearray[state->pos];

   ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);

   if (state->samples) {
      ast_seekstream(chan->stream, state->samples, SEEK_SET);
   }

   return 0;
}
static struct mohclass* get_mohbydigit ( char  digit) [static, read]
Note:
This function should be called with the mohclasses list locked

Definition at line 417 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

{
   return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
}
static char* handle_cli_moh_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1713 of file res_musiconhold.c.

References ast_cli_args::argc, ast_cli_entry::args, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, reload, and ast_cli_entry::usage.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "moh reload";
      e->usage =
         "Usage: moh reload\n"
         "       Reloads the MusicOnHold module.\n"
         "       Alias for 'module reload res_musiconhold.so'\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != e->args)
      return CLI_SHOWUSAGE;

   reload();

   return CLI_SUCCESS;
}
static char* handle_cli_moh_show_classes ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1773 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), ast_getformatname(), ast_test_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, MOH_CUSTOM, mohclass_unref, S_OR, and ast_cli_entry::usage.

{
   struct mohclass *class;
   struct ao2_iterator i;

   switch (cmd) {
   case CLI_INIT:
      e->command = "moh show classes";
      e->usage =
         "Usage: moh show classes\n"
         "       Lists all MusicOnHold classes.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != e->args)
      return CLI_SHOWUSAGE;

   i = ao2_iterator_init(mohclasses, 0);
   for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
      ast_cli(a->fd, "Class: %s\n", class->name);
      ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
      ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
      if (ast_test_flag(class, MOH_CUSTOM)) {
         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
      }
      if (strcasecmp(class->mode, "files")) {
         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
      }
   }
   ao2_iterator_destroy(&i);

   return CLI_SUCCESS;
}
static char* handle_cli_moh_show_files ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1735 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, mohclass_unref, and ast_cli_entry::usage.

{
   struct mohclass *class;
   struct ao2_iterator i;

   switch (cmd) {
   case CLI_INIT:
      e->command = "moh show files";
      e->usage =
         "Usage: moh show files\n"
         "       Lists all loaded file-based MusicOnHold classes and their\n"
         "       files.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != e->args)
      return CLI_SHOWUSAGE;

   i = ao2_iterator_init(mohclasses, 0);
   for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
      int x;

      if (!class->total_files) {
         continue;
      }

      ast_cli(a->fd, "Class: %s\n", class->name);
      for (x = 0; x < class->total_files; x++) {
         ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
      }
   }
   ao2_iterator_destroy(&i);

   return CLI_SUCCESS;
}
static int init_app_class ( struct mohclass class) [static]

Definition at line 1124 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and monmp3thread().

Referenced by _moh_register().

{
#ifdef HAVE_DAHDI
   int x;
#endif

   if (!strcasecmp(class->mode, "custom")) {
      ast_set_flag(class, MOH_CUSTOM);
   } else if (!strcasecmp(class->mode, "mp3nb")) {
      ast_set_flag(class, MOH_SINGLE);
   } else if (!strcasecmp(class->mode, "quietmp3nb")) {
      ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
   } else if (!strcasecmp(class->mode, "quietmp3")) {
      ast_set_flag(class, MOH_QUIET);
   }
      
   class->srcfd = -1;
   class->pseudofd = -1;

#ifdef HAVE_DAHDI
   /* Open /dev/zap/pseudo for timing...  Is
      there a better, yet reliable way to do this? */
   class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
   if (class->pseudofd < 0) {
      ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
   } else {
      x = 320;
      ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
   }
#endif

   if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
      ast_log(LOG_WARNING, "Unable to create moh thread...\n");
      if (class->pseudofd > -1) {
         close(class->pseudofd);
         class->pseudofd = -1;
      }
      return -1;
   }

   return 0;
}
static int init_files_class ( struct mohclass class) [static]

Definition at line 1063 of file res_musiconhold.c.

References ast_set_flag, ast_verbose(), MOH_RANDOMIZE, moh_scan_files(), option_verbose, and VERBOSE_PREFIX_3.

Referenced by _moh_register().

{
   int res;

   res = moh_scan_files(class);

   if (res < 0) {
      return -1;
   }

   if (!res) {
      if (option_verbose > 2) {
         ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
               class->dir, class->name);
      }
      return -1;
   }

#if 0
   /* XXX This isn't correct.  Args is an application for custom mode. XXX */
   if (strchr(class->args, 'r')) {
      ast_set_flag(class, MOH_RANDOMIZE);
   }
#endif

   return 0;
}
static int load_module ( void  ) [static]

Definition at line 1831 of file res_musiconhold.c.

References ao2_t_container_alloc, ARRAY_LEN, ast_check_realtime(), ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application, ast_register_atexit(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

static int load_moh_classes ( int  reload) [static]

Definition at line 1598 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_destroy(), ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, HANDLE_REF, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_register, moh_rescan_files(), MOH_SORTALPHA, mohclass_unref, ast_variable::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, ast_variable::value, and var.

Referenced by load_module().

{
   struct ast_config *cfg;
   struct ast_variable *var;
   struct mohclass *class; 
   char *cat;
   int numclasses = 0;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

   cfg = ast_config_load("musiconhold.conf", config_flags);

   if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
      if (ast_check_realtime("musiconhold") && reload) {
         ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
      }
      if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
         moh_rescan_files();
      }
      return 0;
   }

   if (reload) {
      ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
   }
   
   ast_clear_flag(global_flags, AST_FLAGS_ALL);

   cat = ast_category_browse(cfg, NULL);
   for (; cat; cat = ast_category_browse(cfg, cat)) {
      /* Setup common options from [general] section */
      if (!strcasecmp(cat, "general")) {
         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
            if (!strcasecmp(var->name, "cachertclasses")) {
               ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
            } else {
               ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
            }
         }
      }
      /* These names were deprecated in 1.4 and should not be used until after the next major release. */
      if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
            !strcasecmp(cat, "general")) {
         continue;
      }

      if (!(class = moh_class_malloc())) {
         break;
      }

      ast_copy_string(class->name, cat, sizeof(class->name));  
      for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
         if (!strcasecmp(var->name, "mode"))
            ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
         else if (!strcasecmp(var->name, "directory"))
            ast_copy_string(class->dir, var->value, sizeof(class->dir));
         else if (!strcasecmp(var->name, "application"))
            ast_copy_string(class->args, var->value, sizeof(class->args));
         else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
            class->digit = *var->value;
         else if (!strcasecmp(var->name, "random"))
            ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
         else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
            ast_set_flag(class, MOH_RANDOMIZE);
         else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
            ast_set_flag(class, MOH_SORTALPHA);
         else if (!strcasecmp(var->name, "format")) {
            class->format = ast_getformatbyname(var->value);
            if (!class->format) {
               ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
               class->format = AST_FORMAT_SLINEAR;
            }
         }
      }

      if (ast_strlen_zero(class->dir)) {
         if (!strcasecmp(class->mode, "custom")) {
            strcpy(class->dir, "nodir");
         } else {
            ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
            class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
            continue;
         }
      }
      if (ast_strlen_zero(class->mode)) {
         ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
         class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
         continue;
      }
      if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
         ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
         class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
         continue;
      }

      /* Don't leak a class when it's already registered */
      if (!moh_register(class, reload, HANDLE_REF)) {
         numclasses++;
      }
   }

   ast_config_destroy(cfg);

   ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
         moh_classes_delete_marked, NULL, "Purge marked classes");

   return numclasses;
}
static void local_ast_moh_cleanup ( struct ast_channel chan) [static]

Definition at line 1223 of file res_musiconhold.c.

References ast_free, ast_module_unref(), moh_files_state::class, mohclass_unref, ast_channel::music_state, and ast_module_info::self.

Referenced by load_module().

{
   struct moh_files_state *state = chan->music_state;

   if (state) {
      if (state->class) {
         state->class = mohclass_unref(state->class, "Channel MOH state destruction");
      }
      ast_free(chan->music_state);
      chan->music_state = NULL;
      /* Only held a module reference if we had a music state */
      ast_module_unref(ast_module_info->self);
   }
}
static int local_ast_moh_start ( struct ast_channel chan,
const char *  mclass,
const char *  interpclass 
) [static]

Definition at line 1263 of file res_musiconhold.c.

References mohclass::args, ast_activate_generator(), ast_check_realtime(), ast_copy_string(), AST_FLAG_MOH, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_true(), ast_variables_destroy(), moh_files_state::class, mohclass::digit, mohclass::dir, DONT_UNREF, EVENT_FLAG_CALL, mohclass::format, get_mohbyname, LOG_NOTICE, LOG_WARNING, manager_event, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, MOH_SORTALPHA, mohclass_unref, monmp3thread(), ast_channel::music_state, ast_channel::musicclass, ast_channel::name, mohclass::name, ast_variable::name, ast_variable::next, mohclass::pseudofd, mohclass::realtime, respawn_time, SENTINEL, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::total_files, ast_channel::uniqueid, ast_variable::value, and var.

Referenced by load_module().

{
   struct mohclass *mohclass = NULL;
   struct moh_files_state *state = chan->music_state;
   struct ast_variable *var = NULL;
   int res;
   int realtime_possible = ast_check_realtime("musiconhold");

   /* The following is the order of preference for which class to use:
    * 1) The channels explicitly set musicclass, which should *only* be
    *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
    * 2) The mclass argument. If a channel is calling ast_moh_start() as the
    *    result of receiving a HOLD control frame, this should be the
    *    payload that came with the frame.
    * 3) The interpclass argument. This would be from the mohinterpret
    *    option from channel drivers. This is the same as the old musicclass
    *    option.
    * 4) The default class.
    */
   if (!ast_strlen_zero(chan->musicclass)) {
      mohclass = get_mohbyname(chan->musicclass, 1, 0);
      if (!mohclass && realtime_possible) {
         var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
      }
   }
   if (!mohclass && !var && !ast_strlen_zero(mclass)) {
      mohclass = get_mohbyname(mclass, 1, 0);
      if (!mohclass && realtime_possible) {
         var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
      }
   }
   if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
      mohclass = get_mohbyname(interpclass, 1, 0);
      if (!mohclass && realtime_possible) {
         var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
      }
   }

   if (!mohclass && !var) {
      mohclass = get_mohbyname("default", 1, 0);
      if (!mohclass && realtime_possible) {
         var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
      }
   }

   /* If no moh class found in memory, then check RT. Note that the logic used
    * above guarantees that if var is non-NULL, then mohclass must be NULL.
    */
   if (var) {
      struct ast_variable *tmp = NULL;

      if ((mohclass = moh_class_malloc())) {
         mohclass->realtime = 1;
         for (tmp = var; tmp; tmp = tmp->next) {
            if (!strcasecmp(tmp->name, "name"))
               ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
            else if (!strcasecmp(tmp->name, "mode"))
               ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
            else if (!strcasecmp(tmp->name, "directory"))
               ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
            else if (!strcasecmp(tmp->name, "application"))
               ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
            else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
               mohclass->digit = *tmp->value;
            else if (!strcasecmp(tmp->name, "random"))
               ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
            else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
               ast_set_flag(mohclass, MOH_RANDOMIZE);
            else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
               ast_set_flag(mohclass, MOH_SORTALPHA);
            else if (!strcasecmp(tmp->name, "format")) {
               mohclass->format = ast_getformatbyname(tmp->value);
               if (!mohclass->format) {
                  ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
                  mohclass->format = AST_FORMAT_SLINEAR;
               }
            }
         }
         ast_variables_destroy(var);
         if (ast_strlen_zero(mohclass->dir)) {
            if (!strcasecmp(mohclass->mode, "custom")) {
               strcpy(mohclass->dir, "nodir");
            } else {
               ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
               mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
               return -1;
            }
         }
         if (ast_strlen_zero(mohclass->mode)) {
            ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
            mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
            return -1;
         }
         if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
            ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
            mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
            return -1;
         }

         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
            /* CACHERTCLASSES enabled, let's add this class to default tree */
            if (state && state->class) {
               /* Class already exist for this channel */
               ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
               if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
                  /* we found RT class with the same name, seems like we should continue playing existing one */
                  /* XXX This code is impossible to reach */
                  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
                  mohclass = state->class;
               }
            }
            /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
             * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
             * be that the destructor would be called when the generator on the channel is deactivated. The container then
             * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
             * invalid memory.
             */
            if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
               mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
               return -1;
            }
         } else {
            /* We don't register RT moh class, so let's init it manualy */

            time(&mohclass->start);
            mohclass->start -= respawn_time;
   
            if (!strcasecmp(mohclass->mode, "files")) {
               if (!moh_scan_files(mohclass)) {
                  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
                  return -1;
               }
               if (strchr(mohclass->args, 'r'))
                  ast_set_flag(mohclass, MOH_RANDOMIZE);
            } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {

               if (!strcasecmp(mohclass->mode, "custom"))
                  ast_set_flag(mohclass, MOH_CUSTOM);
               else if (!strcasecmp(mohclass->mode, "mp3nb"))
                  ast_set_flag(mohclass, MOH_SINGLE);
               else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
                  ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
               else if (!strcasecmp(mohclass->mode, "quietmp3"))
                  ast_set_flag(mohclass, MOH_QUIET);
         
               mohclass->srcfd = -1;
#ifdef HAVE_DAHDI
               /* Open /dev/dahdi/pseudo for timing...  Is
                  there a better, yet reliable way to do this? */
               mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
               if (mohclass->pseudofd < 0) {
                  ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
               } else {
                  int x = 320;
                  ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
               }
#else
               mohclass->pseudofd = -1;
#endif
               /* Let's check if this channel already had a moh class before */
               if (state && state->class) {
                  /* Class already exist for this channel */
                  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
                  if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
                     /* we found RT class with the same name, seems like we should continue playing existing one */
                     mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
                     mohclass = state->class;
                  }
               } else {
                  if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
                     ast_log(LOG_WARNING, "Unable to create moh...\n");
                     if (mohclass->pseudofd > -1) {
                        close(mohclass->pseudofd);
                        mohclass->pseudofd = -1;
                     }
                     mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
                     return -1;
                  }
               }
            } else {
               ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
               mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
               return -1;
            }
         }
      } else {
         ast_variables_destroy(var);
      }
   }

   if (!mohclass) {
      return -1;
   }

   manager_event(EVENT_FLAG_CALL, "MusicOnHold",
      "State: Start\r\n"
      "Channel: %s\r\n"
      "UniqueID: %s\r\n",
      chan->name, chan->uniqueid);

   ast_set_flag(chan, AST_FLAG_MOH);

   if (mohclass->total_files) {
      res = ast_activate_generator(chan, &moh_file_stream, mohclass);
   } else {
      res = ast_activate_generator(chan, &mohgen, mohclass);
   }

   mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");

   return res;
}
static void local_ast_moh_stop ( struct ast_channel chan) [static]
static int moh_add_file ( struct mohclass class,
const char *  filepath 
) [static]

Definition at line 946 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

{
   if (!class->allowed_files) {
      if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
         return -1;
      class->allowed_files = INITIAL_NUM_FILES;
   } else if (class->total_files == class->allowed_files) {
      if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
         class->allowed_files = 0;
         class->total_files = 0;
         return -1;
      }
      class->allowed_files *= 2;
   }

   if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
      return -1;

   class->total_files++;

   return 0;
}
static void* moh_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 881 of file res_musiconhold.c.

References ast_calloc, ast_codec2str(), ast_log(), ast_module_ref(), ast_set_write_format(), ast_verb, moh_files_state::class, mohclass::format, LOG_WARNING, moh_release(), mohalloc(), mohclass_ref, ast_channel::music_state, mohclass::name, ast_channel::name, mohdata::origwfmt, ast_module_info::self, and ast_channel::writeformat.

{
   struct mohdata *res;
   struct mohclass *class = params;
   struct moh_files_state *state;

   /* Initiating music_state for current channel. Channel should know name of moh class */
   if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
      chan->music_state = state;
      state->class = mohclass_ref(class, "Copying reference into state container");
      ast_module_ref(ast_module_info->self);
   } else
      state = chan->music_state;
   if (state && state->class != class) {
      memset(state, 0, sizeof(*state));
      state->class = class;
   }

   if ((res = mohalloc(class))) {
      res->origwfmt = chan->writeformat;
      if (ast_set_write_format(chan, class->format)) {
         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
         moh_release(NULL, res);
         res = NULL;
      }
      ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
   }
   return res;
}
static int moh_class_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1822 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

{
   struct mohclass *class = obj, *class2 = arg;

   return strcasecmp(class->name, class2->name) ? 0 :
      (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
      CMP_MATCH | CMP_STOP;
}
static void moh_class_destructor ( void *  obj) [static]

Definition at line 1497 of file res_musiconhold.c.

References ast_debug, AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_wait_for_input(), buff, errno, free, LOG_DEBUG, LOG_WARNING, and mohclass::pid.

Referenced by _moh_class_malloc().

{
   struct mohclass *class = obj;
   struct mohdata *member;
   pthread_t tid = 0;

   ast_debug(1, "Destroying MOH class '%s'\n", class->name);

   /* Kill the thread first, so it cannot restart the child process while the
    * class is being destroyed */
   if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
      tid = class->thread;
      class->thread = AST_PTHREADT_NULL;
      pthread_cancel(tid);
      /* We'll collect the exit status later, after we ensure all the readers
       * are dead. */
   }

   if (class->pid > 1) {
      char buff[8192];
      int bytes, tbytes = 0, stime = 0, pid = 0;

      ast_log(LOG_DEBUG, "killing %d!\n", class->pid);

      stime = time(NULL) + 2;
      pid = class->pid;
      class->pid = 0;

      /* Back when this was just mpg123, SIGKILL was fine.  Now we need
       * to give the process a reason and time enough to kill off its
       * children. */
      do {
         if (killpg(pid, SIGHUP) < 0) {
            ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
         }
         usleep(100000);
         if (killpg(pid, SIGTERM) < 0) {
            if (errno == ESRCH) {
               break;
            }
            ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
         }
         usleep(100000);
         if (killpg(pid, SIGKILL) < 0) {
            if (errno == ESRCH) {
               break;
            }
            ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
         }
      } while (0);

      while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
            (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
         tbytes = tbytes + bytes;
      }

      ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);

      close(class->srcfd);
   }

   while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
      free(member);
   }

   if (class->pseudofd > -1) {
      close(class->pseudofd);
      class->pseudofd = -1;
   }

   if (class->filearray) {
      int i;
      for (i = 0; i < class->total_files; i++) {
         free(class->filearray[i]);
      }
      free(class->filearray);
      class->filearray = NULL;
   }

   /* Finally, collect the exit status of the monitor thread */
   if (tid > 0) {
      pthread_join(tid, NULL);
   }
}
static int moh_class_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 1815 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

{
   const struct mohclass *class = obj;

   return ast_str_case_hash(class->name);
}
static int moh_class_inuse ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1872 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

{
   struct mohclass *class = obj;

   return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
}
static int moh_class_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1582 of file res_musiconhold.c.

References mohclass::delete.

Referenced by load_moh_classes().

{
   struct mohclass *class = obj;

   class->delete = 1;

   return 0;
}
static int moh_classes_delete_marked ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1591 of file res_musiconhold.c.

References CMP_MATCH, and mohclass::delete.

Referenced by load_moh_classes().

{
   struct mohclass *class = obj;

   return class->delete ? CMP_MATCH : 0;
}
static int moh_diff ( struct mohclass old,
struct mohclass new 
) [static]

Definition at line 1105 of file res_musiconhold.c.

References mohclass::args, mohclass::dir, mohclass::flags, and mohclass::mode.

Referenced by _moh_register().

{
   if (!old || !new) {
      return -1;
   }

   if (strcmp(old->dir, new->dir)) {
      return -1;
   } else if (strcmp(old->mode, new->mode)) {
      return -1;
   } else if (strcmp(old->args, new->args)) {
      return -1;
   } else if (old->flags != new->flags) {
      return -1;
   }

   return 0;
}
static int moh_digit_match ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 408 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

{
   char *digit = arg;
   struct mohclass *class = obj;

   return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
}
static void* moh_files_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 369 of file res_musiconhold.c.

References ast_calloc, ast_copy_string(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, moh_files_state::class, MOH_RANDOMIZE, mohclass_ref, ast_channel::music_state, ast_channel::name, moh_files_state::name, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_total, ast_module_info::self, and ast_channel::writeformat.

{
   struct moh_files_state *state;
   struct mohclass *class = params;

   if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
      chan->music_state = state;
      ast_module_ref(ast_module_info->self);
   } else {
      state = chan->music_state;
   }

   if (!state) {
      return NULL;
   }

   /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
    * malloc may allocate a different class to the same memory block.  This
    * might only happen when two reloads are generated in a short period of
    * time, but it's still important to protect against.
    * PROG: Compare the quick operation first, to save CPU. */
   if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
      memset(state, 0, sizeof(*state));
      if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
         state->pos = ast_random() % class->total_files;
      }
   }

   state->class = mohclass_ref(class, "Reffing music class for channel");
   state->origwfmt = chan->writeformat;
   /* For comparison on restart of MOH (see above) */
   ast_copy_string(state->name, class->name, sizeof(state->name));
   state->save_total = class->total_files;

   ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
   
   return chan->music_state;
}
static int moh_files_generator ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 335 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_frfree, ast_log(), ast_write(), errno, f, LOG_WARNING, moh_files_readframe(), ast_channel::music_state, ast_channel::name, moh_files_state::sample_queue, moh_files_state::samples, and ast_frame::samples.

{
   struct moh_files_state *state = chan->music_state;
   struct ast_frame *f = NULL;
   int res = 0;

   state->sample_queue += samples;

   while (state->sample_queue > 0) {
      ast_channel_lock(chan);
      if ((f = moh_files_readframe(chan))) {
         /* We need to be sure that we unlock
          * the channel prior to calling
          * ast_write. Otherwise, the recursive locking
          * that occurs can cause deadlocks when using
          * indirect channels, like local channels
          */
         ast_channel_unlock(chan);
         state->samples += f->samples;
         state->sample_queue -= f->samples;
         res = ast_write(chan, f);
         ast_frfree(f);
         if (res < 0) {
            ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
            return -1;
         }
      } else {
         ast_channel_unlock(chan);
         return -1;  
      }
   }
   return res;
}
static struct ast_frame* moh_files_readframe ( struct ast_channel chan) [static, read]

Definition at line 323 of file res_musiconhold.c.

References ast_moh_files_next(), ast_readframe(), f, and ast_channel::stream.

Referenced by moh_files_generator().

{
   struct ast_frame *f = NULL;
   
   if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
      if (!ast_moh_files_next(chan))
         f = ast_readframe(chan->stream);
   }

   return f;
}
static void moh_files_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 228 of file res_musiconhold.c.

References ast_closestream(), ast_log(), ast_set_write_format(), ast_verbose(), moh_files_state::class, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_pos, ast_channel::stream, and VERBOSE_PREFIX_3.

{
   struct moh_files_state *state;

   if (!chan || !chan->music_state) {
      return;
   }

   state = chan->music_state;

   if (chan->stream) {
      ast_closestream(chan->stream);
      chan->stream = NULL;
   }
   
   if (option_verbose > 2) {
      ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
   }

   if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
      ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
   }

   state->save_pos = state->pos;

   state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
}
static int moh_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 911 of file res_musiconhold.c.

References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), buf, ast_frame::data, ast_frame::datalen, errno, mohdata::f, mohclass::format, LOG_WARNING, moh, ast_channel::name, mohdata::parent, mohdata::pipe, ast_frame::ptr, and ast_frame::samples.

{
   struct mohdata *moh = data;
   short buf[1280 + AST_FRIENDLY_OFFSET / 2];
   int res;

   len = ast_codec_get_len(moh->parent->format, samples);

   if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
      ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
      len = sizeof(buf) - AST_FRIENDLY_OFFSET;
   }
   res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
   if (res <= 0)
      return 0;

   moh->f.datalen = res;
   moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
   moh->f.samples = ast_codec_get_samples(&moh->f);

   if (ast_write(chan, &moh->f) < 0) {
      ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
      return -1;
   }

   return 0;
}
static void moh_handle_digit ( struct ast_channel chan,
char  digit 
) [static]

Definition at line 422 of file res_musiconhold.c.

References ast_moh_start(), ast_moh_stop(), ast_strdupa, ast_string_field_set, get_mohbydigit(), mohclass_unref, and musicclass.

{
   struct mohclass *class;
   const char *classname = NULL;

   if ((class = get_mohbydigit(digit))) {
      classname = ast_strdupa(class->name);
      class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
      ast_string_field_set(chan,musicclass,classname);
      ast_moh_stop(chan);
      ast_moh_start(chan, classname, NULL);
   }
}
static void moh_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 852 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, LOG_WARNING, mohclass::members, moh, mohclass_unref, ast_channel::name, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

{
   struct mohdata *moh = data;
   struct mohclass *class = moh->parent;
   int oldwfmt;

   ao2_lock(class);
   AST_LIST_REMOVE(&moh->parent->members, moh, list); 
   ao2_unlock(class);
   
   close(moh->pipe[0]);
   close(moh->pipe[1]);

   oldwfmt = moh->origwfmt;

   moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");

   ast_free(moh);

   if (chan) {
      if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
               chan->name, ast_getformatname(oldwfmt));
      }

      ast_verb(3, "Stopped music on hold on %s\n", chan->name);
   }
}
static void moh_rescan_files ( void  ) [static]

Definition at line 1091 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, and moh_scan_files().

Referenced by load_moh_classes().

                                   {
   struct ao2_iterator i;
   struct mohclass *c;

   i = ao2_iterator_init(mohclasses, 0);

   while ((c = ao2_iterator_next(&i))) {
      moh_scan_files(c);
      ao2_ref(c, -1);
   }

   ao2_iterator_destroy(&i);
}
static int moh_scan_files ( struct mohclass class) [static]

Definition at line 979 of file res_musiconhold.c.

References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_free, ast_log(), ast_test_flag, errno, ext, LOG_WARNING, moh_add_file(), moh_sort_compare(), and MOH_SORTALPHA.

Referenced by init_files_class(), local_ast_moh_start(), and moh_rescan_files().

                                                  {

   DIR *files_DIR;
   struct dirent *files_dirent;
   char dir_path[PATH_MAX];
   char path[PATH_MAX];
   char filepath[PATH_MAX];
   char *ext;
   struct stat statbuf;
   int dirnamelen;
   int i;

   if (class->dir[0] != '/') {
      ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
      strncat(dir_path, "/", sizeof(dir_path) - 1);
      strncat(dir_path, class->dir, sizeof(dir_path) - 1);
   } else {
      ast_copy_string(dir_path, class->dir, sizeof(dir_path));
   }
   ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
   files_DIR = opendir(dir_path);
   if (!files_DIR) {
      ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
      return -1;
   }

   for (i = 0; i < class->total_files; i++)
      ast_free(class->filearray[i]);

   class->total_files = 0;
   dirnamelen = strlen(dir_path) + 2;
   if (!getcwd(path, sizeof(path))) {
      ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
      return -1;
   }
   if (chdir(dir_path) < 0) {
      ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
      return -1;
   }
   while ((files_dirent = readdir(files_DIR))) {
      /* The file name must be at least long enough to have the file type extension */
      if ((strlen(files_dirent->d_name) < 4))
         continue;

      /* Skip files that starts with a dot */
      if (files_dirent->d_name[0] == '.')
         continue;

      /* Skip files without extensions... they are not audio */
      if (!strchr(files_dirent->d_name, '.'))
         continue;

      snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);

      if (stat(filepath, &statbuf))
         continue;

      if (!S_ISREG(statbuf.st_mode))
         continue;

      if ((ext = strrchr(filepath, '.')))
         *ext = '\0';

      /* if the file is present in multiple formats, ensure we only put it into the list once */
      for (i = 0; i < class->total_files; i++)
         if (!strcmp(filepath, class->filearray[i]))
            break;

      if (i == class->total_files) {
         if (moh_add_file(class, filepath))
            break;
      }
   }

   closedir(files_DIR);
   if (chdir(path) < 0) {
      ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
      return -1;
   }
   if (ast_test_flag(class, MOH_SORTALPHA))
      qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
   return class->total_files;
}
static int moh_sort_compare ( const void *  i1,
const void *  i2 
) [static]

Definition at line 969 of file res_musiconhold.c.

Referenced by moh_scan_files().

{
   char *s1, *s2;

   s1 = ((char **)i1)[0];
   s2 = ((char **)i2)[0];

   return strcasecmp(s1, s2);
}
static struct mohdata* mohalloc ( struct mohclass cl) [static, read]

Definition at line 819 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohdata::f, mohclass::flags, mohclass::format, ast_frame::frametype, LOG_WARNING, mohclass::members, moh, mohclass_ref, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

{
   struct mohdata *moh;
   long flags; 
   
   if (!(moh = ast_calloc(1, sizeof(*moh))))
      return NULL;
   
   if (pipe(moh->pipe)) {
      ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
      ast_free(moh);
      return NULL;
   }

   /* Make entirely non-blocking */
   flags = fcntl(moh->pipe[0], F_GETFL);
   fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
   flags = fcntl(moh->pipe[1], F_GETFL);
   fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);

   moh->f.frametype = AST_FRAME_VOICE;
   moh->f.subclass = cl->format;
   moh->f.offset = AST_FRIENDLY_OFFSET;

   moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");

   ao2_lock(cl);
   AST_LIST_INSERT_HEAD(&cl->members, moh, list);
   ao2_unlock(cl);
   
   return moh;
}
static void* monmp3thread ( void *  data) [static]

Definition at line 585 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_codec_get_len(), ast_debug, AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_samp2tv(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), buf, errno, len(), LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

{
#define  MOH_MS_INTERVAL      100

   struct mohclass *class = data;
   struct mohdata *moh;
   char buf[8192];
   short sbuf[8192];
   int res, res2;
   int len;
   struct timeval deadline, tv_tmp;

   deadline.tv_sec = 0;
   deadline.tv_usec = 0;
   for(;/* ever */;) {
      pthread_testcancel();
      /* Spawn mp3 player if it's not there */
      if (class->srcfd < 0) {
         if ((class->srcfd = spawn_mp3(class)) < 0) {
            ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
            /* Try again later */
            sleep(500);
            pthread_testcancel();
         }
      }
      if (class->pseudofd > -1) {
#ifdef SOLARIS
         thr_yield();
#endif
         /* Pause some amount of time */
         res = read(class->pseudofd, buf, sizeof(buf));
         pthread_testcancel();
      } else {
         long delta;
         /* Reliable sleep */
         tv_tmp = ast_tvnow();
         if (ast_tvzero(deadline))
            deadline = tv_tmp;
         delta = ast_tvdiff_ms(tv_tmp, deadline);
         if (delta < MOH_MS_INTERVAL) {   /* too early */
            deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
            usleep(1000 * (MOH_MS_INTERVAL - delta));
            pthread_testcancel();
         } else {
            ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
            deadline = tv_tmp;
         }
         res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
      }
      if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
         continue;
      /* Read mp3 audio */
      len = ast_codec_get_len(class->format, res);

      if ((res2 = read(class->srcfd, sbuf, len)) != len) {
         if (!res2) {
            close(class->srcfd);
            class->srcfd = -1;
            pthread_testcancel();
            if (class->pid > 1) {
               do {
                  if (killpg(class->pid, SIGHUP) < 0) {
                     if (errno == ESRCH) {
                        break;
                     }
                     ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
                  }
                  usleep(100000);
                  if (killpg(class->pid, SIGTERM) < 0) {
                     if (errno == ESRCH) {
                        break;
                     }
                     ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
                  }
                  usleep(100000);
                  if (killpg(class->pid, SIGKILL) < 0) {
                     if (errno == ESRCH) {
                        break;
                     }
                     ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
                  }
               } while (0);
               class->pid = 0;
            }
         } else {
            ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
         }
         continue;
      }

      pthread_testcancel();

      ao2_lock(class);
      AST_LIST_TRAVERSE(&class->members, moh, list) {
         /* Write data */
         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
            ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
         }
      }
      ao2_unlock(class);
   }
   return NULL;
}
static int play_moh_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 689 of file res_musiconhold.c.

References mohclass::args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

{
   char *parse;
   char *class;
   int timeout = -1;
   int res;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(class);
      AST_APP_ARG(duration);
   );

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (!ast_strlen_zero(args.duration)) {
      if (sscanf(args.duration, "%30d", &timeout) == 1) {
         timeout *= 1000;
      } else {
         ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
      }
   }

   class = S_OR(args.class, NULL);
   if (ast_moh_start(chan, class, NULL)) {
      ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
      return 0;
   }

   if (timeout > 0)
      res = ast_safe_sleep(chan, timeout);
   else {
      while (!(res = ast_safe_sleep(chan, 10000)));
   }

   ast_moh_stop(chan);

   return res;
}
static int reload ( void  ) [static]
static int set_moh_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 752 of file res_musiconhold.c.

References ast_log(), ast_string_field_set, ast_strlen_zero(), LOG_WARNING, and musicclass.

Referenced by load_module().

{
   static int deprecation_warning = 0;

   if (!deprecation_warning) {
      deprecation_warning = 1;
      ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
   }

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
      return -1;
   }
   ast_string_field_set(chan, musicclass, data);
   return 0;
}
static int spawn_mp3 ( struct mohclass class) [static]

Definition at line 444 of file res_musiconhold.c.

References ast_close_fds_above_n(), ast_copy_string(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_strlen_zero(), ast_test_flag, mohclass::dir, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, and strsep().

Referenced by monmp3thread().

{
   int fds[2];
   int files = 0;
   char fns[MAX_MP3S][80];
   char *argv[MAX_MP3S + 50];
   char xargs[256];
   char *argptr;
   int argc = 0;
   DIR *dir = NULL;
   struct dirent *de;

   
   if (!strcasecmp(class->dir, "nodir")) {
      files = 1;
   } else {
      dir = opendir(class->dir);
      if (!dir && strncasecmp(class->dir, "http://", 7)) {
         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
         return -1;
      }
   }

   if (!ast_test_flag(class, MOH_CUSTOM)) {
      argv[argc++] = "mpg123";
      argv[argc++] = "-q";
      argv[argc++] = "-s";
      argv[argc++] = "--mono";
      argv[argc++] = "-r";
      argv[argc++] = "8000";
      
      if (!ast_test_flag(class, MOH_SINGLE)) {
         argv[argc++] = "-b";
         argv[argc++] = "2048";
      }
      
      argv[argc++] = "-f";
      
      if (ast_test_flag(class, MOH_QUIET))
         argv[argc++] = "4096";
      else
         argv[argc++] = "8192";
      
      /* Look for extra arguments and add them to the list */
      ast_copy_string(xargs, class->args, sizeof(xargs));
      argptr = xargs;
      while (!ast_strlen_zero(argptr)) {
         argv[argc++] = argptr;
         strsep(&argptr, ",");
      }
   } else  {
      /* Format arguments for argv vector */
      ast_copy_string(xargs, class->args, sizeof(xargs));
      argptr = xargs;
      while (!ast_strlen_zero(argptr)) {
         argv[argc++] = argptr;
         strsep(&argptr, " ");
      }
   }

   if (!strncasecmp(class->dir, "http://", 7)) {
      ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
      argv[argc++] = fns[files];
      files++;
   } else if (dir) {
      while ((de = readdir(dir)) && (files < MAX_MP3S)) {
         if ((strlen(de->d_name) > 3) && 
             ((ast_test_flag(class, MOH_CUSTOM) && 
               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
            ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
            argv[argc++] = fns[files];
            files++;
         }
      }
   }
   argv[argc] = NULL;
   if (dir) {
      closedir(dir);
   }
   if (pipe(fds)) {  
      ast_log(LOG_WARNING, "Pipe failed\n");
      return -1;
   }
   if (!files) {
      ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
      close(fds[0]);
      close(fds[1]);
      return -1;
   }
   if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
      sleep(respawn_time - (time(NULL) - class->start));
   }

   time(&class->start);
   class->pid = ast_safe_fork(0);
   if (class->pid < 0) {
      close(fds[0]);
      close(fds[1]);
      ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
      return -1;
   }
   if (!class->pid) {
      if (ast_opt_high_priority)
         ast_set_priority(0);

      close(fds[0]);
      /* Stdout goes to pipe */
      dup2(fds[1], STDOUT_FILENO);

      /* Close everything else */
      ast_close_fds_above_n(STDERR_FILENO);

      /* Child */
      if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
         ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
         _exit(1);
      }
      setpgid(0, getpid());
      if (ast_test_flag(class, MOH_CUSTOM)) {
         execv(argv[0], argv);
      } else {
         /* Default install is /usr/local/bin */
         execv(LOCAL_MPG_123, argv);
         /* Many places have it in /usr/bin */
         execv(MPG_123, argv);
         /* Check PATH as a last-ditch effort */
         execvp("mpg123", argv);
      }
      /* Can't use logger, since log FDs are closed */
      fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
      close(fds[1]);
      _exit(1);
   } else {
      /* Parent */
      close(fds[1]);
   }
   return fds[0];
}
static int start_moh_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 769 of file res_musiconhold.c.

References mohclass::args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

{
   char *parse;
   char *class;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(class);
   );

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   class = S_OR(args.class, NULL);
   if (ast_moh_start(chan, class, NULL)) 
      ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);

   return 0;
}
static int stop_moh_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 788 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

{
   ast_moh_stop(chan);

   return 0;
}
static int unload_module ( void  ) [static]

Definition at line 1879 of file res_musiconhold.c.

References ao2_t_callback, ARRAY_LEN, ast_cli_unregister_multiple(), ast_log(), ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), ast_unregister_atexit(), LOG_WARNING, moh_class_inuse(), and mohclass_unref.

{
   int res = 0;
   struct mohclass *class = NULL;

   /* XXX This check shouldn't be required if module ref counting was being used
    * properly ... */
   if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
      class = mohclass_unref(class, "unref of class from module unload callback");
      res = -1;
   }

   if (res < 0) {
      ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
      return res;
   }

   ast_uninstall_music_functions();

   ast_moh_destroy();
   res = ast_unregister_application(play_moh);
   res |= ast_unregister_application(wait_moh);
   res |= ast_unregister_application(set_moh);
   res |= ast_unregister_application(start_moh);
   res |= ast_unregister_application(stop_moh);
   ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
   ast_unregister_atexit(ast_moh_destroy);

   return res;
}
static int wait_moh_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 729 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), LOG_WARNING, and ast_channel::name.

Referenced by load_module().

{
   static int deprecation_warning = 0;
   int res;

   if (!deprecation_warning) {
      deprecation_warning = 1;
      ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
   }

   if (!data || !atoi(data)) {
      ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
      return -1;
   }
   if (ast_moh_start(chan, NULL, NULL)) {
      ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
      return 0;
   }
   res = ast_safe_sleep(chan, atoi(data) * 1000);
   ast_moh_stop(chan);
   return res;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Music On Hold Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1914 of file res_musiconhold.c.

Definition at line 1914 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
   AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
   AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
}

Definition at line 1809 of file res_musiconhold.c.

struct ast_flags global_flags[1] = {{0}} [static]

global MOH_ flags

Definition at line 152 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 436 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 192 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 939 of file res_musiconhold.c.

char* play_moh = "MusicOnHold" [static]

Definition at line 79 of file res_musiconhold.c.

char* play_moh_desc = "Returns 0 when done, -1 on hangup.\n" [static]

Definition at line 91 of file res_musiconhold.c.

char* play_moh_syn = "Play Music On Hold indefinitely" [static]

Definition at line 85 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 127 of file res_musiconhold.c.

Referenced by _moh_register(), and local_ast_moh_start().

char* set_moh = "SetMusicOnHold" [static]

Definition at line 81 of file res_musiconhold.c.

char* set_moh_desc = " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n" [static]

Definition at line 109 of file res_musiconhold.c.

char* set_moh_syn = "Set default Music On Hold class" [static]

Definition at line 87 of file res_musiconhold.c.

char* start_moh = "StartMusicOnHold" [static]

Definition at line 82 of file res_musiconhold.c.

char* start_moh_desc = "music source for the channel will be used. Always returns 0.\n" [static]

Definition at line 119 of file res_musiconhold.c.

char* start_moh_syn = "Play Music On Hold" [static]

Definition at line 88 of file res_musiconhold.c.

char* stop_moh = "StopMusicOnHold" [static]

Definition at line 83 of file res_musiconhold.c.

char* stop_moh_desc = "Stops playing music on hold.\n" [static]

Definition at line 124 of file res_musiconhold.c.

char* stop_moh_syn = "Stop Playing Music On Hold" [static]

Definition at line 89 of file res_musiconhold.c.

char* wait_moh = "WaitMusicOnHold" [static]

Definition at line 80 of file res_musiconhold.c.

char* wait_moh_desc = " !!! DEPRECATED. Use MusicOnHold instead !!!\n" [static]

Definition at line 99 of file res_musiconhold.c.

char* wait_moh_syn = "Wait, playing Music On Hold" [static]

Definition at line 86 of file res_musiconhold.c.