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/stasis.h"
#include "asterisk/stasis_channels.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_PREFERCHANNELCLASS   (1 << 7)
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)
#define moh_register(moh, reload, unref)   _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define MOH_SINGLE   (1 << 1)
#define MOH_SORTALPHA   (1 << 4)
#define MOH_SORTMODE   (3 << 3)
#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)
 Load the module.
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_parse_options (struct ast_variable *var, struct mohclass *mohclass)
static void moh_post_start (struct ast_channel *chan, const char *moh_class_name)
static void moh_post_stop (struct ast_channel *chan)
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 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)

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, .support_level = AST_MODULE_SUPPORT_CORE, .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 start_moh [] = "StartMusicOnHold"
static const char stop_moh [] = "StopMusicOnHold"


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

Referenced by local_ast_moh_start().

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

Definition at line 869 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 79 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 78 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 204 of file res_musiconhold.c.

#define MAX_MP3S   256

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

Referenced by ast_moh_files_next().

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 154 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 1370 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 159 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 156 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_QUIET   (1 << 0)

Definition at line 146 of file res_musiconhold.c.

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

#define MOH_RANDOMIZE   (1 << 3)

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 151 of file res_musiconhold.c.

Referenced by moh_parse_options().

#define moh_register ( moh,
reload,
unref   )     _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)

Note:
This function owns the reference it gets to moh if unref is true

Definition at line 1293 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

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

Referenced by moh_parse_options(), and moh_scan_files().

#define MOH_SORTMODE   (3 << 3)

Definition at line 152 of file res_musiconhold.c.

Referenced by ast_moh_files_next().

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

Definition at line 211 of file res_musiconhold.c.

Referenced by local_ast_moh_start(), 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 205 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 2013 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

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

References __ao2_find(), __ao2_find_debug(), ast_copy_string(), ast_debug, mohclass::flags, mohclass::name, and NULL.

Referenced by _moh_register().

00872 {
00873    struct mohclass *moh = NULL;
00874    struct mohclass tmp_class = {
00875       .flags = 0,
00876    };
00877 
00878    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00879 
00880 #ifdef REF_DEBUG
00881    moh = __ao2_find_debug(mohclasses, &tmp_class, flags,
00882       "get_mohbyname", file, lineno, funcname);
00883 #else
00884    moh = __ao2_find(mohclasses, &tmp_class, flags);
00885 #endif
00886 
00887    if (!moh && warn) {
00888       ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
00889    }
00890 
00891    return moh;
00892 }

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

Definition at line 1372 of file res_musiconhold.c.

References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AO2_ALLOC_OPT_LOCK_MUTEX, ao2_bump, ast_format_slin, mohclass::format, and moh_class_destructor().

01373 {
01374    struct mohclass *class;
01375 
01376    if ((class =
01377 #ifdef REF_DEBUG
01378          __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01379             AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
01380 #elif defined(__AST_DEBUG_MALLOC)
01381          __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01382             AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
01383 #else
01384          ao2_alloc(sizeof(*class), moh_class_destructor)
01385 #endif
01386       )) {
01387       class->format = ao2_bump(ast_format_slin);
01388       class->srcfd = -1;
01389    }
01390 
01391    return class;
01392 }

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

Definition at line 1294 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, NULL, and mohclass::start.

01295 {
01296    struct mohclass *mohclass = NULL;
01297 
01298    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01299 
01300    if (mohclass && !moh_diff(mohclass, moh)) {
01301       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01302       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01303       if (unref) {
01304          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01305       }
01306       return -1;
01307    } else if (mohclass) {
01308       /* Found a class, but it's different from the one being registered */
01309       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01310    }
01311 
01312    time(&moh->start);
01313    moh->start -= respawn_time;
01314 
01315    if (!strcasecmp(moh->mode, "files")) {
01316       if (init_files_class(moh)) {
01317          if (unref) {
01318             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01319          }
01320          return -1;
01321       }
01322    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01323          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01324          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01325       if (init_app_class(moh)) {
01326          if (unref) {
01327             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01328          }
01329          return -1;
01330       }
01331    } else {
01332       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01333       if (unref) {
01334          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01335       }
01336       return -1;
01337    }
01338 
01339    ao2_t_link(mohclasses, moh, "Adding class to container");
01340 
01341    if (unref) {
01342       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01343    }
01344 
01345    return 0;
01346 }

static void ast_moh_destroy ( void   )  [static]

Definition at line 1796 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01797 {
01798    ast_verb(2, "Destroying musiconhold processes\n");
01799    if (mohclasses) {
01800       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01801       ao2_ref(mohclasses, -1);
01802       mohclasses = NULL;
01803    }
01804 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 309 of file res_musiconhold.c.

References mohclass::announcement, moh_files_state::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, MOH_SORTMODE, mohclass::name, NULL, 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().

00310 {
00311    struct moh_files_state *state = ast_channel_music_state(chan);
00312    int tries;
00313 
00314    /* Discontinue a stream if it is running already */
00315    if (ast_channel_stream(chan)) {
00316       ast_closestream(ast_channel_stream(chan));
00317       ast_channel_stream_set(chan, NULL);
00318    }
00319 
00320    if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
00321       state->announcement = 1;
00322       if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
00323          ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
00324          return 0;
00325       }
00326    } else {
00327       state->announcement = 0;
00328    }
00329 
00330    if (!state->class->total_files) {
00331       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00332       return -1;
00333    }
00334 
00335    if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
00336       /* First time so lets play the file. */
00337       state->save_pos = -1;
00338    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
00339       /* If a specific file has been saved confirm it still exists and that it is still valid */
00340       state->pos = state->save_pos;
00341       state->save_pos = -1;
00342    } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
00343       /* Get a random file and ensure we can open it */
00344       for (tries = 0; tries < 20; tries++) {
00345          state->pos = ast_random() % state->class->total_files;
00346          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00347             break;
00348          }
00349       }
00350       state->save_pos = -1;
00351       state->samples = 0;
00352    } else {
00353       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00354       state->pos++;
00355       state->pos %= state->class->total_files;
00356       state->save_pos = -1;
00357       state->samples = 0;
00358    }
00359 
00360    for (tries = 0; tries < state->class->total_files; ++tries) {
00361       if (ast_openstream_full(chan, state->class->filearray[state->pos], ast_channel_language(chan), 1)) {
00362          break;
00363       }
00364 
00365       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00366       state->pos++;
00367       state->pos %= state->class->total_files;
00368    }
00369 
00370    if (tries == state->class->total_files) {
00371       return -1;
00372    }
00373 
00374    /* Record the pointer to the filename for position resuming later */
00375    ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
00376 
00377    ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->class->filearray[state->pos]);
00378 
00379    if (state->samples) {
00380       size_t loc;
00381       /* seek *SHOULD* be good since it's from a known location */
00382       ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
00383       /* if the seek failed then recover because if there is not a valid read,
00384        * moh_files_generate will return -1 and MOH will stop */
00385       loc = ast_tellstream(ast_channel_stream(chan));
00386       if (state->samples > loc && loc) {
00387          /* seek one sample from the end for one guaranteed valid read */
00388          ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
00389       }
00390    }
00391 
00392    return 0;
00393 }

