Thu Apr 8 01:23:29 2010

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 <sys/ioctl.h>
#include <dahdi/user.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/astobj2.h"
Include dependency graph for res_musiconhold.c:

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

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

Functions

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

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, }
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 char * play_moh = "MusicOnHold"
static char * play_moh_desc
static char * play_moh_syn = "Play Music On Hold indefinitely"
static int respawn_time = 20
static char * set_moh = "SetMusicOnHold"
static char * set_moh_desc
static char * set_moh_syn = "Set default Music On Hold class"
static char * start_moh = "StartMusicOnHold"
static char * start_moh_desc
static char * start_moh_syn = "Play Music On Hold"
static char * stop_moh = "StopMusicOnHold"
static char * stop_moh_desc
static char * stop_moh_syn = "Stop Playing Music On Hold"
static char * wait_moh = "WaitMusicOnHold"
static char * wait_moh_desc
static char * wait_moh_syn = "Wait, playing Music On Hold"

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

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 75 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 74 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 193 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 195 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 146 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 1192 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 149 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 140 of file res_musiconhold.c.

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

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

Definition at line 1122 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

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

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

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

Definition at line 199 of file res_musiconhold.c.

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

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

Definition at line 194 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1847 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1847 of file res_musiconhold.c.

static struct mohclass* _get_mohbyname ( const char *  name,
int  warn,
int  flags,
const char *  file,
int  lineno,
const char *  funcname 
) [static, read]

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

00775 {
00776    struct mohclass *moh = NULL;
00777    struct mohclass tmp_class = {
00778       .flags = 0,
00779    };
00780 
00781    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00782 
00783 #ifdef REF_DEBUG
00784    moh = _ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00785 #else
00786    moh = _ao2_find(mohclasses, &tmp_class, flags);
00787 #endif
00788 
00789    if (!moh && warn) {
00790       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00791    }
00792 
00793    return moh;
00794 }

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

Definition at line 1194 of file res_musiconhold.c.

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

01195 {
01196    struct mohclass *class;
01197 
01198    if ((class =
01199 #ifdef REF_DEBUG
01200          _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01201 #elif defined(__AST_DEBUG_MALLOC)
01202          _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01203 #else
01204          ao2_alloc(sizeof(*class), moh_class_destructor)
01205 #endif
01206       )) {
01207       class->format = AST_FORMAT_SLINEAR;
01208    }
01209 
01210    return class;
01211 }

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

Definition at line 1123 of file res_musiconhold.c.

References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, mohclass::mode, moh_diff(), MOH_NOTDELETED, mohclass_unref, mohclass::name, and mohclass::start.

01124 {
01125    struct mohclass *mohclass = NULL;
01126 
01127    if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
01128       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01129       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01130       if (unref) {
01131          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01132       }
01133       return -1;
01134    } else if (mohclass) {
01135       /* Found a class, but it's different from the one being registered */
01136       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01137    }
01138 
01139    time(&moh->start);
01140    moh->start -= respawn_time;
01141    
01142    if (!strcasecmp(moh->mode, "files")) {
01143       if (init_files_class(moh)) {
01144          if (unref) {
01145             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01146          }
01147          return -1;
01148       }
01149    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01150          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01151          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01152       if (init_app_class(moh)) {
01153          if (unref) {
01154             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01155          }
01156          return -1;
01157       }
01158    } else {
01159       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01160       if (unref) {
01161          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01162       }
01163       return -1;
01164    }
01165 
01166    ao2_t_link(mohclasses, moh, "Adding class to container");
01167 
01168    if (unref) {
01169       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01170    }
01171    
01172    return 0;
01173 }

static void ast_moh_destroy ( void   )  [static]

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

01641 {
01642    ast_verb(2, "Destroying musiconhold processes\n");
01643    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01644 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 255 of file res_musiconhold.c.

References ast_closestream(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

00256 {
00257    struct moh_files_state *state = chan->music_state;
00258    int tries;
00259 
00260    /* Discontinue a stream if it is running already */
00261    if (chan->stream) {
00262       ast_closestream(chan->stream);
00263       chan->stream = NULL;
00264    }
00265 
00266    if (!state->class->total_files) {
00267       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00268       return -1;
00269    }
00270 
00271    /* If a specific file has been saved confirm it still exists and that it is still valid */
00272    if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00273       state->pos = state->save_pos;
00274       state->save_pos = -1;
00275    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00276       /* Get a random file and ensure we can open it */
00277       for (tries = 0; tries < 20; tries++) {
00278          state->pos = ast_random() % state->class->total_files;
00279          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00280             break;
00281       }
00282       state->save_pos = -1;
00283       state->samples = 0;
00284    } else {
00285       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00286       state->pos++;
00287       state->pos %= state->class->total_files;
00288       state->save_pos = -1;
00289       state->samples = 0;
00290    }
00291 
00292    if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00293       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00294       state->pos++;
00295       state->pos %= state->class->total_files;
00296       return -1;
00297    }
00298 
00299    /* Record the pointer to the filename for position resuming later */
00300    state->save_pos_filename = state->class->filearray[state->pos];
00301 
00302    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00303 
00304    if (state->samples)
00305       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00306 
00307    return 0;
00308 }

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

Definition at line 394 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00395 {
00396    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00397 }

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

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

01647 {
01648    switch (cmd) {
01649    case CLI_INIT:
01650       e->command = "moh reload";
01651       e->usage =
01652          "Usage: moh reload\n"
01653          "       Reloads the MusicOnHold module.\n"
01654          "       Alias for 'module reload res_musiconhold.so'\n";
01655       return NULL;
01656    case CLI_GENERATE:
01657       return NULL;
01658    }
01659 
01660    if (a->argc != e->args)
01661       return CLI_SHOWUSAGE;
01662 
01663    reload();
01664 
01665    return CLI_SUCCESS;
01666 }

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

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

