Wed Apr 6 11:30:09 2011

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"

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 71 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 827 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 70 of file res_musiconhold.c.

#define INITIAL_NUM_FILES   8

Definition at line 69 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 217 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 219 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 170 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 1264 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_CUSTOM   (1 << 2)

Definition at line 166 of file res_musiconhold.c.

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

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 173 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 164 of file res_musiconhold.c.

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

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 167 of file res_musiconhold.c.

Referenced by ast_moh_files_next(), init_files_class(), load_moh_classes(), local_ast_moh_start(), and moh_files_alloc().

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

Referenced by local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

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

Referenced by load_moh_classes(), and local_ast_moh_start().

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

Definition at line 223 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)

Definition at line 226 of file res_musiconhold.c.

Referenced by _moh_register(), handle_cli_moh_show_classes(), handle_cli_moh_show_files(), local_ast_moh_cleanup(), local_ast_moh_start(), moh_files_release(), moh_handle_digit(), moh_release(), and unload_module().

#define MPG_123   "/usr/bin/mpg123"

Definition at line 218 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1930 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1930 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]

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

00830 {
00831    struct mohclass *moh = NULL;
00832    struct mohclass tmp_class = {
00833       .flags = 0,
00834    };
00835 
00836    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00837 
00838 #ifdef REF_DEBUG
00839    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00840 #else
00841    moh = __ao2_find(mohclasses, &tmp_class, flags);
00842 #endif
00843 
00844    if (!moh && warn) {
00845       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00846    }
00847 
00848    return moh;
00849 }

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

Definition at line 1266 of file res_musiconhold.c.

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

01267 {
01268    struct mohclass *class;
01269 
01270    if ((class =
01271 #ifdef REF_DEBUG
01272          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01273 #elif defined(__AST_DEBUG_MALLOC)
01274          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01275 #else
01276          ao2_alloc(sizeof(*class), moh_class_destructor)
01277 #endif
01278       )) {
01279       class->format = AST_FORMAT_SLINEAR;
01280    }
01281 
01282    return class;
01283 }

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

Definition at line 1195 of file res_musiconhold.c.

References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, moh, moh_diff(), MOH_NOTDELETED, and mohclass_unref.

01196 {
01197    struct mohclass *mohclass = NULL;
01198 
01199    if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
01200       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01201       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01202       if (unref) {
01203          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01204       }
01205       return -1;
01206    } else if (mohclass) {
01207       /* Found a class, but it's different from the one being registered */
01208       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01209    }
01210 
01211    time(&moh->start);
01212    moh->start -= respawn_time;
01213 
01214    if (!strcasecmp(moh->mode, "files")) {
01215       if (init_files_class(moh)) {
01216          if (unref) {
01217             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01218          }
01219          return -1;
01220       }
01221    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01222          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01223          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01224       if (init_app_class(moh)) {
01225          if (unref) {
01226             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01227          }
01228          return -1;
01229       }
01230    } else {
01231       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01232       if (unref) {
01233          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01234       }
01235       return -1;
01236    }
01237 
01238    ao2_t_link(mohclasses, moh, "Adding class to container");
01239 
01240    if (unref) {
01241       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01242    }
01243 
01244    return 0;
01245 }

static void ast_moh_destroy ( void   )  [static]

Definition at line 1722 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01723 {
01724    ast_verb(2, "Destroying musiconhold processes\n");
01725    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01726 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 279 of file res_musiconhold.c.

References ast_closestream(), ast_fileexists(), ast_log(), ast_random(), ast_test_flag, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, state, and ast_channel::stream.

Referenced by moh_files_readframe().

00280 {
00281    struct moh_files_state *state = chan->music_state;
00282    int tries;
00283 
00284    /* Discontinue a stream if it is running already */
00285    if (chan->stream) {
00286       ast_closestream(chan->stream);
00287       chan->stream = NULL;
00288    }
00289 
00290    if (!state->class->total_files) {
00291       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00292       return -1;
00293    }
00294 
00295    if (state->pos == 0 && state->save_pos_filename == NULL) {
00296       /* First time so lets play the file. */
00297       state->save_pos = -1;
00298    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00299       /* If a specific file has been saved confirm it still exists and that it is still valid */
00300       state->pos = state->save_pos;
00301       state->save_pos = -1;
00302    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00303       /* Get a random file and ensure we can open it */
00304       for (tries = 0; tries < 20; tries++) {
00305          state->pos = ast_random() % state->class->total_files;
00306          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00307             break;
00308          }
00309       }
00310       state->save_pos = -1;
00311       state->samples = 0;
00312    } else {
00313       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00314       state->pos++;
00315       state->pos %= state->class->total_files;
00316       state->save_pos = -1;
00317       state->samples = 0;
00318    }
00319 
00320    for (tries = 0; tries < state->class->total_files; ++tries) {
00321       if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00322          break;
00323       }
00324 
00325       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00326       state->pos++;
00327       state->pos %= state->class->total_files;
00328    }
00329 
00330    if (tries == state->class->total_files) {
00331       return -1;
00332    }
00333 
00334    /* Record the pointer to the filename for position resuming later */
00335    state->save_pos_filename = state->class->filearray[state->pos];
00336 
00337    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00338 
00339    if (state->samples) {
00340       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00341    }
00342 
00343    return 0;
00344 }

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

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

