Mon Jun 27 16:51:19 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 1936 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1936 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       class->srcfd = -1;
01281    }
01282 
01283    return class;
01284 }

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 1728 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().

01729 {
01730    ast_verb(2, "Destroying musiconhold processes\n");
01731    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01732 }

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 1734 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.

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

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

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

01795 {
01796    struct mohclass *class;
01797    struct ao2_iterator i;
01798 
01799    switch (cmd) {
01800    case CLI_INIT:
01801       e->command = "moh show classes";
01802       e->usage =
01803          "Usage: moh show classes\n"
01804          "       Lists all MusicOnHold classes.\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    i = ao2_iterator_init(mohclasses, 0);
01814    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01815       ast_cli(a->fd, "Class: %s\n", class->name);
01816       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01817       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01818       if (ast_test_flag(class, MOH_CUSTOM)) {
01819          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01820       }
01821       if (strcasecmp(class->mode, "files")) {
01822          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01823       }
01824    }
01825    ao2_iterator_destroy(&i);
01826 
01827    return CLI_SUCCESS;
01828 }

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

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

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

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 1852 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().

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

static int load_moh_classes ( int  reload  )  [static]

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

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

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 1286 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().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

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

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

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

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01844 {
01845    struct mohclass *class = obj, *class2 = arg;
01846 
01847    return strcasecmp(class->name, class2->name) ? 0 :
01848       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01849       CMP_MATCH | CMP_STOP;
01850 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1518 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

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

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

Definition at line 1836 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01837 {
01838    const struct mohclass *class = obj;
01839 
01840    return ast_str_case_hash(class->name);
01841 }

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

Definition at line 1893 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01894 {
01895    struct mohclass *class = obj;
01896 
01897    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01898 }

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

Definition at line 1603 of file res_musiconhold.c.

Referenced by load_moh_classes().

01604 {
01605    struct mohclass *class = obj;
01606 
01607    class->delete = 1;
01608 
01609    return 0;
01610 }

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

Definition at line 1612 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01613 {
01614    struct mohclass *class = obj;
01615 
01616    return class->delete ? CMP_MATCH : 0;
01617 }

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 1900 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.

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

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

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1936 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 1830 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 Mon Jun 27 16:51:19 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7