static struct mohclass* get_mohbydigit ( char  digit  )  [static, read]

Note:
This function should be called with the mohclasses list locked

Definition at line 516 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00517 {
00518    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00519 }

static char* handle_cli_moh_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1806 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, NULL, reload(), and ast_cli_entry::usage.

01807 {
01808    switch (cmd) {
01809    case CLI_INIT:
01810       e->command = "moh reload";
01811       e->usage =
01812          "Usage: moh reload\n"
01813          "       Reloads the MusicOnHold module.\n"
01814          "       Alias for 'module reload res_musiconhold.so'\n";
01815       return NULL;
01816    case CLI_GENERATE:
01817       return NULL;
01818    }
01819 
01820    if (a->argc != e->args)
01821       return CLI_SHOWUSAGE;
01822 
01823    reload();
01824 
01825    return CLI_SUCCESS;
01826 }

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

Definition at line 1866 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_format_get_name(), ast_test_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, MOH_CUSTOM, mohclass_unref, NULL, S_OR, and ast_cli_entry::usage.

01867 {
01868    struct mohclass *class;
01869    struct ao2_iterator i;
01870 
01871    switch (cmd) {
01872    case CLI_INIT:
01873       e->command = "moh show classes";
01874       e->usage =
01875          "Usage: moh show classes\n"
01876          "       Lists all MusicOnHold classes.\n";
01877       return NULL;
01878    case CLI_GENERATE:
01879       return NULL;
01880    }
01881 
01882    if (a->argc != e->args)
01883       return CLI_SHOWUSAGE;
01884 
01885    i = ao2_iterator_init(mohclasses, 0);
01886    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01887       ast_cli(a->fd, "Class: %s\n", class->name);
01888       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01889       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01890       if (ast_test_flag(class, MOH_CUSTOM)) {
01891          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01892       }
01893       if (strcasecmp(class->mode, "files")) {
01894          ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
01895       }
01896    }
01897    ao2_iterator_destroy(&i);
01898 
01899    return CLI_SUCCESS;
01900 }

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

Definition at line 1828 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, NULL, and ast_cli_entry::usage.

01829 {
01830    struct mohclass *class;
01831    struct ao2_iterator i;
01832 
01833    switch (cmd) {
01834    case CLI_INIT:
01835       e->command = "moh show files";
01836       e->usage =
01837          "Usage: moh show files\n"
01838          "       Lists all loaded file-based MusicOnHold classes and their\n"
01839          "       files.\n";
01840       return NULL;
01841    case CLI_GENERATE:
01842       return NULL;
01843    }
01844 
01845    if (a->argc != e->args)
01846       return CLI_SHOWUSAGE;
01847 
01848    i = ao2_iterator_init(mohclasses, 0);
01849    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01850       int x;
01851 
01852       if (!class->total_files) {
01853          continue;
01854       }
01855 
01856       ast_cli(a->fd, "Class: %s\n", class->name);
01857       for (x = 0; x < class->total_files; x++) {
01858          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01859       }
01860    }
01861    ao2_iterator_destroy(&i);
01862 
01863    return CLI_SUCCESS;
01864 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1254 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, monmp3thread(), and NULL.

Referenced by _moh_register().

01255 {
01256    if (!strcasecmp(class->mode, "custom")) {
01257       ast_set_flag(class, MOH_CUSTOM);
01258    } else if (!strcasecmp(class->mode, "mp3nb")) {
01259       ast_set_flag(class, MOH_SINGLE);
01260    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01261       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01262    } else if (!strcasecmp(class->mode, "quietmp3")) {
01263       ast_set_flag(class, MOH_QUIET);
01264    }
01265 
01266    class->srcfd = -1;
01267 
01268    if (!(class->timer = ast_timer_open())) {
01269       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01270       return -1;
01271    }
01272    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01273       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01274       ast_timer_close(class->timer);
01275       class->timer = NULL;
01276    }
01277 
01278    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01279       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01280       if (class->timer) {
01281          ast_timer_close(class->timer);
01282          class->timer = NULL;
01283       }
01284       return -1;
01285    }
01286 
01287    return 0;
01288 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1200 of file res_musiconhold.c.

References ast_verb, and moh_scan_files().

Referenced by _moh_register().

01201 {
01202    int res;
01203 
01204    res = moh_scan_files(class);
01205 
01206    if (res < 0) {
01207       return -1;
01208    }
01209 
01210    if (!res) {
01211       ast_verb(3, "Files not found in %s for moh class:%s\n",
01212          class->dir, class->name);
01213       return -1;
01214    }
01215 
01216    return 0;
01217 }

static int load_module ( void   )  [static]

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 1934 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_xml, 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(), start_moh_exec(), and stop_moh_exec().

01935 {
01936    int res;
01937 
01938    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01939       return AST_MODULE_LOAD_DECLINE;
01940    }
01941 
01942    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01943       ast_log(LOG_WARNING, "No music on hold classes configured, "
01944             "disabling music on hold.\n");
01945    } else {
01946       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01947             local_ast_moh_cleanup);
01948    }
01949 
01950    res = ast_register_application_xml(play_moh, play_moh_exec);
01951    ast_register_atexit(ast_moh_destroy);
01952    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01953    if (!res)
01954       res = ast_register_application_xml(start_moh, start_moh_exec);
01955    if (!res)
01956       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01957 
01958    return AST_MODULE_LOAD_SUCCESS;
01959 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1708 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_log, ast_set2_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_parse_options(), MOH_PREFERCHANNELCLASS, moh_register, moh_rescan_files(), mohclass_unref, ast_variable::name, ast_variable::next, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, ast_variable::value, and var.

Referenced by load_module(), and reload().

01709 {
01710    struct ast_config *cfg;
01711    struct ast_variable *var;
01712    struct mohclass *class; 
01713    char *cat;
01714    int numclasses = 0;
01715    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01716 
01717    cfg = ast_config_load("musiconhold.conf", config_flags);
01718 
01719    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01720       if (ast_check_realtime("musiconhold") && reload) {
01721          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01722          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01723       }
01724       return 0;
01725    }
01726    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01727       moh_rescan_files();
01728       return 0;
01729    }
01730 
01731    if (reload) {
01732       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01733    }
01734 
01735    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01736    ast_set2_flag(global_flags, 1, MOH_PREFERCHANNELCLASS);
01737 
01738    cat = ast_category_browse(cfg, NULL);
01739    for (; cat; cat = ast_category_browse(cfg, cat)) {
01740       /* Setup common options from [general] section */
01741       if (!strcasecmp(cat, "general")) {
01742          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01743             if (!strcasecmp(var->name, "cachertclasses")) {
01744                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01745             } else if (!strcasecmp(var->name, "preferchannelclass")) {
01746                ast_set2_flag(global_flags, ast_true(var->value), MOH_PREFERCHANNELCLASS);
01747             } else {
01748                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01749             }
01750          }
01751       }
01752 
01753       if (!(class = moh_class_malloc())) {
01754          break;
01755       }
01756 
01757       moh_parse_options(ast_variable_browse(cfg, cat), class);
01758       /* For compatibility with the past, we overwrite any name=name
01759        * with the context [name]. */
01760       ast_copy_string(class->name, cat, sizeof(class->name));
01761 
01762       if (ast_strlen_zero(class->dir)) {
01763          if (!strcasecmp(class->mode, "custom")) {
01764             strcpy(class->dir, "nodir");
01765          } else {
01766             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01767             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01768             continue;
01769          }
01770       }
01771       if (ast_strlen_zero(class->mode)) {
01772          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01773          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01774          continue;
01775       }
01776       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01777          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01778          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01779          continue;
01780       }
01781 
01782       /* Don't leak a class when it's already registered */
01783       if (!moh_register(class, reload, HANDLE_REF)) {
01784          numclasses++;
01785       }
01786    }
01787 
01788    ast_config_destroy(cfg);
01789 
01790    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01791          moh_classes_delete_marked, NULL, "Purge marked classes");
01792 
01793    return numclasses;
01794 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1348 of file res_musiconhold.c.