01707 {
01708    struct mohclass *class;
01709    struct ao2_iterator i;
01710 
01711    switch (cmd) {
01712    case CLI_INIT:
01713       e->command = "moh show classes";
01714       e->usage =
01715          "Usage: moh show classes\n"
01716          "       Lists all MusicOnHold classes.\n";
01717       return NULL;
01718    case CLI_GENERATE:
01719       return NULL;
01720    }
01721 
01722    if (a->argc != e->args)
01723       return CLI_SHOWUSAGE;
01724 
01725    i = ao2_iterator_init(mohclasses, 0);
01726    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01727       ast_cli(a->fd, "Class: %s\n", class->name);
01728       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01729       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01730       if (ast_test_flag(class, MOH_CUSTOM)) {
01731          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01732       }
01733       if (strcasecmp(class->mode, "files")) {
01734          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01735       }
01736    }
01737    ao2_iterator_destroy(&i);
01738 
01739    return CLI_SUCCESS;
01740 }

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

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

01669 {
01670    struct mohclass *class;
01671    struct ao2_iterator i;
01672 
01673    switch (cmd) {
01674    case CLI_INIT:
01675       e->command = "moh show files";
01676       e->usage =
01677          "Usage: moh show files\n"
01678          "       Lists all loaded file-based MusicOnHold classes and their\n"
01679          "       files.\n";
01680       return NULL;
01681    case CLI_GENERATE:
01682       return NULL;
01683    }
01684 
01685    if (a->argc != e->args)
01686       return CLI_SHOWUSAGE;
01687 
01688    i = ao2_iterator_init(mohclasses, 0);
01689    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01690       int x;
01691 
01692       if (!class->total_files) {
01693          continue;
01694       }
01695 
01696       ast_cli(a->fd, "Class: %s\n", class->name);
01697       for (x = 0; x < class->total_files; x++) {
01698          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01699       }
01700    }
01701    ao2_iterator_destroy(&i);
01702 
01703    return CLI_SUCCESS;
01704 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1076 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and monmp3thread().

Referenced by _moh_register().

01077 {
01078 #ifdef HAVE_DAHDI
01079    int x;
01080 #endif
01081 
01082    if (!strcasecmp(class->mode, "custom")) {
01083       ast_set_flag(class, MOH_CUSTOM);
01084    } else if (!strcasecmp(class->mode, "mp3nb")) {
01085       ast_set_flag(class, MOH_SINGLE);
01086    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01087       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01088    } else if (!strcasecmp(class->mode, "quietmp3")) {
01089       ast_set_flag(class, MOH_QUIET);
01090    }
01091       
01092    class->srcfd = -1;
01093    class->pseudofd = -1;
01094 
01095 #ifdef HAVE_DAHDI
01096    /* Open /dev/zap/pseudo for timing...  Is
01097       there a better, yet reliable way to do this? */
01098    class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
01099    if (class->pseudofd < 0) {
01100       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
01101    } else {
01102       x = 320;
01103       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
01104    }
01105 #endif
01106 
01107    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01108       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01109       if (class->pseudofd > -1) {
01110          close(class->pseudofd);
01111          class->pseudofd = -1;
01112       }
01113       return -1;
01114    }
01115 
01116    return 0;
01117 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1031 of file res_musiconhold.c.

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

Referenced by _moh_register().

01032 {
01033    int res;
01034 
01035    res = moh_scan_files(class);
01036 
01037    if (res < 0) {
01038       return -1;
01039    }
01040 
01041    if (!res) {
01042       if (option_verbose > 2) {
01043          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01044                class->dir, class->name);
01045       }
01046       return -1;
01047    }
01048 
01049    if (strchr(class->args, 'r')) {
01050       ast_set_flag(class, MOH_RANDOMIZE);
01051    }
01052 
01053    return 0;
01054 }

static int load_module ( void   )  [static]

Definition at line 1764 of file res_musiconhold.c.

References ao2_t_container_alloc, ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application, ast_register_atexit(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

01765 {
01766    int res;
01767 
01768    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01769       return AST_MODULE_LOAD_DECLINE;
01770    }
01771 
01772    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01773       ast_log(LOG_WARNING, "No music on hold classes configured, "
01774             "disabling music on hold.\n");
01775    } else {
01776       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01777             local_ast_moh_cleanup);
01778    }
01779 
01780    res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
01781    ast_register_atexit(ast_moh_destroy);
01782    ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01783    if (!res)
01784       res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
01785    if (!res)
01786       res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
01787    if (!res)
01788       res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
01789    if (!res)
01790       res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
01791 
01792    return AST_MODULE_LOAD_SUCCESS;
01793 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1539 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01540 {
01541    struct ast_config *cfg;
01542    struct ast_variable *var;
01543    struct mohclass *class; 
01544    char *cat;
01545    int numclasses = 0;
01546    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01547 
01548    cfg = ast_config_load("musiconhold.conf", config_flags);
01549 
01550    if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01551       return 0;
01552 
01553    if (reload) {
01554       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01555    }
01556    
01557    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01558 
01559    cat = ast_category_browse(cfg, NULL);
01560    for (; cat; cat = ast_category_browse(cfg, cat)) {
01561       /* Setup common options from [general] section */
01562       if (!strcasecmp(cat, "general")) {
01563          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01564             if (!strcasecmp(var->name, "cachertclasses")) {
01565                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01566             } else {
01567                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01568             }
01569          }
01570       }
01571       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01572       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01573             !strcasecmp(cat, "general")) {
01574          continue;
01575       }
01576 
01577       if (!(class = moh_class_malloc())) {
01578          break;
01579       }
01580 
01581       ast_copy_string(class->name, cat, sizeof(class->name));  
01582       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01583          if (!strcasecmp(var->name, "mode"))
01584             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01585          else if (!strcasecmp(var->name, "directory"))
01586             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01587          else if (!strcasecmp(var->name, "application"))
01588             ast_copy_string(class->args, var->value, sizeof(class->args));
01589          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01590             class->digit = *var->value;
01591          else if (!strcasecmp(var->name, "random"))
01592             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01593          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01594             ast_set_flag(class, MOH_RANDOMIZE);
01595          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01596             ast_set_flag(class, MOH_SORTALPHA);
01597          else if (!strcasecmp(var->name, "format")) {
01598             class->format = ast_getformatbyname(var->value);
01599             if (!class->format) {
01600                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01601                class->format = AST_FORMAT_SLINEAR;
01602             }
01603          }
01604       }
01605 
01606       if (ast_strlen_zero(class->dir)) {
01607          if (!strcasecmp(class->mode, "custom")) {
01608             strcpy(class->dir, "nodir");
01609          } else {
01610             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01611             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01612             continue;
01613          }
01614       }
01615       if (ast_strlen_zero(class->mode)) {
01616          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01617          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01618          continue;
01619       }
01620       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01621          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01622          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01623          continue;
01624       }
01625 
01626       /* Don't leak a class when it's already registered */
01627       if (!moh_register(class, reload, HANDLE_REF)) {
01628          numclasses++;
01629       }
01630    }
01631 
01632    ast_config_destroy(cfg);
01633 
01634    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01635          moh_classes_delete_marked, NULL, "Purge marked classes");
01636 
01637    return numclasses;
01638 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1175 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01176 {
01177    struct moh_files_state *state = chan->music_state;
01178 
01179    if (state) {
01180       if (state->class) {
01181          state->class = mohclass_unref(state->class, "Channel MOH state destruction");
01182       }
01183       ast_free(chan->music_state);
01184       chan->music_state = NULL;
01185       /* Only held a module reference if we had a music state */
01186       ast_module_unref(ast_module_info->self);
01187    }
01188 }

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

