Wed Oct 28 13:33:30 2009

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include "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 dependency graph for res_musiconhold.c:

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

#define INITIAL_NUM_FILES   8
#define LOCAL_MPG_123   "/usr/local/bin/mpg123"
#define MAX_MP3S   256
#define MOH_CACHERTCLASSES   (1 << 5)
#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#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 void ast_moh_destroy (void)
static int ast_moh_files_next (struct ast_channel *chan)
static struct mohclassget_mohbydigit (char digit)
static struct mohclassget_mohbyname (const char *name, int warn)
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 struct mohclassmoh_class_malloc (void)
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 int moh_register (struct mohclass *moh, int reload, int unref)
static void moh_release (struct ast_channel *chan, void *data)
static int moh_scan_files (struct mohclass *class)
static int moh_sort_compare (const void *i1, const void *i2)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int play_moh_exec (struct ast_channel *chan, 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_DEFAULT , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_moh []
static struct ast_flags global_flags [1] = {{0}}
static struct ast_generator moh_file_stream
static struct ao2_containermohclasses
static struct ast_generator mohgen
static 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 INITIAL_NUM_FILES   8

Definition at line 68 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 209 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 211 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 165 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_QUIET   (1 << 0)

Definition at line 159 of file res_musiconhold.c.

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

#define MOH_RANDOMIZE   (1 << 3)

#define MOH_SINGLE   (1 << 1)

Definition at line 160 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 163 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 215 of file res_musiconhold.c.

Referenced by 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 210 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1767 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1767 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1562 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01563 {
01564    ast_verb(2, "Destroying musiconhold processes\n");
01565    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01566 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 246 of file res_musiconhold.c.

References ast_closestream(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, ast_channel::name, 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().

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

static struct mohclass* get_mohbydigit ( char  digit  )  [static, read]

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

Definition at line 376 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00377 {
00378    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00379 }

static struct mohclass* get_mohbyname ( const char *  name,
int  warn 
) [static, read]

Definition at line 737 of file res_musiconhold.c.

References ao2_t_find, ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, and mohclass::name.

Referenced by local_ast_moh_start(), and moh_register().

00738 {
00739    struct mohclass *moh = NULL;
00740    struct mohclass tmp_class = {
00741       .flags = 0,
00742    };
00743 
00744    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00745 
00746    moh = ao2_t_find(mohclasses, &tmp_class, 0, "Finding by name");
00747 
00748    if (!moh && warn) {
00749       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00750    }
00751 
00752    return moh;
00753 }

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

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

01569 {
01570    switch (cmd) {
01571    case CLI_INIT:
01572       e->command = "moh reload";
01573       e->usage =
01574          "Usage: moh reload\n"
01575          "       Reloads the MusicOnHold module.\n"
01576          "       Alias for 'module reload res_musiconhold.so'\n";
01577       return NULL;
01578    case CLI_GENERATE:
01579       return NULL;
01580    }
01581 
01582    if (a->argc != e->args)
01583       return CLI_SHOWUSAGE;
01584 
01585    reload();
01586 
01587    return CLI_SUCCESS;
01588 }

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

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

01629 {
01630    struct mohclass *class;
01631    struct ao2_iterator i;
01632 
01633    switch (cmd) {
01634    case CLI_INIT:
01635       e->command = "moh show classes";
01636       e->usage =
01637          "Usage: moh show classes\n"
01638          "       Lists all MusicOnHold classes.\n";
01639       return NULL;
01640    case CLI_GENERATE:
01641       return NULL;
01642    }
01643 
01644    if (a->argc != e->args)
01645       return CLI_SHOWUSAGE;
01646 
01647    i = ao2_iterator_init(mohclasses, 0);
01648    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01649       ast_cli(a->fd, "Class: %s\n", class->name);
01650       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01651       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01652       if (ast_test_flag(class, MOH_CUSTOM)) {
01653          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01654       }
01655       if (strcasecmp(class->mode, "files")) {
01656          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01657       }
01658    }
01659    ao2_iterator_destroy(&i);
01660 
01661    return CLI_SUCCESS;
01662 }

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

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

01591 {
01592    struct mohclass *class;
01593    struct ao2_iterator i;
01594 
01595    switch (cmd) {
01596    case CLI_INIT:
01597       e->command = "moh show files";
01598       e->usage =
01599          "Usage: moh show files\n"
01600          "       Lists all loaded file-based MusicOnHold classes and their\n"
01601          "       files.\n";
01602       return NULL;
01603    case CLI_GENERATE:
01604       return NULL;
01605    }
01606 
01607    if (a->argc != e->args)
01608       return CLI_SHOWUSAGE;
01609 
01610    i = ao2_iterator_init(mohclasses, 0);
01611    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01612       int x;
01613 
01614       if (!class->total_files) {
01615          continue;
01616       }
01617 
01618       ast_cli(a->fd, "Class: %s\n", class->name);
01619       for (x = 0; x < class->total_files; x++) {
01620          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01621       }
01622    }
01623    ao2_iterator_destroy(&i);
01624 
01625    return CLI_SUCCESS;
01626 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1043 of file res_musiconhold.c.

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

Referenced by moh_register().

01044 {
01045 #ifdef HAVE_DAHDI
01046    int x;
01047 #endif
01048 
01049    if (!strcasecmp(class->mode, "custom")) {
01050       ast_set_flag(class, MOH_CUSTOM);
01051    } else if (!strcasecmp(class->mode, "mp3nb")) {
01052       ast_set_flag(class, MOH_SINGLE);
01053    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01054       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01055    } else if (!strcasecmp(class->mode, "quietmp3")) {
01056       ast_set_flag(class, MOH_QUIET);
01057    }
01058       
01059    class->srcfd = -1;
01060    class->pseudofd = -1;
01061 
01062 #ifdef HAVE_DAHDI
01063    /* Open /dev/zap/pseudo for timing...  Is
01064       there a better, yet reliable way to do this? */
01065    class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
01066    if (class->pseudofd < 0) {
01067       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
01068    } else {
01069       x = 320;
01070       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
01071    }
01072 #endif
01073 
01074    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01075       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01076       if (class->pseudofd > -1) {
01077          close(class->pseudofd);
01078          class->pseudofd = -1;
01079       }
01080       return -1;
01081    }
01082 
01083    return 0;
01084 }

static int init_files_class ( struct mohclass class  )  [static]

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

00999 {
01000    int res;
01001 
01002    res = moh_scan_files(class);
01003 
01004    if (res < 0) {
01005       return -1;
01006    }
01007 
01008    if (!res) {
01009       if (option_verbose > 2) {
01010          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01011                class->dir, class->name);
01012       }
01013       return -1;
01014    }
01015 
01016    if (strchr(class->args, 'r')) {
01017       ast_set_flag(class, MOH_RANDOMIZE);
01018    }
01019 
01020    return 0;
01021 }