References ao2_cleanup, ast_channel_music_state(), ast_channel_music_state_set(), ast_free, ast_log, ast_module_unref, moh_files_state::class, LOG_WARNING, mohclass_unref, moh_files_state::mohwfmt, NULL, and moh_files_state::origwfmt.

Referenced by load_module(), and reload().

01349 {
01350    struct moh_files_state *state = ast_channel_music_state(chan);
01351 
01352    if (state) {
01353       ast_channel_music_state_set(chan, NULL);
01354       if (state->class) {
01355          /* This should never happen.  We likely just leaked some resource. */
01356          state->class =
01357             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01358          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01359       }
01360       ao2_cleanup(state->origwfmt);
01361       ao2_cleanup(state->mohwfmt);
01362       ast_free(state);
01363       /* Only held a module reference if we had a music state */
01364       ast_module_unref(ast_module_info->self);
01365    }
01366 }

static int local_ast_moh_start ( struct ast_channel chan,
const char *  mclass,
const char *  interpclass 
) [static]

Definition at line 1394 of file res_musiconhold.c.

References mohclass::args, ARRAY_LEN, ast_activate_generator(), ast_channel_flags(), ast_channel_music_state(), ast_channel_musicclass(), ast_check_realtime(), AST_FLAG_MOH, ast_load_realtime(), ast_log, ast_pthread_create_background, ast_set_flag, ast_strlen_zero, ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_variables_destroy(), moh_files_state::class, mohclass::dir, DONT_UNREF, errno, get_mohbyname, LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, mohclass_ref, mohclass_unref, monmp3thread(), mohclass::name, NULL, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, mohclass::total_files, and var.

Referenced by load_module(), and reload().

01395 {
01396    struct mohclass *mohclass = NULL;
01397    struct moh_files_state *state = ast_channel_music_state(chan);
01398    struct ast_variable *var = NULL;
01399    int res = 0;
01400    int i;
01401    int realtime_possible = ast_check_realtime("musiconhold");
01402    const char *classes[] = {NULL, NULL, interpclass, "default"};
01403 
01404    if (ast_test_flag(global_flags, MOH_PREFERCHANNELCLASS)) {
01405       classes[0] = ast_channel_musicclass(chan);
01406       classes[1] = mclass;
01407    } else {
01408       classes[0] = mclass;
01409       classes[1] = ast_channel_musicclass(chan);
01410    }
01411 
01412    /* The following is the order of preference for which class to use:
01413     * 1) The channels explicitly set musicclass, which should *only* be
01414     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01415     *    Unless preferchannelclass in musiconhold.conf is false
01416     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01417     *    result of receiving a HOLD control frame, this should be the
01418     *    payload that came with the frame.
01419     * 3) The channels explicitly set musicclass, which should *only* be
01420     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01421     * 4) The interpclass argument. This would be from the mohinterpret
01422     *    option from channel drivers. This is the same as the old musicclass
01423     *    option.
01424     * 5) The default class.
01425     */
01426 
01427    for (i = 0; i < ARRAY_LEN(classes); ++i) {
01428       if (!ast_strlen_zero(classes[i])) {
01429          mohclass = get_mohbyname(classes[i], 1, 0);
01430          if (!mohclass && realtime_possible) {
01431             var = ast_load_realtime("musiconhold", "name", classes[i], SENTINEL);
01432          }
01433          if (mohclass || var) {
01434             break;
01435          }
01436       }
01437    }
01438 
01439    /* If no moh class found in memory, then check RT. Note that the logic used
01440     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01441     */
01442    if (var) {
01443       if ((mohclass = moh_class_malloc())) {
01444          mohclass->realtime = 1;
01445 
01446          moh_parse_options(var, mohclass);
01447          ast_variables_destroy(var);
01448 
01449          if (ast_strlen_zero(mohclass->dir)) {
01450             if (!strcasecmp(mohclass->mode, "custom")) {
01451                strcpy(mohclass->dir, "nodir");
01452             } else {
01453                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01454                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01455                return -1;
01456             }
01457          }
01458          if (ast_strlen_zero(mohclass->mode)) {
01459             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01460             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01461             return -1;
01462          }
01463          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01464             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01465             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01466             return -1;
01467          }
01468 
01469          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01470             /* CACHERTCLASSES enabled, let's add this class to default tree */
01471             if (state && state->class) {
01472                /* Class already exist for this channel */
01473                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01474             }
01475             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01476              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01477              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01478              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01479              * invalid memory.
01480              */
01481             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01482                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01483                return -1;
01484             }
01485          } else {
01486             /* We don't register RT moh class, so let's init it manualy */
01487 
01488             time(&mohclass->start);
01489             mohclass->start -= respawn_time;
01490 
01491             if (!strcasecmp(mohclass->mode, "files")) {
01492                if (!moh_scan_files(mohclass)) {
01493                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01494                   return -1;
01495                }
01496                if (strchr(mohclass->args, 'r')) {
01497                   static int deprecation_warning = 0;
01498                   if (!deprecation_warning) {
01499                      ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14.  Please use 'sort=random' instead.\n");
01500                      deprecation_warning = 1;
01501                   }
01502                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01503                }
01504             } 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")) {
01505 
01506                if (!strcasecmp(mohclass->mode, "custom"))
01507                   ast_set_flag(mohclass, MOH_CUSTOM);
01508                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01509                   ast_set_flag(mohclass, MOH_SINGLE);
01510                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01511                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01512                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01513                   ast_set_flag(mohclass, MOH_QUIET);
01514 
01515                mohclass->srcfd = -1;
01516                if (!(mohclass->timer = ast_timer_open())) {
01517                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01518                }
01519                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01520                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01521                   ast_timer_close(mohclass->timer);
01522                   mohclass->timer = NULL;
01523                }
01524 
01525                /* Let's check if this channel already had a moh class before */
01526                if (state && state->class) {
01527                   /* Class already exist for this channel */
01528                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01529                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01530                      /* we found RT class with the same name, seems like we should continue playing existing one */
01531                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01532                      mohclass = mohclass_ref(state->class, "using existing class from state");
01533                   }
01534                } else {
01535                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01536                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01537                      if (mohclass->timer) {
01538                         ast_timer_close(mohclass->timer);
01539                         mohclass->timer = NULL;
01540                      }
01541                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01542                      return -1;
01543                   }
01544                }
01545             } else {
01546                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01547                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01548                return -1;
01549             }
01550          }
01551       } else {
01552          ast_variables_destroy(var);
01553          var = NULL;
01554       }
01555    }
01556 
01557    if (!mohclass) {
01558       return -1;
01559    }
01560 
01561    /* If we are using a cached realtime class with files, re-scan the files */
01562    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01563       if (!moh_scan_files(mohclass)) {
01564          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01565          return -1;
01566       }
01567    }
01568 
01569    if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
01570       if (mohclass->total_files) {
01571          res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01572       } else {
01573          res = ast_activate_generator(chan, &mohgen, mohclass);
01574       }
01575    }
01576    if (!res) {
01577       ast_channel_latest_musicclass_set(chan, mohclass->name);
01578       ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
01579    }
01580 
01581    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01582 
01583    return res;
01584 }

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

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

