Tue Aug 20 16:35:14 2013

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/manager.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.h"

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

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

Functions

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

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_moh []
static struct ast_flags global_flags [1] = {{0}}
static struct ast_generator moh_file_stream
static struct ao2_containermohclasses
static struct ast_generator mohgen
static const char play_moh [] = "MusicOnHold"
static int respawn_time = 20
static const char set_moh [] = "SetMusicOnHold"
static const char start_moh [] = "StartMusicOnHold"
static const char stop_moh [] = "StopMusicOnHold"
static const char wait_moh [] = "WaitMusicOnHold"

Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define DONT_UNREF   0

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

#define MAX_MP3S   256

Definition at line 223 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 174 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 1301 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 177 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

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

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

Definition at line 169 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 172 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 227 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 222 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1991 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

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

00847 {
00848    struct mohclass *moh = NULL;
00849    struct mohclass tmp_class = {
00850       .flags = 0,
00851    };
00852 
00853    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00854 
00855 #ifdef REF_DEBUG
00856    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00857 #else
00858    moh = __ao2_find(mohclasses, &tmp_class, flags);
00859 #endif
00860 
00861    if (!moh && warn) {
00862       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00863    }
00864 
00865    return moh;
00866 }

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

Definition at line 1303 of file res_musiconhold.c.

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

01304 {
01305    struct mohclass *class;
01306 
01307    if ((class =
01308 #ifdef REF_DEBUG
01309          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01310 #elif defined(__AST_DEBUG_MALLOC)
01311          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01312 #else
01313          ao2_alloc(sizeof(*class), moh_class_destructor)
01314 #endif
01315       )) {
01316       class->format = AST_FORMAT_SLINEAR;
01317       class->srcfd = -1;
01318    }
01319 
01320    return class;
01321 }

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

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

01228 {
01229    struct mohclass *mohclass = NULL;
01230 
01231    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01232 
01233    if (mohclass && !moh_diff(mohclass, moh)) {
01234       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01235       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01236       if (unref) {
01237          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01238       }
01239       return -1;
01240    } else if (mohclass) {
01241       /* Found a class, but it's different from the one being registered */
01242       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01243    }
01244 
01245    time(&moh->start);
01246    moh->start -= respawn_time;
01247 
01248    if (!strcasecmp(moh->mode, "files")) {
01249       if (init_files_class(moh)) {
01250          if (unref) {
01251             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01252          }
01253          return -1;
01254       }
01255    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01256          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01257          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01258       if (init_app_class(moh)) {
01259          if (unref) {
01260             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01261          }
01262          return -1;
01263       }
01264    } else {
01265       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01266       if (unref) {
01267          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01268       }
01269       return -1;
01270    }
01271 
01272    ao2_t_link(mohclasses, moh, "Adding class to container");
01273 
01274    if (unref) {
01275       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01276    }
01277 
01278    return 0;
01279 }

static void ast_moh_destroy ( void   )  [static]

Definition at line 1779 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01780 {
01781    ast_verb(2, "Destroying musiconhold processes\n");
01782    if (mohclasses) {
01783       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01784       ao2_ref(mohclasses, -1);
01785       mohclasses = NULL;
01786    }
01787 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 283 of file res_musiconhold.c.

References ast_closestream(), ast_copy_string(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_strlen_zero(), ast_tellstream(), 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().

00284 {
00285    struct moh_files_state *state = chan->music_state;
00286    int tries;
00287 
00288    /* Discontinue a stream if it is running already */
00289    if (chan->stream) {
00290       ast_closestream(chan->stream);
00291       chan->stream = NULL;
00292    }
00293 
00294    if (!state->class->total_files) {
00295       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00296       return -1;
00297    }
00298 
00299    if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
00300       /* First time so lets play the file. */
00301       state->save_pos = -1;
00302    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
00303       /* If a specific file has been saved confirm it still exists and that it is still valid */
00304       state->pos = state->save_pos;
00305       state->save_pos = -1;
00306    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00307       /* Get a random file and ensure we can open it */
00308       for (tries = 0; tries < 20; tries++) {
00309          state->pos = ast_random() % state->class->total_files;
00310          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00311             break;
00312          }
00313       }
00314       state->save_pos = -1;
00315       state->samples = 0;
00316    } else {
00317       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00318       state->pos++;
00319       state->pos %= state->class->total_files;
00320       state->save_pos = -1;
00321       state->samples = 0;
00322    }
00323 
00324    for (tries = 0; tries < state->class->total_files; ++tries) {
00325       if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00326          break;
00327       }
00328 
00329       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00330       state->pos++;
00331       state->pos %= state->class->total_files;
00332    }
00333 
00334    if (tries == state->class->total_files) {
00335       return -1;
00336    }
00337 
00338    /* Record the pointer to the filename for position resuming later */
00339    ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
00340 
00341    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00342 
00343    if (state->samples) {
00344       size_t loc;
00345       /* seek *SHOULD* be good since it's from a known location */
00346       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00347       /* if the seek failed then recover because if there is not a valid read,
00348        * moh_files_generate will return -1 and MOH will stop */
00349       loc = ast_tellstream(chan->stream);
00350       if (state->samples > loc && loc) {
00351          /* seek one sample from the end for one guaranteed valid read */
00352          ast_seekstream(chan->stream, 1, SEEK_END);
00353       }
00354    }
00355 
00356    return 0;
00357 }

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

