Thu Apr 3 08:22:31 2014

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

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

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

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

Functions

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

Variables

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

Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define DONT_UNREF   0

Definition at line 72 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

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

Definition at line 845 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 71 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 70 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 221 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 223 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CACHERTCLASSES   (1 << 5)

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

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

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 168 of file res_musiconhold.c.

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

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

Definition at line 1233 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

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

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

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

Definition at line 227 of file res_musiconhold.c.

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

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

Definition at line 222 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1998 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

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

References __ao2_find(), __ao2_find_debug(), ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, and mohclass::name.

Referenced by _moh_register().

00848 {
00849    struct mohclass *moh = NULL;
00850    struct mohclass tmp_class = {
00851       .flags = 0,
00852    };
00853 
00854    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00855 
00856 #ifdef REF_DEBUG
00857    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00858 #else
00859    moh = __ao2_find(mohclasses, &tmp_class, flags);
00860 #endif
00861 
00862    if (!moh && warn) {
00863       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00864    }
00865 
00866    return moh;
00867 }

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

Definition at line 1310 of file res_musiconhold.c.

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

01311 {
01312    struct mohclass *class;
01313 
01314    if ((class =
01315 #ifdef REF_DEBUG
01316          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01317 #elif defined(__AST_DEBUG_MALLOC)
01318          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01319 #else
01320          ao2_alloc(sizeof(*class), moh_class_destructor)
01321 #endif
01322       )) {
01323       class->format = AST_FORMAT_SLINEAR;
01324       class->srcfd = -1;
01325    }
01326 
01327    return class;
01328 }

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

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

01235 {
01236    struct mohclass *mohclass = NULL;
01237 
01238    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01239 
01240    if (mohclass && !moh_diff(mohclass, moh)) {
01241       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01242       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01243       if (unref) {
01244          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01245       }
01246       return -1;
01247    } else if (mohclass) {
01248       /* Found a class, but it's different from the one being registered */
01249       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01250    }
01251 
01252    time(&moh->start);
01253    moh->start -= respawn_time;
01254 
01255    if (!strcasecmp(moh->mode, "files")) {
01256       if (init_files_class(moh)) {
01257          if (unref) {
01258             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01259          }
01260          return -1;
01261       }
01262    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01263          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01264          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01265       if (init_app_class(moh)) {
01266          if (unref) {
01267             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01268          }
01269          return -1;
01270       }
01271    } else {
01272       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01273       if (unref) {
01274          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01275       }
01276       return -1;
01277    }
01278 
01279    ao2_t_link(mohclasses, moh, "Adding class to container");
01280 
01281    if (unref) {
01282       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01283    }
01284 
01285    return 0;
01286 }

static void ast_moh_destroy ( void   )  [static]

Definition at line 1786 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01787 {
01788    ast_verb(2, "Destroying musiconhold processes\n");
01789    if (mohclasses) {
01790       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01791       ao2_ref(mohclasses, -1);
01792       mohclasses = NULL;
01793    }
01794 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 284 of file res_musiconhold.c.

References 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_RANDOMIZE, ast_channel::music_state, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

00285 {
00286    struct moh_files_state *state = chan->music_state;
00287    int tries;
00288 
00289    /* Discontinue a stream if it is running already */
00290    if (chan->stream) {
00291       ast_closestream(chan->stream);
00292       chan->stream = NULL;
00293    }
00294 
00295    if (!state->class->total_files) {
00296       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00297       return -1;
00298    }
00299 
00300    if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
00301       /* First time so lets play the file. */
00302       state->save_pos = -1;
00303    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
00304       /* If a specific file has been saved confirm it still exists and that it is still valid */
00305       state->pos = state->save_pos;
00306       state->save_pos = -1;
00307    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00308       /* Get a random file and ensure we can open it */
00309       for (tries = 0; tries < 20; tries++) {
00310          state->pos = ast_random() % state->class->total_files;
00311          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00312             break;
00313          }
00314       }
00315       state->save_pos = -1;
00316       state->samples = 0;
00317    } else {
00318       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00319       state->pos++;
00320       state->pos %= state->class->total_files;
00321       state->save_pos = -1;
00322       state->samples = 0;
00323    }
00324 
00325    for (tries = 0; tries < state->class->total_files; ++tries) {
00326       if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00327          break;
00328       }
00329 
00330       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00331       state->pos++;
00332       state->pos %= state->class->total_files;
00333    }
00334 
00335    if (tries == state->class->total_files) {
00336       return -1;
00337    }
00338 
00339    /* Record the pointer to the filename for position resuming later */
00340    ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
00341 
00342    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00343 
00344    if (state->samples) {
00345       size_t loc;
00346       /* seek *SHOULD* be good since it's from a known location */
00347       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00348       /* if the seek failed then recover because if there is not a valid read,
00349        * moh_files_generate will return -1 and MOH will stop */
00350       loc = ast_tellstream(chan->stream);
00351       if (state->samples > loc && loc) {
00352          /* seek one sample from the end for one guaranteed valid read */
00353          ast_seekstream(chan->stream, 1, SEEK_END);
00354       }
00355    }
00356 
00357    return 0;
00358 }

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

Definition at line 457 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00458 {
00459    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00460 }

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

Definition at line 1796 of file res_musiconhold.c.

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

01797 {
01798    switch (cmd) {
01799    case CLI_INIT:
01800       e->command = "moh reload";
01801       e->usage =
01802          "Usage: moh reload\n"
01803          "       Reloads the MusicOnHold module.\n"
01804          "       Alias for 'module reload res_musiconhold.so'\n";
01805       return NULL;
01806    case CLI_GENERATE:
01807       return NULL;
01808    }
01809 
01810    if (a->argc != e->args)
01811       return CLI_SHOWUSAGE;
01812 
01813    reload();
01814 
01815    return CLI_SUCCESS;
01816 }

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