static int load_module ( void   )  [static]

Definition at line 1684 of file res_musiconhold.c.

References ao2_t_container_alloc, ARRAY_LEN, 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().

01685 {
01686    int res;
01687 
01688    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01689       return AST_MODULE_LOAD_DECLINE;
01690    }
01691 
01692    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01693       ast_log(LOG_WARNING, "No music on hold classes configured, "
01694             "disabling music on hold.\n");
01695    } else {
01696       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01697             local_ast_moh_cleanup);
01698    }
01699 
01700    res = ast_register_application_xml(play_moh, play_moh_exec);
01701    ast_register_atexit(ast_moh_destroy);
01702    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01703    if (!res)
01704       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01705    if (!res)
01706       res = ast_register_application_xml(set_moh, set_moh_exec);
01707    if (!res)
01708       res = ast_register_application_xml(start_moh, start_moh_exec);
01709    if (!res)
01710       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01711 
01712    return AST_MODULE_LOAD_SUCCESS;
01713 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1460 of file res_musiconhold.c.

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

Referenced by load_module().

01461 {
01462    struct ast_config *cfg;
01463    struct ast_variable *var;
01464    struct mohclass *class; 
01465    char *cat;
01466    int numclasses = 0;
01467    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01468 
01469    cfg = ast_config_load("musiconhold.conf", config_flags);
01470 
01471    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01472       return 0;
01473    }
01474 
01475    if (reload) {
01476       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01477    }
01478    
01479    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01480 
01481    cat = ast_category_browse(cfg, NULL);
01482    for (; cat; cat = ast_category_browse(cfg, cat)) {
01483       /* Setup common options from [general] section */
01484       if (!strcasecmp(cat, "general")) {
01485          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01486             if (!strcasecmp(var->name, "cachertclasses")) {
01487                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01488             } else {
01489                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01490             }
01491          }
01492       }
01493       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01494       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01495             !strcasecmp(cat, "general")) {
01496          continue;
01497       }
01498 
01499       if (!(class = moh_class_malloc())) {
01500          break;
01501       }
01502 
01503       ast_copy_string(class->name, cat, sizeof(class->name));  
01504       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01505          if (!strcasecmp(var->name, "mode"))
01506             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01507          else if (!strcasecmp(var->name, "directory"))
01508             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01509          else if (!strcasecmp(var->name, "application"))
01510             ast_copy_string(class->args, var->value, sizeof(class->args));
01511          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01512             class->digit = *var->value;
01513          else if (!strcasecmp(var->name, "random"))
01514             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01515          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01516             ast_set_flag(class, MOH_RANDOMIZE);
01517          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01518             ast_set_flag(class, MOH_SORTALPHA);
01519          else if (!strcasecmp(var->name, "format")) {
01520             class->format = ast_getformatbyname(var->value);
01521             if (!class->format) {
01522                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01523                class->format = AST_FORMAT_SLINEAR;
01524             }
01525          }
01526       }
01527 
01528       if (ast_strlen_zero(class->dir)) {
01529          if (!strcasecmp(class->mode, "custom")) {
01530             strcpy(class->dir, "nodir");
01531          } else {
01532             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01533             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01534             continue;
01535          }
01536       }
01537       if (ast_strlen_zero(class->mode)) {
01538          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01539          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01540          continue;
01541       }
01542       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01543          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01544          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01545          continue;
01546       }
01547 
01548       /* Don't leak a class when it's already registered */
01549       if (!moh_register(class, reload, 1)) {
01550          numclasses++;
01551       }
01552    }
01553 
01554    ast_config_destroy(cfg);
01555 
01556    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01557          moh_classes_delete_marked, NULL, "Purge marked classes");
01558 
01559    return numclasses;
01560 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1135 of file res_musiconhold.c.