Definition at line 1213 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01214 {
01215    struct mohclass *mohclass = NULL;
01216    struct moh_files_state *state = chan->music_state;
01217    struct ast_variable *var = NULL;
01218    int res;
01219    int realtime_possible = ast_check_realtime("musiconhold");
01220 
01221    /* The following is the order of preference for which class to use:
01222     * 1) The channels explicitly set musicclass, which should *only* be
01223     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01224     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01225     *    result of receiving a HOLD control frame, this should be the
01226     *    payload that came with the frame.
01227     * 3) The interpclass argument. This would be from the mohinterpret
01228     *    option from channel drivers. This is the same as the old musicclass
01229     *    option.
01230     * 4) The default class.
01231     */
01232    if (!ast_strlen_zero(chan->musicclass)) {
01233       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01234       if (!mohclass && realtime_possible) {
01235          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01236       }
01237    }
01238    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01239       mohclass = get_mohbyname(mclass, 1, 0);
01240       if (!mohclass && realtime_possible) {
01241          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01242       }
01243    }
01244    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01245       mohclass = get_mohbyname(interpclass, 1, 0);
01246       if (!mohclass && realtime_possible) {
01247          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01248       }
01249    }
01250 
01251    if (!mohclass && !var) {
01252       mohclass = get_mohbyname("default", 1, 0);
01253       if (!mohclass && realtime_possible) {
01254          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01255       }
01256    }
01257 
01258    /* If no moh class found in memory, then check RT. Note that the logic used
01259     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01260     */
01261    if (var) {
01262       struct ast_variable *tmp = NULL;
01263 
01264       if ((mohclass = moh_class_malloc())) {
01265          mohclass->realtime = 1;
01266          for (tmp = var; tmp; tmp = tmp->next) {
01267             if (!strcasecmp(tmp->name, "name"))
01268                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01269             else if (!strcasecmp(tmp->name, "mode"))
01270                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01271             else if (!strcasecmp(tmp->name, "directory"))
01272                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01273             else if (!strcasecmp(tmp->name, "application"))
01274                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01275             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01276                mohclass->digit = *tmp->value;
01277             else if (!strcasecmp(tmp->name, "random"))
01278                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01279             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01280                ast_set_flag(mohclass, MOH_RANDOMIZE);
01281             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01282                ast_set_flag(mohclass, MOH_SORTALPHA);
01283             else if (!strcasecmp(tmp->name, "format")) {
01284                mohclass->format = ast_getformatbyname(tmp->value);
01285                if (!mohclass->format) {
01286                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01287                   mohclass->format = AST_FORMAT_SLINEAR;
01288                }
01289             }
01290          }
01291          ast_variables_destroy(var);
01292          if (ast_strlen_zero(mohclass->dir)) {
01293             if (!strcasecmp(mohclass->mode, "custom")) {
01294                strcpy(mohclass->dir, "nodir");
01295             } else {
01296                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01297                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01298                return -1;
01299             }
01300          }
01301          if (ast_strlen_zero(mohclass->mode)) {
01302             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01303             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01304             return -1;
01305          }
01306          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01307             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01308             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01309             return -1;
01310          }
01311 
01312          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01313             /* CACHERTCLASSES enabled, let's add this class to default tree */
01314             if (state && state->class) {
01315                /* Class already exist for this channel */
01316                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01317                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01318                   /* we found RT class with the same name, seems like we should continue playing existing one */
01319                   /* XXX This code is impossible to reach */
01320                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01321                   mohclass = state->class;
01322                }
01323             }
01324             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01325              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01326              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01327              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01328              * invalid memory.
01329              */
01330             moh_register(mohclass, 0, DONT_UNREF);
01331          } else {
01332             /* We don't register RT moh class, so let's init it manualy */
01333 
01334             time(&mohclass->start);
01335             mohclass->start -= respawn_time;
01336    
01337             if (!strcasecmp(mohclass->mode, "files")) {
01338                if (!moh_scan_files(mohclass)) {
01339                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01340                   return -1;
01341                }
01342                if (strchr(mohclass->args, 'r'))
01343                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01344             } 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")) {
01345 
01346                if (!strcasecmp(mohclass->mode, "custom"))
01347                   ast_set_flag(mohclass, MOH_CUSTOM);
01348                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01349                   ast_set_flag(mohclass, MOH_SINGLE);
01350                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01351                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01352                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01353                   ast_set_flag(mohclass, MOH_QUIET);
01354          
01355                mohclass->srcfd = -1;
01356 #ifdef HAVE_DAHDI
01357                /* Open /dev/dahdi/pseudo for timing...  Is
01358                   there a better, yet reliable way to do this? */
01359                mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
01360                if (mohclass->pseudofd < 0) {
01361                   ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
01362                } else {
01363                   int x = 320;
01364                   ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
01365                }
01366 #else
01367                mohclass->pseudofd = -1;
01368 #endif
01369                /* Let's check if this channel already had a moh class before */
01370                if (state && state->class) {
01371                   /* Class already exist for this channel */
01372                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01373                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01374                      /* we found RT class with the same name, seems like we should continue playing existing one */
01375                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01376                      mohclass = state->class;
01377                   }
01378                } else {
01379                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01380                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01381                      if (mohclass->pseudofd > -1) {
01382                         close(mohclass->pseudofd);
01383                         mohclass->pseudofd = -1;
01384                      }
01385                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01386                      return -1;
01387                   }
01388                }
01389             } else {
01390                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01391                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01392                return -1;
01393             }
01394          }
01395       } else {
01396          ast_variables_destroy(var);
01397       }
01398    }
01399 
01400    if (!mohclass) {
01401       return -1;
01402    }
01403 
01404    manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01405       "State: Start\r\n"
01406       "Channel: %s\r\n"
01407       "UniqueID: %s\r\n",
01408       chan->name, chan->uniqueid);
01409 
01410    ast_set_flag(chan, AST_FLAG_MOH);
01411 
01412    if (mohclass->total_files) {
01413       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01414    } else {
01415       res = ast_activate_generator(chan, &mohgen, mohclass);
01416    }
01417 
01418    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01419 
01420    return res;
01421 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1423 of file res_musiconhold.c.

References ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, EVENT_FLAG_CALL, manager_event, ast_channel::music_state, and ast_channel::stream.

Referenced by load_module(), and reload().

01424 {
01425    struct moh_files_state *state = chan->music_state;
01426    ast_clear_flag(chan, AST_FLAG_MOH);
01427    ast_deactivate_generator(chan);
01428 
01429    if (state) {
01430       if (chan->stream) {
01431          ast_closestream(chan->stream);
01432          chan->stream = NULL;
01433       }
01434    }
01435 
01436    manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01437       "State: Stop\r\n"
01438       "Channel: %s\r\n"
01439       "UniqueID: %s\r\n",
01440       chan->name, chan->uniqueid);
01441 }

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

Definition at line 923 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

00924 {
00925    if (!class->allowed_files) {
00926       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00927          return -1;
00928       class->allowed_files = INITIAL_NUM_FILES;
00929    } else if (class->total_files == class->allowed_files) {
00930       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00931          class->allowed_files = 0;
00932          class->total_files = 0;
00933          return -1;
00934       }
00935       class->allowed_files *= 2;
00936    }
00937 
00938    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00939       return -1;
00940 
00941    class->total_files++;
00942 
00943    return 0;
00944 }

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

Definition at line 858 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, mohdata::origwfmt, and ast_channel::writeformat.

00859 {
00860    struct mohdata *res;
00861    struct mohclass *class = params;
00862    struct moh_files_state *state;
00863 
00864    /* Initiating music_state for current channel. Channel should know name of moh class */
00865    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00866       chan->music_state = state;
00867       state->class = mohclass_ref(class, "Copying reference into state container");
00868       ast_module_ref(ast_module_info->self);
00869    } else
00870       state = chan->music_state;
00871    if (state && state->class != class) {
00872       memset(state, 0, sizeof(*state));
00873       state->class = class;
00874    }
00875 
00876    if ((res = mohalloc(class))) {
00877       res->origwfmt = chan->writeformat;
00878       if (ast_set_write_format(chan, class->format)) {
00879          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00880          moh_release(NULL, res);
00881          res = NULL;
00882       }
00883       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00884    }
00885    return res;
00886 }

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

Definition at line 1755 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01756 {
01757    struct mohclass *class = obj, *class2 = arg;
01758 
01759    return strcasecmp(class->name, class2->name) ? 0 :
01760       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01761       CMP_MATCH | CMP_STOP;
01762 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1443 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

01444 {
01445    struct mohclass *class = obj;
01446    struct mohdata *member;
01447    pthread_t tid = 0;
01448 
01449    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01450 
01451    /* Kill the thread first, so it cannot restart the child process while the
01452     * class is being destroyed */
01453    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01454       tid = class->thread;
01455       class->thread = AST_PTHREADT_NULL;
01456       pthread_cancel(tid);
01457       /* We'll collect the exit status later, after we ensure all the readers
01458        * are dead. */
01459    }
01460 
01461    if (class->pid > 1) {
01462       char buff[8192];
01463       int bytes, tbytes = 0, stime = 0, pid = 0;
01464 
01465       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01466 
01467       stime = time(NULL) + 2;
01468       pid = class->pid;
01469       class->pid = 0;
01470 
01471       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01472        * to give the process a reason and time enough to kill off its
01473        * children. */
01474       do {
01475          if (killpg(pid, SIGHUP) < 0) {
01476             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01477          }
01478          usleep(100000);
01479          if (killpg(pid, SIGTERM) < 0) {
01480             if (errno == ESRCH) {
01481                break;
01482             }
01483             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01484          }
01485          usleep(100000);
01486          if (killpg(pid, SIGKILL) < 0) {
01487             if (errno == ESRCH) {
01488                break;
01489             }
01490             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01491          }
01492       } while (0);
01493 
01494       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01495             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01496          tbytes = tbytes + bytes;
01497       }
01498 
01499       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01500 
01501       close(class->srcfd);
01502    }
01503 
01504    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01505       free(member);
01506    }
01507 
01508    if (class->filearray) {
01509       int i;
01510       for (i = 0; i < class->total_files; i++) {
01511          free(class->filearray[i]);
01512       }
01513       free(class->filearray);
01514       class->filearray = NULL;
01515    }
01516 
01517    /* Finally, collect the exit status of the monitor thread */
01518    if (tid > 0) {
01519       pthread_join(tid, NULL);
01520    }
01521 }

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