Definition at line 440 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00441 {
00442    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00443 }

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

Definition at line 1728 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.

01729 {
01730    switch (cmd) {
01731    case CLI_INIT:
01732       e->command = "moh reload";
01733       e->usage =
01734          "Usage: moh reload\n"
01735          "       Reloads the MusicOnHold module.\n"
01736          "       Alias for 'module reload res_musiconhold.so'\n";
01737       return NULL;
01738    case CLI_GENERATE:
01739       return NULL;
01740    }
01741 
01742    if (a->argc != e->args)
01743       return CLI_SHOWUSAGE;
01744 
01745    reload();
01746 
01747    return CLI_SUCCESS;
01748 }

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

Definition at line 1788 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.

01789 {
01790    struct mohclass *class;
01791    struct ao2_iterator i;
01792 
01793    switch (cmd) {
01794    case CLI_INIT:
01795       e->command = "moh show classes";
01796       e->usage =
01797          "Usage: moh show classes\n"
01798          "       Lists all MusicOnHold classes.\n";
01799       return NULL;
01800    case CLI_GENERATE:
01801       return NULL;
01802    }
01803 
01804    if (a->argc != e->args)
01805       return CLI_SHOWUSAGE;
01806 
01807    i = ao2_iterator_init(mohclasses, 0);
01808    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01809       ast_cli(a->fd, "Class: %s\n", class->name);
01810       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01811       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01812       if (ast_test_flag(class, MOH_CUSTOM)) {
01813          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01814       }
01815       if (strcasecmp(class->mode, "files")) {
01816          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01817       }
01818    }
01819    ao2_iterator_destroy(&i);
01820 
01821    return CLI_SUCCESS;
01822 }

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

Definition at line 1750 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.

01751 {
01752    struct mohclass *class;
01753    struct ao2_iterator i;
01754 
01755    switch (cmd) {
01756    case CLI_INIT:
01757       e->command = "moh show files";
01758       e->usage =
01759          "Usage: moh show files\n"
01760          "       Lists all loaded file-based MusicOnHold classes and their\n"
01761          "       files.\n";
01762       return NULL;
01763    case CLI_GENERATE:
01764       return NULL;
01765    }
01766 
01767    if (a->argc != e->args)
01768       return CLI_SHOWUSAGE;
01769 
01770    i = ao2_iterator_init(mohclasses, 0);
01771    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01772       int x;
01773 
01774       if (!class->total_files) {
01775          continue;
01776       }
01777 
01778       ast_cli(a->fd, "Class: %s\n", class->name);
01779       for (x = 0; x < class->total_files; x++) {
01780          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01781       }
01782    }
01783    ao2_iterator_destroy(&i);
01784 
01785    return CLI_SUCCESS;
01786 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1156 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, mohclass::mode, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), mohclass::thread, and mohclass::timer.

Referenced by _moh_register().

01157 {
01158    if (!strcasecmp(class->mode, "custom")) {
01159       ast_set_flag(class, MOH_CUSTOM);
01160    } else if (!strcasecmp(class->mode, "mp3nb")) {
01161       ast_set_flag(class, MOH_SINGLE);
01162    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01163       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01164    } else if (!strcasecmp(class->mode, "quietmp3")) {
01165       ast_set_flag(class, MOH_QUIET);
01166    }
01167 
01168    class->srcfd = -1;
01169 
01170    if (!(class->timer = ast_timer_open())) {
01171       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01172    }
01173    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01174       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01175       ast_timer_close(class->timer);
01176       class->timer = NULL;
01177    }
01178 
01179    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01180       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01181       if (class->timer) {
01182          ast_timer_close(class->timer);
01183          class->timer = NULL;
01184       }
01185       return -1;
01186    }
01187 
01188    return 0;
01189 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1095 of file res_musiconhold.c.