Definition at line 456 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00457 {
00458    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00459 }

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

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

01790 {
01791    switch (cmd) {
01792    case CLI_INIT:
01793       e->command = "moh reload";
01794       e->usage =
01795          "Usage: moh reload\n"
01796          "       Reloads the MusicOnHold module.\n"
01797          "       Alias for 'module reload res_musiconhold.so'\n";
01798       return NULL;
01799    case CLI_GENERATE:
01800       return NULL;
01801    }
01802 
01803    if (a->argc != e->args)
01804       return CLI_SHOWUSAGE;
01805 
01806    reload();
01807 
01808    return CLI_SUCCESS;
01809 }

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

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

01850 {
01851    struct mohclass *class;
01852    struct ao2_iterator i;
01853 
01854    switch (cmd) {
01855    case CLI_INIT:
01856       e->command = "moh show classes";
01857       e->usage =
01858          "Usage: moh show classes\n"
01859          "       Lists all MusicOnHold classes.\n";
01860       return NULL;
01861    case CLI_GENERATE:
01862       return NULL;
01863    }
01864 
01865    if (a->argc != e->args)
01866       return CLI_SHOWUSAGE;
01867 
01868    i = ao2_iterator_init(mohclasses, 0);
01869    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01870       ast_cli(a->fd, "Class: %s\n", class->name);
01871       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01872       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01873       if (ast_test_flag(class, MOH_CUSTOM)) {
01874          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01875       }
01876       if (strcasecmp(class->mode, "files")) {
01877          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01878       }
01879    }
01880    ao2_iterator_destroy(&i);
01881 
01882    return CLI_SUCCESS;
01883 }

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

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

01812 {
01813    struct mohclass *class;
01814    struct ao2_iterator i;
01815 
01816    switch (cmd) {
01817    case CLI_INIT:
01818       e->command = "moh show files";
01819       e->usage =
01820          "Usage: moh show files\n"
01821          "       Lists all loaded file-based MusicOnHold classes and their\n"
01822          "       files.\n";
01823       return NULL;
01824    case CLI_GENERATE:
01825       return NULL;
01826    }
01827 
01828    if (a->argc != e->args)
01829       return CLI_SHOWUSAGE;
01830 
01831    i = ao2_iterator_init(mohclasses, 0);
01832    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01833       int x;
01834 
01835       if (!class->total_files) {
01836          continue;
01837       }
01838 
01839       ast_cli(a->fd, "Class: %s\n", class->name);
01840       for (x = 0; x < class->total_files; x++) {
01841          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01842       }
01843    }
01844    ao2_iterator_destroy(&i);
01845 
01846    return CLI_SUCCESS;
01847 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1187 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), errno, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and monmp3thread().

Referenced by _moh_register().

