Wed Aug 7 17:16:11 2019

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 838 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 1294 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 1219 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 local_ast_moh_start(), 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 1980 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

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

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

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

Definition at line 1296 of file res_musiconhold.c.

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

01297 {
01298    struct mohclass *class;
01299 
01300    if ((class =
01301 #ifdef REF_DEBUG
01302          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01303 #elif defined(__AST_DEBUG_MALLOC)
01304          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01305 #else
01306          ao2_alloc(sizeof(*class), moh_class_destructor)
01307 #endif
01308       )) {
01309       class->format = AST_FORMAT_SLINEAR;
01310       class->srcfd = -1;
01311    }
01312 
01313    return class;
01314 }

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

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

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

static void ast_moh_destroy ( void   )  [static]

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

01769 {
01770    ast_verb(2, "Destroying musiconhold processes\n");
01771    if (mohclasses) {
01772       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01773       ao2_ref(mohclasses, -1);
01774       mohclasses = NULL;
01775    }
01776 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

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

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

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

Definition at line 450 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00451 {
00452    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00453 }

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

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

01779 {
01780    switch (cmd) {
01781    case CLI_INIT:
01782       e->command = "moh reload";
01783       e->usage =
01784          "Usage: moh reload\n"
01785          "       Reloads the MusicOnHold module.\n"
01786          "       Alias for 'module reload res_musiconhold.so'\n";
01787       return NULL;
01788    case CLI_GENERATE:
01789       return NULL;
01790    }
01791 
01792    if (a->argc != e->args)
01793       return CLI_SHOWUSAGE;
01794 
01795    reload();
01796 
01797    return CLI_SUCCESS;
01798 }

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

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

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

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

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

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

static int init_app_class ( struct mohclass class  )  [static]

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

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

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1124 of file res_musiconhold.c.

References ast_verbose, 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    return 0;
01143 }

static int load_module ( void   )  [static]

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

01897 {
01898    int res;
01899 
01900    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01901       return AST_MODULE_LOAD_DECLINE;
01902    }
01903 
01904    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01905       ast_log(LOG_WARNING, "No music on hold classes configured, "
01906             "disabling music on hold.\n");
01907    } else {
01908       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01909             local_ast_moh_cleanup);
01910    }
01911 
01912    res = ast_register_application_xml(play_moh, play_moh_exec);
01913    ast_register_atexit(ast_moh_destroy);
01914    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01915    if (!res)
01916       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01917    if (!res)
01918       res = ast_register_application_xml(set_moh, set_moh_exec);
01919    if (!res)
01920       res = ast_register_application_xml(start_moh, start_moh_exec);
01921    if (!res)
01922       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01923 
01924    return AST_MODULE_LOAD_SUCCESS;
01925 }

static int load_moh_classes ( int  reload  )  [static]

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

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

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

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

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

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

Definition at line 1316 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_ref, 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().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

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

01533 {
01534    ast_clear_flag(chan, AST_FLAG_MOH);
01535    ast_deactivate_generator(chan);
01536 
01537    ast_channel_lock(chan);
01538    if (chan->music_state) {
01539       if (chan->stream) {
01540          ast_closestream(chan->stream);
01541          chan->stream = NULL;
01542       }
01543    }
01544 
01545    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01546       "State: Stop\r\n"
01547       "Channel: %s\r\n"
01548       "UniqueID: %s\r\n",
01549       chan->name, chan->uniqueid);
01550    ast_channel_unlock(chan);
01551 }

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

Definition at line 1001 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

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

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

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

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

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