References mohclass::args, ast_set_flag, ast_verbose, mohclass::dir, MOH_RANDOMIZE, moh_scan_files(), mohclass::name, option_verbose, and VERBOSE_PREFIX_3.

Referenced by _moh_register().

01096 {
01097    int res;
01098 
01099    res = moh_scan_files(class);
01100 
01101    if (res < 0) {
01102       return -1;
01103    }
01104 
01105    if (!res) {
01106       if (option_verbose > 2) {
01107          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01108                class->dir, class->name);
01109       }
01110       return -1;
01111    }
01112 
01113 #if 0
01114    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01115    if (strchr(class->args, 'r')) {
01116       ast_set_flag(class, MOH_RANDOMIZE);
01117    }
01118 #endif
01119 
01120    return 0;
01121 }

static int load_module ( void   )  [static]

Definition at line 1846 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(), cli_moh, 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().

01847 {
01848    int res;
01849 
01850    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01851       return AST_MODULE_LOAD_DECLINE;
01852    }
01853 
01854    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01855       ast_log(LOG_WARNING, "No music on hold classes configured, "
01856             "disabling music on hold.\n");
01857    } else {
01858       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01859             local_ast_moh_cleanup);
01860    }
01861 
01862    res = ast_register_application_xml(play_moh, play_moh_exec);
01863    ast_register_atexit(ast_moh_destroy);
01864    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01865    if (!res)
01866       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01867    if (!res)
01868       res = ast_register_application_xml(set_moh, set_moh_exec);
01869    if (!res)
01870       res = ast_register_application_xml(start_moh, start_moh_exec);
01871    if (!res)
01872       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01873 
01874    return AST_MODULE_LOAD_SUCCESS;
01875 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1613 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, global_flags, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_rescan_files(), MOH_SORTALPHA, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, and var.

Referenced by load_module().

01614 {
01615    struct ast_config *cfg;
01616    struct ast_variable *var;
01617    struct mohclass *class; 
01618    char *cat;
01619    int numclasses = 0;
01620    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01621 
01622    cfg = ast_config_load("musiconhold.conf", config_flags);
01623 
01624    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01625       if (ast_check_realtime("musiconhold") && reload) {
01626          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01627          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01628       }
01629       if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01630          moh_rescan_files();
01631       }
01632       return 0;
01633    }
01634 
01635    if (reload) {
01636       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01637    }
01638 
01639    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01640 
01641    cat = ast_category_browse(cfg, NULL);
01642    for (; cat; cat = ast_category_browse(cfg, cat)) {
01643       /* Setup common options from [general] section */
01644       if (!strcasecmp(cat, "general")) {
01645          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01646             if (!strcasecmp(var->name, "cachertclasses")) {
01647                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01648             } else {
01649                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01650             }
01651          }
01652       }
01653       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01654       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01655             !strcasecmp(cat, "general")) {
01656          continue;
01657       }
01658 
01659       if (!(class = moh_class_malloc())) {
01660          break;
01661       }
01662 
01663       ast_copy_string(class->name, cat, sizeof(class->name));  
01664       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01665          if (!strcasecmp(var->name, "mode"))
01666             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01667          else if (!strcasecmp(var->name, "directory"))
01668             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01669          else if (!strcasecmp(var->name, "application"))
01670             ast_copy_string(class->args, var->value, sizeof(class->args));
01671          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01672             class->digit = *var->value;
01673          else if (!strcasecmp(var->name, "random"))
01674             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01675          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01676             ast_set_flag(class, MOH_RANDOMIZE);
01677          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01678             ast_set_flag(class, MOH_SORTALPHA);
01679          else if (!strcasecmp(var->name, "format")) {
01680             class->format = ast_getformatbyname(var->value);
01681             if (!class->format) {
01682                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01683                class->format = AST_FORMAT_SLINEAR;
01684             }
01685          }
01686       }
01687 
01688       if (ast_strlen_zero(class->dir)) {
01689          if (!strcasecmp(class->mode, "custom")) {
01690             strcpy(class->dir, "nodir");
01691          } else {
01692             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01693             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01694             continue;
01695          }
01696       }
01697       if (ast_strlen_zero(class->mode)) {
01698          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01699          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01700          continue;
01701       }
01702       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01703          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01704          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01705          continue;
01706       }
01707 
01708       /* Don't leak a class when it's already registered */
01709       if (!moh_register(class, reload, HANDLE_REF)) {
01710          numclasses++;
01711       }
01712    }
01713 
01714    ast_config_destroy(cfg);
01715 
01716    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01717          moh_classes_delete_marked, NULL, "Purge marked classes");
01718 
01719    return numclasses;
01720 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1247 of file res_musiconhold.c.