Definition at line 1856 of file res_musiconhold.c.

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

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

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

Definition at line 1818 of file res_musiconhold.c.

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

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

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1194 of file res_musiconhold.c.

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

Referenced by _moh_register().

01195 {
01196    if (!strcasecmp(class->mode, "custom")) {
01197       ast_set_flag(class, MOH_CUSTOM);
01198    } else if (!strcasecmp(class->mode, "mp3nb")) {
01199       ast_set_flag(class, MOH_SINGLE);
01200    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01201       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01202    } else if (!strcasecmp(class->mode, "quietmp3")) {
01203       ast_set_flag(class, MOH_QUIET);
01204    }
01205 
01206    class->srcfd = -1;
01207 
01208    if (!(class->timer = ast_timer_open())) {
01209       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01210       return -1;
01211    }
01212    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01213       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01214       ast_timer_close(class->timer);
01215       class->timer = NULL;
01216    }
01217 
01218    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01219       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01220       if (class->timer) {
01221          ast_timer_close(class->timer);
01222          class->timer = NULL;
01223       }
01224       return -1;
01225    }
01226 
01227    return 0;
01228 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1131 of file res_musiconhold.c.

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

Referenced by _moh_register().

01132 {
01133    int res;
01134 
01135    res = moh_scan_files(class);
01136 
01137    if (res < 0) {
01138       return -1;
01139    }
01140 
01141    if (!res) {
01142       if (option_verbose > 2) {
01143          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01144                class->dir, class->name);
01145       }
01146       return -1;
01147    }
01148 
01149 #if 0
01150    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01151    if (strchr(class->args, 'r')) {
01152       ast_set_flag(class, MOH_RANDOMIZE);
01153    }
01154 #endif
01155 
01156    return 0;
01157 }

static int load_module ( void   )  [static]

Definition at line 1914 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(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

01915 {
01916    int res;
01917 
01918    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01919       return AST_MODULE_LOAD_DECLINE;
01920    }
01921 
01922    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01923       ast_log(LOG_WARNING, "No music on hold classes configured, "
01924             "disabling music on hold.\n");
01925    } else {
01926       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01927             local_ast_moh_cleanup);
01928    }
01929 
01930    res = ast_register_application_xml(play_moh, play_moh_exec);
01931    ast_register_atexit(ast_moh_destroy);
01932    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01933    if (!res)
01934       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01935    if (!res)
01936       res = ast_register_application_xml(set_moh, set_moh_exec);
01937    if (!res)
01938       res = ast_register_application_xml(start_moh, start_moh_exec);
01939    if (!res)
01940       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01941 
01942    return AST_MODULE_LOAD_SUCCESS;
01943 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1676 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01677 {
01678    struct ast_config *cfg;
01679    struct ast_variable *var;
01680    struct mohclass *class; 
01681    char *cat;
01682    int numclasses = 0;
01683    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01684 
01685    cfg = ast_config_load("musiconhold.conf", config_flags);
01686 
01687    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01688       if (ast_check_realtime("musiconhold") && reload) {
01689          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01690          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01691       }
01692       return 0;
01693    }
01694    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01695       moh_rescan_files();
01696       return 0;
01697    }
01698 
01699    if (reload) {
01700       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01701    }
01702 
01703    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01704 
01705    cat = ast_category_browse(cfg, NULL);
01706    for (; cat; cat = ast_category_browse(cfg, cat)) {
01707       /* Setup common options from [general] section */
01708       if (!strcasecmp(cat, "general")) {
01709          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01710             if (!strcasecmp(var->name, "cachertclasses")) {
01711                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01712             } else {
01713                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01714             }
01715          }
01716       }
01717       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01718       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01719             !strcasecmp(cat, "general")) {
01720          continue;
01721       }
01722 
01723       if (!(class = moh_class_malloc())) {
01724          break;
01725       }
01726 
01727       ast_copy_string(class->name, cat, sizeof(class->name));  
01728       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01729          if (!strcasecmp(var->name, "mode"))
01730             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01731          else if (!strcasecmp(var->name, "directory"))
01732             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01733          else if (!strcasecmp(var->name, "application"))
01734             ast_copy_string(class->args, var->value, sizeof(class->args));
01735          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01736             class->digit = *var->value;
01737          else if (!strcasecmp(var->name, "random"))
01738             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01739          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01740             ast_set_flag(class, MOH_RANDOMIZE);
01741          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01742             ast_set_flag(class, MOH_SORTALPHA);
01743          else if (!strcasecmp(var->name, "format")) {
01744             class->format = ast_getformatbyname(var->value);
01745             if (!class->format) {
01746                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01747                class->format = AST_FORMAT_SLINEAR;
01748             }
01749          }
01750       }
01751 
01752       if (ast_strlen_zero(class->dir)) {
01753          if (!strcasecmp(class->mode, "custom")) {
01754             strcpy(class->dir, "nodir");
01755          } else {
01756             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01757             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01758             continue;
01759          }
01760       }
01761       if (ast_strlen_zero(class->mode)) {
01762          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01763          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01764          continue;
01765       }
01766       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01767          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01768          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01769          continue;
01770       }
01771 
01772       /* Don't leak a class when it's already registered */
01773       if (!moh_register(class, reload, HANDLE_REF)) {
01774          numclasses++;
01775       }
01776    }
01777 
01778    ast_config_destroy(cfg);
01779 
01780    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01781          moh_classes_delete_marked, NULL, "Purge marked classes");
01782 
01783    return numclasses;
01784 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1288 of file res_musiconhold.c.

References ast_free, ast_log(), ast_module_unref(), moh_files_state::class, LOG_WARNING, mohclass_unref, and ast_channel::music_state.