References ast_free, and ast_channel::music_state.

Referenced by load_module().

01136 {
01137    struct moh_files_state *state = chan->music_state;
01138 
01139    if (state) {
01140       ast_free(chan->music_state);
01141       chan->music_state = NULL;
01142    }
01143 }

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

Definition at line 1158 of file res_musiconhold.c.

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

Referenced by load_module().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1368 of file res_musiconhold.c.

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

Referenced by load_module().

01369 {
01370    struct moh_files_state *state = chan->music_state;
01371    ast_clear_flag(chan, AST_FLAG_MOH);
01372    ast_deactivate_generator(chan);
01373 
01374    if (state) {
01375       if (chan->stream) {
01376          ast_closestream(chan->stream);
01377          chan->stream = NULL;
01378       }
01379    }
01380 
01381    manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01382       "State: Stop\r\n"
01383       "Channel: %s\r\n"
01384       "UniqueID: %s\r\n",
01385       chan->name, chan->uniqueid);
01386 }

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

Definition at line 881 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

00882 {
00883    if (!class->allowed_files) {
00884       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00885          return -1;
00886       class->allowed_files = INITIAL_NUM_FILES;
00887    } else if (class->total_files == class->allowed_files) {
00888       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00889          class->allowed_files = 0;
00890          class->total_files = 0;
00891          return -1;
00892       }
00893       class->allowed_files *= 2;
00894    }
00895 
00896    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00897       return -1;
00898 
00899    class->total_files++;
00900 
00901    return 0;
00902 }

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

