Tue Mar 2 17:34:22 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 72 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

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

Definition at line 768 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 71 of file res_musiconhold.c.

Referenced by load_moh_classes().

#define INITIAL_NUM_FILES   8

Definition at line 70 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 189 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 191 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 142 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 1188 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 145 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

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

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

Definition at line 137 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 140 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 195 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 190 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1843 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

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

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

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

Definition at line 1190 of file res_musiconhold.c.

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

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

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

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

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

static void ast_moh_destroy ( void   )  [static]

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

01637 {
01638    ast_verb(2, "Destroying musiconhold processes\n");
01639    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01640 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

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

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

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

Definition at line 390 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00391 {
00392    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00393 }

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

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

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

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

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

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

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

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

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

static int init_app_class ( struct mohclass class  )  [static]

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

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

static int init_files_class ( struct mohclass class  )  [static]

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

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

static int load_module ( void   )  [static]

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

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

static int load_moh_classes ( int  reload  )  [static]

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

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

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

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

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

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

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

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

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

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

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

Definition at line 919 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

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

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

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

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

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

Definition at line 1751 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

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

static void moh_class_destructor ( void *  obj  )  [static]

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

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

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

Definition at line 1744 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01745 {
01746    const struct mohclass *class = obj;
01747 
01748    return ast_str_case_hash(class->name);
01749 }

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

Definition at line 1801 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01802 {
01803    struct mohclass *class = obj;
01804 
01805    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01806 }

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

Definition at line 1519 of file res_musiconhold.c.

Referenced by load_moh_classes().

01520 {
01521    struct mohclass *class = obj;
01522 
01523    class->delete = 1;
01524 
01525    return 0;
01526 }

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

Definition at line 1528 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01529 {
01530    struct mohclass *class = obj;
01531 
01532    return class->delete ? CMP_MATCH : 0;
01533 }

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

Definition at line 1053 of file res_musiconhold.c.

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

Referenced by _moh_register().

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

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

Definition at line 381 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00382 {
00383    char *digit = arg;
00384    struct mohclass *class = obj;
00385 
00386    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00387 }

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

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

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

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

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

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

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

Definition at line 306 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00307 {
00308    struct ast_frame *f = NULL;
00309    
00310    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00311       if (!ast_moh_files_next(chan))
00312          f = ast_readframe(chan->stream);
00313    }
00314 
00315    return f;
00316 }

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

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

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

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

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

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

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

Definition at line 395 of file res_musiconhold.c.

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

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

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

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

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

static int moh_scan_files ( struct mohclass class  )  [static]

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

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

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

Definition at line 942 of file res_musiconhold.c.

Referenced by moh_scan_files().

00943 {
00944    char *s1, *s2;
00945 
00946    s1 = ((char **)i1)[0];
00947    s2 = ((char **)i2)[0];
00948 
00949    return strcasecmp(s1, s2);
00950 }

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

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

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

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

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

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

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

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

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

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

Definition at line 725 of file res_musiconhold.c.

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

Referenced by load_module().

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

static int spawn_mp3 ( struct mohclass class  )  [static]

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

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

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

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

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

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

Definition at line 761 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00762 {
00763    ast_moh_stop(chan);
00764 
00765    return 0;
00766 }

static int unload_module ( void   )  [static]

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

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

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

Definition at line 702 of file res_musiconhold.c.

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

Referenced by load_module().

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


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

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

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

global MOH_ flags

Definition at line 147 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 409 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 187 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 912 of file res_musiconhold.c.

char* play_moh = "MusicOnHold" [static]

Definition at line 74 of file res_musiconhold.c.

char* play_moh_desc [static]

Definition at line 86 of file res_musiconhold.c.

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

Definition at line 80 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 122 of file res_musiconhold.c.

char* set_moh = "SetMusicOnHold" [static]

Definition at line 76 of file res_musiconhold.c.

char* set_moh_desc [static]

Definition at line 104 of file res_musiconhold.c.

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

Definition at line 82 of file res_musiconhold.c.

char* start_moh = "StartMusicOnHold" [static]

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

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

Definition at line 83 of file res_musiconhold.c.

char* stop_moh = "StopMusicOnHold" [static]

Definition at line 78 of file res_musiconhold.c.

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

Definition at line 119 of file res_musiconhold.c.

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

Definition at line 84 of file res_musiconhold.c.

char* wait_moh = "WaitMusicOnHold" [static]

Definition at line 75 of file res_musiconhold.c.

char* wait_moh_desc [static]

Definition at line 94 of file res_musiconhold.c.

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

Definition at line 81 of file res_musiconhold.c.


Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1