References ast_free, ast_module_unref(), mohclass_unref, ast_channel::music_state, and state.

Referenced by load_module().

01248 {
01249    struct moh_files_state *state = chan->music_state;
01250 
01251    if (state) {
01252       if (state->class) {
01253          state->class = mohclass_unref(state->class, "Channel MOH state destruction");
01254       }
01255       ast_free(chan->music_state);
01256       chan->music_state = NULL;
01257       /* Only held a module reference if we had a music state */
01258       ast_module_unref(ast_module_info->self);
01259    }
01260 }

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

Definition at line 1285 of file res_musiconhold.c.

References mohclass::args, ast_check_realtime(), ast_copy_string(), AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_true(), ast_variables_destroy(), mohclass::digit, mohclass::dir, DONT_UNREF, errno, mohclass::format, get_mohbyname, global_flags, 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, ast_channel::musicclass, mohclass::name, ast_variable::name, ast_variable::next, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, state, mohclass::thread, mohclass::timer, ast_variable::value, and var.

Referenced by load_module().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1496 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, ast_channel::name, ast_channel::stream, and ast_channel::uniqueid.

Referenced by load_module().

01497 {
01498    ast_clear_flag(chan, AST_FLAG_MOH);
01499    ast_deactivate_generator(chan);
01500 
01501    ast_channel_lock(chan);
01502    if (chan->music_state) {
01503       if (chan->stream) {
01504          ast_closestream(chan->stream);
01505          chan->stream = NULL;
01506       }
01507    }
01508 
01509    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01510       "State: Stop\r\n"
01511       "Channel: %s\r\n"
01512       "UniqueID: %s\r\n",
01513       chan->name, chan->uniqueid);
01514    ast_channel_unlock(chan);
01515 }

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

Definition at line 978 of file res_musiconhold.c.

References mohclass::allowed_files, ast_calloc, ast_realloc, ast_strdup, mohclass::filearray, INITIAL_NUM_FILES, and mohclass::total_files.

Referenced by moh_scan_files().

00979 {
00980    if (!class->allowed_files) {
00981       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00982          return -1;
00983       class->allowed_files = INITIAL_NUM_FILES;
00984    } else if (class->total_files == class->allowed_files) {
00985       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00986          class->allowed_files = 0;
00987          class->total_files = 0;
00988          return -1;
00989       }
00990       class->allowed_files *= 2;
00991    }
00992 
00993    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00994       return -1;
00995 
00996    class->total_files++;
00997 
00998    return 0;
00999 }

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

Definition at line 913 of file res_musiconhold.c.

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

00914 {
00915    struct mohdata *res;
00916    struct mohclass *class = params;
00917    struct moh_files_state *state;
00918 
00919    /* Initiating music_state for current channel. Channel should know name of moh class */
00920    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00921       chan->music_state = state;
00922       state->class = mohclass_ref(class, "Copying reference into state container");
00923       ast_module_ref(ast_module_info->self);
00924    } else
00925       state = chan->music_state;
00926    if (state && state->class != class) {
00927       memset(state, 0, sizeof(*state));
00928       state->class = class;
00929    }
00930 
00931    if ((res = mohalloc(class))) {
00932       res->origwfmt = chan->writeformat;
00933       if (ast_set_write_format(chan, class->format)) {
00934          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00935          moh_release(NULL, res);
00936          res = NULL;
00937       }
00938       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00939    }
00940    return res;
00941 }

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