Definition at line 1748 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01749 {
01750    const struct mohclass *class = obj;
01751 
01752    return ast_str_case_hash(class->name);
01753 }

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

Definition at line 1805 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01806 {
01807    struct mohclass *class = obj;
01808 
01809    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01810 }

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

Definition at line 1523 of file res_musiconhold.c.

Referenced by load_moh_classes().

01524 {
01525    struct mohclass *class = obj;
01526 
01527    class->delete = 1;
01528 
01529    return 0;
01530 }

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

Definition at line 1532 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01533 {
01534    struct mohclass *class = obj;
01535 
01536    return class->delete ? CMP_MATCH : 0;
01537 }

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

Definition at line 1057 of file res_musiconhold.c.

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

Referenced by _moh_register().

01058 {
01059    if (!old || !new) {
01060       return -1;
01061    }
01062 
01063    if (strcmp(old->dir, new->dir)) {
01064       return -1;
01065    } else if (strcmp(old->mode, new->mode)) {
01066       return -1;
01067    } else if (strcmp(old->args, new->args)) {
01068       return -1;
01069    } else if (old->flags != new->flags) {
01070       return -1;
01071    }
01072 
01073    return 0;
01074 }

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

Definition at line 385 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00386 {
00387    char *digit = arg;
00388    struct mohclass *class = obj;
00389 
00390    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00391 }

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

Definition at line 346 of file res_musiconhold.c.

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

00347 {
00348    struct moh_files_state *state;
00349    struct mohclass *class = params;
00350 
00351    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00352       chan->music_state = state;
00353       ast_module_ref(ast_module_info->self);
00354    } else {
00355       state = chan->music_state;
00356    }
00357 
00358    if (!state) {
00359       return NULL;
00360    }
00361 
00362    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00363     * malloc may allocate a different class to the same memory block.  This
00364     * might only happen when two reloads are generated in a short period of
00365     * time, but it's still important to protect against.
00366     * PROG: Compare the quick operation first, to save CPU. */
00367    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00368       memset(state, 0, sizeof(*state));
00369       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00370          state->pos = ast_random() % class->total_files;
00371       }
00372    }
00373 
00374    state->class = mohclass_ref(class, "Reffing music class for channel");
00375    state->origwfmt = chan->writeformat;
00376    /* For comparison on restart of MOH (see above) */
00377    ast_copy_string(state->name, class->name, sizeof(state->name));
00378    state->save_total = class->total_files;
00379 
00380    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00381    
00382    return chan->music_state;
00383 }

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

Definition at line 322 of file res_musiconhold.c.

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

00323 {
00324    struct moh_files_state *state = chan->music_state;
00325    struct ast_frame *f = NULL;
00326    int res = 0;
00327 
00328    state->sample_queue += samples;
00329 
00330    while (state->sample_queue > 0) {
00331       if ((f = moh_files_readframe(chan))) {
00332          state->samples += f->samples;
00333          state->sample_queue -= f->samples;
00334          res = ast_write(chan, f);
00335          ast_frfree(f);
00336          if (res < 0) {
00337             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00338             return -1;
00339          }
00340       } else
00341          return -1;  
00342    }
00343    return res;
00344 }

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

Definition at line 310 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00311 {
00312    struct ast_frame *f = NULL;
00313    
00314    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00315       if (!ast_moh_files_next(chan))
00316          f = ast_readframe(chan->stream);
00317    }
00318 
00319    return f;
00320 }

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

Definition at line 227 of file res_musiconhold.c.

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

00228 {
00229    struct moh_files_state *state;
00230 
00231    if (!chan || !chan->music_state) {
00232       return;
00233    }
00234 
00235    state = chan->music_state;
00236 
00237    if (chan->stream) {
00238       ast_closestream(chan->stream);
00239       chan->stream = NULL;
00240    }
00241    
00242    if (option_verbose > 2) {
00243       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00244    }
00245 
00246    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00247       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00248    }
00249 
00250    state->save_pos = state->pos;
00251 
00252    state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00253 }

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

Definition at line 888 of file res_musiconhold.c.

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

00889 {
00890    struct mohdata *moh = data;
00891    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00892    int res;
00893 
00894    len = ast_codec_get_len(moh->parent->format, samples);
00895 
00896    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00897       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00898       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00899    }
00900    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00901    if (res <= 0)
00902       return 0;
00903 
00904    moh->f.datalen = res;
00905    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00906    moh->f.samples = ast_codec_get_samples(&moh->f);
00907 
00908    if (ast_write(chan, &moh->f) < 0) {
00909       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00910       return -1;
00911    }
00912 
00913    return 0;
00914 }

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

Definition at line 399 of file res_musiconhold.c.

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

00400 {
00401    struct mohclass *class;
00402    const char *classname = NULL;
00403 
00404    if ((class = get_mohbydigit(digit))) {
00405       classname = ast_strdupa(class->name);
00406       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00407       ast_string_field_set(chan,musicclass,classname);
00408       ast_moh_stop(chan);
00409       ast_moh_start(chan, classname, NULL);
00410    }
00411 }

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

Definition at line 829 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, mohclass::members, moh, mohclass_unref, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