Definition at line 817 of file res_musiconhold.c.

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

00818 {
00819    struct mohdata *res;
00820    struct mohclass *class = params;
00821    struct moh_files_state *state;
00822 
00823    /* Initiating music_state for current channel. Channel should know name of moh class */
00824    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00825       chan->music_state = state;
00826       state->class = class;
00827    } else
00828       state = chan->music_state;
00829    if (state && state->class != class) {
00830       memset(state, 0, sizeof(*state));
00831       state->class = class;
00832    }
00833 
00834    if ((res = mohalloc(class))) {
00835       res->origwfmt = chan->writeformat;
00836       if (ast_set_write_format(chan, class->format)) {
00837          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00838          moh_release(NULL, res);
00839          res = NULL;
00840       }
00841       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00842    }
00843    return res;
00844 }

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

Definition at line 1677 of file res_musiconhold.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

01678 {
01679    struct mohclass *class = obj, *class2 = arg;
01680 
01681    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01682 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1388 of file res_musiconhold.c.

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

Referenced by moh_class_malloc().

01389 {
01390    struct mohclass *class = obj;
01391    struct mohdata *member;
01392 
01393    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01394 
01395    if (class->pid > 1) {
01396       char buff[8192];
01397       int bytes, tbytes = 0, stime = 0, pid = 0;
01398 
01399       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01400 
01401       stime = time(NULL) + 2;
01402       pid = class->pid;
01403       class->pid = 0;
01404 
01405       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01406        * to give the process a reason and time enough to kill off its
01407        * children. */
01408       killpg(pid, SIGHUP);
01409       usleep(100000);
01410       killpg(pid, SIGTERM);
01411       usleep(100000);
01412       killpg(pid, SIGKILL);
01413 
01414       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01415             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01416          tbytes = tbytes + bytes;
01417       }
01418 
01419       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01420 
01421       close(class->srcfd);
01422    }
01423 
01424    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01425       free(member);
01426    }
01427 
01428    if (class->thread) {
01429       pthread_cancel(class->thread);
01430       pthread_join(class->thread, NULL);
01431       class->thread = AST_PTHREADT_NULL;
01432    }
01433 
01434    if (class->filearray) {
01435       int i;
01436       for (i = 0; i < class->total_files; i++) {
01437          free(class->filearray[i]);
01438       }
01439       free(class->filearray);
01440       class->filearray = NULL;
01441    }
01442 }

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

Definition at line 1670 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01671 {
01672    const struct mohclass *class = obj;
01673 
01674    return ast_str_case_hash(class->name);
01675 }

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

Definition at line 1725 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01726 {
01727    struct mohclass *class = obj;
01728 
01729    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01730 }

static struct mohclass* moh_class_malloc ( void   )  [static, read]

Definition at line 1147 of file res_musiconhold.c.

References ao2_t_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().

Referenced by load_moh_classes(), and local_ast_moh_start().

01148 {
01149    struct mohclass *class;
01150 
01151    if ((class = ao2_t_alloc(sizeof(*class), moh_class_destructor, "Allocating new moh class"))) {
01152       class->format = AST_FORMAT_SLINEAR;
01153    }
01154 
01155    return class;
01156 }

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

Definition at line 1444 of file res_musiconhold.c.

Referenced by load_moh_classes().

01445 {
01446    struct mohclass *class = obj;
01447 
01448    class->delete = 1;
01449 
01450    return 0;
01451 }

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

Definition at line 1453 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01454 {
01455    struct mohclass *class = obj;
01456 
01457    return class->delete ? CMP_MATCH : 0;
01458 }

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