Definition at line 1887 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01888 {
01889    struct mohclass *class = obj, *class2 = arg;
01890 
01891    return strcasecmp(class->name, class2->name) ? 0 :
01892       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01893       CMP_MATCH | CMP_STOP;
01894 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1553 of file res_musiconhold.c.

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

Referenced by _moh_class_malloc().

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

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

Definition at line 1880 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01881 {
01882    const struct mohclass *class = obj;
01883 
01884    return ast_str_case_hash(class->name);
01885 }

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

Definition at line 1937 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01938 {
01939    struct mohclass *class = obj;
01940 
01941    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01942 }

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

Definition at line 1642 of file res_musiconhold.c.

Referenced by load_moh_classes().

01643 {
01644    struct mohclass *class = obj;
01645 
01646    class->delete = 1;
01647 
01648    return 0;
01649 }

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

Definition at line 1651 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01652 {
01653    struct mohclass *class = obj;
01654 
01655    return class->delete ? CMP_MATCH : 0;
01656 }

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

Definition at line 1161 of file res_musiconhold.c.

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

Referenced by _moh_register().

01162 {
01163    if (!old || !new) {
01164       return -1;
01165    }
01166 
01167    if (strcmp(old->dir, new->dir)) {
01168       return -1;
01169    } else if (strcmp(old->mode, new->mode)) {
01170       return -1;
01171    } else if (strcmp(old->args, new->args)) {
01172       return -1;
01173    } else if (old->flags != new->flags) {
01174       return -1;
01175    }
01176 
01177    return 0;
01178 }

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

Definition at line 441 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00442 {
00443    char *digit = arg;
00444    struct mohclass *class = obj;
00445 
00446    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00447 }

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

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

00403 {
00404    struct moh_files_state *state;
00405    struct mohclass *class = params;
00406 
00407    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00408       chan->music_state = state;
00409       ast_module_ref(ast_module_info->self);
00410    } else {
00411       state = chan->music_state;
00412       if (!state) {
00413          return NULL;
00414       }
00415       if (state->class) {
00416          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00417          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00418       }
00419    }
00420 
00421    /* Resume MOH from where we left off last time or start from scratch? */
00422    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00423       /* Start MOH from scratch. */
00424       memset(state, 0, sizeof(*state));
00425       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00426          state->pos = ast_random() % class->total_files;
00427       }
00428    }
00429 
00430    state->class = mohclass_ref(class, "Reffing music class for channel");
00431    state->origwfmt = chan->writeformat;
00432    /* For comparison on restart of MOH (see above) */
00433    ast_copy_string(state->name, class->name, sizeof(state->name));
00434    state->save_total = class->total_files;
00435 
00436    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00437    
00438    return chan->music_state;
00439 }

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

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

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

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

Definition at line 355 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00356 {
00357    struct ast_frame *f = NULL;
00358    
00359    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00360       if (!ast_moh_files_next(chan))
00361          f = ast_readframe(chan->stream);
00362    }
00363 
00364    return f;
00365 }

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

Definition at line 251 of file res_musiconhold.c.

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

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

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

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

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

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

Definition at line 455 of file res_musiconhold.c.

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

00456 {
00457    struct mohclass *class;
00458    const char *classname = NULL;
00459 
00460    if ((class = get_mohbydigit(digit))) {
00461       classname = ast_strdupa(class->name);
00462       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00463       ast_string_field_set(chan,musicclass,classname);
00464       ast_moh_stop(chan);
00465       ast_moh_start(chan, classname, NULL);
00466    }
00467 }

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

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

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

static void moh_rescan_files ( void   )  [static]

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

01145                                    {
01146    struct ao2_iterator i;
01147    struct mohclass *c;
01148 
01149    i = ao2_iterator_init(mohclasses, 0);
01150 
01151    while ((c = ao2_iterator_next(&i))) {
01152       if (!strcasecmp(c->mode, "files")) {
01153          moh_scan_files(c);
01154       }
01155       ao2_ref(c, -1);
01156    }
01157 
01158    ao2_iterator_destroy(&i);
01159 }

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

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

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

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

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

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

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

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

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

Definition at line 795 of file res_musiconhold.c.

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

Referenced by load_module().

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

static int spawn_mp3 ( struct mohclass class  )  [static]

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

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

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

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

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

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

Definition at line 831 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00832 {
00833    ast_moh_stop(chan);
00834 
00835    return 0;
00836 }

static int unload_module ( void   )  [static]

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

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

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

Definition at line 772 of file res_musiconhold.c.

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

Referenced by load_module().

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


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

Definition at line 1980 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 1874 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 469 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 994 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 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1