01188 {
01189    if (!strcasecmp(class->mode, "custom")) {
01190       ast_set_flag(class, MOH_CUSTOM);
01191    } else if (!strcasecmp(class->mode, "mp3nb")) {
01192       ast_set_flag(class, MOH_SINGLE);
01193    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01194       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01195    } else if (!strcasecmp(class->mode, "quietmp3")) {
01196       ast_set_flag(class, MOH_QUIET);
01197    }
01198 
01199    class->srcfd = -1;
01200 
01201    if (!(class->timer = ast_timer_open())) {
01202       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01203       return -1;
01204    }
01205    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01206       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01207       ast_timer_close(class->timer);
01208       class->timer = NULL;
01209    }
01210 
01211    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01212       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01213       if (class->timer) {
01214          ast_timer_close(class->timer);
01215          class->timer = NULL;
01216       }
01217       return -1;
01218    }
01219 
01220    return 0;
01221 }

static int init_files_class ( struct mohclass class  )  [static]

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

01125 {
01126    int res;
01127 
01128    res = moh_scan_files(class);
01129 
01130    if (res < 0) {
01131       return -1;
01132    }
01133 
01134    if (!res) {
01135       if (option_verbose > 2) {
01136          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01137                class->dir, class->name);
01138       }
01139       return -1;
01140    }
01141 
01142 #if 0
01143    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01144    if (strchr(class->args, 'r')) {
01145       ast_set_flag(class, MOH_RANDOMIZE);
01146    }
01147 #endif
01148 
01149    return 0;
01150 }

static int load_module ( void   )  [static]

Definition at line 1907 of file res_musiconhold.c.

References ao2_t_container_alloc, ARRAY_LEN, ast_check_realtime(), ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application_xml, ast_register_atexit(), 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().

01908 {
01909    int res;
01910 
01911    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01912       return AST_MODULE_LOAD_DECLINE;
01913    }
01914 
01915    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01916       ast_log(LOG_WARNING, "No music on hold classes configured, "
01917             "disabling music on hold.\n");
01918    } else {
01919       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01920             local_ast_moh_cleanup);
01921    }
01922 
01923    res = ast_register_application_xml(play_moh, play_moh_exec);
01924    ast_register_atexit(ast_moh_destroy);
01925    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01926    if (!res)
01927       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01928    if (!res)
01929       res = ast_register_application_xml(set_moh, set_moh_exec);
01930    if (!res)
01931       res = ast_register_application_xml(start_moh, start_moh_exec);
01932    if (!res)
01933       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01934 
01935    return AST_MODULE_LOAD_SUCCESS;
01936 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1669 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_check_realtime(), 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_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, HANDLE_REF, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_register, moh_rescan_files(), 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().

01670 {
01671    struct ast_config *cfg;
01672    struct ast_variable *var;
01673    struct mohclass *class; 
01674    char *cat;
01675    int numclasses = 0;
01676    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01677 
01678    cfg = ast_config_load("musiconhold.conf", config_flags);
01679 
01680    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01681       if (ast_check_realtime("musiconhold") && reload) {
01682          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01683          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01684       }
01685       return 0;
01686    }
01687    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01688       moh_rescan_files();
01689       return 0;
01690    }
01691 
01692    if (reload) {
01693       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01694    }
01695 
01696    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01697 
01698    cat = ast_category_browse(cfg, NULL);
01699    for (; cat; cat = ast_category_browse(cfg, cat)) {
01700       /* Setup common options from [general] section */
01701       if (!strcasecmp(cat, "general")) {
01702          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01703             if (!strcasecmp(var->name, "cachertclasses")) {
01704                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01705             } else {
01706                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01707             }
01708          }
01709       }
01710       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01711       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01712             !strcasecmp(cat, "general")) {
01713          continue;
01714       }
01715 
01716       if (!(class = moh_class_malloc())) {
01717          break;
01718       }
01719 
01720       ast_copy_string(class->name, cat, sizeof(class->name));  
01721       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01722          if (!strcasecmp(var->name, "mode"))
01723             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01724          else if (!strcasecmp(var->name, "directory"))
01725             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01726          else if (!strcasecmp(var->name, "application"))
01727             ast_copy_string(class->args, var->value, sizeof(class->args));
01728          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01729             class->digit = *var->value;
01730          else if (!strcasecmp(var->name, "random"))
01731             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01732          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01733             ast_set_flag(class, MOH_RANDOMIZE);
01734          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01735             ast_set_flag(class, MOH_SORTALPHA);
01736          else if (!strcasecmp(var->name, "format")) {
01737             class->format = ast_getformatbyname(var->value);
01738             if (!class->format) {
01739                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01740                class->format = AST_FORMAT_SLINEAR;
01741             }
01742          }
01743       }
01744 
01745       if (ast_strlen_zero(class->dir)) {
01746          if (!strcasecmp(class->mode, "custom")) {
01747             strcpy(class->dir, "nodir");
01748          } else {
01749             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01750             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01751             continue;
01752          }
01753       }
01754       if (ast_strlen_zero(class->mode)) {
01755          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01756          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01757          continue;
01758       }
01759       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01760          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01761          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01762          continue;
01763       }
01764 
01765       /* Don't leak a class when it's already registered */
01766       if (!moh_register(class, reload, HANDLE_REF)) {
01767          numclasses++;
01768       }
01769    }
01770 
01771    ast_config_destroy(cfg);
01772 
01773    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01774          moh_classes_delete_marked, NULL, "Purge marked classes");
01775 
01776    return numclasses;
01777 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1281 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01282 {
01283    struct moh_files_state *state = chan->music_state;
01284 
01285    if (state) {
01286       if (state->class) {
01287          /* This should never happen.  We likely just leaked some resource. */
01288          state->class =
01289             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01290          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01291       }
01292       ast_free(chan->music_state);
01293       chan->music_state = NULL;
01294       /* Only held a module reference if we had a music state */
01295       ast_module_unref(ast_module_info->self);
01296    }
01297 }

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