01077 {
01078    if (!class->allowed_files) {
01079       class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray));
01080       if (!class->filearray) {
01081          return -1;
01082       }
01083       class->allowed_files = INITIAL_NUM_FILES;
01084    } else if (class->total_files == class->allowed_files) {
01085       char **new_array;
01086 
01087       new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2);
01088       if (!new_array) {
01089          return -1;
01090       }
01091       class->filearray = new_array;
01092       class->allowed_files *= 2;
01093    }
01094 
01095    class->filearray[class->total_files] = ast_strdup(filepath);
01096    if (!class->filearray[class->total_files]) {
01097       return -1;
01098    }
01099 
01100    class->total_files++;
01101 
01102    return 0;
01103 }

static void* moh_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 964 of file res_musiconhold.c.

References ao2_bump, ao2_cleanup, ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_name(), ast_channel_writeformat(), ast_format_get_name(), ast_log, ast_module_ref, ast_set_write_format(), moh_files_state::class, mohclass::format, LOG_WARNING, moh_post_start(), moh_release(), mohalloc(), mohclass_ref, mohclass_unref, moh_files_state::mohwfmt, mohclass::name, NULL, mohdata::origwfmt, and moh_files_state::origwfmt.

00965 {
00966    struct mohdata *res;
00967    struct mohclass *class = params;
00968    struct moh_files_state *state;
00969 
00970    /* Initiating music_state for current channel. Channel should know name of moh class */
00971    state = ast_channel_music_state(chan);
00972    if (!state && (state = ast_calloc(1, sizeof(*state)))) {
00973       ast_channel_music_state_set(chan, state);
00974       ast_module_ref(ast_module_info->self);
00975    } else {
00976       if (!state) {
00977          return NULL;
00978       }
00979       if (state->class) {
00980          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00981          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00982       }
00983       ao2_cleanup(state->origwfmt);
00984       ao2_cleanup(state->mohwfmt);
00985       memset(state, 0, sizeof(*state));
00986    }
00987 
00988    if ((res = mohalloc(class))) {
00989       res->origwfmt = ao2_bump(ast_channel_writeformat(chan));
00990       if (ast_set_write_format(chan, class->format)) {
00991          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
00992             ast_format_get_name(class->format));
00993          moh_release(NULL, res);
00994          res = NULL;
00995       } else {
00996          state->class = mohclass_ref(class, "Placing reference into state container");
00997          moh_post_start(chan, class->name);
00998       }
00999    }
01000    return res;
01001 }

static int moh_class_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1915 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01916 {
01917    struct mohclass *class = obj, *class2 = arg;
01918 
01919    return strcasecmp(class->name, class2->name) ? 0 :
01920       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01921       CMP_MATCH | CMP_STOP;
01922 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1601 of file res_musiconhold.c.

References ao2_cleanup, ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_log, AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, errno, LOG_WARNING, NULL, and mohclass::pid.

Referenced by _moh_class_malloc().

01602 {
01603    struct mohclass *class = obj;
01604    struct mohdata *member;
01605    pthread_t tid = 0;
01606 
01607    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01608 
01609    ao2_lock(class);
01610    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01611       ast_free(member);
01612    }
01613    ao2_unlock(class);
01614 
01615    /* Kill the thread first, so it cannot restart the child process while the
01616     * class is being destroyed */
01617    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01618       tid = class->thread;
01619       class->thread = AST_PTHREADT_NULL;
01620       pthread_cancel(tid);
01621       /* We'll collect the exit status later, after we ensure all the readers
01622        * are dead. */
01623    }
01624 
01625    if (class->pid > 1) {
01626       char buff[8192];
01627       int bytes, tbytes = 0, stime = 0, pid = 0;
01628 
01629       ast_debug(1, "killing %d!\n", class->pid);
01630 
01631       stime = time(NULL) + 2;
01632       pid = class->pid;
01633       class->pid = 0;
01634 
01635       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01636        * to give the process a reason and time enough to kill off its
01637        * children. */
01638       do {
01639          if (killpg(pid, SIGHUP) < 0) {
01640             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01641          }
01642          usleep(100000);
01643          if (killpg(pid, SIGTERM) < 0) {
01644             if (errno == ESRCH) {
01645                break;
01646             }
01647             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01648          }
01649          usleep(100000);
01650          if (killpg(pid, SIGKILL) < 0) {
01651             if (errno == ESRCH) {
01652                break;
01653             }
01654             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01655          }
01656       } while (0);
01657 
01658       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01659             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01660          tbytes = tbytes + bytes;
01661       }
01662 
01663       ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01664 
01665       close(class->srcfd);
01666       class->srcfd = -1;
01667    }
01668 
01669    if (class->filearray) {
01670       int i;
01671       for (i = 0; i < class->total_files; i++) {
01672          ast_free(class->filearray[i]);
01673       }
01674       ast_free(class->filearray);
01675       class->filearray = NULL;
01676    }
01677 
01678    if (class->timer) {
01679       ast_timer_close(class->timer);
01680       class->timer = NULL;
01681    }
01682 
01683    ao2_cleanup(class->format);
01684 
01685    /* Finally, collect the exit status of the monitor thread */
01686    if (tid > 0) {
01687       pthread_join(tid, NULL);
01688    }
01689 
01690 }

static int moh_class_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 1908 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01909 {
01910    const struct mohclass *class = obj;
01911 
01912    return ast_str_case_hash(class->name);
01913 }

static int moh_class_inuse ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1971 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01972 {
01973    struct mohclass *class = obj;
01974 
01975    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01976 }

static int moh_class_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1692 of file res_musiconhold.c.

Referenced by load_moh_classes().

01693 {
01694    struct mohclass *class = obj;
01695 
01696    class->delete = 1;
01697 
01698    return 0;
01699 }

static int moh_classes_delete_marked ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1701 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01702 {
01703    struct mohclass *class = obj;
01704 
01705    return class->delete ? CMP_MATCH : 0;
01706 }

static int moh_diff ( struct mohclass old,
struct mohclass new 
) [static]

Definition at line 1235 of file res_musiconhold.c.

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