Referenced by load_module(), and reload().

01289 {
01290    struct moh_files_state *state = chan->music_state;
01291 
01292    if (state) {
01293       if (state->class) {
01294          /* This should never happen.  We likely just leaked some resource. */
01295          state->class =
01296             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01297          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01298       }
01299       ast_free(chan->music_state);
01300       chan->music_state = NULL;
01301       /* Only held a module reference if we had a music state */
01302       ast_module_unref(ast_module_info->self);
01303    }
01304 }

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

Definition at line 1330 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01331 {
01332    struct mohclass *mohclass = NULL;
01333    struct moh_files_state *state = chan->music_state;
01334    struct ast_variable *var = NULL;
01335    int res;
01336    int realtime_possible = ast_check_realtime("musiconhold");
01337 
01338    /* The following is the order of preference for which class to use:
01339     * 1) The channels explicitly set musicclass, which should *only* be
01340     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01341     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01342     *    result of receiving a HOLD control frame, this should be the
01343     *    payload that came with the frame.
01344     * 3) The interpclass argument. This would be from the mohinterpret
01345     *    option from channel drivers. This is the same as the old musicclass
01346     *    option.
01347     * 4) The default class.
01348     */
01349    if (!ast_strlen_zero(chan->musicclass)) {
01350       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01351       if (!mohclass && realtime_possible) {
01352          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01353       }
01354    }
01355    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01356       mohclass = get_mohbyname(mclass, 1, 0);
01357       if (!mohclass && realtime_possible) {
01358          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01359       }
01360    }
01361    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01362       mohclass = get_mohbyname(interpclass, 1, 0);
01363       if (!mohclass && realtime_possible) {
01364          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01365       }
01366    }
01367 
01368    if (!mohclass && !var) {
01369       mohclass = get_mohbyname("default", 1, 0);
01370       if (!mohclass && realtime_possible) {
01371          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01372       }
01373    }
01374 
01375    /* If no moh class found in memory, then check RT. Note that the logic used
01376     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01377     */
01378    if (var) {
01379       struct ast_variable *tmp = NULL;
01380 
01381       if ((mohclass = moh_class_malloc())) {
01382          mohclass->realtime = 1;
01383          for (tmp = var; tmp; tmp = tmp->next) {
01384             if (!strcasecmp(tmp->name, "name"))
01385                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01386             else if (!strcasecmp(tmp->name, "mode"))
01387                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01388             else if (!strcasecmp(tmp->name, "directory"))
01389                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01390             else if (!strcasecmp(tmp->name, "application"))
01391                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01392             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01393                mohclass->digit = *tmp->value;
01394             else if (!strcasecmp(tmp->name, "random"))
01395                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01396             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01397                ast_set_flag(mohclass, MOH_RANDOMIZE);
01398             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01399                ast_set_flag(mohclass, MOH_SORTALPHA);
01400             else if (!strcasecmp(tmp->name, "format")) {
01401                mohclass->format = ast_getformatbyname(tmp->value);
01402                if (!mohclass->format) {
01403                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01404                   mohclass->format = AST_FORMAT_SLINEAR;
01405                }
01406             }
01407          }
01408          ast_variables_destroy(var);
01409          if (ast_strlen_zero(mohclass->dir)) {
01410             if (!strcasecmp(mohclass->mode, "custom")) {
01411                strcpy(mohclass->dir, "nodir");
01412             } else {
01413                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01414                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01415                return -1;
01416             }
01417          }
01418          if (ast_strlen_zero(mohclass->mode)) {
01419             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01420             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01421             return -1;
01422          }
01423          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01424             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01425             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01426             return -1;
01427          }
01428 
01429          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01430             /* CACHERTCLASSES enabled, let's add this class to default tree */
01431             if (state && state->class) {
01432                /* Class already exist for this channel */
01433                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01434                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01435                   /* we found RT class with the same name, seems like we should continue playing existing one */
01436                   /* XXX This code is impossible to reach */
01437                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01438                   mohclass = state->class;
01439                }
01440             }
01441             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01442              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01443              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01444              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01445              * invalid memory.
01446              */
01447             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01448                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01449                return -1;
01450             }
01451          } else {
01452             /* We don't register RT moh class, so let's init it manualy */
01453 
01454             time(&mohclass->start);
01455             mohclass->start -= respawn_time;
01456 
01457             if (!strcasecmp(mohclass->mode, "files")) {
01458                if (!moh_scan_files(mohclass)) {
01459                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01460                   return -1;
01461                }
01462                if (strchr(mohclass->args, 'r'))
01463                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01464             } 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")) {
01465 
01466                if (!strcasecmp(mohclass->mode, "custom"))
01467                   ast_set_flag(mohclass, MOH_CUSTOM);
01468                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01469                   ast_set_flag(mohclass, MOH_SINGLE);
01470                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01471                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01472                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01473                   ast_set_flag(mohclass, MOH_QUIET);
01474 
01475                mohclass->srcfd = -1;
01476                if (!(mohclass->timer = ast_timer_open())) {
01477                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01478                }
01479                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01480                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01481                   ast_timer_close(mohclass->timer);
01482                   mohclass->timer = NULL;
01483                }
01484 
01485                /* Let's check if this channel already had a moh class before */
01486                if (state && state->class) {
01487                   /* Class already exist for this channel */
01488                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01489                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01490                      /* we found RT class with the same name, seems like we should continue playing existing one */
01491                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01492                      mohclass = state->class;
01493                   }
01494                } else {
01495                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01496                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01497                      if (mohclass->timer) {
01498                         ast_timer_close(mohclass->timer);
01499                         mohclass->timer = NULL;
01500                      }
01501                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01502                      return -1;
01503                   }
01504                }
01505             } else {
01506                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01507                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01508                return -1;
01509             }
01510          }
01511       } else {
01512          ast_variables_destroy(var);
01513          var = NULL;
01514       }
01515    }
01516 
01517    if (!mohclass) {
01518       return -1;
01519    }
01520 
01521    /* If we are using a cached realtime class with files, re-scan the files */
01522    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01523       if (!moh_scan_files(mohclass)) {
01524          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01525          return -1;
01526       }
01527    }
01528 
01529    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01530       "State: Start\r\n"
01531       "Channel: %s\r\n"
01532       "UniqueID: %s\r\n"
01533       "Class: %s\r\n",
01534       chan->name, chan->uniqueid,
01535       mohclass->name);
01536 
01537    ast_set_flag(chan, AST_FLAG_MOH);
01538 
01539    if (mohclass->total_files) {
01540       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01541    } else {
01542       res = ast_activate_generator(chan, &mohgen, mohclass);
01543    }
01544 
01545    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01546 
01547    return res;
01548 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1550 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_manager_event, EVENT_FLAG_CALL, ast_channel::music_state, and ast_channel::stream.

