Sat Apr 26 2014 22:03:11

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 "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 "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.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_ANNOUNCEMENT   (1 << 6)
#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 void moh_files_write_format_change (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, const char *data)
static int reload (void)
static int set_moh_exec (struct ast_channel *chan, const char *data)
static int spawn_mp3 (struct mohclass *class)
static int start_moh_exec (struct ast_channel *chan, const char *data)
static int stop_moh_exec (struct ast_channel *chan, const char *data)
static int unload_module (void)
static int wait_moh_exec (struct ast_channel *chan, const char *data)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
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 const char play_moh [] = "MusicOnHold"
static int respawn_time = 20
static const char set_moh [] = "SetMusicOnHold"
static const char start_moh [] = "StartMusicOnHold"
static const char stop_moh [] = "StopMusicOnHold"
static const char wait_moh [] = "WaitMusicOnHold"

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 72 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 883 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 71 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 70 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 225 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MAX_MP3S   256

Definition at line 227 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_ANNOUNCEMENT   (1 << 6)

Do we play announcement files between songs on this channel?

Definition at line 177 of file res_musiconhold.c.

Referenced by ast_moh_files_next(), and load_moh_classes().

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 176 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 1346 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 180 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 170 of file res_musiconhold.c.

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

#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 1271 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

Definition at line 171 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 174 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 231 of file res_musiconhold.c.

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

#define MPG_123   "/usr/bin/mpg123"

Definition at line 226 of file res_musiconhold.c.

Referenced by spawn_mp3().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2035 of file res_musiconhold.c.

static void __unreg_module ( void  ) [static]

Definition at line 2035 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 885 of file res_musiconhold.c.

References __ao2_find(), __ao2_find_debug(), ast_copy_string(), ast_debug, mohclass::flags, 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", file, lineno, funcname);
#else
   moh = __ao2_find(mohclasses, &tmp_class, flags);
#endif

   if (!moh && warn) {
      ast_debug(1, "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 1348 of file res_musiconhold.c.

References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AO2_ALLOC_OPT_LOCK_MUTEX, ast_format_set(), AST_FORMAT_SLINEAR, and moh_class_destructor().

{
   struct mohclass *class;

   if ((class =
#ifdef REF_DEBUG
         __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
            AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
#elif defined(__AST_DEBUG_MALLOC)
         __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
            AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
#else
         ao2_alloc(sizeof(*class), moh_class_destructor)
#endif
      )) {
      ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
      class->srcfd = -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 1272 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;

   mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);

   if (mohclass && !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 1823 of file res_musiconhold.c.

References ao2_ref, 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");
   if (mohclasses) {
      ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
      ao2_ref(mohclasses, -1);
      mohclasses = NULL;
   }
}
static int ast_moh_files_next ( struct ast_channel chan) [static]

Definition at line 287 of file res_musiconhold.c.

References moh_files_state::announcement, mohclass::announcement, ast_channel_language(), ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_copy_string(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_strlen_zero(), ast_tellstream(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, LOG_WARNING, MOH_ANNOUNCEMENT, MOH_RANDOMIZE, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, and mohclass::total_files.

Referenced by moh_files_readframe().

{
   struct moh_files_state *state = ast_channel_music_state(chan);
   int tries;

   /* Discontinue a stream if it is running already */
   if (ast_channel_stream(chan)) {
      ast_closestream(ast_channel_stream(chan));
      ast_channel_stream_set(chan, NULL);
   }

   if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
      state->announcement = 1;
      if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
         ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
         return 0;
      }
   } else {
      state->announcement = 0;
   }

   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 && ast_strlen_zero(state->save_pos_filename)) {
      /* First time so lets play the file. */
      state->save_pos = -1;
   } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(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], ast_channel_language(chan), 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 */
   ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));

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

   if (state->samples) {
      size_t loc;
      /* seek *SHOULD* be good since it's from a known location */
      ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
      /* if the seek failed then recover because if there is not a valid read,
       * moh_files_generate will return -1 and MOH will stop */
      loc = ast_tellstream(ast_channel_stream(chan));
      if (state->samples > loc && loc) {
         /* seek one sample from the end for one guaranteed valid read */
         ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
      }
   }

   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 494 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 1833 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 1893 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 1855 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 1232 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), errno, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and monmp3thread().

Referenced by _moh_register().