Definition at line 1323 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_manager_event, ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_true(), ast_variables_destroy(), moh_files_state::class, mohclass::digit, mohclass::dir, DONT_UNREF, errno, EVENT_FLAG_CALL, mohclass::format, get_mohbyname, 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, mohclass::name, ast_variable::name, ast_variable::next, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, mohclass::total_files, ast_variable::value, and var.

Referenced by load_module(), and reload().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1543 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_manager_event, EVENT_FLAG_CALL, ast_channel::music_state, and ast_channel::stream.

Referenced by load_module(), and reload().

01544 {
01545    ast_clear_flag(chan, AST_FLAG_MOH);
01546    ast_deactivate_generator(chan);
01547 
01548    ast_channel_lock(chan);
01549    if (chan->music_state) {
01550       if (chan->stream) {
01551          ast_closestream(chan->stream);
01552          chan->stream = NULL;
01553       }
01554    }
01555 
01556    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01557       "State: Stop\r\n"
01558       "Channel: %s\r\n"
01559       "UniqueID: %s\r\n",
01560       chan->name, chan->uniqueid);
01561    ast_channel_unlock(chan);
01562 }

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

Definition at line 1007 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

01008 {
01009    if (!class->allowed_files) {
01010       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
01011          return -1;
01012       class->allowed_files = INITIAL_NUM_FILES;
01013    } else if (class->total_files == class->allowed_files) {
01014       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
01015          class->allowed_files = 0;
01016          class->total_files = 0;
01017          return -1;
01018       }
01019       class->allowed_files *= 2;
01020    }
01021 
01022    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
01023       return -1;
01024 
01025    class->total_files++;
01026 
01027    return 0;
01028 }

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

Definition at line 936 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, mohclass_unref, ast_channel::music_state, mohclass::name, mohdata::origwfmt, and ast_channel::writeformat.

00937 {
00938    struct mohdata *res;
00939    struct mohclass *class = params;
00940    struct moh_files_state *state;
00941 
00942    /* Initiating music_state for current channel. Channel should know name of moh class */
00943    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00944       chan->music_state = state;
00945       ast_module_ref(ast_module_info->self);
00946    } else {
00947       state = chan->music_state;
00948       if (!state) {
00949          return NULL;
00950       }
00951       if (state->class) {
00952          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00953          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00954       }
00955       memset(state, 0, sizeof(*state));
00956    }
00957 
00958    if ((res = mohalloc(class))) {
00959       res->origwfmt = chan->writeformat;
00960       if (ast_set_write_format(chan, class->format)) {
00961          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00962          moh_release(NULL, res);
00963          res = NULL;
00964       } else {
00965          state->class = mohclass_ref(class, "Placing reference into state container");
00966       }
00967       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00968    }
00969    return res;
00970 }

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