00830 {
00831    struct mohdata *moh = data;
00832    struct mohclass *class = moh->parent;
00833    int oldwfmt;
00834 
00835    ao2_lock(class);
00836    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00837    ao2_unlock(class);
00838    
00839    close(moh->pipe[0]);
00840    close(moh->pipe[1]);
00841 
00842    oldwfmt = moh->origwfmt;
00843 
00844    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00845 
00846    ast_free(moh);
00847 
00848    if (chan) {
00849       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00850          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00851                chan->name, ast_getformatname(oldwfmt));
00852       }
00853 
00854       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00855    }
00856 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 956 of file res_musiconhold.c.

References ast_free, ast_log(), ast_test_flag, errno, ext, LOG_WARNING, moh_add_file(), moh_sort_compare(), and MOH_SORTALPHA.

Referenced by init_files_class(), and local_ast_moh_start().

00956                                                   {
00957 
00958    DIR *files_DIR;
00959    struct dirent *files_dirent;
00960    char path[PATH_MAX];
00961    char filepath[PATH_MAX];
00962    char *ext;
00963    struct stat statbuf;
00964    int dirnamelen;
00965    int i;
00966 
00967    files_DIR = opendir(class->dir);
00968    if (!files_DIR) {
00969       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00970       return -1;
00971    }
00972 
00973    for (i = 0; i < class->total_files; i++)
00974       ast_free(class->filearray[i]);
00975 
00976    class->total_files = 0;
00977    dirnamelen = strlen(class->dir) + 2;
00978    if (!getcwd(path, sizeof(path))) {
00979       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00980       return -1;
00981    }
00982    if (chdir(class->dir) < 0) {
00983       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00984       return -1;
00985    }
00986    while ((files_dirent = readdir(files_DIR))) {
00987       /* The file name must be at least long enough to have the file type extension */
00988       if ((strlen(files_dirent->d_name) < 4))
00989          continue;
00990 
00991       /* Skip files that starts with a dot */
00992       if (files_dirent->d_name[0] == '.')
00993          continue;
00994 
00995       /* Skip files without extensions... they are not audio */
00996       if (!strchr(files_dirent->d_name, '.'))
00997          continue;
00998 
00999       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
01000 
01001       if (stat(filepath, &statbuf))
01002          continue;
01003 
01004       if (!S_ISREG(statbuf.st_mode))
01005          continue;
01006 
01007       if ((ext = strrchr(filepath, '.')))
01008          *ext = '\0';
01009 
01010       /* if the file is present in multiple formats, ensure we only put it into the list once */
01011       for (i = 0; i < class->total_files; i++)
01012          if (!strcmp(filepath, class->filearray[i]))
01013             break;
01014 
01015       if (i == class->total_files) {
01016          if (moh_add_file(class, filepath))
01017             break;
01018       }
01019    }
01020 
01021    closedir(files_DIR);
01022    if (chdir(path) < 0) {
01023       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01024       return -1;
01025    }
01026    if (ast_test_flag(class, MOH_SORTALPHA))
01027       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01028    return class->total_files;
01029 }

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

Definition at line 946 of file res_musiconhold.c.

Referenced by moh_scan_files().

00947 {
00948    char *s1, *s2;
00949 
00950    s1 = ((char **)i1)[0];
00951    s2 = ((char **)i2)[0];
00952 
00953    return strcasecmp(s1, s2);
00954 }

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

Definition at line 796 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohdata::f, mohclass::flags, mohclass::format, ast_frame::frametype, LOG_WARNING, mohclass::members, moh, mohclass_ref, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

00797 {
00798    struct mohdata *moh;
00799    long flags; 
00800    
00801    if (!(moh = ast_calloc(1, sizeof(*moh))))
00802       return NULL;
00803    
00804    if (pipe(moh->pipe)) {
00805       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00806       ast_free(moh);
00807       return NULL;
00808    }
00809 
00810    /* Make entirely non-blocking */
00811    flags = fcntl(moh->pipe[0], F_GETFL);
00812    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00813    flags = fcntl(moh->pipe[1], F_GETFL);
00814    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00815 
00816    moh->f.frametype = AST_FRAME_VOICE;
00817    moh->f.subclass = cl->format;
00818    moh->f.offset = AST_FRIENDLY_OFFSET;
00819 
00820    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00821 
00822    ao2_lock(cl);
00823    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00824    ao2_unlock(cl);
00825    
00826    return moh;
00827 }

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

Definition at line 562 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_samp2tv(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), buf, errno, len(), LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

