Wed Aug 18 22:34:31 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"

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

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 75 of file res_musiconhold.c.

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

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_CUSTOM   (1 << 2)

Definition at line 142 of file res_musiconhold.c.

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

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

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

Definition at line 143 of file res_musiconhold.c.

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

#define moh_register ( a,
b,
 )     _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)

Note:
This function owns the reference it gets to moh if unref is true

Definition at line 1132 of file res_musiconhold.c.

Referenced by 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(), and local_ast_moh_start().

#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)

Definition at line 202 of file res_musiconhold.c.

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

#define MPG_123   "/usr/bin/mpg123"

Definition at line 194 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1858 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1858 of file res_musiconhold.c.

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

Definition at line 784 of file res_musiconhold.c.

References _ao2_find(), _ao2_find_debug(), ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, mohclasses, and mohclass::name.

Referenced by _moh_register().

00785 {
00786    struct mohclass *moh = NULL;
00787    struct mohclass tmp_class = {
00788       .flags = 0,
00789    };
00790 
00791    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00792 
00793 #ifdef REF_DEBUG
00794    moh = _ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00795 #else
00796    moh = _ao2_find(mohclasses, &tmp_class, flags);
00797 #endif
00798 
00799    if (!moh && warn) {
00800       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00801    }
00802 
00803    return moh;
00804 }

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

Definition at line 1204 of file res_musiconhold.c.

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

01205 {
01206    struct mohclass *class;
01207 
01208    if ((class =
01209 #ifdef REF_DEBUG
01210          _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01211 #elif defined(__AST_DEBUG_MALLOC)
01212          _ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01213 #else
01214          ao2_alloc(sizeof(*class), moh_class_destructor)
01215 #endif
01216       )) {
01217       class->format = AST_FORMAT_SLINEAR;
01218    }
01219 
01220    return class;
01221 }

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

Definition at line 1133 of file res_musiconhold.c.

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

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

static void ast_moh_destroy ( void   )  [static]

Definition at line 1651 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01652 {
01653    ast_verb(2, "Destroying musiconhold processes\n");
01654    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01655 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 255 of file res_musiconhold.c.

References ast_closestream(), ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_test_flag, chan, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, 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]

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

Definition at line 404 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00405 {
00406    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00407 }

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

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

01658 {
01659    switch (cmd) {
01660    case CLI_INIT:
01661       e->command = "moh reload";
01662       e->usage =
01663          "Usage: moh reload\n"
01664          "       Reloads the MusicOnHold module.\n"
01665          "       Alias for 'module reload res_musiconhold.so'\n";
01666       return NULL;
01667    case CLI_GENERATE:
01668       return NULL;
01669    }
01670 
01671    if (a->argc != e->args)
01672       return CLI_SHOWUSAGE;
01673 
01674    reload();
01675 
01676    return CLI_SUCCESS;
01677 }

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

Definition at line 1717 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, mohclasses, S_OR, and ast_cli_entry::usage.

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

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

Definition at line 1679 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, mohclasses, and ast_cli_entry::usage.

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

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1086 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, LOG_WARNING, mohclass::mode, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), mohclass::pseudofd, and mohclass::thread.

Referenced by _moh_register().

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

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1041 of file res_musiconhold.c.

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

Referenced by _moh_register().

01042 {
01043    int res;
01044 
01045    res = moh_scan_files(class);
01046 
01047    if (res < 0) {
01048       return -1;
01049    }
01050 
01051    if (!res) {
01052       if (option_verbose > 2) {
01053          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01054                class->dir, class->name);
01055       }
01056       return -1;
01057    }
01058 
01059    if (strchr(class->args, 'r')) {
01060       ast_set_flag(class, MOH_RANDOMIZE);
01061    }
01062 
01063    return 0;
01064 }

static int load_module ( void   )  [static]

Definition at line 1775 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(), cli_moh, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), mohclasses, play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

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

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1550 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_clear_flag, ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, global_flags, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), MOH_RANDOMIZE, MOH_SORTALPHA, mohclasses, OBJ_NODATA, and var.

Referenced by load_module().

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

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1185 of file res_musiconhold.c.

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

Referenced by load_module().