Referenced by _moh_register().

01236 {
01237    if (!old || !new) {
01238       return -1;
01239    }
01240 
01241    if (strcmp(old->dir, new->dir)) {
01242       return -1;
01243    } else if (strcmp(old->mode, new->mode)) {
01244       return -1;
01245    } else if (strcmp(old->args, new->args)) {
01246       return -1;
01247    } else if (old->flags != new->flags) {
01248       return -1;
01249    }
01250 
01251    return 0;
01252 }

static int moh_digit_match ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 507 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00508 {
00509    char *digit = arg;
00510    struct mohclass *class = obj;
00511 
00512    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00513 }

static void* moh_files_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 464 of file res_musiconhold.c.

References ao2_cleanup, ao2_replace, ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_writeformat(), ast_copy_string(), ast_log, ast_module_ref, ast_random(), ast_test_flag, LOG_WARNING, moh_post_start(), MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::name, mohclass::name, NULL, and mohclass::total_files.

00465 {
00466    struct moh_files_state *state;
00467    struct mohclass *class = params;
00468 
00469    state = ast_channel_music_state(chan);
00470    if (!state && (state = ast_calloc(1, sizeof(*state)))) {
00471       ast_channel_music_state_set(chan, state);
00472       ast_module_ref(ast_module_info->self);
00473    } else {
00474       if (!state) {
00475          return NULL;
00476       }
00477       if (state->class) {
00478          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00479          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00480       }
00481    }
00482 
00483    /* Resume MOH from where we left off last time or start from scratch? */
00484    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00485       /* Start MOH from scratch. */
00486       ao2_cleanup(state->origwfmt);
00487       ao2_cleanup(state->mohwfmt);
00488       memset(state, 0, sizeof(*state));
00489       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00490          state->pos = ast_random() % class->total_files;
00491       }
00492    }
00493 
00494    state->class = mohclass_ref(class, "Reffing music class for channel");
00495    /* it's possible state is not a new allocation, don't leak old refs */
00496    ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
00497    ao2_replace(state->mohwfmt, ast_channel_writeformat(chan));
00498    /* For comparison on restart of MOH (see above) */
00499    ast_copy_string(state->name, class->name, sizeof(state->name));
00500    state->save_total = class->total_files;
00501 
00502    moh_post_start(chan, class->name);
00503 
00504    return state;
00505 }

static int moh_files_generator ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 426 of file res_musiconhold.c.

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

00427 {
00428    struct moh_files_state *state = ast_channel_music_state(chan);
00429    struct ast_frame *f = NULL;
00430    int res = 0;
00431 
00432    state->sample_queue += samples;
00433 
00434    while (state->sample_queue > 0) {
00435       ast_channel_lock(chan);
00436       f = moh_files_readframe(chan);
00437 
00438       /* We need to be sure that we unlock
00439        * the channel prior to calling
00440        * ast_write. Otherwise, the recursive locking
00441        * that occurs can cause deadlocks when using
00442        * indirect channels, like local channels
00443        */
00444       ast_channel_unlock(chan);
00445       if (!f) {
00446          return -1;
00447       }
00448 
00449       state->samples += f->samples;
00450       state->sample_queue -= f->samples;
00451       if (ast_format_cmp(f->subclass.format, state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
00452          ao2_replace(state->mohwfmt, f->subclass.format);
00453       }
00454       res = ast_write(chan, f);
00455       ast_frfree(f);
00456       if (res < 0) {
00457          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
00458          return -1;
00459       }
00460    }
00461    return res;
00462 }

static struct ast_frame* moh_files_readframe ( struct ast_channel chan  )  [static, read]

Definition at line 395 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00396 {
00397    struct ast_frame *f = NULL;
00398 
00399    if (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
00400       if (!ast_moh_files_next(chan))
00401          f = ast_readframe(ast_channel_stream(chan));
00402    }
00403 
00404    return f;
00405 }

static void moh_files_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 277 of file res_musiconhold.c.

References moh_files_state::announcement, ao2_cleanup, ao2_ref, ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_format_get_name(), ast_log, ast_set_write_format(), moh_files_state::class, LOG_WARNING, moh_post_stop(), mohclass_unref, moh_files_state::mohwfmt, NULL, moh_files_state::origwfmt, moh_files_state::pos, and moh_files_state::save_pos.

00278 {
00279    struct moh_files_state *state;
00280 
00281    if (!chan || !ast_channel_music_state(chan)) {
00282       return;
00283    }
00284 
00285    state = ast_channel_music_state(chan);
00286 
00287    if (ast_channel_stream(chan)) {
00288       ast_closestream(ast_channel_stream(chan));
00289       ast_channel_stream_set(chan, NULL);
00290    }
00291 
00292    moh_post_stop(chan);
00293 
00294    ao2_ref(state->mohwfmt, -1);
00295    state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
00296    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00297       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
00298          ast_format_get_name(state->origwfmt));
00299    }
00300    ao2_cleanup(state->origwfmt);
00301    state->origwfmt = NULL;
00302 
00303    state->save_pos = state->pos;
00304    state->announcement = 0;
00305 
00306    state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00307 }

static void moh_files_write_format_change ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 407 of file res_musiconhold.c.

References ao2_bump, ao2_replace, ast_channel_music_state(), ast_channel_writeformat(), ast_set_write_format(), moh_files_state::mohwfmt, NULL, moh_files_state::origwfmt, and tmp().

00408 {
00409    struct moh_files_state *state = ast_channel_music_state(chan);
00410 
00411    /* In order to prevent a recursive call to this function as a result
00412     * of setting the moh write format back on the channel. Clear
00413     * the moh write format before setting the write format on the channel.*/
00414    if (state->origwfmt) {
00415       struct ast_format *tmp;
00416 
00417       tmp = ao2_bump(ast_channel_writeformat(chan));
00418       ao2_replace(state->origwfmt, NULL);
00419       if (state->mohwfmt) {
00420          ast_set_write_format(chan, state->mohwfmt);
00421       }
00422       state->origwfmt = tmp;
00423    }
00424 }

static int moh_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 1003 of file res_musiconhold.c.

References ast_channel_name(), ast_codec_samples_count(), ast_format_determine_length(), AST_FRIENDLY_OFFSET, ast_log, ast_write(), buf, ast_frame::data, ast_frame::datalen, errno, mohdata::f, mohclass::format, LOG_WARNING, mohdata::parent, mohdata::pipe, ast_frame::ptr, and ast_frame::samples.

01004 {
01005    struct mohdata *moh = data;
01006    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
01007    int res;
01008 
01009    len = ast_format_determine_length(moh->parent->format, samples);
01010 
01011    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
01012       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
01013       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
01014    }
01015    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
01016    if (res <= 0)
01017       return 0;
01018 
01019    moh->f.datalen = res;
01020    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
01021    moh->f.samples = ast_codec_samples_count(&moh->f);
01022 
01023    if (ast_write(chan, &moh->f) < 0) {
01024       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
01025       return -1;
01026    }
01027 
01028    return 0;
01029 }

static void moh_handle_digit ( struct ast_channel chan,
char  digit 
) [static]