Definition at line 1024 of file res_musiconhold.c.

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

Referenced by moh_register().

01025 {
01026    if (!old || !new) {
01027       return -1;
01028    }
01029 
01030    if (strcmp(old->dir, new->dir)) {
01031       return -1;
01032    } else if (strcmp(old->mode, new->mode)) {
01033       return -1;
01034    } else if (strcmp(old->args, new->args)) {
01035       return -1;
01036    } else if (old->flags != new->flags) {
01037       return -1;
01038    }
01039 
01040    return 0;
01041 }

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

Definition at line 367 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00368 {
00369    char *digit = arg;
00370    struct mohclass *class = obj;
00371 
00372    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00373 }

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

Definition at line 337 of file res_musiconhold.c.

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

00338 {
00339    struct moh_files_state *state;
00340    struct mohclass *class = params;
00341 
00342    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00343       chan->music_state = state;
00344    } else {
00345       state = chan->music_state;
00346    }
00347 
00348    if (!state) {
00349       return NULL;
00350    }
00351 
00352    if (state->class != class) {
00353       memset(state, 0, sizeof(*state));
00354       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00355          state->pos = ast_random() % class->total_files;
00356       }
00357    }
00358 
00359    state->class = mohclass_ref(class, "Reffing music class for channel");
00360    state->origwfmt = chan->writeformat;
00361 
00362    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00363    
00364    return chan->music_state;
00365 }

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

Definition at line 313 of file res_musiconhold.c.

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

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

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

Definition at line 301 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00302 {
00303    struct ast_frame *f = NULL;
00304    
00305    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00306       if (!ast_moh_files_next(chan))
00307          f = ast_readframe(chan->stream);
00308    }
00309 
00310    return f;
00311 }

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

Definition at line 218 of file res_musiconhold.c.

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

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

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

Definition at line 846 of file res_musiconhold.c.

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

00847 {
00848    struct mohdata *moh = data;
00849    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00850    int res;
00851 
00852    len = ast_codec_get_len(moh->parent->format, samples);
00853 
00854    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00855       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00856       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00857    }
00858    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00859    if (res <= 0)
00860       return 0;
00861 
00862    moh->f.datalen = res;
00863    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00864    moh->f.samples = ast_codec_get_samples(&moh->f);
00865 
00866    if (ast_write(chan, &moh->f) < 0) {
00867       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00868       return -1;
00869    }
00870 
00871    return 0;
00872 }

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

Definition at line 381 of file res_musiconhold.c.

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

00382 {
00383    struct mohclass *class;
00384    const char *classname = NULL;
00385 
00386    if ((class = get_mohbydigit(digit))) {
00387       classname = ast_strdupa(class->name);
00388       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00389       ast_string_field_set(chan,musicclass,classname);
00390       ast_moh_stop(chan);
00391       ast_moh_start(chan, classname, NULL);
00392    }
00393 }

static int moh_register ( struct mohclass moh,
int  reload,
int  unref 
) [static]

Note:
This function owns the reference it gets to moh

Definition at line 1089 of file res_musiconhold.c.

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

Referenced by load_moh_classes(), and local_ast_moh_start().

01090 {
01091    struct mohclass *mohclass = NULL;
01092 
01093    if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
01094       if (!mohclass->delete) {
01095          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01096          mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01097          if (unref) {
01098             moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01099          }
01100          return -1;
01101       }
01102       mohclass = mohclass_unref(mohclass, "Unreffing mohclass we just found by name");
01103    }
01104 
01105    time(&moh->start);
01106    moh->start -= respawn_time;
01107    
01108    if (!strcasecmp(moh->mode, "files")) {
01109       if (init_files_class(moh)) {
01110          moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01111          return -1;
01112       }
01113    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01114          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01115          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01116       if (init_app_class(moh)) {
01117          moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01118          return -1;
01119       }
01120    } else {
01121       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01122       moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01123       return -1;
01124    }
01125 
01126    ao2_t_link(mohclasses, moh, "Adding class to container");
01127 
01128    if (unref) {
01129       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01130    }
01131    
01132    return 0;
01133 }

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