Definition at line 1898 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01899 {
01900    struct mohclass *class = obj, *class2 = arg;
01901 
01902    return strcasecmp(class->name, class2->name) ? 0 :
01903       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01904       CMP_MATCH | CMP_STOP;
01905 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1564 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

01565 {
01566    struct mohclass *class = obj;
01567    struct mohdata *member;
01568    pthread_t tid = 0;
01569 
01570    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01571 
01572    ao2_lock(class);
01573    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01574       free(member);
01575    }
01576    ao2_unlock(class);
01577 
01578    /* Kill the thread first, so it cannot restart the child process while the
01579     * class is being destroyed */
01580    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01581       tid = class->thread;
01582       class->thread = AST_PTHREADT_NULL;
01583       pthread_cancel(tid);
01584       /* We'll collect the exit status later, after we ensure all the readers
01585        * are dead. */
01586    }
01587 
01588    if (class->pid > 1) {
01589       char buff[8192];
01590       int bytes, tbytes = 0, stime = 0, pid = 0;
01591 
01592       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01593 
01594       stime = time(NULL) + 2;
01595       pid = class->pid;
01596       class->pid = 0;
01597 
01598       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01599        * to give the process a reason and time enough to kill off its
01600        * children. */
01601       do {
01602          if (killpg(pid, SIGHUP) < 0) {
01603             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01604          }
01605          usleep(100000);
01606          if (killpg(pid, SIGTERM) < 0) {
01607             if (errno == ESRCH) {
01608                break;
01609             }
01610             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01611          }
01612          usleep(100000);
01613          if (killpg(pid, SIGKILL) < 0) {
01614             if (errno == ESRCH) {
01615                break;
01616             }
01617             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01618          }
01619       } while (0);
01620 
01621       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01622             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01623          tbytes = tbytes + bytes;
01624       }
01625 
01626       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01627 
01628       close(class->srcfd);
01629       class->srcfd = -1;
01630    }
01631 
01632    if (class->filearray) {
01633       int i;
01634       for (i = 0; i < class->total_files; i++) {
01635          free(class->filearray[i]);
01636       }
01637       free(class->filearray);
01638       class->filearray = NULL;
01639    }
01640 
01641    if (class->timer) {
01642       ast_timer_close(class->timer);
01643       class->timer = NULL;
01644    }
01645 
01646    /* Finally, collect the exit status of the monitor thread */
01647    if (tid > 0) {
01648       pthread_join(tid, NULL);
01649    }
01650 
01651 }

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

Definition at line 1891 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01892 {
01893    const struct mohclass *class = obj;
01894 
01895    return ast_str_case_hash(class->name);
01896 }

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

Definition at line 1948 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01949 {
01950    struct mohclass *class = obj;
01951 
01952    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01953 }

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

Definition at line 1653 of file res_musiconhold.c.

Referenced by load_moh_classes().

01654 {
01655    struct mohclass *class = obj;
01656 
01657    class->delete = 1;
01658 
01659    return 0;
01660 }

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

Definition at line 1662 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01663 {
01664    struct mohclass *class = obj;
01665 
01666    return class->delete ? CMP_MATCH : 0;
01667 }

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

Definition at line 1168 of file res_musiconhold.c.

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

Referenced by _moh_register().

01169 {
01170    if (!old || !new) {
01171       return -1;
01172    }
01173 
01174    if (strcmp(old->dir, new->dir)) {
01175       return -1;
01176    } else if (strcmp(old->mode, new->mode)) {
01177       return -1;
01178    } else if (strcmp(old->args, new->args)) {
01179       return -1;
01180    } else if (old->flags != new->flags) {
01181       return -1;
01182    }
01183 
01184    return 0;
01185 }

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

Definition at line 447 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00448 {
00449    char *digit = arg;
00450    struct mohclass *class = obj;
00451 
00452    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00453 }

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

Definition at line 405 of file res_musiconhold.c.