Definition at line 521 of file res_musiconhold.c.

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

00522 {
00523    struct mohclass *class;
00524    const char *classname = NULL;
00525 
00526    if ((class = get_mohbydigit(digit))) {
00527       classname = ast_strdupa(class->name);
00528       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00529       ast_channel_musicclass_set(chan, classname);
00530       ast_moh_stop(chan);
00531       ast_moh_start(chan, classname, NULL);
00532    }
00533 }

static void moh_parse_options ( struct ast_variable var,
struct mohclass mohclass 
) [static]

Definition at line 1038 of file res_musiconhold.c.

References ao2_bump, mohclass::args, ast_copy_string(), ast_format_cache_get, ast_format_slin, ast_log, ast_set2_flag, ast_set_flag, ast_true(), mohclass::digit, mohclass::dir, mohclass::format, LOG_WARNING, mohclass::mode, MOH_RANDOMIZE, MOH_RANDSTART, MOH_SORTALPHA, mohclass::name, ast_variable::name, ast_variable::next, and ast_variable::value.

Referenced by load_moh_classes(), and local_ast_moh_start().

01039 {
01040    for (; var; var = var->next) {
01041       if (!strcasecmp(var->name, "name")) {
01042          ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
01043       } else if (!strcasecmp(var->name, "mode")) {
01044          ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
01045       } else if (!strcasecmp(var->name, "directory")) {
01046          ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
01047       } else if (!strcasecmp(var->name, "application")) {
01048          ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
01049       } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
01050          mohclass->digit = *var->value;
01051       } else if (!strcasecmp(var->name, "random")) {
01052          static int deprecation_warning = 0;
01053          if (!deprecation_warning) {
01054             ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14.  Please use 'sort=random' instead.\n");
01055             deprecation_warning = 1;
01056          }
01057          ast_set2_flag(mohclass, ast_true(var->value), MOH_RANDOMIZE);
01058       } else if (!strcasecmp(var->name, "sort")) {
01059          if (!strcasecmp(var->value, "random")) {
01060             ast_set_flag(mohclass, MOH_RANDOMIZE);
01061          } else if (!strcasecmp(var->value, "alpha")) {
01062             ast_set_flag(mohclass, MOH_SORTALPHA);
01063          } else if (!strcasecmp(var->value, "randstart")) {
01064             ast_set_flag(mohclass, MOH_RANDSTART);
01065          }
01066       } else if (!strcasecmp(var->name, "format")) {
01067          mohclass->format = ast_format_cache_get(var->value);
01068          if (!mohclass->format) {
01069             ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01070             mohclass->format = ao2_bump(ast_format_slin);
01071          }
01072                }
01073        }
01074 }

static void moh_post_start ( struct ast_channel chan,
const char *  moh_class_name 
) [static]

Definition at line 235 of file res_musiconhold.c.

References ao2_cleanup, ast_assert, ast_channel_blob_create_from_cache(), ast_channel_moh_start_type(), ast_channel_name(), ast_channel_topic(), ast_channel_uniqueid(), ast_json_pack(), ast_json_unref(), ast_verb, NULL, stasis_message_data(), and stasis_publish().

Referenced by moh_alloc(), and moh_files_alloc().

00236 {
00237    struct stasis_message *message;
00238    struct ast_json *json_object;
00239 
00240    ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
00241       moh_class_name, ast_channel_name(chan));
00242 
00243    json_object = ast_json_pack("{s: s}", "class", moh_class_name);
00244    if (!json_object) {
00245       return;
00246    }
00247 
00248    message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
00249       ast_channel_moh_start_type(), json_object);
00250    if (message) {
00251       /* A channel snapshot must have been in the cache. */
00252       ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
00253 
00254       stasis_publish(ast_channel_topic(chan), message);
00255    }
00256    ao2_cleanup(message);
00257    ast_json_unref(json_object);
00258 }

static void moh_post_stop ( struct ast_channel chan  )  [static]

Definition at line 260 of file res_musiconhold.c.

References ao2_cleanup, ast_assert, ast_channel_blob_create_from_cache(), ast_channel_moh_stop_type(), ast_channel_name(), ast_channel_topic(), ast_channel_uniqueid(), ast_verb, NULL, stasis_message_data(), and stasis_publish().

Referenced by moh_files_release(), and moh_release().

00261 {
00262    struct stasis_message *message;
00263 
00264    ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
00265 
00266    message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
00267       ast_channel_moh_stop_type(), NULL);
00268    if (message) {
00269       /* A channel snapshot must have been in the cache. */
00270       ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
00271 
00272       stasis_publish(ast_channel_topic(chan), message);
00273    }
00274    ao2_cleanup(message);
00275 }

static void moh_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 927 of file res_musiconhold.c.

References ao2_cleanup, ao2_lock, ao2_unlock, ast_channel_music_state(), ast_channel_name(), ast_format_get_name(), ast_free, AST_LIST_REMOVE, ast_log, ast_set_write_format(), moh_files_state::class, LOG_WARNING, mohclass::members, moh_post_stop(), mohclass_unref, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

00928 {
00929    struct mohdata *moh = data;
00930    struct mohclass *class = moh->parent;
00931    struct ast_format *oldwfmt;
00932 
00933    ao2_lock(class);
00934    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00935    ao2_unlock(class);
00936    
00937    close(moh->pipe[0]);
00938    close(moh->pipe[1]);
00939 
00940    oldwfmt = moh->origwfmt;
00941 
00942    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00943 
00944    ast_free(moh);
00945 
00946    if (chan) {
00947       struct moh_files_state *state;
00948 
00949       state = ast_channel_music_state(chan);
00950       if (state && state->class) {
00951          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00952       }
00953       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00954          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00955                ast_channel_name(chan), ast_format_get_name(oldwfmt));
00956       }
00957 
00958       moh_post_stop(chan);
00959    }
00960 
00961    ao2_cleanup(oldwfmt);
00962 }

static void moh_rescan_files ( void   )  [static]

Definition at line 1219 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, c, mohclass::mode, and moh_scan_files().

Referenced by load_moh_classes().