01186 {
01187    struct moh_files_state *state = chan->music_state;
01188 
01189    if (state) {
01190       if (state->class) {
01191          state->class = mohclass_unref(state->class, "Channel MOH state destruction");
01192       }
01193       ast_free(chan->music_state);
01194       chan->music_state = NULL;
01195       /* Only held a module reference if we had a music state */
01196       ast_module_unref(ast_module_info->self);
01197    }
01198 }

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

Definition at line 1223 of file res_musiconhold.c.

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

Referenced by load_module().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1433 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, chan, EVENT_FLAG_CALL, manager_event, ast_channel::music_state, ast_channel::name, ast_channel::stream, and ast_channel::uniqueid.

Referenced by load_module().

01434 {
01435    ast_clear_flag(chan, AST_FLAG_MOH);
01436    ast_deactivate_generator(chan);
01437 
01438    ast_channel_lock(chan);
01439    if (chan->music_state) {
01440       if (chan->stream) {
01441          ast_closestream(chan->stream);
01442          chan->stream = NULL;
01443       }
01444    }
01445 
01446    manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01447       "State: Stop\r\n"
01448       "Channel: %s\r\n"
01449       "UniqueID: %s\r\n",
01450       chan->name, chan->uniqueid);
01451    ast_channel_unlock(chan);
01452 }

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

Definition at line 933 of file res_musiconhold.c.

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

00934 {
00935    if (!class->allowed_files) {
00936       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00937          return -1;
00938       class->allowed_files = INITIAL_NUM_FILES;
00939    } else if (class->total_files == class->allowed_files) {
00940       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00941          class->allowed_files = 0;
00942          class->total_files = 0;
00943          return -1;
00944       }
00945       class->allowed_files *= 2;
00946    }
00947 
00948    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00949       return -1;
00950 
00951    class->total_files++;
00952 
00953    return 0;
00954 }

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

Definition at line 868 of file res_musiconhold.c.

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

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

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

Definition at line 1766 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01767 {
01768    struct mohclass *class = obj, *class2 = arg;
01769 
01770    return strcasecmp(class->name, class2->name) ? 0 :
01771       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01772       CMP_MATCH | CMP_STOP;
01773 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1454 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, and LOG_WARNING.

Referenced by _moh_class_malloc().

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

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

Definition at line 1759 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01760 {
01761    const struct mohclass *class = obj;
01762 
01763    return ast_str_case_hash(class->name);
01764 }

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

Definition at line 1816 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01817 {
01818    struct mohclass *class = obj;
01819 
01820    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01821 }

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

Definition at line 1534 of file res_musiconhold.c.

Referenced by load_moh_classes().

01535 {
01536    struct mohclass *class = obj;
01537 
01538    class->delete = 1;
01539 
01540    return 0;
01541 }

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

Definition at line 1543 of file res_musiconhold.c.

References CMP_MATCH.

01544 {
01545    struct mohclass *class = obj;
01546 
01547    return class->delete ? CMP_MATCH : 0;
01548 }

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

Definition at line 1067 of file res_musiconhold.c.

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

Referenced by _moh_register().

01068 {
01069    if (!old || !new) {
01070       return -1;
01071    }
01072 
01073    if (strcmp(old->dir, new->dir)) {
01074       return -1;
01075    } else if (strcmp(old->mode, new->mode)) {
01076       return -1;
01077    } else if (strcmp(old->args, new->args)) {
01078       return -1;
01079    } else if (old->flags != new->flags) {
01080       return -1;
01081    }
01082 
01083    return 0;
01084 }

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

Definition at line 395 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00396 {
00397    char *digit = arg;
00398    struct mohclass *class = obj;
00399 
00400    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00401 }

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

Definition at line 356 of file res_musiconhold.c.

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

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

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_channel_lock, ast_channel_unlock, ast_frfree, ast_log(), ast_write(), chan, errno, f, LOG_WARNING, moh_files_readframe(), ast_channel::music_state, moh_files_state::sample_queue, 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       ast_channel_lock(chan);
00332       if ((f = moh_files_readframe(chan))) {
00333          /* We need to be sure that we unlock
00334           * the channel prior to calling
00335           * ast_write. Otherwise, the recursive locking
00336           * that occurs can cause deadlocks when using
00337           * indirect channels, like local channels
00338           */
00339          ast_channel_unlock(chan);
00340          state->samples += f->samples;
00341          state->sample_queue -= f->samples;
00342          res = ast_write(chan, f);
00343          ast_frfree(f);
00344          if (res < 0) {
00345             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00346             return -1;
00347          }
00348       } else {
00349          ast_channel_unlock(chan);
00350          return -1;  
00351       }
00352    }
00353    return res;
00354 }

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