Referenced by load_module(), and reload().

01551 {
01552    ast_clear_flag(chan, AST_FLAG_MOH);
01553    ast_deactivate_generator(chan);
01554 
01555    ast_channel_lock(chan);
01556    if (chan->music_state) {
01557       if (chan->stream) {
01558          ast_closestream(chan->stream);
01559          chan->stream = NULL;
01560       }
01561    }
01562 
01563    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01564       "State: Stop\r\n"
01565       "Channel: %s\r\n"
01566       "UniqueID: %s\r\n",
01567       chan->name, chan->uniqueid);
01568    ast_channel_unlock(chan);
01569 }

static int moh_add_file ( struct mohclass class,
const char *  filepath 
) [static]

Definition at line 1008 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

01009 {
01010    if (!class->allowed_files) {
01011       class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray));
01012       if (!class->filearray) {
01013          return -1;
01014       }
01015       class->allowed_files = INITIAL_NUM_FILES;
01016    } else if (class->total_files == class->allowed_files) {
01017       char **new_array;
01018 
01019       new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2);
01020       if (!new_array) {
01021          return -1;
01022       }
01023       class->filearray = new_array;
01024       class->allowed_files *= 2;
01025    }
01026 
01027    class->filearray[class->total_files] = ast_strdup(filepath);
01028    if (!class->filearray[class->total_files]) {
01029       return -1;
01030    }
01031 
01032    class->total_files++;
01033 
01034    return 0;
01035 }

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

Definition at line 937 of file res_musiconhold.c.

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

00938 {
00939    struct mohdata *res;
00940    struct mohclass *class = params;
00941    struct moh_files_state *state;
00942 
00943    /* Initiating music_state for current channel. Channel should know name of moh class */
00944    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00945       chan->music_state = state;
00946       ast_module_ref(ast_module_info->self);
00947    } else {
00948       state = chan->music_state;
00949       if (!state) {
00950          return NULL;
00951       }
00952       if (state->class) {
00953          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00954          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00955       }
00956       memset(state, 0, sizeof(*state));
00957    }
00958 
00959    if ((res = mohalloc(class))) {
00960       res->origwfmt = chan->writeformat;
00961       if (ast_set_write_format(chan, class->format)) {
00962          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00963          moh_release(NULL, res);
00964          res = NULL;
00965       } else {
00966          state->class = mohclass_ref(class, "Placing reference into state container");
00967       }
00968       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00969    }
00970    return res;
00971 }

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

Definition at line 1905 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01906 {
01907    struct mohclass *class = obj, *class2 = arg;
01908 
01909    return strcasecmp(class->name, class2->name) ? 0 :
01910       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01911       CMP_MATCH | CMP_STOP;
01912 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1571 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

01572 {
01573    struct mohclass *class = obj;
01574    struct mohdata *member;
01575    pthread_t tid = 0;
01576 
01577    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01578 
01579    ao2_lock(class);
01580    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01581       free(member);
01582    }
01583    ao2_unlock(class);
01584 
01585    /* Kill the thread first, so it cannot restart the child process while the
01586     * class is being destroyed */
01587    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01588       tid = class->thread;
01589       class->thread = AST_PTHREADT_NULL;
01590       pthread_cancel(tid);
01591       /* We'll collect the exit status later, after we ensure all the readers
01592        * are dead. */
01593    }
01594 
01595    if (class->pid > 1) {
01596       char buff[8192];
01597       int bytes, tbytes = 0, stime = 0, pid = 0;
01598 
01599       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01600 
01601       stime = time(NULL) + 2;
01602       pid = class->pid;
01603       class->pid = 0;
01604 
01605       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01606        * to give the process a reason and time enough to kill off its
01607        * children. */
01608       do {
01609          if (killpg(pid, SIGHUP) < 0) {
01610             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01611          }
01612          usleep(100000);
01613          if (killpg(pid, SIGTERM) < 0) {
01614             if (errno == ESRCH) {
01615                break;
01616             }
01617             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01618          }
01619          usleep(100000);
01620          if (killpg(pid, SIGKILL) < 0) {
01621             if (errno == ESRCH) {
01622                break;
01623             }
01624             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01625          }
01626       } while (0);
01627 
01628       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01629             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01630          tbytes = tbytes + bytes;
01631       }
01632 
01633       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01634 
01635       close(class->srcfd);
01636       class->srcfd = -1;
01637    }
01638 
01639    if (class->filearray) {
01640       int i;
01641       for (i = 0; i < class->total_files; i++) {
01642          free(class->filearray[i]);
01643       }
01644       free(class->filearray);
01645       class->filearray = NULL;
01646    }
01647 
01648    if (class->timer) {
01649       ast_timer_close(class->timer);
01650       class->timer = NULL;
01651    }
01652 
01653    /* Finally, collect the exit status of the monitor thread */
01654    if (tid > 0) {
01655       pthread_join(tid, NULL);
01656    }
01657 
01658 }

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