01219                                    {
01220    struct ao2_iterator i;
01221    struct mohclass *c;
01222 
01223    i = ao2_iterator_init(mohclasses, 0);
01224 
01225    while ((c = ao2_iterator_next(&i))) {
01226       if (!strcasecmp(c->mode, "files")) {
01227          moh_scan_files(c);
01228       }
01229       ao2_ref(c, -1);
01230    }
01231 
01232    ao2_iterator_destroy(&i);
01233 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 1115 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().

01115                                                   {
01116 
01117    DIR *files_DIR;
01118    struct dirent *files_dirent;
01119    char dir_path[PATH_MAX];
01120    char path[PATH_MAX];
01121    char filepath[PATH_MAX];
01122    char *ext;
01123    struct stat statbuf;
01124    int i;
01125 
01126    if (class->dir[0] != '/') {
01127       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01128       strncat(dir_path, "/", sizeof(dir_path) - 1);
01129       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01130    } else {
01131       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01132    }
01133    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01134    files_DIR = opendir(dir_path);
01135    if (!files_DIR) {
01136       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01137       return -1;
01138    }
01139 
01140    for (i = 0; i < class->total_files; i++) {
01141       ast_free(class->filearray[i]);
01142    }
01143    class->total_files = 0;
01144 
01145    if (!getcwd(path, sizeof(path))) {
01146       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01147       closedir(files_DIR);
01148       return -1;
01149    }
01150    if (chdir(dir_path) < 0) {
01151       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01152       closedir(files_DIR);
01153       return -1;
01154    }
01155    while ((files_dirent = readdir(files_DIR))) {
01156       /* The file name must be at least long enough to have the file type extension */
01157       if ((strlen(files_dirent->d_name) < 4))
01158          continue;
01159 
01160       /* Skip files that starts with a dot */
01161       if (files_dirent->d_name[0] == '.')
01162          continue;
01163 
01164       /* Skip files without extensions... they are not audio */
01165       if (!strchr(files_dirent->d_name, '.'))
01166          continue;
01167 
01168       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01169 
01170       if (stat(filepath, &statbuf))
01171          continue;
01172 
01173       if (!S_ISREG(statbuf.st_mode))
01174          continue;
01175 
01176       if ((ext = strrchr(filepath, '.')))
01177          *ext = '\0';
01178 
01179       /* if the file is present in multiple formats, ensure we only put it into the list once */
01180       for (i = 0; i < class->total_files; i++)
01181          if (!strcmp(filepath, class->filearray[i]))
01182             break;
01183 
01184       if (i == class->total_files) {
01185          if (moh_add_file(class, filepath))
01186             break;
01187       }
01188    }
01189 
01190    closedir(files_DIR);
01191    if (chdir(path) < 0) {
01192       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01193       return -1;
01194    }
01195    if (ast_test_flag(class, MOH_SORTALPHA))
01196       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01197    return class->total_files;
01198 }

static int moh_sort_compare ( const void *  i1,
const void *  i2 
) [static]

Definition at line 1105 of file res_musiconhold.c.

Referenced by moh_scan_files().

01106 {
01107    char *s1, *s2;
01108 
01109    s1 = ((char **)i1)[0];
01110    s2 = ((char **)i2)[0];
01111 
01112    return strcasecmp(s1, s2);
01113 }

static struct mohdata* mohalloc ( struct mohclass cl  )  [static, read]

Definition at line 894 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_subclass::format, ast_frame::frametype, LOG_WARNING, mohclass::members, mohclass_ref, NULL, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

00895 {
00896    struct mohdata *moh;
00897    long flags;
00898 
00899    if (!(moh = ast_calloc(1, sizeof(*moh))))
00900       return NULL;
00901 
00902    if (pipe(moh->pipe)) {
00903       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00904       ast_free(moh);
00905       return NULL;
00906    }
00907 
00908    /* Make entirely non-blocking */
00909    flags = fcntl(moh->pipe[0], F_GETFL);
00910    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00911    flags = fcntl(moh->pipe[1], F_GETFL);
00912    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00913 
00914    moh->f.frametype = AST_FRAME_VOICE;
00915    moh->f.subclass.format = cl->format;
00916    moh->f.offset = AST_FRIENDLY_OFFSET;
00917 
00918    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00919 
00920    ao2_lock(cl);
00921    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00922    ao2_unlock(cl);
00923    
00924    return moh;
00925 }

static void* monmp3thread ( void *  data  )  [static]

Definition at line 684 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_debug, ast_format_determine_length(), ast_format_get_sample_rate(), 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_MS_INTERVAL, NULL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

00685 {
00686 #define  MOH_MS_INTERVAL      100
00687 
00688    struct mohclass *class = data;
00689    struct mohdata *moh;
00690    short sbuf[8192];
00691    int res = 0, res2;
00692    int len;
00693    struct timeval deadline, tv_tmp;
00694 
00695    deadline.tv_sec = 0;
00696    deadline.tv_usec = 0;
00697    for(;/* ever */;) {
00698       pthread_testcancel();
00699       /* Spawn mp3 player if it's not there */
00700       if (class->srcfd < 0) {
00701          if ((class->srcfd = spawn_mp3(class)) < 0) {
00702             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00703             /* Try again later */
00704             sleep(500);
00705             continue;
00706          }
00707       }
00708       if (class->timer) {
00709          struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
00710 
00711 #ifdef SOLARIS
00712          thr_yield();
00713 #endif
00714          /* Pause some amount of time */
00715          if (ast_poll(&pfd, 1, -1) > 0) {
00716             if (ast_timer_ack(class->timer, 1) < 0) {
00717                ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
00718                return NULL;
00719             }
00720             /* 25 samples per second => 40ms framerate => 320 samples */
00721             res = 320; /* 320/40 = 8 samples/ms */
00722          } else {
00723             ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00724             res = 0;
00725          }
00726          pthread_testcancel();
00727       } else {
00728          long delta;
00729          /* Reliable sleep */
00730          tv_tmp = ast_tvnow();
00731          if (ast_tvzero(deadline))
00732             deadline = tv_tmp;
00733          delta = ast_tvdiff_ms(tv_tmp, deadline);
00734          if (delta < MOH_MS_INTERVAL) {   /* too early */
00735             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00736             usleep(1000 * (MOH_MS_INTERVAL - delta));
00737             pthread_testcancel();
00738          } else {
00739             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00740             deadline = tv_tmp;
00741          }
00742          /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
00743          res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
00744       }
00745       /* For non-8000Hz formats, we need to alter the resolution */
00746       res = res * ast_format_get_sample_rate(class->format) / 8000;
00747 
00748       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00749          continue;
00750       /* Read mp3 audio */
00751       len = ast_format_determine_length(class->format, res);
00752 
00753       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00754          if (!res2) {
00755             close(class->srcfd);
00756             class->srcfd = -1;
00757             pthread_testcancel();
00758             if (class->pid > 1) {
00759                do {
00760                   if (killpg(class->pid, SIGHUP) < 0) {
00761                      if (errno == ESRCH) {
00762                         break;
00763                      }
00764                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00765                   }
00766                   usleep(100000);
00767                   if (killpg(class->pid, SIGTERM) < 0) {
00768                      if (errno == ESRCH) {
00769                         break;
00770                      }
00771                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00772                   }
00773                   usleep(100000);
00774                   if (killpg(class->pid, SIGKILL) < 0) {
00775                      if (errno == ESRCH) {
00776                         break;
00777                      }
00778                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00779                   }
00780                } while (0);
00781                class->pid = 0;
00782             }
00783          } else {
00784             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00785          }
00786          continue;
00787       }
00788 
00789       pthread_testcancel();
00790 
00791       ao2_lock(class);
00792       AST_LIST_TRAVERSE(&class->members, moh, list) {
00793          /* Write data */
00794          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00795             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00796          }
00797       }
00798       ao2_unlock(class);
00799    }
00800    return NULL;
00801 }

static int play_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 803 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_strdupa, ast_strlen_zero, LOG_WARNING, parse(), S_OR, and timeout.

Referenced by load_module().