{
   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;

   if (!(class->timer = ast_timer_open())) {
      ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
      return -1;
   }
   if (class->timer && ast_timer_set_rate(class->timer, 25)) {
      ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
      ast_timer_close(class->timer);
      class->timer = NULL;
   }

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

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

Definition at line 1171 of file res_musiconhold.c.

References ast_set_flag, ast_verb, MOH_RANDOMIZE, and moh_scan_files().

Referenced by _moh_register().

{
   int res;

   res = moh_scan_files(class);

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

   if (!res) {
      ast_verb(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]
static int load_moh_classes ( int  reload) [static]

Definition at line 1710 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_set(), 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_ANNOUNCEMENT, 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(), and reload().

{
   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_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");
      }
      return 0;
   }
   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, "announcement")) {
            ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
            ast_set_flag(class, MOH_ANNOUNCEMENT);
         } 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")) {
            ast_getformatbyname(var->value, &class->format);
            if (!class->format.id) {
               ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
               ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
            }
         }
      }

      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 1326 of file res_musiconhold.c.

References ast_channel_music_state(), ast_channel_music_state_set(), ast_free, ast_log(), ast_module_unref(), moh_files_state::class, LOG_WARNING, mohclass_unref, and ast_module_info::self.

Referenced by load_module(), and reload().

{
   struct moh_files_state *state = ast_channel_music_state(chan);

   if (state) {
      if (state->class) {
         /* This should never happen.  We likely just leaked some resource. */
         state->class =
            mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
         ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
      }
      ast_free(ast_channel_music_state(chan));
      ast_channel_music_state_set(chan, 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 1370 of file res_musiconhold.c.

References mohclass::args, ast_activate_generator(), ast_channel_flags(), ast_channel_music_state(), ast_channel_musicclass(), ast_channel_name(), ast_channel_uniqueid(), ast_check_realtime(), ast_copy_string(), AST_FLAG_MOH, ast_format_set(), AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), ast_manager_event, ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_true(), ast_variables_destroy(), moh_files_state::class, mohclass::digit, mohclass::dir, DONT_UNREF, errno, EVENT_FLAG_CALL, mohclass::format, get_mohbyname, ast_format::id, LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, MOH_SORTALPHA, mohclass_ref, mohclass_unref, monmp3thread(), ast_variable::name, mohclass::name, ast_variable::next, mohclass::realtime, respawn_time, SENTINEL, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, mohclass::total_files, ast_variable::value, and var.

Referenced by load_module(), and reload().

{
   struct mohclass *mohclass = NULL;
   struct moh_files_state *state = ast_channel_music_state(chan);
   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(ast_channel_musicclass(chan))) {
      mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
      if (!mohclass && realtime_possible) {
         var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), 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")) {
               ast_getformatbyname(tmp->value, &mohclass->format);
               if (!mohclass->format.id) {
                  ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
                  ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
               }
            }
         }
         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);
            }
            /* 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;
               if (!(mohclass->timer = ast_timer_open())) {
                  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
               }
               if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
                  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
                  ast_timer_close(mohclass->timer);
                  mohclass->timer = NULL;
               }

               /* 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 = mohclass_ref(state->class, "using existing class from state");
                  }
               } else {
                  if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
                     ast_log(LOG_WARNING, "Unable to create moh...\n");
                     if (mohclass->timer) {
                        ast_timer_close(mohclass->timer);
                        mohclass->timer = NULL;
                     }
                     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);
         var = NULL;
      }
   }

   if (!mohclass) {
      return -1;
   }

   /* If we are using a cached realtime class with files, re-scan the files */
   if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
      if (!moh_scan_files(mohclass)) {
         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
         return -1;
      }
   }

   ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
      "State: Start\r\n"
      "Channel: %s\r\n"
      "UniqueID: %s\r\n"
      "Class: %s\r\n",
      ast_channel_name(chan), ast_channel_uniqueid(chan),
      mohclass->name);

   ast_set_flag(ast_channel_flags(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 int moh_add_file ( struct mohclass class,
const char *  filepath 
) [static]

Definition at line 1047 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) {
      class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray));
      if (!class->filearray) {
         return -1;
      }
      class->allowed_files = INITIAL_NUM_FILES;
   } else if (class->total_files == class->allowed_files) {
      char **new_array;

      new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2);
      if (!new_array) {
         return -1;
      }
      class->filearray = new_array;
      class->allowed_files *= 2;
   }

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

   class->total_files++;

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

Definition at line 976 of file res_musiconhold.c.

References ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_name(), ast_channel_writeformat(), ast_codec2str(), ast_format_copy(), ast_log(), ast_module_ref(), ast_set_write_format(), ast_verb, moh_files_state::class, mohclass::format, LOG_WARNING, moh_release(), mohalloc(), mohclass_ref, mohclass_unref, mohclass::name, mohdata::origwfmt, ast_module_info::self, and state.