Definition at line 788 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, LOG_WARNING, mohclass::members, moh, mohclass_unref, ast_channel::name, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

00789 {
00790    struct mohdata *moh = data;
00791    struct mohclass *class = moh->parent;
00792    int oldwfmt;
00793 
00794    ao2_lock(class);
00795    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00796    ao2_unlock(class);
00797    
00798    close(moh->pipe[0]);
00799    close(moh->pipe[1]);
00800 
00801    oldwfmt = moh->origwfmt;
00802 
00803    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00804 
00805    ast_free(moh);
00806 
00807    if (chan) {
00808       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00809          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00810                chan->name, ast_getformatname(oldwfmt));
00811       }
00812 
00813       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00814    }
00815 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 914 of file res_musiconhold.c.

References ast_config_AST_VAR_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(), and local_ast_moh_start().

00914                                                   {
00915 
00916    DIR *files_DIR;
00917    struct dirent *files_dirent;
00918    char dir_path[PATH_MAX];
00919    char path[PATH_MAX];
00920    char filepath[PATH_MAX];
00921    char *ext;
00922    struct stat statbuf;
00923    int dirnamelen;
00924    int i;
00925 
00926    if (class->dir[0] != '/') {
00927       ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
00928       strncat(dir_path, "/", sizeof(dir_path) - 1);
00929       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
00930    } else {
00931       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
00932    }
00933    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
00934    files_DIR = opendir(dir_path);
00935    if (!files_DIR) {
00936       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
00937       return -1;
00938    }
00939 
00940    for (i = 0; i < class->total_files; i++)
00941       ast_free(class->filearray[i]);
00942 
00943    class->total_files = 0;
00944    dirnamelen = strlen(dir_path) + 2;
00945    if (!getcwd(path, sizeof(path))) {
00946       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00947       return -1;
00948    }
00949    if (chdir(dir_path) < 0) {
00950       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00951       return -1;
00952    }
00953    while ((files_dirent = readdir(files_DIR))) {
00954       /* The file name must be at least long enough to have the file type extension */
00955       if ((strlen(files_dirent->d_name) < 4))
00956          continue;
00957 
00958       /* Skip files that starts with a dot */
00959       if (files_dirent->d_name[0] == '.')
00960          continue;
00961 
00962       /* Skip files without extensions... they are not audio */
00963       if (!strchr(files_dirent->d_name, '.'))
00964          continue;
00965 
00966       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
00967 
00968       if (stat(filepath, &statbuf))
00969          continue;
00970 
00971       if (!S_ISREG(statbuf.st_mode))
00972          continue;
00973 
00974       if ((ext = strrchr(filepath, '.')))
00975          *ext = '\0';
00976 
00977       /* if the file is present in multiple formats, ensure we only put it into the list once */
00978       for (i = 0; i < class->total_files; i++)
00979          if (!strcmp(filepath, class->filearray[i]))
00980             break;
00981 
00982       if (i == class->total_files) {
00983          if (moh_add_file(class, filepath))
00984             break;
00985       }
00986    }
00987 
00988    closedir(files_DIR);
00989    if (chdir(path) < 0) {
00990       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00991       return -1;
00992    }
00993    if (ast_test_flag(class, MOH_SORTALPHA))
00994       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
00995    return class->total_files;
00996 }

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

Definition at line 904 of file res_musiconhold.c.

Referenced by moh_scan_files().

00905 {
00906    char *s1, *s2;
00907 
00908    s1 = ((char **)i1)[0];
00909    s2 = ((char **)i2)[0];
00910 
00911    return strcasecmp(s1, s2);
00912 }

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

Definition at line 755 of file res_musiconhold.c.

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

Referenced by moh_alloc().