References ast_calloc, ast_copy_string(), ast_log(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, moh_files_state::class, LOG_WARNING, MOH_RANDOMIZE, mohclass_ref, mohclass_unref, 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.

00406 {
00407    struct moh_files_state *state;
00408    struct mohclass *class = params;
00409 
00410    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00411       chan->music_state = state;
00412       ast_module_ref(ast_module_info->self);
00413    } else {
00414       state = chan->music_state;
00415       if (!state) {
00416          return NULL;
00417       }
00418       if (state->class) {
00419          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00420          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00421       }
00422    }
00423 
00424    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00425     * malloc may allocate a different class to the same memory block.  This
00426     * might only happen when two reloads are generated in a short period of
00427     * time, but it's still important to protect against.
00428     * PROG: Compare the quick operation first, to save CPU. */
00429    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00430       memset(state, 0, sizeof(*state));
00431       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00432          state->pos = ast_random() % class->total_files;
00433       }
00434    }
00435 
00436    state->class = mohclass_ref(class, "Reffing music class for channel");
00437    state->origwfmt = chan->writeformat;
00438    /* For comparison on restart of MOH (see above) */
00439    ast_copy_string(state->name, class->name, sizeof(state->name));
00440    state->save_total = class->total_files;
00441 
00442    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00443    
00444    return chan->music_state;
00445 }

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

Definition at line 371 of file res_musiconhold.c.

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

00372 {
00373    struct moh_files_state *state = chan->music_state;
00374    struct ast_frame *f = NULL;
00375    int res = 0;
00376 
00377    state->sample_queue += samples;
00378 
00379    while (state->sample_queue > 0) {
00380       ast_channel_lock(chan);
00381       if ((f = moh_files_readframe(chan))) {
00382          /* We need to be sure that we unlock
00383           * the channel prior to calling
00384           * ast_write. Otherwise, the recursive locking
00385           * that occurs can cause deadlocks when using
00386           * indirect channels, like local channels
00387           */
00388          ast_channel_unlock(chan);
00389          state->samples += f->samples;
00390          state->sample_queue -= f->samples;
00391          res = ast_write(chan, f);
00392          ast_frfree(f);
00393          if (res < 0) {
00394             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00395             return -1;
00396          }
00397       } else {
00398          ast_channel_unlock(chan);
00399          return -1;  
00400       }
00401    }
00402    return res;
00403 }

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

Definition at line 359 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00360 {
00361    struct ast_frame *f = NULL;
00362    
00363    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00364       if (!ast_moh_files_next(chan))
00365          f = ast_readframe(chan->stream);
00366    }
00367 
00368    return f;
00369 }

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

Definition at line 255 of file res_musiconhold.c.

References ast_closestream(), ast_getformatname(), 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.

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

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

Definition at line 972 of file res_musiconhold.c.

References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), 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.

00973 {
00974    struct mohdata *moh = data;
00975    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00976    int res;
00977 
00978    len = ast_codec_get_len(moh->parent->format, samples);
00979 
00980    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00981       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00982       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00983    }
00984    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00985    if (res <= 0)
00986       return 0;
00987 
00988    moh->f.datalen = res;
00989    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00990    moh->f.samples = ast_codec_get_samples(&moh->f);
00991 
00992    if (ast_write(chan, &moh->f) < 0) {
00993       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00994       return -1;
00995    }
00996 
00997    return 0;
00998 }

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

Definition at line 461 of file res_musiconhold.c.

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

00462 {
00463    struct mohclass *class;
00464    const char *classname = NULL;
00465 
00466    if ((class = get_mohbydigit(digit))) {
00467       classname = ast_strdupa(class->name);
00468       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00469       ast_string_field_set(chan,musicclass,classname);
00470       ast_moh_stop(chan);
00471       ast_moh_start(chan, classname, NULL);
00472    }
00473 }

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

Definition at line 901 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, moh_files_state::class, LOG_WARNING, mohclass::members, moh, mohclass_unref, ast_channel::music_state, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

00902 {
00903    struct mohdata *moh = data;
00904    struct mohclass *class = moh->parent;
00905    format_t oldwfmt;
00906 
00907    ao2_lock(class);
00908    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00909    ao2_unlock(class);
00910    
00911    close(moh->pipe[0]);
00912    close(moh->pipe[1]);
00913 
00914    oldwfmt = moh->origwfmt;
00915 
00916    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00917 
00918    ast_free(moh);
00919 
00920    if (chan) {
00921       struct moh_files_state *state;
00922 
00923       state = chan->music_state;
00924       if (state && state->class) {
00925          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00926       }
00927       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00928          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00929                chan->name, ast_getformatname(oldwfmt));
00930       }
00931 
00932       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00933    }
00934 }

static void moh_rescan_files ( void   )  [static]

Definition at line 1152 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, mohclass::mode, and moh_scan_files().

Referenced by load_moh_classes().