{
   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 (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
      ast_channel_music_state_set(chan, state);
      ast_module_ref(ast_module_info->self);
   } else {
      state = ast_channel_music_state(chan);
      if (!state) {
         return NULL;
      }
      if (state->class) {
         mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
         ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
      }
      memset(state, 0, sizeof(*state));
   }

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

Definition at line 1942 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 1605 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_debug, AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, errno, free, 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);

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

   /* 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_debug(1, "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_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);

      close(class->srcfd);
      class->srcfd = -1;
   }

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

   if (class->timer) {
      ast_timer_close(class->timer);
      class->timer = 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 1935 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 1992 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 1694 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 1703 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 1213 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 485 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 441 of file res_musiconhold.c.

References ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_name(), ast_channel_writeformat(), ast_copy_string(), ast_format_copy(), ast_log(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, moh_files_state::class, LOG_WARNING, MOH_RANDOMIZE, mohclass_ref, mohclass_unref, moh_files_state::mohwfmt, moh_files_state::name, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_total, ast_module_info::self, and state.

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

   if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
      ast_channel_music_state_set(chan, state);
      ast_module_ref(ast_module_info->self);
   } else {
      state = ast_channel_music_state(chan);
      if (!state) {
         return NULL;
      }
      if (state->class) {
         mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
         ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
      }
   }

   /* 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");
   ast_format_copy(&state->origwfmt, ast_channel_writeformat(chan));
   ast_format_copy(&state->mohwfmt, ast_channel_writeformat(chan));

   /* 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, ast_channel_name(chan));
   
   return ast_channel_music_state(chan);
}
static int moh_files_generator ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 404 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_music_state(), ast_channel_name(), ast_channel_unlock, ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_format_copy(), ast_frfree, ast_log(), ast_write(), errno, f, ast_frame_subclass::format, LOG_WARNING, moh_files_readframe(), moh_files_state::mohwfmt, moh_files_state::sample_queue, ast_frame::samples, moh_files_state::samples, and ast_frame::subclass.

{
   struct moh_files_state *state = ast_channel_music_state(chan);
   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;
         if (ast_format_cmp(&f->subclass.format, &state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
            ast_format_copy(&state->mohwfmt, &f->subclass.format);
         }
         res = ast_write(chan, f);
         ast_frfree(f);
         if (res < 0) {
            ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), 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 373 of file res_musiconhold.c.

References ast_channel_stream(), ast_moh_files_next(), ast_readframe(), and f.

Referenced by moh_files_generator().

{
   struct ast_frame *f = NULL;

   if (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
      if (!ast_moh_files_next(chan))
         f = ast_readframe(ast_channel_stream(chan));
   }

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

Definition at line 259 of file res_musiconhold.c.

References moh_files_state::announcement, ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_format_clear(), ast_getformatname(), ast_log(), ast_set_write_format(), ast_verb, moh_files_state::class, ast_format::id, LOG_WARNING, mohclass_unref, moh_files_state::mohwfmt, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_pos, and state.

{
   struct moh_files_state *state;

   if (!chan || !ast_channel_music_state(chan)) {
      return;
   }

   state = ast_channel_music_state(chan);

   if (ast_channel_stream(chan)) {
      ast_closestream(ast_channel_stream(chan));
      ast_channel_stream_set(chan, NULL);
   }
   
   ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));

   ast_format_clear(&state->mohwfmt); /* make sure to clear this format before restoring the original format. */
   if (state->origwfmt.id && ast_set_write_format(chan, &state->origwfmt)) {
      ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan), ast_getformatname(&state->origwfmt));
   }

   state->save_pos = state->pos;
   state->announcement = 0;

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

Definition at line 385 of file res_musiconhold.c.

References ast_channel_music_state(), ast_channel_writeformat(), ast_format_clear(), ast_format_copy(), ast_set_write_format(), ast_format::id, moh_files_state::mohwfmt, and moh_files_state::origwfmt.

{
   struct moh_files_state *state = ast_channel_music_state(chan);

   /* In order to prevent a recursive call to this function as a result
    * of setting the moh write format back on the channel. Clear
    * the moh write format before setting the write format on the channel.*/
   if (&state->origwfmt.id) {
      struct ast_format tmp;

      ast_format_copy(&tmp, ast_channel_writeformat(chan));
      if (state->mohwfmt.id) {
         ast_format_clear(&state->origwfmt);
         ast_set_write_format(chan, &state->mohwfmt);
      }
      ast_format_copy(&state->origwfmt, &tmp);
   }
}
static int moh_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 1012 of file res_musiconhold.c.