00804 {
00805    char *parse;
00806    char *class;
00807    int timeout = -1;
00808    int res;
00809    AST_DECLARE_APP_ARGS(args,
00810       AST_APP_ARG(class);
00811       AST_APP_ARG(duration);
00812    );
00813 
00814    parse = ast_strdupa(data);
00815 
00816    AST_STANDARD_APP_ARGS(args, parse);
00817 
00818    if (!ast_strlen_zero(args.duration)) {
00819       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00820          timeout *= 1000;
00821       } else {
00822          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00823       }
00824    }
00825 
00826    class = S_OR(args.class, NULL);
00827    if (ast_moh_start(chan, class, NULL)) {
00828       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00829       return 0;
00830    }
00831 
00832    if (timeout > 0)
00833       res = ast_safe_sleep(chan, timeout);
00834    else {
00835       while (!(res = ast_safe_sleep(chan, 10000)));
00836    }
00837 
00838    ast_moh_stop(chan);
00839 
00840    return res;
00841 }

static int reload ( void   )  [static]

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 543 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, NULL, and strsep().

Referenced by monmp3thread().

00544 {
00545    int fds[2];
00546    int files = 0;
00547    char fns[MAX_MP3S][80];
00548    char *argv[MAX_MP3S + 50];
00549    char xargs[256];
00550    char *argptr;
00551    int argc = 0;
00552    DIR *dir = NULL;
00553    struct dirent *de;
00554 
00555    
00556    if (!strcasecmp(class->dir, "nodir")) {
00557       files = 1;
00558    } else {
00559       dir = opendir(class->dir);
00560       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00561          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00562          return -1;
00563       }
00564    }
00565 
00566    if (!ast_test_flag(class, MOH_CUSTOM)) {
00567       argv[argc++] = "mpg123";
00568       argv[argc++] = "-q";
00569       argv[argc++] = "-s";
00570       argv[argc++] = "--mono";
00571       argv[argc++] = "-r";
00572       argv[argc++] = "8000";
00573       
00574       if (!ast_test_flag(class, MOH_SINGLE)) {
00575          argv[argc++] = "-b";
00576          argv[argc++] = "2048";
00577       }
00578       
00579       argv[argc++] = "-f";
00580       
00581       if (ast_test_flag(class, MOH_QUIET))
00582          argv[argc++] = "4096";
00583       else
00584          argv[argc++] = "8192";
00585       
00586       /* Look for extra arguments and add them to the list */
00587       ast_copy_string(xargs, class->args, sizeof(xargs));
00588       argptr = xargs;
00589       while (!ast_strlen_zero(argptr)) {
00590          argv[argc++] = argptr;
00591          strsep(&argptr, ",");
00592       }
00593    } else  {
00594       /* Format arguments for argv vector */
00595       ast_copy_string(xargs, class->args, sizeof(xargs));
00596       argptr = xargs;
00597       while (!ast_strlen_zero(argptr)) {
00598          argv[argc++] = argptr;
00599          strsep(&argptr, " ");
00600       }
00601    }
00602 
00603    if (!strncasecmp(class->dir, "http://", 7)) {
00604       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00605       argv[argc++] = fns[files];
00606       files++;
00607    } else if (dir) {
00608       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00609          if ((strlen(de->d_name) > 3) && 
00610              ((ast_test_flag(class, MOH_CUSTOM) && 
00611                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00612                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00613               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00614             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00615             argv[argc++] = fns[files];
00616             files++;
00617          }
00618       }
00619    }
00620    argv[argc] = NULL;
00621    if (dir) {
00622       closedir(dir);
00623    }
00624    if (pipe(fds)) {  
00625       ast_log(LOG_WARNING, "Pipe failed\n");
00626       return -1;
00627    }
00628    if (!files) {
00629       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00630       close(fds[0]);
00631       close(fds[1]);
00632       return -1;
00633    }
00634    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00635       sleep(respawn_time - (time(NULL) - class->start));
00636    }
00637 
00638    time(&class->start);
00639    class->pid = ast_safe_fork(0);
00640    if (class->pid < 0) {
00641       close(fds[0]);
00642       close(fds[1]);
00643       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00644       return -1;
00645    }
00646    if (!class->pid) {
00647       if (ast_opt_high_priority)
00648          ast_set_priority(0);
00649 
00650       close(fds[0]);
00651       /* Stdout goes to pipe */
00652       dup2(fds[1], STDOUT_FILENO);
00653 
00654       /* Close everything else */
00655       ast_close_fds_above_n(STDERR_FILENO);
00656 
00657       /* Child */
00658       if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00659          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00660          _exit(1);
00661       }
00662       setpgid(0, getpid());
00663       if (ast_test_flag(class, MOH_CUSTOM)) {
00664          execv(argv[0], argv);
00665       } else {
00666          /* Default install is /usr/local/bin */
00667          execv(LOCAL_MPG_123, argv);
00668          /* Many places have it in /usr/bin */
00669          execv(MPG_123, argv);
00670          /* Check PATH as a last-ditch effort */
00671          execvp("mpg123", argv);
00672       }
00673       /* Can't use logger, since log FDs are closed */
00674       fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00675       close(fds[1]);
00676       _exit(1);
00677    } else {
00678       /* Parent */
00679       close(fds[1]);
00680    }
00681    return fds[0];
00682 }

static int start_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 843 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, ast_strdupa, LOG_WARNING, parse(), and S_OR.

Referenced by load_module().

00844 {
00845    char *parse;
00846    char *class;
00847    AST_DECLARE_APP_ARGS(args,
00848       AST_APP_ARG(class);
00849    );
00850 
00851    parse = ast_strdupa(data);
00852 
00853    AST_STANDARD_APP_ARGS(args, parse);
00854 
00855    class = S_OR(args.class, NULL);
00856    if (ast_moh_start(chan, class, NULL)) 
00857       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00858 
00859    return 0;
00860 }

static int stop_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 862 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00863 {
00864    ast_moh_stop(chan);
00865 
00866    return 0;
00867 }

static int unload_module ( void   )  [static]

Definition at line 1978 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(), mohclass_unref, and NULL.

01979 {
01980    int res = 0;
01981    struct mohclass *class = NULL;
01982 
01983    /* XXX This check shouldn't be required if module ref counting was being used
01984     * properly ... */
01985    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01986       class = mohclass_unref(class, "unref of class from module unload callback");
01987       res = -1;
01988    }
01989 
01990    if (res < 0) {
01991       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01992       return res;
01993    }
01994 
01995    ast_uninstall_music_functions();
01996 
01997    ast_moh_destroy();
01998    res = ast_unregister_application(play_moh);
01999    res |= ast_unregister_application(start_moh);
02000    res |= ast_unregister_application(stop_moh);
02001    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
02002    ast_unregister_atexit(ast_moh_destroy);
02003 
02004    return res;
02005 }


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, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static]

Definition at line 2013 of file res_musiconhold.c.

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

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

global MOH_ flags

Definition at line 161 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 535 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 202 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 1031 of file res_musiconhold.c.

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

Definition at line 125 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 129 of file res_musiconhold.c.

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

Definition at line 126 of file res_musiconhold.c.

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

Definition at line 127 of file res_musiconhold.c.


Generated on Thu Apr 16 06:32:54 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6