Definition at line 1898 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01899 {
01900    const struct mohclass *class = obj;
01901 
01902    return ast_str_case_hash(class->name);
01903 }

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

Definition at line 1955 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01956 {
01957    struct mohclass *class = obj;
01958 
01959    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01960 }

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

Definition at line 1660 of file res_musiconhold.c.

Referenced by load_moh_classes().

01661 {
01662    struct mohclass *class = obj;
01663 
01664    class->delete = 1;
01665 
01666    return 0;
01667 }

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

Definition at line 1669 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01670 {
01671    struct mohclass *class = obj;
01672 
01673    return class->delete ? CMP_MATCH : 0;
01674 }

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

Definition at line 1175 of file res_musiconhold.c.

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

Referenced by _moh_register().

01176 {
01177    if (!old || !new) {
01178       return -1;
01179    }
01180 
01181    if (strcmp(old->dir, new->dir)) {
01182       return -1;
01183    } else if (strcmp(old->mode, new->mode)) {
01184       return -1;
01185    } else if (strcmp(old->args, new->args)) {
01186       return -1;
01187    } else if (old->flags != new->flags) {
01188       return -1;
01189    }
01190 
01191    return 0;
01192 }

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

Definition at line 448 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00449 {
00450    char *digit = arg;
00451    struct mohclass *class = obj;
00452 
00453    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00454 }

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

Definition at line 406 of file res_musiconhold.c.

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

00407 {
00408    struct moh_files_state *state;
00409    struct mohclass *class = params;
00410 
00411    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00412       chan->music_state = state;
00413       ast_module_ref(ast_module_info->self);
00414    } else {
00415       state = chan->music_state;
00416       if (!state) {
00417          return NULL;
00418       }
00419       if (state->class) {
00420          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00421          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00422       }
00423    }
00424 
00425    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00426     * malloc may allocate a different class to the same memory block.  This
00427     * might only happen when two reloads are generated in a short period of
00428     * time, but it's still important to protect against.
00429     * PROG: Compare the quick operation first, to save CPU. */
00430    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00431       memset(state, 0, sizeof(*state));
00432       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00433          state->pos = ast_random() % class->total_files;
00434       }
00435    }
00436 
00437    state->class = mohclass_ref(class, "Reffing music class for channel");
00438    state->origwfmt = chan->writeformat;
00439    /* For comparison on restart of MOH (see above) */
00440    ast_copy_string(state->name, class->name, sizeof(state->name));
00441    state->save_total = class->total_files;
00442 
00443    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00444    
00445    return chan->music_state;
00446 }

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

Definition at line 372 of file res_musiconhold.c.

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

00373 {
00374    struct moh_files_state *state = chan->music_state;
00375    struct ast_frame *f = NULL;
00376    int res = 0;
00377 
00378    state->sample_queue += samples;
00379 
00380    while (state->sample_queue > 0) {
00381       ast_channel_lock(chan);
00382       if ((f = moh_files_readframe(chan))) {
00383          /* We need to be sure that we unlock
00384           * the channel prior to calling
00385           * ast_write. Otherwise, the recursive locking
00386           * that occurs can cause deadlocks when using
00387           * indirect channels, like local channels
00388           */
00389          ast_channel_unlock(chan);
00390          state->samples += f->samples;
00391          state->sample_queue -= f->samples;
00392          res = ast_write(chan, f);
00393          ast_frfree(f);
00394          if (res < 0) {
00395             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00396             return -1;
00397          }
00398       } else {
00399          ast_channel_unlock(chan);
00400          return -1;  
00401       }
00402    }
00403    return res;
00404 }

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

Definition at line 360 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00361 {
00362    struct ast_frame *f = NULL;
00363    
00364    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00365       if (!ast_moh_files_next(chan))
00366          f = ast_readframe(chan->stream);
00367    }
00368 
00369    return f;
00370 }

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

Definition at line 256 of file res_musiconhold.c.

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

00257 {
00258    struct moh_files_state *state;
00259 
00260    if (!chan || !chan->music_state) {
00261       return;
00262    }
00263 
00264    state = chan->music_state;
00265 
00266    if (chan->stream) {
00267       ast_closestream(chan->stream);
00268       chan->stream = NULL;
00269    }
00270    
00271    if (option_verbose > 2) {
00272       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00273    }
00274 
00275    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00276       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
00277    }
00278 
00279    state->save_pos = state->pos;
00280 
00281    state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00282 }

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

Definition at line 973 of file res_musiconhold.c.

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

00974 {
00975    struct mohdata *moh = data;
00976    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00977    int res;
00978 
00979    len = ast_codec_get_len(moh->parent->format, samples);
00980 
00981    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00982       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00983       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00984    }
00985    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00986    if (res <= 0)
00987       return 0;
00988 
00989    moh->f.datalen = res;
00990    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00991    moh->f.samples = ast_codec_get_samples(&moh->f);
00992 
00993    if (ast_write(chan, &moh->f) < 0) {
00994       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00995       return -1;
00996    }
00997 
00998    return 0;
00999 }

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

Definition at line 462 of file res_musiconhold.c.

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

00463 {
00464    struct mohclass *class;
00465    const char *classname = NULL;
00466 
00467    if ((class = get_mohbydigit(digit))) {
00468       classname = ast_strdupa(class->name);
00469       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00470       ast_string_field_set(chan,musicclass,classname);
00471       ast_moh_stop(chan);
00472       ast_moh_start(chan, classname, NULL);
00473    }
00474 }

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