References ast_channel_name(), ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), ast_frame::data, ast_frame::datalen, errno, mohdata::f, mohclass::format, LOG_WARNING, moh, 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, ast_channel_name(chan));
      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", ast_channel_name(chan), strerror(errno));
      return -1;
   }

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

Definition at line 499 of file res_musiconhold.c.

References ast_moh_start(), ast_moh_stop(), get_mohbydigit(), and mohclass_unref.

{
   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_channel_musicclass_set(chan, 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 941 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_channel_music_state(), ast_channel_name(), ast_format_copy(), ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, moh_files_state::class, ast_format::id, LOG_WARNING, mohclass::members, moh, mohclass_unref, mohdata::origwfmt, mohdata::parent, mohdata::pipe, and state.

Referenced by moh_alloc().

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

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

   ast_format_copy(&oldwfmt, &moh->origwfmt);

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

   ast_free(moh);

   if (chan) {
      struct moh_files_state *state;

      state = ast_channel_music_state(chan);
      if (state && state->class) {
         state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
      }
      if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
               ast_channel_name(chan), ast_getformatname(&oldwfmt));
      }

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

Definition at line 1197 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, mohclass::mode, 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))) {
      if (!strcasecmp(c->mode, "files")) {
         moh_scan_files(c);
      }
      ao2_ref(c, -1);
   }

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

Definition at line 1086 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 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;

   if (!getcwd(path, sizeof(path))) {
      ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
      closedir(files_DIR);
      return -1;
   }
   if (chdir(dir_path) < 0) {
      ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
      closedir(files_DIR);
      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 1076 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 908 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_calloc, ast_format_copy(), AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohdata::f, mohclass::flags, ast_frame_subclass::format, 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;
   ast_format_copy(&moh->f.subclass.format, &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 663 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_poll, ast_samp2tv(), ast_timer_ack(), ast_timer_fd(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), errno, len(), LOG_ERROR, 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;
   short sbuf[8192];
   int res = 0, 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);
            continue;
         }
      }
      if (class->timer) {
         struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };

#ifdef SOLARIS
         thr_yield();
#endif
         /* Pause some amount of time */
         if (ast_poll(&pfd, 1, -1) > 0) {
            if (ast_timer_ack(class->timer, 1) < 0) {
               ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
               return NULL;
            }
            res = 320;
         } else {
            ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
            res = 0;
         }
         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,
const char *  data 
) [static]

Definition at line 777 of file res_musiconhold.c.

References mohclass::args, AST_APP_ARG, ast_channel_name(), AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, 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, ast_channel_name(chan));
      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 set_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 840 of file res_musiconhold.c.

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

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_channel_musicclass_set(chan, data);
   return 0;
}
static int spawn_mp3 ( struct mohclass class) [static]

Definition at line 522 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, and MPG_123.

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,
const char *  data 
) [static]

Definition at line 857 of file res_musiconhold.c.

References mohclass::args, AST_APP_ARG, ast_channel_name(), AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), AST_STANDARD_APP_ARGS, LOG_WARNING, 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, ast_channel_name(chan));

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

Definition at line 876 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 1999 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,
const char *  data 
) [static]

Definition at line 817 of file res_musiconhold.c.

References ast_channel_name(), ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), and LOG_WARNING.

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), ast_channel_name(chan));
      return 0;
   }
   res = ast_safe_sleep(chan, atoi(data) * 1000);
   ast_moh_stop(chan);
   return res;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static]

Definition at line 2035 of file res_musiconhold.c.

Definition at line 2035 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 1929 of file res_musiconhold.c.

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

global MOH_ flags

Definition at line 182 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 513 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 223 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 1040 of file res_musiconhold.c.

const char play_moh[] = "MusicOnHold" [static]

Definition at line 147 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 153 of file res_musiconhold.c.

Referenced by _moh_register(), and local_ast_moh_start().

const char set_moh[] = "SetMusicOnHold" [static]

Definition at line 149 of file res_musiconhold.c.

const char start_moh[] = "StartMusicOnHold" [static]

Definition at line 150 of file res_musiconhold.c.

const char stop_moh[] = "StopMusicOnHold" [static]

Definition at line 151 of file res_musiconhold.c.

const char wait_moh[] = "WaitMusicOnHold" [static]

Definition at line 148 of file res_musiconhold.c.