Definition at line 1837 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01838 {
01839    struct mohclass *class = obj, *class2 = arg;
01840 
01841    return strcasecmp(class->name, class2->name) ? 0 :
01842       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01843       CMP_MATCH | CMP_STOP;
01844 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1517 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

01518 {
01519    struct mohclass *class = obj;
01520    struct mohdata *member;
01521    pthread_t tid = 0;
01522 
01523    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01524 
01525    /* Kill the thread first, so it cannot restart the child process while the
01526     * class is being destroyed */
01527    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01528       tid = class->thread;
01529       class->thread = AST_PTHREADT_NULL;
01530       pthread_cancel(tid);
01531       /* We'll collect the exit status later, after we ensure all the readers
01532        * are dead. */
01533    }
01534 
01535    if (class->pid > 1) {
01536       char buff[8192];
01537       int bytes, tbytes = 0, stime = 0, pid = 0;
01538 
01539       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01540 
01541       stime = time(NULL) + 2;
01542       pid = class->pid;
01543       class->pid = 0;
01544 
01545       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01546        * to give the process a reason and time enough to kill off its
01547        * children. */
01548       do {
01549          if (killpg(pid, SIGHUP) < 0) {
01550             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01551          }
01552          usleep(100000);
01553          if (killpg(pid, SIGTERM) < 0) {
01554             if (errno == ESRCH) {
01555                break;
01556             }
01557             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01558          }
01559          usleep(100000);
01560          if (killpg(pid, SIGKILL) < 0) {
01561             if (errno == ESRCH) {
01562                break;
01563             }
01564             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01565          }
01566       } while (0);
01567 
01568       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01569             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01570          tbytes = tbytes + bytes;
01571       }
01572 
01573       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01574 
01575       close(class->srcfd);
01576    }
01577 
01578    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01579       free(member);
01580    }
01581 
01582    if (class->filearray) {
01583       int i;
01584       for (i = 0; i < class->total_files; i++) {
01585          free(class->filearray[i]);
01586       }
01587       free(class->filearray);
01588       class->filearray = NULL;
01589    }
01590 
01591    /* Finally, collect the exit status of the monitor thread */
01592    if (tid > 0) {
01593       pthread_join(tid, NULL);
01594    }
01595 }

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

Definition at line 1830 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01831 {
01832    const struct mohclass *class = obj;
01833 
01834    return ast_str_case_hash(class->name);
01835 }

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

Definition at line 1887 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01888 {
01889    struct mohclass *class = obj;
01890 
01891    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01892 }

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

Definition at line 1597 of file res_musiconhold.c.

Referenced by load_moh_classes().

01598 {
01599    struct mohclass *class = obj;
01600 
01601    class->delete = 1;
01602 
01603    return 0;
01604 }

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

Definition at line 1606 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01607 {
01608    struct mohclass *class = obj;
01609 
01610    return class->delete ? CMP_MATCH : 0;
01611 }

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

Definition at line 1137 of file res_musiconhold.c.

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

Referenced by _moh_register().

01138 {
01139    if (!old || !new) {
01140       return -1;
01141    }
01142 
01143    if (strcmp(old->dir, new->dir)) {
01144       return -1;
01145    } else if (strcmp(old->mode, new->mode)) {
01146       return -1;
01147    } else if (strcmp(old->args, new->args)) {
01148       return -1;
01149    } else if (old->flags != new->flags) {
01150       return -1;
01151    }
01152 
01153    return 0;
01154 }

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

Definition at line 431 of file res_musiconhold.c.

References CMP_MATCH, and CMP_STOP.

Referenced by get_mohbydigit().

00432 {
00433    char *digit = arg;
00434    struct mohclass *class = obj;
00435 
00436    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00437 }

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

Definition at line 392 of file res_musiconhold.c.

References ast_calloc, ast_copy_string(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, MOH_RANDOMIZE, mohclass_ref, ast_channel::music_state, ast_channel::name, state, and ast_channel::writeformat.

00393 {
00394    struct moh_files_state *state;
00395    struct mohclass *class = params;
00396 
00397    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00398       chan->music_state = state;
00399       ast_module_ref(ast_module_info->self);
00400    } else {
00401       state = chan->music_state;
00402    }
00403 
00404    if (!state) {
00405       return NULL;
00406    }
00407 
00408    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00409     * malloc may allocate a different class to the same memory block.  This
00410     * might only happen when two reloads are generated in a short period of
00411     * time, but it's still important to protect against.
00412     * PROG: Compare the quick operation first, to save CPU. */
00413    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00414       memset(state, 0, sizeof(*state));
00415       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00416          state->pos = ast_random() % class->total_files;
00417       }
00418    }
00419 
00420    state->class = mohclass_ref(class, "Reffing music class for channel");
00421    state->origwfmt = chan->writeformat;
00422    /* For comparison on restart of MOH (see above) */
00423    ast_copy_string(state->name, class->name, sizeof(state->name));
00424    state->save_total = class->total_files;
00425 
00426    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00427    
00428    return chan->music_state;
00429 }

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