00756 {
00757    struct mohdata *moh;
00758    long flags; 
00759    
00760    if (!(moh = ast_calloc(1, sizeof(*moh))))
00761       return NULL;
00762    
00763    if (pipe(moh->pipe)) {
00764       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00765       ast_free(moh);
00766       return NULL;
00767    }
00768 
00769    /* Make entirely non-blocking */
00770    flags = fcntl(moh->pipe[0], F_GETFL);
00771    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00772    flags = fcntl(moh->pipe[1], F_GETFL);
00773    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00774 
00775    moh->f.frametype = AST_FRAME_VOICE;
00776    moh->f.subclass = cl->format;
00777    moh->f.offset = AST_FRIENDLY_OFFSET;
00778 
00779    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00780 
00781    ao2_lock(cl);
00782    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00783    ao2_unlock(cl);
00784    
00785    return moh;
00786 }

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

Definition at line 544 of file res_musiconhold.c.

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

Referenced by init_app_class(), and local_ast_moh_start().

00545 {
00546 #define  MOH_MS_INTERVAL      100
00547 
00548    struct mohclass *class = data;
00549    struct mohdata *moh;
00550    char buf[8192];
00551    short sbuf[8192];
00552    int res, res2;
00553    int len;
00554    struct timeval deadline, tv_tmp;
00555 
00556    deadline.tv_sec = 0;
00557    deadline.tv_usec = 0;
00558    for(;/* ever */;) {
00559       pthread_testcancel();
00560       /* Spawn mp3 player if it's not there */
00561       if (class->srcfd < 0) {
00562          if ((class->srcfd = spawn_mp3(class)) < 0) {
00563             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00564             /* Try again later */
00565             sleep(500);
00566             pthread_testcancel();
00567          }
00568       }
00569       if (class->pseudofd > -1) {
00570 #ifdef SOLARIS
00571          thr_yield();
00572 #endif
00573          /* Pause some amount of time */
00574          res = read(class->pseudofd, buf, sizeof(buf));
00575          pthread_testcancel();
00576       } else {
00577          long delta;
00578          /* Reliable sleep */
00579          tv_tmp = ast_tvnow();
00580          if (ast_tvzero(deadline))
00581             deadline = tv_tmp;
00582          delta = ast_tvdiff_ms(tv_tmp, deadline);
00583          if (delta < MOH_MS_INTERVAL) {   /* too early */
00584             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00585             usleep(1000 * (MOH_MS_INTERVAL - delta));
00586             pthread_testcancel();
00587          } else {
00588             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00589             deadline = tv_tmp;
00590          }
00591          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00592       }
00593       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00594          continue;
00595       /* Read mp3 audio */
00596       len = ast_codec_get_len(class->format, res);
00597 
00598       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00599          if (!res2) {
00600             close(class->srcfd);
00601             class->srcfd = -1;
00602             pthread_testcancel();
00603             if (class->pid > 1) {
00604                killpg(class->pid, SIGHUP);
00605                usleep(100000);
00606                killpg(class->pid, SIGTERM);
00607                usleep(100000);
00608                killpg(class->pid, SIGKILL);
00609                class->pid = 0;
00610             }
00611          } else {
00612             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00613          }
00614          continue;
00615       }
00616 
00617       pthread_testcancel();
00618 
00619       ao2_lock(class);
00620       AST_LIST_TRAVERSE(&class->members, moh, list) {
00621          /* Write data */
00622          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00623             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00624          }
00625       }
00626       ao2_unlock(class);
00627    }
00628    return NULL;
00629 }

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

Definition at line 631 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, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