Definition at line 902 of file res_musiconhold.c.

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

Referenced by moh_alloc().

00903 {
00904    struct mohdata *moh = data;
00905    struct mohclass *class = moh->parent;
00906    format_t oldwfmt;
00907 
00908    ao2_lock(class);
00909    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00910    ao2_unlock(class);
00911    
00912    close(moh->pipe[0]);
00913    close(moh->pipe[1]);
00914 
00915    oldwfmt = moh->origwfmt;
00916 
00917    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00918 
00919    ast_free(moh);
00920 
00921    if (chan) {
00922       struct moh_files_state *state;
00923 
00924       state = chan->music_state;
00925       if (state && state->class) {
00926          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00927       }
00928       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00929          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00930                chan->name, ast_getformatname(oldwfmt));
00931       }
00932 
00933       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00934    }
00935 }

static void moh_rescan_files ( void   )  [static]

Definition at line 1159 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01159                                    {
01160    struct ao2_iterator i;
01161    struct mohclass *c;
01162 
01163    i = ao2_iterator_init(mohclasses, 0);
01164 
01165    while ((c = ao2_iterator_next(&i))) {
01166       if (!strcasecmp(c->mode, "files")) {
01167          moh_scan_files(c);
01168       }
01169       ao2_ref(c, -1);
01170    }
01171 
01172    ao2_iterator_destroy(&i);
01173 }

static int moh_scan_files ( struct mohclass class  )  [static]

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

01047                                                   {
01048 
01049    DIR *files_DIR;
01050    struct dirent *files_dirent;
01051    char dir_path[PATH_MAX];
01052    char path[PATH_MAX];
01053    char filepath[PATH_MAX];
01054    char *ext;
01055    struct stat statbuf;
01056    int i;
01057 
01058    if (class->dir[0] != '/') {
01059       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01060       strncat(dir_path, "/", sizeof(dir_path) - 1);
01061       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01062    } else {
01063       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01064    }
01065    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01066    files_DIR = opendir(dir_path);
01067    if (!files_DIR) {
01068       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01069       return -1;
01070    }
01071 
01072    for (i = 0; i < class->total_files; i++)
01073       ast_free(class->filearray[i]);
01074 
01075    class->total_files = 0;
01076    if (!getcwd(path, sizeof(path))) {
01077       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01078       closedir(files_DIR);
01079       return -1;
01080    }
01081    if (chdir(dir_path) < 0) {
01082       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01083       closedir(files_DIR);
01084       return -1;
01085    }
01086    while ((files_dirent = readdir(files_DIR))) {
01087       /* The file name must be at least long enough to have the file type extension */
01088       if ((strlen(files_dirent->d_name) < 4))
01089          continue;
01090 
01091       /* Skip files that starts with a dot */
01092       if (files_dirent->d_name[0] == '.')
01093          continue;
01094 
01095       /* Skip files without extensions... they are not audio */
01096       if (!strchr(files_dirent->d_name, '.'))
01097          continue;
01098 
01099       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01100 
01101       if (stat(filepath, &statbuf))
01102          continue;
01103 
01104       if (!S_ISREG(statbuf.st_mode))
01105          continue;
01106 
01107       if ((ext = strrchr(filepath, '.')))
01108          *ext = '\0';
01109 
01110       /* if the file is present in multiple formats, ensure we only put it into the list once */
01111       for (i = 0; i < class->total_files; i++)
01112          if (!strcmp(filepath, class->filearray[i]))
01113             break;
01114 
01115       if (i == class->total_files) {
01116          if (moh_add_file(class, filepath))
01117             break;
01118       }
01119    }
01120 
01121    closedir(files_DIR);
01122    if (chdir(path) < 0) {
01123       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01124       return -1;
01125    }
01126    if (ast_test_flag(class, MOH_SORTALPHA))
01127       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01128    return class->total_files;
01129 }

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

Definition at line 1037 of file res_musiconhold.c.

Referenced by moh_scan_files().

01038 {
01039    char *s1, *s2;
01040 
01041    s1 = ((char **)i1)[0];
01042    s2 = ((char **)i2)[0];
01043 
01044    return strcasecmp(s1, s2);
01045 }

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

Definition at line 869 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(), ast_frame_subclass::codec, errno, mohdata::f, mohclass::flags, mohclass::format, ast_frame::frametype, LOG_WARNING, mohclass::members, moh, mohclass_ref, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

00870 {
00871    struct mohdata *moh;
00872    long flags; 
00873    
00874    if (!(moh = ast_calloc(1, sizeof(*moh))))
00875       return NULL;
00876    
00877    if (pipe(moh->pipe)) {
00878       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00879       ast_free(moh);
00880       return NULL;
00881    }
00882 
00883    /* Make entirely non-blocking */
00884    flags = fcntl(moh->pipe[0], F_GETFL);
00885    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00886    flags = fcntl(moh->pipe[1], F_GETFL);
00887    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00888 
00889    moh->f.frametype = AST_FRAME_VOICE;
00890    moh->f.subclass.codec = cl->format;
00891    moh->f.offset = AST_FRIENDLY_OFFSET;
00892 
00893    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00894 
00895    ao2_lock(cl);
00896    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00897    ao2_unlock(cl);
00898    
00899    return moh;
00900 }

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

Definition at line 625 of file res_musiconhold.c.

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

Referenced by init_app_class(), and local_ast_moh_start().