Definition at line 358 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, and state.

00359 {
00360    struct moh_files_state *state = chan->music_state;
00361    struct ast_frame *f = NULL;
00362    int res = 0;
00363 
00364    state->sample_queue += samples;
00365 
00366    while (state->sample_queue > 0) {
00367       ast_channel_lock(chan);
00368       if ((f = moh_files_readframe(chan))) {
00369          /* We need to be sure that we unlock
00370           * the channel prior to calling
00371           * ast_write. Otherwise, the recursive locking
00372           * that occurs can cause deadlocks when using
00373           * indirect channels, like local channels
00374           */
00375          ast_channel_unlock(chan);
00376          state->samples += f->samples;
00377          state->sample_queue -= f->samples;
00378          res = ast_write(chan, f);
00379          ast_frfree(f);
00380          if (res < 0) {
00381             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00382             return -1;
00383          }
00384       } else {
00385          ast_channel_unlock(chan);
00386          return -1;  
00387       }
00388    }
00389    return res;
00390 }

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

Definition at line 346 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00347 {
00348    struct ast_frame *f = NULL;
00349    
00350    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00351       if (!ast_moh_files_next(chan))
00352          f = ast_readframe(chan->stream);
00353    }
00354 
00355    return f;
00356 }

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

Definition at line 251 of file res_musiconhold.c.

References ast_closestream(), ast_getformatname(), ast_log(), ast_set_write_format(), ast_verbose, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, state, ast_channel::stream, and VERBOSE_PREFIX_3.

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

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

Definition at line 943 of file res_musiconhold.c.

References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, LOG_WARNING, moh, and ast_channel::name.

00944 {
00945    struct mohdata *moh = data;
00946    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00947    int res;
00948 
00949    len = ast_codec_get_len(moh->parent->format, samples);
00950 
00951    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00952       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00953       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00954    }
00955    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00956    if (res <= 0)
00957       return 0;
00958 
00959    moh->f.datalen = res;
00960    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00961    moh->f.samples = ast_codec_get_samples(&moh->f);
00962 
00963    if (ast_write(chan, &moh->f) < 0) {
00964       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00965       return -1;
00966    }
00967 
00968    return 0;
00969 }

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

Definition at line 445 of file res_musiconhold.c.

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

00446 {
00447    struct mohclass *class;
00448    const char *classname = NULL;
00449 
00450    if ((class = get_mohbydigit(digit))) {
00451       classname = ast_strdupa(class->name);
00452       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00453       ast_string_field_set(chan,musicclass,classname);
00454       ast_moh_stop(chan);
00455       ast_moh_start(chan, classname, NULL);
00456    }
00457 }

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

Definition at line 884 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, LOG_WARNING, moh, mohclass_unref, and ast_channel::name.

Referenced by moh_alloc().

00885 {
00886    struct mohdata *moh = data;
00887    struct mohclass *class = moh->parent;
00888    format_t oldwfmt;
00889 
00890    ao2_lock(class);
00891    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00892    ao2_unlock(class);
00893    
00894    close(moh->pipe[0]);
00895    close(moh->pipe[1]);
00896 
00897    oldwfmt = moh->origwfmt;
00898 
00899    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00900 
00901    ast_free(moh);
00902 
00903    if (chan) {
00904       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00905          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00906                chan->name, ast_getformatname(oldwfmt));
00907       }
00908 
00909       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00910    }
00911 }

static void moh_rescan_files ( void   )  [static]