00632 {
00633    char *parse;
00634    char *class;
00635    int timeout = -1;
00636    int res;
00637    AST_DECLARE_APP_ARGS(args,
00638       AST_APP_ARG(class);
00639       AST_APP_ARG(duration);
00640    );
00641 
00642    parse = ast_strdupa(data);
00643 
00644    AST_STANDARD_APP_ARGS(args, parse);
00645 
00646    if (!ast_strlen_zero(args.duration)) {
00647       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00648          timeout *= 1000;
00649       } else {
00650          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00651       }
00652    }
00653 
00654    class = S_OR(args.class, NULL);
00655    if (ast_moh_start(chan, class, NULL)) {
00656       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00657       return 0;
00658    }
00659 
00660    if (timeout > 0)
00661       res = ast_safe_sleep(chan, timeout);
00662    else {
00663       while (!(res = ast_safe_sleep(chan, 10000)));
00664    }
00665 
00666    ast_moh_stop(chan);
00667 
00668    return res;
00669 }

static int reload ( void   )  [static]

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

Definition at line 694 of file res_musiconhold.c.

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

Referenced by load_module().

00695 {
00696    static int deprecation_warning = 0;
00697 
00698    if (!deprecation_warning) {
00699       deprecation_warning = 1;
00700       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00701    }
00702 
00703    if (ast_strlen_zero(data)) {
00704       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00705       return -1;
00706    }
00707    ast_string_field_set(chan, musicclass, data);
00708    return 0;
00709 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 403 of file res_musiconhold.c.

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

Referenced by monmp3thread().

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

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

Definition at line 711 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, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

00712 {
00713    char *parse;
00714    char *class;
00715    AST_DECLARE_APP_ARGS(args,
00716       AST_APP_ARG(class);
00717    );
00718 
00719    parse = ast_strdupa(data);
00720 
00721    AST_STANDARD_APP_ARGS(args, parse);
00722 
00723    class = S_OR(args.class, NULL);
00724    if (ast_moh_start(chan, class, NULL)) 
00725       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00726 
00727    return 0;
00728 }

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

Definition at line 730 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00731 {
00732    ast_moh_stop(chan);
00733 
00734    return 0;
00735 }

static int unload_module ( void   )  [static]

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

01733 {
01734    int res = 0;
01735    struct mohclass *class = NULL;
01736 
01737    /* XXX This check shouldn't be required if module ref counting was being used
01738     * properly ... */
01739    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01740       class = mohclass_unref(class, "unref of class from module unload callback");
01741       res = -1;
01742    }
01743 
01744    if (res < 0) {
01745       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01746       return res;
01747    }
01748 
01749    ast_uninstall_music_functions();
01750 
01751    ast_moh_destroy();
01752    res = ast_unregister_application(play_moh);
01753    res |= ast_unregister_application(wait_moh);
01754    res |= ast_unregister_application(set_moh);
01755    res |= ast_unregister_application(start_moh);
01756    res |= ast_unregister_application(stop_moh);
01757    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01758    ast_unregister_atexit(ast_moh_destroy);
01759 
01760    return res;
01761 }

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

Definition at line 671 of file res_musiconhold.c.

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

Referenced by load_module().

00672 {
00673    static int deprecation_warning = 0;
00674    int res;
00675 
00676    if (!deprecation_warning) {
00677       deprecation_warning = 1;
00678       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00679    }
00680 
00681    if (!data || !atoi(data)) {
00682       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00683       return -1;
00684    }
00685    if (ast_moh_start(chan, NULL, NULL)) {
00686       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00687       return 0;
00688    }
00689    res = ast_safe_sleep(chan, atoi(data) * 1000);
00690    ast_moh_stop(chan);
00691    return res;
00692 }


Variable Documentation

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

Definition at line 1767 of file res_musiconhold.c.

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

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

global MOH_ flags

Definition at line 167 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 395 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 207 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 874 of file res_musiconhold.c.

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

Definition at line 141 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 147 of file res_musiconhold.c.

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

Definition at line 143 of file res_musiconhold.c.

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

Definition at line 144 of file res_musiconhold.c.

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

Definition at line 145 of file res_musiconhold.c.

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

Definition at line 142 of file res_musiconhold.c.


Generated on Wed Oct 28 13:33:30 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6