00626 {
00627 #define  MOH_MS_INTERVAL      100
00628 
00629    struct mohclass *class = data;
00630    struct mohdata *moh;
00631    short sbuf[8192];
00632    int res = 0, res2;
00633    int len;
00634    struct timeval deadline, tv_tmp;
00635 
00636    deadline.tv_sec = 0;
00637    deadline.tv_usec = 0;
00638    for(;/* ever */;) {
00639       pthread_testcancel();
00640       /* Spawn mp3 player if it's not there */
00641       if (class->srcfd < 0) {
00642          if ((class->srcfd = spawn_mp3(class)) < 0) {
00643             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00644             /* Try again later */
00645             sleep(500);
00646             continue;
00647          }
00648       }
00649       if (class->timer) {
00650          struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
00651 
00652 #ifdef SOLARIS
00653          thr_yield();
00654 #endif
00655          /* Pause some amount of time */
00656          if (ast_poll(&pfd, 1, -1) > 0) {
00657             if (ast_timer_ack(class->timer, 1) < 0) {
00658                ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
00659                return NULL;
00660             }
00661             res = 320;
00662          } else {
00663             ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00664             res = 0;
00665          }
00666          pthread_testcancel();
00667       } else {
00668          long delta;
00669          /* Reliable sleep */
00670          tv_tmp = ast_tvnow();
00671          if (ast_tvzero(deadline))
00672             deadline = tv_tmp;
00673          delta = ast_tvdiff_ms(tv_tmp, deadline);
00674          if (delta < MOH_MS_INTERVAL) {   /* too early */
00675             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00676             usleep(1000 * (MOH_MS_INTERVAL - delta));
00677             pthread_testcancel();
00678          } else {
00679             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00680             deadline = tv_tmp;
00681          }
00682          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00683       }
00684       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00685          continue;
00686       /* Read mp3 audio */
00687       len = ast_codec_get_len(class->format, res);
00688 
00689       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00690          if (!res2) {
00691             close(class->srcfd);
00692             class->srcfd = -1;
00693             pthread_testcancel();
00694             if (class->pid > 1) {
00695                do {
00696                   if (killpg(class->pid, SIGHUP) < 0) {
00697                      if (errno == ESRCH) {
00698                         break;
00699                      }
00700                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00701                   }
00702                   usleep(100000);
00703                   if (killpg(class->pid, SIGTERM) < 0) {
00704                      if (errno == ESRCH) {
00705                         break;
00706                      }
00707                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00708                   }
00709                   usleep(100000);
00710                   if (killpg(class->pid, SIGKILL) < 0) {
00711                      if (errno == ESRCH) {
00712                         break;
00713                      }
00714                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00715                   }
00716                } while (0);
00717                class->pid = 0;
00718             }
00719          } else {
00720             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00721          }
00722          continue;
00723       }
00724 
00725       pthread_testcancel();
00726 
00727       ao2_lock(class);
00728       AST_LIST_TRAVERSE(&class->members, moh, list) {
00729          /* Write data */
00730          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00731             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00732          }
00733       }
00734       ao2_unlock(class);
00735    }
00736    return NULL;
00737 }

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

Definition at line 739 of file res_musiconhold.c.

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

Referenced by load_module().

00740 {
00741    char *parse;
00742    char *class;
00743    int timeout = -1;
00744    int res;
00745    AST_DECLARE_APP_ARGS(args,
00746       AST_APP_ARG(class);
00747       AST_APP_ARG(duration);
00748    );
00749 
00750    parse = ast_strdupa(data);
00751 
00752    AST_STANDARD_APP_ARGS(args, parse);
00753 
00754    if (!ast_strlen_zero(args.duration)) {
00755       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00756          timeout *= 1000;
00757       } else {
00758          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00759       }
00760    }
00761 
00762    class = S_OR(args.class, NULL);
00763    if (ast_moh_start(chan, class, NULL)) {
00764       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00765       return 0;
00766    }
00767 
00768    if (timeout > 0)
00769       res = ast_safe_sleep(chan, timeout);
00770    else {
00771       while (!(res = ast_safe_sleep(chan, 10000)));
00772    }
00773 
00774    ast_moh_stop(chan);
00775 
00776    return res;
00777 }

static int reload ( void   )  [static]
static int set_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 802 of file res_musiconhold.c.

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

Referenced by load_module().