Definition at line 1123 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01123                                    {
01124    struct ao2_iterator i;
01125    struct mohclass *c;
01126 
01127    i = ao2_iterator_init(mohclasses, 0);
01128 
01129    while ((c = ao2_iterator_next(&i))) {
01130       moh_scan_files(c);
01131       ao2_ref(c, -1);
01132    }
01133 
01134    ao2_iterator_destroy(&i);
01135 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 1011 of file res_musiconhold.c.

References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_free, ast_log(), mohclass::dir, errno, ext, mohclass::filearray, LOG_WARNING, moh_add_file(), mohclass::name, and mohclass::total_files.

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

01011                                                   {
01012 
01013    DIR *files_DIR;
01014    struct dirent *files_dirent;
01015    char dir_path[PATH_MAX];
01016    char path[PATH_MAX];
01017    char filepath[PATH_MAX];
01018    char *ext;
01019    struct stat statbuf;
01020    int dirnamelen;
01021    int i;
01022 
01023    if (class->dir[0] != '/') {
01024       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01025       strncat(dir_path, "/", sizeof(dir_path) - 1);
01026       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01027    } else {
01028       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01029    }
01030    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01031    files_DIR = opendir(dir_path);
01032    if (!files_DIR) {
01033       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01034       return -1;
01035    }
01036 
01037    for (i = 0; i < class->total_files; i++)
01038       ast_free(class->filearray[i]);
01039 
01040    class->total_files = 0;
01041    dirnamelen = strlen(dir_path) + 2;
01042    if (!getcwd(path, sizeof(path))) {
01043       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01044       return -1;
01045    }
01046    if (chdir(dir_path) < 0) {
01047       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01048       return -1;
01049    }
01050    while ((files_dirent = readdir(files_DIR))) {
01051       /* The file name must be at least long enough to have the file type extension */
01052       if ((strlen(files_dirent->d_name) < 4))
01053          continue;
01054 
01055       /* Skip files that starts with a dot */
01056       if (files_dirent->d_name[0] == '.')
01057          continue;
01058 
01059       /* Skip files without extensions... they are not audio */
01060       if (!strchr(files_dirent->d_name, '.'))
01061          continue;
01062 
01063       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01064 
01065       if (stat(filepath, &statbuf))
01066          continue;
01067 
01068       if (!S_ISREG(statbuf.st_mode))
01069          continue;
01070 
01071       if ((ext = strrchr(filepath, '.')))
01072          *ext = '\0';
01073 
01074       /* if the file is present in multiple formats, ensure we only put it into the list once */
01075       for (i = 0; i < class->total_files; i++)
01076          if (!strcmp(filepath, class->filearray[i]))
01077             break;
01078 
01079       if (i == class->total_files) {
01080          if (moh_add_file(class, filepath))
01081             break;
01082       }
01083    }
01084 
01085    closedir(files_DIR);
01086    if (chdir(path) < 0) {
01087       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01088       return -1;
01089    }
01090    if (ast_test_flag(class, MOH_SORTALPHA))
01091       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01092    return class->total_files;
01093 }

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

Definition at line 1001 of file res_musiconhold.c.

01002 {
01003    char *s1, *s2;
01004 
01005    s1 = ((char **)i1)[0];
01006    s2 = ((char **)i2)[0];
01007 
01008    return strcasecmp(s1, s2);
01009 }

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

Definition at line 851 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, mohclass::format, mohdata::list, LOG_WARNING, mohclass::members, moh, mohclass_ref, and mohdata::pipe.

Referenced by moh_alloc().

00852 {
00853    struct mohdata *moh;
00854    long flags; 
00855    
00856    if (!(moh = ast_calloc(1, sizeof(*moh))))
00857       return NULL;
00858    
00859    if (pipe(moh->pipe)) {
00860       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00861       ast_free(moh);
00862       return NULL;
00863    }
00864 
00865    /* Make entirely non-blocking */
00866    flags = fcntl(moh->pipe[0], F_GETFL);
00867    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00868    flags = fcntl(moh->pipe[1], F_GETFL);
00869    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00870 
00871    moh->f.frametype = AST_FRAME_VOICE;
00872    moh->f.subclass.codec = cl->format;
00873    moh->f.offset = AST_FRIENDLY_OFFSET;
00874 
00875    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00876 
00877    ao2_lock(cl);
00878    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00879    ao2_unlock(cl);
00880    
00881    return moh;
00882 }

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

Definition at line 608 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, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

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

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

Definition at line 721 of file res_musiconhold.c.

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

Referenced by load_module().

00722 {
00723    char *parse;
00724    char *class;
00725    int timeout = -1;
00726    int res;
00727    AST_DECLARE_APP_ARGS(args,
00728       AST_APP_ARG(class);
00729       AST_APP_ARG(duration);
00730    );
00731 
00732    parse = ast_strdupa(data);
00733 
00734    AST_STANDARD_APP_ARGS(args, parse);
00735 
00736    if (!ast_strlen_zero(args.duration)) {
00737       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00738          timeout *= 1000;
00739       } else {
00740          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00741       }
00742    }
00743 
00744    class = S_OR(args.class, NULL);
00745    if (ast_moh_start(chan, class, NULL)) {
00746       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00747       return 0;
00748    }
00749 
00750    if (timeout > 0)
00751       res = ast_safe_sleep(chan, timeout);
00752    else {
00753       while (!(res = ast_safe_sleep(chan, 10000)));
00754    }
00755 
00756    ast_moh_stop(chan);
00757 
00758    return res;
00759 }