00563 {
00564 #define  MOH_MS_INTERVAL      100
00565 
00566    struct mohclass *class = data;
00567    struct mohdata *moh;
00568    char buf[8192];
00569    short sbuf[8192];
00570    int res, res2;
00571    int len;
00572    struct timeval deadline, tv_tmp;
00573 
00574    deadline.tv_sec = 0;
00575    deadline.tv_usec = 0;
00576    for(;/* ever */;) {
00577       pthread_testcancel();
00578       /* Spawn mp3 player if it's not there */
00579       if (class->srcfd < 0) {
00580          if ((class->srcfd = spawn_mp3(class)) < 0) {
00581             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00582             /* Try again later */
00583             sleep(500);
00584             pthread_testcancel();
00585          }
00586       }
00587       if (class->pseudofd > -1) {
00588 #ifdef SOLARIS
00589          thr_yield();
00590 #endif
00591          /* Pause some amount of time */
00592          res = read(class->pseudofd, buf, sizeof(buf));
00593          pthread_testcancel();
00594       } else {
00595          long delta;
00596          /* Reliable sleep */
00597          tv_tmp = ast_tvnow();
00598          if (ast_tvzero(deadline))
00599             deadline = tv_tmp;
00600          delta = ast_tvdiff_ms(tv_tmp, deadline);
00601          if (delta < MOH_MS_INTERVAL) {   /* too early */
00602             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00603             usleep(1000 * (MOH_MS_INTERVAL - delta));
00604             pthread_testcancel();
00605          } else {
00606             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00607             deadline = tv_tmp;
00608          }
00609          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00610       }
00611       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00612          continue;
00613       /* Read mp3 audio */
00614       len = ast_codec_get_len(class->format, res);
00615 
00616       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00617          if (!res2) {
00618             close(class->srcfd);
00619             class->srcfd = -1;
00620             pthread_testcancel();
00621             if (class->pid > 1) {
00622                do {
00623                   if (killpg(class->pid, SIGHUP) < 0) {
00624                      if (errno == ESRCH) {
00625                         break;
00626                      }
00627                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00628                   }
00629                   usleep(100000);
00630                   if (killpg(class->pid, SIGTERM) < 0) {
00631                      if (errno == ESRCH) {
00632                         break;
00633                      }
00634                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00635                   }
00636                   usleep(100000);
00637                   if (killpg(class->pid, SIGKILL) < 0) {
00638                      if (errno == ESRCH) {
00639                         break;
00640                      }
00641                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00642                   }
00643                } while (0);
00644                class->pid = 0;
00645             }
00646          } else {
00647             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00648          }
00649          continue;
00650       }
00651 
00652       pthread_testcancel();
00653 
00654       ao2_lock(class);
00655       AST_LIST_TRAVERSE(&class->members, moh, list) {
00656          /* Write data */
00657          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00658             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00659          }
00660       }
00661       ao2_unlock(class);
00662    }
00663    return NULL;
00664 }

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

Definition at line 666 of file res_musiconhold.c.

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

Referenced by load_module().

00667 {
00668    char *parse;
00669    char *class;
00670    int timeout = -1;
00671    int res;
00672    AST_DECLARE_APP_ARGS(args,
00673       AST_APP_ARG(class);
00674       AST_APP_ARG(duration);
00675    );
00676 
00677    parse = ast_strdupa(data);
00678 
00679    AST_STANDARD_APP_ARGS(args, parse);
00680 
00681    if (!ast_strlen_zero(args.duration)) {
00682       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00683          timeout *= 1000;
00684       } else {
00685          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00686       }
00687    }
00688 
00689    class = S_OR(args.class, NULL);
00690    if (ast_moh_start(chan, class, NULL)) {
00691       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00692       return 0;
00693    }
00694 
00695    if (timeout > 0)
00696       res = ast_safe_sleep(chan, timeout);
00697    else {
00698       while (!(res = ast_safe_sleep(chan, 10000)));
00699    }
00700 
00701    ast_moh_stop(chan);
00702 
00703    return res;
00704 }

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

Definition at line 729 of file res_musiconhold.c.

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

Referenced by load_module().

00730 {
00731    static int deprecation_warning = 0;
00732 
00733    if (!deprecation_warning) {
00734       deprecation_warning = 1;
00735       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00736    }
00737 
00738    if (ast_strlen_zero(data)) {
00739       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00740       return -1;
00741    }
00742    ast_string_field_set(chan, musicclass, data);
00743    return 0;
00744 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 421 of file res_musiconhold.c.

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

Referenced by monmp3thread().

00422 {
00423    int fds[2];
00424    int files = 0;
00425    char fns[MAX_MP3S][80];
00426    char *argv[MAX_MP3S + 50];
00427    char xargs[256];
00428    char *argptr;
00429    int argc = 0;
00430    DIR *dir = NULL;
00431    struct dirent *de;
00432 
00433    
00434    if (!strcasecmp(class->dir, "nodir")) {
00435       files = 1;
00436    } else {
00437       dir = opendir(class->dir);
00438       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00439          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00440          return -1;
00441       }
00442    }
00443 
00444    if (!ast_test_flag(class, MOH_CUSTOM)) {
00445       argv[argc++] = "mpg123";
00446       argv[argc++] = "-q";
00447       argv[argc++] = "-s";
00448       argv[argc++] = "--mono";
00449       argv[argc++] = "-r";
00450       argv[argc++] = "8000";
00451       
00452       if (!ast_test_flag(class, MOH_SINGLE)) {
00453          argv[argc++] = "-b";
00454          argv[argc++] = "2048";
00455       }
00456       
00457       argv[argc++] = "-f";
00458       
00459       if (ast_test_flag(class, MOH_QUIET))
00460          argv[argc++] = "4096";
00461       else
00462          argv[argc++] = "8192";
00463       
00464       /* Look for extra arguments and add them to the list */
00465       ast_copy_string(xargs, class->args, sizeof(xargs));
00466       argptr = xargs;
00467       while (!ast_strlen_zero(argptr)) {
00468          argv[argc++] = argptr;
00469          strsep(&argptr, ",");
00470       }
00471    } else  {
00472       /* Format arguments for argv vector */
00473       ast_copy_string(xargs, class->args, sizeof(xargs));
00474       argptr = xargs;
00475       while (!ast_strlen_zero(argptr)) {
00476          argv[argc++] = argptr;
00477          strsep(&argptr, " ");
00478       }
00479    }
00480 
00481    if (!strncasecmp(class->dir, "http://", 7)) {
00482       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00483       argv[argc++] = fns[files];
00484       files++;
00485    } else if (dir) {
00486       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00487          if ((strlen(de->d_name) > 3) && 
00488              ((ast_test_flag(class, MOH_CUSTOM) && 
00489                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00490                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00491               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00492             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00493             argv[argc++] = fns[files];
00494             files++;
00495          }
00496       }
00497    }
00498    argv[argc] = NULL;
00499    if (dir) {
00500       closedir(dir);
00501    }
00502    if (pipe(fds)) {  
00503       ast_log(LOG_WARNING, "Pipe failed\n");
00504       return -1;
00505    }
00506    if (!files) {
00507       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00508       close(fds[0]);
00509       close(fds[1]);
00510       return -1;
00511    }
00512    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00513       sleep(respawn_time - (time(NULL) - class->start));
00514    }
00515 
00516    time(&class->start);
00517    class->pid = ast_safe_fork(0);
00518    if (class->pid < 0) {
00519       close(fds[0]);
00520       close(fds[1]);
00521       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00522       return -1;
00523    }
00524    if (!class->pid) {
00525       if (ast_opt_high_priority)
00526          ast_set_priority(0);
00527 
00528       close(fds[0]);
00529       /* Stdout goes to pipe */
00530       dup2(fds[1], STDOUT_FILENO);
00531 
00532       /* Close everything else */
00533       ast_close_fds_above_n(STDERR_FILENO);
00534 
00535       /* Child */
00536       if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00537          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00538          _exit(1);
00539       }
00540       setpgid(0, getpid());
00541       if (ast_test_flag(class, MOH_CUSTOM)) {
00542          execv(argv[0], argv);
00543       } else {
00544          /* Default install is /usr/local/bin */
00545          execv(LOCAL_MPG_123, argv);
00546          /* Many places have it in /usr/bin */
00547          execv(MPG_123, argv);
00548          /* Check PATH as a last-ditch effort */
00549          execvp("mpg123", argv);
00550       }
00551       /* Can't use logger, since log FDs are closed */
00552       fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00553       close(fds[1]);
00554       _exit(1);
00555    } else {
00556       /* Parent */
00557       close(fds[1]);
00558    }
00559    return fds[0];
00560 }

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