00803 {
00804    static int deprecation_warning = 0;
00805 
00806    if (!deprecation_warning) {
00807       deprecation_warning = 1;
00808       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00809    }
00810 
00811    if (ast_strlen_zero(data)) {
00812       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00813       return -1;
00814    }
00815    ast_string_field_set(chan, musicclass, data);
00816    return 0;
00817 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 484 of file res_musiconhold.c.

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

Referenced by monmp3thread().

00485 {
00486    int fds[2];
00487    int files = 0;
00488    char fns[MAX_MP3S][80];
00489    char *argv[MAX_MP3S + 50];
00490    char xargs[256];
00491    char *argptr;
00492    int argc = 0;
00493    DIR *dir = NULL;
00494    struct dirent *de;
00495 
00496    
00497    if (!strcasecmp(class->dir, "nodir")) {
00498       files = 1;
00499    } else {
00500       dir = opendir(class->dir);
00501       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00502          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00503          return -1;
00504       }
00505    }
00506 
00507    if (!ast_test_flag(class, MOH_CUSTOM)) {
00508       argv[argc++] = "mpg123";
00509       argv[argc++] = "-q";
00510       argv[argc++] = "-s";
00511       argv[argc++] = "--mono";
00512       argv[argc++] = "-r";
00513       argv[argc++] = "8000";
00514       
00515       if (!ast_test_flag(class, MOH_SINGLE)) {
00516          argv[argc++] = "-b";
00517          argv[argc++] = "2048";
00518       }
00519       
00520       argv[argc++] = "-f";
00521       
00522       if (ast_test_flag(class, MOH_QUIET))
00523          argv[argc++] = "4096";
00524       else
00525          argv[argc++] = "8192";
00526       
00527       /* Look for extra arguments and add them to the list */
00528       ast_copy_string(xargs, class->args, sizeof(xargs));
00529       argptr = xargs;
00530       while (!ast_strlen_zero(argptr)) {
00531          argv[argc++] = argptr;
00532          strsep(&argptr, ",");
00533       }
00534    } else  {
00535       /* Format arguments for argv vector */
00536       ast_copy_string(xargs, class->args, sizeof(xargs));
00537       argptr = xargs;
00538       while (!ast_strlen_zero(argptr)) {
00539          argv[argc++] = argptr;
00540          strsep(&argptr, " ");
00541       }
00542    }
00543 
00544    if (!strncasecmp(class->dir, "http://", 7)) {
00545       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00546       argv[argc++] = fns[files];
00547       files++;
00548    } else if (dir) {
00549       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00550          if ((strlen(de->d_name) > 3) && 
00551              ((ast_test_flag(class, MOH_CUSTOM) && 
00552                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00553                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00554               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00555             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00556             argv[argc++] = fns[files];
00557             files++;
00558          }
00559       }
00560    }
00561    argv[argc] = NULL;
00562    if (dir) {
00563       closedir(dir);
00564    }
00565    if (pipe(fds)) {  
00566       ast_log(LOG_WARNING, "Pipe failed\n");
00567       return -1;
00568    }
00569    if (!files) {
00570       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00571       close(fds[0]);
00572       close(fds[1]);
00573       return -1;
00574    }
00575    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00576       sleep(respawn_time - (time(NULL) - class->start));
00577    }
00578 
00579    time(&class->start);
00580    class->pid = ast_safe_fork(0);
00581    if (class->pid < 0) {
00582       close(fds[0]);
00583       close(fds[1]);
00584       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00585       return -1;
00586    }
00587    if (!class->pid) {
00588       if (ast_opt_high_priority)
00589          ast_set_priority(0);
00590 
00591       close(fds[0]);
00592       /* Stdout goes to pipe */
00593       dup2(fds[1], STDOUT_FILENO);
00594 
00595       /* Close everything else */
00596       ast_close_fds_above_n(STDERR_FILENO);
00597 
00598       /* Child */
00599       if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00600          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00601          _exit(1);
00602       }
00603       setpgid(0, getpid());
00604       if (ast_test_flag(class, MOH_CUSTOM)) {
00605          execv(argv[0], argv);
00606       } else {
00607          /* Default install is /usr/local/bin */
00608          execv(LOCAL_MPG_123, argv);
00609          /* Many places have it in /usr/bin */
00610          execv(MPG_123, argv);
00611          /* Check PATH as a last-ditch effort */
00612          execvp("mpg123", argv);
00613       }
00614       /* Can't use logger, since log FDs are closed */
00615       fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00616       close(fds[1]);
00617       _exit(1);
00618    } else {
00619       /* Parent */
00620       close(fds[1]);
00621    }
00622    return fds[0];
00623 }

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

Definition at line 819 of file res_musiconhold.c.

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

Referenced by load_module().

00820 {
00821    char *parse;
00822    char *class;
00823    AST_DECLARE_APP_ARGS(args,
00824       AST_APP_ARG(class);
00825    );
00826 
00827    parse = ast_strdupa(data);
00828 
00829    AST_STANDARD_APP_ARGS(args, parse);
00830 
00831    class = S_OR(args.class, NULL);
00832    if (ast_moh_start(chan, class, NULL)) 
00833       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00834 
00835    return 0;
00836 }

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

Definition at line 838 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00839 {
00840    ast_moh_stop(chan);
00841 
00842    return 0;
00843 }

static int unload_module ( void   )  [static]

Definition at line 1962 of file res_musiconhold.c.

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

01963 {
01964    int res = 0;
01965    struct mohclass *class = NULL;
01966 
01967    /* XXX This check shouldn't be required if module ref counting was being used
01968     * properly ... */
01969    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01970       class = mohclass_unref(class, "unref of class from module unload callback");
01971       res = -1;
01972    }
01973 
01974    if (res < 0) {
01975       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01976       return res;
01977    }
01978 
01979    ast_uninstall_music_functions();
01980 
01981    ast_moh_destroy();
01982    res = ast_unregister_application(play_moh);
01983    res |= ast_unregister_application(wait_moh);
01984    res |= ast_unregister_application(set_moh);
01985    res |= ast_unregister_application(start_moh);
01986    res |= ast_unregister_application(stop_moh);
01987    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01988    ast_unregister_atexit(ast_moh_destroy);
01989 
01990    return res;
01991 }

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

Definition at line 779 of file res_musiconhold.c.

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

Referenced by load_module().

00780 {
00781    static int deprecation_warning = 0;
00782    int res;
00783 
00784    if (!deprecation_warning) {
00785       deprecation_warning = 1;
00786       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00787    }
00788 
00789    if (!data || !atoi(data)) {
00790       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00791       return -1;
00792    }
00793    if (ast_moh_start(chan, NULL, NULL)) {
00794       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00795       return 0;
00796    }
00797    res = ast_safe_sleep(chan, atoi(data) * 1000);
00798    ast_moh_stop(chan);
00799    return res;
00800 }


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

Definition at line 1998 of file res_musiconhold.c.

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

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

global MOH_ flags

Definition at line 179 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 476 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 219 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 1001 of file res_musiconhold.c.

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

Definition at line 147 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 153 of file res_musiconhold.c.

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

Definition at line 149 of file res_musiconhold.c.

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

Definition at line 150 of file res_musiconhold.c.

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

Definition at line 151 of file res_musiconhold.c.

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

Definition at line 148 of file res_musiconhold.c.


Generated on 3 Apr 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1