static int reload ( void   )  [static]

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

Definition at line 784 of file res_musiconhold.c.

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

Referenced by load_module().

00785 {
00786    static int deprecation_warning = 0;
00787 
00788    if (!deprecation_warning) {
00789       deprecation_warning = 1;
00790       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00791    }
00792 
00793    if (ast_strlen_zero(data)) {
00794       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00795       return -1;
00796    }
00797    ast_string_field_set(chan, musicclass, data);
00798    return 0;
00799 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 467 of file res_musiconhold.c.

References mohclass::args, 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, mohclass::pid, mohclass::start, and strsep().

Referenced by monmp3thread().

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

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

Definition at line 801 of file res_musiconhold.c.

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

Referenced by load_module().

00802 {
00803    char *parse;
00804    char *class;
00805    AST_DECLARE_APP_ARGS(args,
00806       AST_APP_ARG(class);
00807    );
00808 
00809    parse = ast_strdupa(data);
00810 
00811    AST_STANDARD_APP_ARGS(args, parse);
00812 
00813    class = S_OR(args.class, NULL);
00814    if (ast_moh_start(chan, class, NULL)) 
00815       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00816 
00817    return 0;
00818 }

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

Definition at line 820 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00821 {
00822    ast_moh_stop(chan);
00823 
00824    return 0;
00825 }

static int unload_module ( void   )  [static]

Definition at line 1894 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(), cli_moh, LOG_WARNING, moh_class_inuse(), and mohclass_unref.

01895 {
01896    int res = 0;
01897    struct mohclass *class = NULL;
01898 
01899    /* XXX This check shouldn't be required if module ref counting was being used
01900     * properly ... */
01901    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01902       class = mohclass_unref(class, "unref of class from module unload callback");
01903       res = -1;
01904    }
01905 
01906    if (res < 0) {
01907       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01908       return res;
01909    }
01910 
01911    ast_uninstall_music_functions();
01912 
01913    ast_moh_destroy();
01914    res = ast_unregister_application(play_moh);
01915    res |= ast_unregister_application(wait_moh);
01916    res |= ast_unregister_application(set_moh);
01917    res |= ast_unregister_application(start_moh);
01918    res |= ast_unregister_application(stop_moh);
01919    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01920    ast_unregister_atexit(ast_moh_destroy);
01921 
01922    return res;
01923 }

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

Definition at line 761 of file res_musiconhold.c.

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

Referenced by load_module().

00762 {
00763    static int deprecation_warning = 0;
00764    int res;
00765 
00766    if (!deprecation_warning) {
00767       deprecation_warning = 1;
00768       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00769    }
00770 
00771    if (!data || !atoi(data)) {
00772       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00773       return -1;
00774    }
00775    if (ast_moh_start(chan, NULL, NULL)) {
00776       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00777       return 0;
00778    }
00779    res = ast_safe_sleep(chan, atoi(data) * 1000);
00780    ast_moh_stop(chan);
00781    return res;
00782 }


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

Definition at line 1930 of file res_musiconhold.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1930 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Initial value:

 {
   { .handler =  handle_cli_moh_reload , .summary =  "Reload MusicOnHold" ,__VA_ARGS__ },
   { .handler =  handle_cli_moh_show_classes , .summary =  "List MusicOnHold classes" ,__VA_ARGS__ },
   { .handler =  handle_cli_moh_show_files , .summary =  "List MusicOnHold file-based classes" ,__VA_ARGS__ }
}

Definition at line 1824 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

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

global MOH_ flags

Definition at line 175 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 459 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 215 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 971 of file res_musiconhold.c.

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

Definition at line 144 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 150 of file res_musiconhold.c.

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

Definition at line 146 of file res_musiconhold.c.

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

Definition at line 147 of file res_musiconhold.c.

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

Definition at line 148 of file res_musiconhold.c.

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

Definition at line 145 of file res_musiconhold.c.


Generated on Wed Apr 6 11:30:09 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7