01152                                    {
01153    struct ao2_iterator i;
01154    struct mohclass *c;
01155 
01156    i = ao2_iterator_init(mohclasses, 0);
01157 
01158    while ((c = ao2_iterator_next(&i))) {
01159       if (!strcasecmp(c->mode, "files")) {
01160          moh_scan_files(c);
01161       }
01162       ao2_ref(c, -1);
01163    }
01164 
01165    ao2_iterator_destroy(&i);
01166 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 1040 of file res_musiconhold.c.

References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, 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(), local_ast_moh_start(), and moh_rescan_files().

01040                                                   {
01041 
01042    DIR *files_DIR;
01043    struct dirent *files_dirent;
01044    char dir_path[PATH_MAX];
01045    char path[PATH_MAX];
01046    char filepath[PATH_MAX];
01047    char *ext;
01048    struct stat statbuf;
01049    int i;
01050 
01051    if (class->dir[0] != '/') {
01052       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01053       strncat(dir_path, "/", sizeof(dir_path) - 1);
01054       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01055    } else {
01056       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01057    }
01058    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01059    files_DIR = opendir(dir_path);
01060    if (!files_DIR) {
01061       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01062       return -1;
01063    }
01064 
01065    for (i = 0; i < class->total_files; i++)
01066       ast_free(class->filearray[i]);
01067 
01068    class->total_files = 0;
01069    if (!getcwd(path, sizeof(path))) {
01070       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01071       closedir(files_DIR);
01072       return -1;
01073    }
01074    if (chdir(dir_path) < 0) {
01075       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01076       closedir(files_DIR);
01077       return -1;
01078    }
01079    while ((files_dirent = readdir(files_DIR))) {
01080       /* The file name must be at least long enough to have the file type extension */
01081       if ((strlen(files_dirent->d_name) < 4))
01082          continue;
01083 
01084       /* Skip files that starts with a dot */
01085       if (files_dirent->d_name[0] == '.')
01086          continue;
01087 
01088       /* Skip files without extensions... they are not audio */
01089       if (!strchr(files_dirent->d_name, '.'))
01090          continue;
01091 
01092       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01093 
01094       if (stat(filepath, &statbuf))
01095          continue;
01096 
01097       if (!S_ISREG(statbuf.st_mode))
01098          continue;
01099 
01100       if ((ext = strrchr(filepath, '.')))
01101          *ext = '\0';
01102 
01103       /* if the file is present in multiple formats, ensure we only put it into the list once */
01104       for (i = 0; i < class->total_files; i++)
01105          if (!strcmp(filepath, class->filearray[i]))
01106             break;
01107 
01108       if (i == class->total_files) {
01109          if (moh_add_file(class, filepath))
01110             break;
01111       }
01112    }
01113 
01114    closedir(files_DIR);
01115    if (chdir(path) < 0) {
01116       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01117       return -1;
01118    }
01119    if (ast_test_flag(class, MOH_SORTALPHA))
01120       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01121    return class->total_files;
01122 }

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

Definition at line 1030 of file res_musiconhold.c.

Referenced by moh_scan_files().

01031 {
01032    char *s1, *s2;
01033 
01034    s1 = ((char **)i1)[0];
01035    s2 = ((char **)i2)[0];
01036 
01037    return strcasecmp(s1, s2);
01038 }

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

Definition at line 868 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(), ast_frame_subclass::codec, 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().

00869 {
00870    struct mohdata *moh;
00871    long flags; 
00872    
00873    if (!(moh = ast_calloc(1, sizeof(*moh))))
00874       return NULL;
00875    
00876    if (pipe(moh->pipe)) {
00877       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00878       ast_free(moh);
00879       return NULL;
00880    }
00881 
00882    /* Make entirely non-blocking */
00883    flags = fcntl(moh->pipe[0], F_GETFL);
00884    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00885    flags = fcntl(moh->pipe[1], F_GETFL);
00886    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00887 
00888    moh->f.frametype = AST_FRAME_VOICE;
00889    moh->f.subclass.codec = cl->format;
00890    moh->f.offset = AST_FRIENDLY_OFFSET;
00891 
00892    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00893 
00894    ao2_lock(cl);
00895    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00896    ao2_unlock(cl);
00897    
00898    return moh;
00899 }

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

Definition at line 624 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_codec_get_len(), ast_debug, AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_poll, ast_samp2tv(), ast_timer_ack(), ast_timer_fd(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), errno, len(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

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

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

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

00739 {
00740    char *parse;
00741    char *class;
00742    int timeout = -1;
00743    int res;
00744    AST_DECLARE_APP_ARGS(args,
00745       AST_APP_ARG(class);
00746       AST_APP_ARG(duration);
00747    );
00748 
00749    parse = ast_strdupa(data);
00750 
00751    AST_STANDARD_APP_ARGS(args, parse);
00752 
00753    if (!ast_strlen_zero(args.duration)) {
00754       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00755          timeout *= 1000;
00756       } else {
00757          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00758       }
00759    }
00760 
00761    class = S_OR(args.class, NULL);
00762    if (ast_moh_start(chan, class, NULL)) {
00763       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00764       return 0;
00765    }
00766 
00767    if (timeout > 0)
00768       res = ast_safe_sleep(chan, timeout);
00769    else {
00770       while (!(res = ast_safe_sleep(chan, 10000)));
00771    }
00772 
00773    ast_moh_stop(chan);
00774 
00775    return res;
00776 }

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

Definition at line 801 of file res_musiconhold.c.

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

Referenced by load_module().

00802 {
00803    static int deprecation_warning = 0;
00804 
00805    if (!deprecation_warning) {
00806       deprecation_warning = 1;
00807       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00808    }
00809 
00810    if (ast_strlen_zero(data)) {
00811       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00812       return -1;
00813    }
00814    ast_string_field_set(chan, musicclass, data);
00815    return 0;
00816 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 483 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, and MPG_123.

Referenced by monmp3thread().

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

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

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

00819 {
00820    char *parse;
00821    char *class;
00822    AST_DECLARE_APP_ARGS(args,
00823       AST_APP_ARG(class);
00824    );
00825 
00826    parse = ast_strdupa(data);
00827 
00828    AST_STANDARD_APP_ARGS(args, parse);
00829 
00830    class = S_OR(args.class, NULL);
00831    if (ast_moh_start(chan, class, NULL)) 
00832       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00833 
00834    return 0;
00835 }

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

Definition at line 837 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00838 {
00839    ast_moh_stop(chan);
00840 
00841    return 0;
00842 }

static int unload_module ( void   )  [static]

Definition at line 1955 of file res_musiconhold.c.

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

01956 {
01957    int res = 0;
01958    struct mohclass *class = NULL;
01959 
01960    /* XXX This check shouldn't be required if module ref counting was being used
01961     * properly ... */
01962    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01963       class = mohclass_unref(class, "unref of class from module unload callback");
01964       res = -1;
01965    }
01966 
01967    if (res < 0) {
01968       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01969       return res;
01970    }
01971 
01972    ast_uninstall_music_functions();
01973 
01974    ast_moh_destroy();
01975    res = ast_unregister_application(play_moh);
01976    res |= ast_unregister_application(wait_moh);
01977    res |= ast_unregister_application(set_moh);
01978    res |= ast_unregister_application(start_moh);
01979    res |= ast_unregister_application(stop_moh);
01980    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01981    ast_unregister_atexit(ast_moh_destroy);
01982 
01983    return res;
01984 }

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

Definition at line 778 of file res_musiconhold.c.

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

Referenced by load_module().

00779 {
00780    static int deprecation_warning = 0;
00781    int res;
00782 
00783    if (!deprecation_warning) {
00784       deprecation_warning = 1;
00785       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00786    }
00787 
00788    if (!data || !atoi(data)) {
00789       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00790       return -1;
00791    }
00792    if (ast_moh_start(chan, NULL, NULL)) {
00793       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00794       return 0;
00795    }
00796    res = ast_safe_sleep(chan, atoi(data) * 1000);
00797    ast_moh_stop(chan);
00798    return res;
00799 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static]

Definition at line 1991 of file res_musiconhold.c.

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

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

global MOH_ flags

Definition at line 179 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 475 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 219 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 1000 of file res_musiconhold.c.

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

Definition at line 147 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 153 of file res_musiconhold.c.

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

Definition at line 149 of file res_musiconhold.c.

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

Definition at line 150 of file res_musiconhold.c.

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

Definition at line 151 of file res_musiconhold.c.

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

Definition at line 148 of file res_musiconhold.c.


Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1