Definition at line 310 of file res_musiconhold.c.

References ast_moh_files_next(), ast_readframe(), chan, 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, chan, moh_files_state::class, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, 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 898 of file res_musiconhold.c.

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

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

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

Definition at line 409 of file res_musiconhold.c.

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

00410 {
00411    struct mohclass *class;
00412    const char *classname = NULL;
00413 
00414    if ((class = get_mohbydigit(digit))) {
00415       classname = ast_strdupa(class->name);
00416       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00417       ast_string_field_set(chan,musicclass,classname);
00418       ast_moh_stop(chan);
00419       ast_moh_start(chan, classname, NULL);
00420    }
00421 }

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

Definition at line 839 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, chan, LOG_WARNING, moh, mohclass_unref, and ast_channel::name.

Referenced by moh_alloc().

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

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 966 of file res_musiconhold.c.

References ast_free, ast_log(), mohclass::dir, errno, ext, mohclass::filearray, LOG_WARNING, and mohclass::total_files.

Referenced by init_files_class(), and local_ast_moh_start().

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

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

Definition at line 956 of file res_musiconhold.c.

00957 {
00958    char *s1, *s2;
00959 
00960    s1 = ((char **)i1)[0];
00961    s2 = ((char **)i2)[0];
00962 
00963    return strcasecmp(s1, s2);
00964 }

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

Definition at line 806 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohclass::format, LOG_WARNING, mohclass::members, moh, mohclass_ref, and mohdata::pipe.

Referenced by moh_alloc().

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

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

Definition at line 572 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(), mohclass::list, LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

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

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

Definition at line 676 of file res_musiconhold.c.

References 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(), chan, LOG_WARNING, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

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

static int reload ( void   )  [static]

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

Definition at line 739 of file res_musiconhold.c.

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

Referenced by load_module().

00740 {
00741    static int deprecation_warning = 0;
00742 
00743    if (!deprecation_warning) {
00744       deprecation_warning = 1;
00745       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00746    }
00747 
00748    if (ast_strlen_zero(data)) {
00749       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00750       return -1;
00751    }
00752    ast_string_field_set(chan, musicclass, data);
00753    return 0;
00754 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 431 of file res_musiconhold.c.

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

Referenced by monmp3thread().

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

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

Definition at line 756 of file res_musiconhold.c.

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

Referenced by load_module().

00757 {
00758    char *parse;
00759    char *class;
00760    AST_DECLARE_APP_ARGS(args,
00761       AST_APP_ARG(class);
00762    );
00763 
00764    parse = ast_strdupa(data);
00765 
00766    AST_STANDARD_APP_ARGS(args, parse);
00767 
00768    class = S_OR(args.class, NULL);
00769    if (ast_moh_start(chan, class, NULL)) 
00770       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00771 
00772    return 0;
00773 }

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

Definition at line 775 of file res_musiconhold.c.

References ast_moh_stop(), and chan.

Referenced by load_module().

00776 {
00777    ast_moh_stop(chan);
00778 
00779    return 0;
00780 }

static int unload_module ( void   )  [static]

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

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

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

Definition at line 716 of file res_musiconhold.c.

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

Referenced by load_module().

00717 {
00718    static int deprecation_warning = 0;
00719    int res;
00720 
00721    if (!deprecation_warning) {
00722       deprecation_warning = 1;
00723       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00724    }
00725 
00726    if (!data || !atoi(data)) {
00727       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00728       return -1;
00729    }
00730    if (ast_moh_start(chan, NULL, NULL)) {
00731       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00732       return 0;
00733    }
00734    res = ast_safe_sleep(chan, atoi(data) * 1000);
00735    ast_moh_stop(chan);
00736    return res;
00737 }


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

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1858 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Initial value:

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

Definition at line 1753 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

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

global MOH_ flags

Definition at line 151 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 423 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 191 of file res_musiconhold.c.

Referenced by _get_mohbyname(), _moh_register(), ast_moh_destroy(), handle_cli_moh_show_classes(), handle_cli_moh_show_files(), load_module(), load_moh_classes(), and unload_module().

struct ast_generator mohgen [static]

Definition at line 926 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 Wed Aug 18 22:34:31 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7