Definition at line 746 of file res_musiconhold.c.

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

Referenced by load_module().

00747 {
00748    char *parse;
00749    char *class;
00750    AST_DECLARE_APP_ARGS(args,
00751       AST_APP_ARG(class);
00752    );
00753 
00754    parse = ast_strdupa(data);
00755 
00756    AST_STANDARD_APP_ARGS(args, parse);
00757 
00758    class = S_OR(args.class, NULL);
00759    if (ast_moh_start(chan, class, NULL)) 
00760       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00761 
00762    return 0;
00763 }

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

Definition at line 765 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00766 {
00767    ast_moh_stop(chan);
00768 
00769    return 0;
00770 }

static int unload_module ( void   )  [static]

Definition at line 1812 of file res_musiconhold.c.

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

01813 {
01814    int res = 0;
01815    struct mohclass *class = NULL;
01816 
01817    /* XXX This check shouldn't be required if module ref counting was being used
01818     * properly ... */
01819    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01820       class = mohclass_unref(class, "unref of class from module unload callback");
01821       res = -1;
01822    }
01823 
01824    if (res < 0) {
01825       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01826       return res;
01827    }
01828 
01829    ast_uninstall_music_functions();
01830 
01831    ast_moh_destroy();
01832    res = ast_unregister_application(play_moh);
01833    res |= ast_unregister_application(wait_moh);
01834    res |= ast_unregister_application(set_moh);
01835    res |= ast_unregister_application(start_moh);
01836    res |= ast_unregister_application(stop_moh);
01837    ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01838    ast_unregister_atexit(ast_moh_destroy);
01839 
01840    return res;
01841 }

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

Definition at line 706 of file res_musiconhold.c.

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

Referenced by load_module().

00707 {
00708    static int deprecation_warning = 0;
00709    int res;
00710 
00711    if (!deprecation_warning) {
00712       deprecation_warning = 1;
00713       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00714    }
00715 
00716    if (!data || !atoi(data)) {
00717       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00718       return -1;
00719    }
00720    if (ast_moh_start(chan, NULL, NULL)) {
00721       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00722       return 0;
00723    }
00724    res = ast_safe_sleep(chan, atoi(data) * 1000);
00725    ast_moh_stop(chan);
00726    return res;
00727 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1847 of file res_musiconhold.c.

Definition at line 1847 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
   AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
   AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
}

Definition at line 1742 of file res_musiconhold.c.

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

global MOH_ flags

Definition at line 151 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 413 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 191 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 916 of file res_musiconhold.c.

char* play_moh = "MusicOnHold" [static]

Definition at line 78 of file res_musiconhold.c.

char* play_moh_desc [static]

Definition at line 90 of file res_musiconhold.c.

char* play_moh_syn = "Play Music On Hold indefinitely" [static]

Definition at line 84 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 126 of file res_musiconhold.c.

char* set_moh = "SetMusicOnHold" [static]

Definition at line 80 of file res_musiconhold.c.

char* set_moh_desc [static]

Definition at line 108 of file res_musiconhold.c.

char* set_moh_syn = "Set default Music On Hold class" [static]

Definition at line 86 of file res_musiconhold.c.

char* start_moh = "StartMusicOnHold" [static]

Definition at line 81 of file res_musiconhold.c.

char* start_moh_desc [static]
Initial value:
 "  StartMusicOnHold(class):\n"
"Starts playing music on hold, uses default music class for channel.\n"
"Starts playing music specified by class.  If omitted, the default\n"
"music source for the channel will be used.  Always returns 0.\n"

Definition at line 118 of file res_musiconhold.c.

char* start_moh_syn = "Play Music On Hold" [static]

Definition at line 87 of file res_musiconhold.c.

char* stop_moh = "StopMusicOnHold" [static]

Definition at line 82 of file res_musiconhold.c.

char* stop_moh_desc [static]
Initial value:
 "  StopMusicOnHold(): "
"Stops playing music on hold.\n"

Definition at line 123 of file res_musiconhold.c.

char* stop_moh_syn = "Stop Playing Music On Hold" [static]

Definition at line 88 of file res_musiconhold.c.

char* wait_moh = "WaitMusicOnHold" [static]

Definition at line 79 of file res_musiconhold.c.

char* wait_moh_desc [static]

Definition at line 98 of file res_musiconhold.c.

char* wait_moh_syn = "Wait, playing Music On Hold" [static]

Definition at line 85 of file res_musiconhold.c.


Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1