Wed Oct 28 11:53:08 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/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, void *data)
static int reload (void)
static int set_moh_exec (struct ast_channel *chan, void *data)
static int spawn_mp3 (struct mohclass *class)
static int start_moh_exec (struct ast_channel *chan, void *data)
static int stop_moh_exec (struct ast_channel *chan, void *data)
static int unload_module (void)
static int wait_moh_exec (struct ast_channel *chan, void *data)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = 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 char * play_moh = "MusicOnHold"
static char * play_moh_desc
static char * play_moh_syn = "Play Music On Hold indefinitely"
static int respawn_time = 20
static char * set_moh = "SetMusicOnHold"
static char * set_moh_desc
static char * set_moh_syn = "Set default Music On Hold class"
static char * start_moh = "StartMusicOnHold"
static char * start_moh_desc
static char * start_moh_syn = "Play Music On Hold"
static char * stop_moh = "StopMusicOnHold"
static char * stop_moh_desc
static char * stop_moh_syn = "Stop Playing Music On Hold"
static char * wait_moh = "WaitMusicOnHold"
static char * wait_moh_desc
static char * wait_moh_syn = "Wait, playing Music On Hold"


Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

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

#define MAX_MP3S   256

Definition at line 184 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 138 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 132 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 133 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 136 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 188 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 183 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1730 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1730 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

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

01526 {
01527    ast_verb(2, "Destroying musiconhold processes\n");
01528    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01529 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

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

00220 {
00221    struct moh_files_state *state = chan->music_state;
00222    int tries;
00223 
00224    /* Discontinue a stream if it is running already */
00225    if (chan->stream) {
00226       ast_closestream(chan->stream);
00227       chan->stream = NULL;
00228    }
00229 
00230    if (!state->class->total_files) {
00231       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00232       return -1;
00233    }
00234 
00235    /* If a specific file has been saved confirm it still exists and that it is still valid */
00236    if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00237       state->pos = state->save_pos;
00238       state->save_pos = -1;
00239    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00240       /* Get a random file and ensure we can open it */
00241       for (tries = 0; tries < 20; tries++) {
00242          state->pos = ast_random() % state->class->total_files;
00243          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00244             break;
00245       }
00246       state->save_pos = -1;
00247       state->samples = 0;
00248    } else {
00249       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00250       state->pos++;
00251       state->pos %= state->class->total_files;
00252       state->save_pos = -1;
00253       state->samples = 0;
00254    }
00255 
00256    if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00257       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00258       state->pos++;
00259       state->pos %= state->class->total_files;
00260       return -1;
00261    }
00262 
00263    /* Record the pointer to the filename for position resuming later */
00264    state->save_pos_filename = state->class->filearray[state->pos];
00265 
00266    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00267 
00268    if (state->samples)
00269       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00270 
00271    return 0;
00272 }

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

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

Definition at line 349 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00350 {
00351    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00352 }

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

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

00711 {
00712    struct mohclass *moh = NULL;
00713    struct mohclass tmp_class = {
00714       .flags = 0,
00715    };
00716 
00717    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00718 
00719    moh = ao2_t_find(mohclasses, &tmp_class, 0, "Finding by name");
00720 
00721    if (!moh && warn) {
00722       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00723    }
00724 
00725    return moh;
00726 }

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

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

01532 {
01533    switch (cmd) {
01534    case CLI_INIT:
01535       e->command = "moh reload";
01536       e->usage =
01537          "Usage: moh reload\n"
01538          "       Reloads the MusicOnHold module.\n"
01539          "       Alias for 'module reload res_musiconhold.so'\n";
01540       return NULL;
01541    case CLI_GENERATE:
01542       return NULL;
01543    }
01544 
01545    if (a->argc != e->args)
01546       return CLI_SHOWUSAGE;
01547 
01548    reload();
01549 
01550    return CLI_SUCCESS;
01551 }

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

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

01592 {
01593    struct mohclass *class;
01594    struct ao2_iterator i;
01595 
01596    switch (cmd) {
01597    case CLI_INIT:
01598       e->command = "moh show classes";
01599       e->usage =
01600          "Usage: moh show classes\n"
01601          "       Lists all MusicOnHold classes.\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 classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01612       ast_cli(a->fd, "Class: %s\n", class->name);
01613       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01614       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01615       if (ast_test_flag(class, MOH_CUSTOM)) {
01616          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01617       }
01618       if (strcasecmp(class->mode, "files")) {
01619          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01620       }
01621    }
01622    ao2_iterator_destroy(&i);
01623 
01624    return CLI_SUCCESS;
01625 }

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

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

01554 {
01555    struct mohclass *class;
01556    struct ao2_iterator i;
01557 
01558    switch (cmd) {
01559    case CLI_INIT:
01560       e->command = "moh show files";
01561       e->usage =
01562          "Usage: moh show files\n"
01563          "       Lists all loaded file-based MusicOnHold classes and their\n"
01564          "       files.\n";
01565       return NULL;
01566    case CLI_GENERATE:
01567       return NULL;
01568    }
01569 
01570    if (a->argc != e->args)
01571       return CLI_SHOWUSAGE;
01572 
01573    i = ao2_iterator_init(mohclasses, 0);
01574    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01575       int x;
01576 
01577       if (!class->total_files) {
01578          continue;
01579       }
01580 
01581       ast_cli(a->fd, "Class: %s\n", class->name);
01582       for (x = 0; x < class->total_files; x++) {
01583          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01584       }
01585    }
01586    ao2_iterator_destroy(&i);
01587 
01588    return CLI_SUCCESS;
01589 }

static int init_app_class ( struct mohclass class  )  [static]

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

01008 {
01009 #ifdef HAVE_DAHDI
01010    int x;
01011 #endif
01012 
01013    if (!strcasecmp(class->mode, "custom")) {
01014       ast_set_flag(class, MOH_CUSTOM);
01015    } else if (!strcasecmp(class->mode, "mp3nb")) {
01016       ast_set_flag(class, MOH_SINGLE);
01017    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01018       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01019    } else if (!strcasecmp(class->mode, "quietmp3")) {
01020       ast_set_flag(class, MOH_QUIET);
01021    }
01022       
01023    class->srcfd = -1;
01024    class->pseudofd = -1;
01025 
01026 #ifdef HAVE_DAHDI
01027    /* Open /dev/zap/pseudo for timing...  Is
01028       there a better, yet reliable way to do this? */
01029    class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
01030    if (class->pseudofd < 0) {
01031       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
01032    } else {
01033       x = 320;
01034       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
01035    }
01036 #endif
01037 
01038    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01039       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01040       if (class->pseudofd > -1) {
01041          close(class->pseudofd);
01042          class->pseudofd = -1;
01043       }
01044       return -1;
01045    }
01046 
01047    return 0;
01048 }

static int init_files_class ( struct mohclass class  )  [static]

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

00963 {
00964    int res;
00965 
00966    res = moh_scan_files(class);
00967 
00968    if (res < 0) {
00969       return -1;
00970    }
00971 
00972    if (!res) {
00973       if (option_verbose > 2) {
00974          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00975                class->dir, class->name);
00976       }
00977       return -1;
00978    }
00979 
00980    if (strchr(class->args, 'r')) {
00981       ast_set_flag(class, MOH_RANDOMIZE);
00982    }
00983 
00984    return 0;
00985 }

static int load_module ( void   )  [static]

Definition at line 1647 of file res_musiconhold.c.

References ao2_t_container_alloc, ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application, ast_register_atexit(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

01648 {
01649    int res;
01650 
01651    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01652       return AST_MODULE_LOAD_DECLINE;
01653    }
01654 
01655    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01656       ast_log(LOG_WARNING, "No music on hold classes configured, "
01657             "disabling music on hold.\n");
01658    } else {
01659       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01660             local_ast_moh_cleanup);
01661    }
01662 
01663    res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
01664    ast_register_atexit(ast_moh_destroy);
01665    ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01666    if (!res)
01667       res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
01668    if (!res)
01669       res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
01670    if (!res)
01671       res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
01672    if (!res)
01673       res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
01674 
01675    return AST_MODULE_LOAD_SUCCESS;
01676 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1424 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_clear_flag, ast_config_destroy(), ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEUNCHANGED, 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().

01425 {
01426    struct ast_config *cfg;
01427    struct ast_variable *var;
01428    struct mohclass *class; 
01429    char *cat;
01430    int numclasses = 0;
01431    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01432 
01433    cfg = ast_config_load("musiconhold.conf", config_flags);
01434 
01435    if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01436       return 0;
01437 
01438    if (reload) {
01439       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01440    }
01441    
01442    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01443 
01444    cat = ast_category_browse(cfg, NULL);
01445    for (; cat; cat = ast_category_browse(cfg, cat)) {
01446       /* Setup common options from [general] section */
01447       if (!strcasecmp(cat, "general")) {
01448          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01449             if (!strcasecmp(var->name, "cachertclasses")) {
01450                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01451             } else {
01452                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01453             }
01454          }
01455       }
01456       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01457       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01458             !strcasecmp(cat, "general")) {
01459          continue;
01460       }
01461 
01462       if (!(class = moh_class_malloc())) {
01463          break;
01464       }
01465 
01466       ast_copy_string(class->name, cat, sizeof(class->name));  
01467       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01468          if (!strcasecmp(var->name, "mode"))
01469             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01470          else if (!strcasecmp(var->name, "directory"))
01471             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01472          else if (!strcasecmp(var->name, "application"))
01473             ast_copy_string(class->args, var->value, sizeof(class->args));
01474          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01475             class->digit = *var->value;
01476          else if (!strcasecmp(var->name, "random"))
01477             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01478          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01479             ast_set_flag(class, MOH_RANDOMIZE);
01480          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01481             ast_set_flag(class, MOH_SORTALPHA);
01482          else if (!strcasecmp(var->name, "format")) {
01483             class->format = ast_getformatbyname(var->value);
01484             if (!class->format) {
01485                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01486                class->format = AST_FORMAT_SLINEAR;
01487             }
01488          }
01489       }
01490 
01491       if (ast_strlen_zero(class->dir)) {
01492          if (!strcasecmp(class->mode, "custom")) {
01493             strcpy(class->dir, "nodir");
01494          } else {
01495             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01496             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01497             continue;
01498          }
01499       }
01500       if (ast_strlen_zero(class->mode)) {
01501          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01502          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01503          continue;
01504       }
01505       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01506          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01507          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01508          continue;
01509       }
01510 
01511       /* Don't leak a class when it's already registered */
01512       if (!moh_register(class, reload, 1)) {
01513          numclasses++;
01514       }
01515    }
01516 
01517    ast_config_destroy(cfg);
01518 
01519    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01520          moh_classes_delete_marked, NULL, "Purge marked classes");
01521 
01522    return numclasses;
01523 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1099 of file res_musiconhold.c.

References ast_free, and ast_channel::music_state.

Referenced by load_module().

01100 {
01101    struct moh_files_state *state = chan->music_state;
01102 
01103    if (state) {
01104       ast_free(chan->music_state);
01105       chan->music_state = NULL;
01106    }
01107 }

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

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

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

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

01333 {
01334    struct moh_files_state *state = chan->music_state;
01335    ast_clear_flag(chan, AST_FLAG_MOH);
01336    ast_deactivate_generator(chan);
01337 
01338    if (state) {
01339       if (chan->stream) {
01340          ast_closestream(chan->stream);
01341          chan->stream = NULL;
01342       }
01343    }
01344 
01345    manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01346       "State: Stop\r\n"
01347       "Channel: %s\r\n"
01348       "UniqueID: %s\r\n",
01349       chan->name, chan->uniqueid);
01350 }

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

Definition at line 854 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

00855 {
00856    if (!class->allowed_files) {
00857       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00858          return -1;
00859       class->allowed_files = INITIAL_NUM_FILES;
00860    } else if (class->total_files == class->allowed_files) {
00861       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00862          class->allowed_files = 0;
00863          class->total_files = 0;
00864          return -1;
00865       }
00866       class->allowed_files *= 2;
00867    }
00868 
00869    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00870       return -1;
00871 
00872    class->total_files++;
00873 
00874    return 0;
00875 }

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

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

00791 {
00792    struct mohdata *res;
00793    struct mohclass *class = params;
00794    struct moh_files_state *state;
00795 
00796    /* Initiating music_state for current channel. Channel should know name of moh class */
00797    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00798       chan->music_state = state;
00799       state->class = class;
00800    } else
00801       state = chan->music_state;
00802    if (state && state->class != class) {
00803       memset(state, 0, sizeof(*state));
00804       state->class = class;
00805    }
00806 
00807    if ((res = mohalloc(class))) {
00808       res->origwfmt = chan->writeformat;
00809       if (ast_set_write_format(chan, class->format)) {
00810          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00811          moh_release(NULL, res);
00812          res = NULL;
00813       }
00814       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00815    }
00816    return res;
00817 }

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

Definition at line 1640 of file res_musiconhold.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

01641 {
01642    struct mohclass *class = obj, *class2 = arg;
01643 
01644    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01645 }

static void moh_class_destructor ( void *  obj  )  [static]

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

01353 {
01354    struct mohclass *class = obj;
01355    struct mohdata *member;
01356 
01357    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01358 
01359    if (class->pid > 1) {
01360       char buff[8192];
01361       int bytes, tbytes = 0, stime = 0, pid = 0;
01362 
01363       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01364 
01365       stime = time(NULL) + 2;
01366       pid = class->pid;
01367       class->pid = 0;
01368 
01369       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01370        * to give the process a reason and time enough to kill off its
01371        * children. */
01372       killpg(pid, SIGHUP);
01373       usleep(100000);
01374       killpg(pid, SIGTERM);
01375       usleep(100000);
01376       killpg(pid, SIGKILL);
01377 
01378       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01379             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01380          tbytes = tbytes + bytes;
01381       }
01382 
01383       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01384 
01385       close(class->srcfd);
01386    }
01387 
01388    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01389       free(member);
01390    }
01391 
01392    if (class->thread) {
01393       pthread_cancel(class->thread);
01394       pthread_join(class->thread, NULL);
01395       class->thread = AST_PTHREADT_NULL;
01396    }
01397 
01398    if (class->filearray) {
01399       int i;
01400       for (i = 0; i < class->total_files; i++) {
01401          free(class->filearray[i]);
01402       }
01403       free(class->filearray);
01404       class->filearray = NULL;
01405    }
01406 }

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

Definition at line 1633 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01634 {
01635    const struct mohclass *class = obj;
01636 
01637    return ast_str_case_hash(class->name);
01638 }

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

Definition at line 1688 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01689 {
01690    struct mohclass *class = obj;
01691 
01692    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01693 }

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

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

01112 {
01113    struct mohclass *class;
01114 
01115    if ((class = ao2_t_alloc(sizeof(*class), moh_class_destructor, "Allocating new moh class"))) {
01116       class->format = AST_FORMAT_SLINEAR;
01117    }
01118 
01119    return class;
01120 }

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

Definition at line 1408 of file res_musiconhold.c.

Referenced by load_moh_classes().

01409 {
01410    struct mohclass *class = obj;
01411 
01412    class->delete = 1;
01413 
01414    return 0;
01415 }

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

Definition at line 1417 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01418 {
01419    struct mohclass *class = obj;
01420 
01421    return class->delete ? CMP_MATCH : 0;
01422 }

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

Definition at line 988 of file res_musiconhold.c.

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

Referenced by moh_register().

00989 {
00990    if (!old || !new) {
00991       return -1;
00992    }
00993 
00994    if (strcmp(old->dir, new->dir)) {
00995       return -1;
00996    } else if (strcmp(old->mode, new->mode)) {
00997       return -1;
00998    } else if (strcmp(old->args, new->args)) {
00999       return -1;
01000    } else if (old->flags != new->flags) {
01001       return -1;
01002    }
01003 
01004    return 0;
01005 }

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

Definition at line 340 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and mohclass::digit.

Referenced by get_mohbydigit().

00341 {
00342    char *digit = arg;
00343    struct mohclass *class = obj;
00344 
00345    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00346 }

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

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

00311 {
00312    struct moh_files_state *state;
00313    struct mohclass *class = params;
00314 
00315    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00316       chan->music_state = state;
00317    } else {
00318       state = chan->music_state;
00319    }
00320 
00321    if (!state) {
00322       return NULL;
00323    }
00324 
00325    if (state->class != class) {
00326       memset(state, 0, sizeof(*state));
00327       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00328          state->pos = ast_random() % class->total_files;
00329       }
00330    }
00331 
00332    state->class = mohclass_ref(class, "Reffing music class for channel");
00333    state->origwfmt = chan->writeformat;
00334 
00335    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00336    
00337    return chan->music_state;
00338 }

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

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

00287 {
00288    struct moh_files_state *state = chan->music_state;
00289    struct ast_frame *f = NULL;
00290    int res = 0;
00291 
00292    state->sample_queue += samples;
00293 
00294    while (state->sample_queue > 0) {
00295       if ((f = moh_files_readframe(chan))) {
00296          state->samples += f->samples;
00297          state->sample_queue -= f->samples;
00298          res = ast_write(chan, f);
00299          ast_frfree(f);
00300          if (res < 0) {
00301             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00302             return -1;
00303          }
00304       } else
00305          return -1;  
00306    }
00307    return res;
00308 }

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

Definition at line 274 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00275 {
00276    struct ast_frame *f = NULL;
00277    
00278    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00279       if (!ast_moh_files_next(chan))
00280          f = ast_readframe(chan->stream);
00281    }
00282 
00283    return f;
00284 }

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

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

00192 {
00193    struct moh_files_state *state;
00194 
00195    if (!chan || !chan->music_state) {
00196       return;
00197    }
00198 
00199    state = chan->music_state;
00200 
00201    if (chan->stream) {
00202       ast_closestream(chan->stream);
00203       chan->stream = NULL;
00204    }
00205    
00206    if (option_verbose > 2) {
00207       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00208    }
00209 
00210    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00211       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00212    }
00213 
00214    state->save_pos = state->pos;
00215 
00216    mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00217 }

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

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

00820 {
00821    struct mohdata *moh = data;
00822    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00823    int res;
00824 
00825    len = ast_codec_get_len(moh->parent->format, samples);
00826 
00827    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00828       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00829       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00830    }
00831    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00832    if (res <= 0)
00833       return 0;
00834 
00835    moh->f.datalen = res;
00836    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00837    moh->f.samples = ast_codec_get_samples(&moh->f);
00838 
00839    if (ast_write(chan, &moh->f) < 0) {
00840       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00841       return -1;
00842    }
00843 
00844    return 0;
00845 }

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

Definition at line 354 of file res_musiconhold.c.

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

00355 {
00356    struct mohclass *class;
00357    const char *classname = NULL;
00358 
00359    if ((class = get_mohbydigit(digit))) {
00360       classname = ast_strdupa(class->name);
00361       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00362       ast_string_field_set(chan,musicclass,classname);
00363       ast_moh_stop(chan);
00364       ast_moh_start(chan, classname, NULL);
00365    }
00366 }

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

01054 {
01055    struct mohclass *mohclass = NULL;
01056 
01057    if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
01058       if (!mohclass->delete) {
01059          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01060          mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01061          if (unref) {
01062             moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01063          }
01064          return -1;
01065       }
01066       mohclass = mohclass_unref(mohclass, "Unreffing mohclass we just found by name");
01067    }
01068 
01069    time(&moh->start);
01070    moh->start -= respawn_time;
01071    
01072    if (!strcasecmp(moh->mode, "files")) {
01073       if (init_files_class(moh)) {
01074          moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01075          return -1;
01076       }
01077    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01078          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01079          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01080       if (init_app_class(moh)) {
01081          moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01082          return -1;
01083       }
01084    } else {
01085       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01086       moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01087       return -1;
01088    }
01089 
01090    ao2_t_link(mohclasses, moh, "Adding class to container");
01091 
01092    if (unref) {
01093       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01094    }
01095    
01096    return 0;
01097 }

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

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

00762 {
00763    struct mohdata *moh = data;
00764    struct mohclass *class = moh->parent;
00765    int oldwfmt;
00766 
00767    ao2_lock(class);
00768    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00769    ao2_unlock(class);
00770    
00771    close(moh->pipe[0]);
00772    close(moh->pipe[1]);
00773 
00774    oldwfmt = moh->origwfmt;
00775 
00776    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00777 
00778    ast_free(moh);
00779 
00780    if (chan) {
00781       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00782          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00783                chan->name, ast_getformatname(oldwfmt));
00784       }
00785 
00786       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00787    }
00788 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 887 of file res_musiconhold.c.

References ast_free, ast_log(), ast_test_flag, errno, ext, LOG_WARNING, moh_add_file(), moh_sort_compare(), and MOH_SORTALPHA.

Referenced by init_files_class(), and local_ast_moh_start().

00887                                                   {
00888 
00889    DIR *files_DIR;
00890    struct dirent *files_dirent;
00891    char path[PATH_MAX];
00892    char filepath[PATH_MAX];
00893    char *ext;
00894    struct stat statbuf;
00895    int dirnamelen;
00896    int i;
00897 
00898    files_DIR = opendir(class->dir);
00899    if (!files_DIR) {
00900       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00901       return -1;
00902    }
00903 
00904    for (i = 0; i < class->total_files; i++)
00905       ast_free(class->filearray[i]);
00906 
00907    class->total_files = 0;
00908    dirnamelen = strlen(class->dir) + 2;
00909    if (!getcwd(path, sizeof(path))) {
00910       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00911       return -1;
00912    }
00913    if (chdir(class->dir) < 0) {
00914       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00915       return -1;
00916    }
00917    while ((files_dirent = readdir(files_DIR))) {
00918       /* The file name must be at least long enough to have the file type extension */
00919       if ((strlen(files_dirent->d_name) < 4))
00920          continue;
00921 
00922       /* Skip files that starts with a dot */
00923       if (files_dirent->d_name[0] == '.')
00924          continue;
00925 
00926       /* Skip files without extensions... they are not audio */
00927       if (!strchr(files_dirent->d_name, '.'))
00928          continue;
00929 
00930       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00931 
00932       if (stat(filepath, &statbuf))
00933          continue;
00934 
00935       if (!S_ISREG(statbuf.st_mode))
00936          continue;
00937 
00938       if ((ext = strrchr(filepath, '.')))
00939          *ext = '\0';
00940 
00941       /* if the file is present in multiple formats, ensure we only put it into the list once */
00942       for (i = 0; i < class->total_files; i++)
00943          if (!strcmp(filepath, class->filearray[i]))
00944             break;
00945 
00946       if (i == class->total_files) {
00947          if (moh_add_file(class, filepath))
00948             break;
00949       }
00950    }
00951 
00952    closedir(files_DIR);
00953    if (chdir(path) < 0) {
00954       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00955       return -1;
00956    }
00957    if (ast_test_flag(class, MOH_SORTALPHA))
00958       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
00959    return class->total_files;
00960 }

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

Definition at line 877 of file res_musiconhold.c.

Referenced by moh_scan_files().

00878 {
00879    char *s1, *s2;
00880 
00881    s1 = ((char **)i1)[0];
00882    s2 = ((char **)i2)[0];
00883 
00884    return strcasecmp(s1, s2);
00885 }

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

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

00729 {
00730    struct mohdata *moh;
00731    long flags; 
00732    
00733    if (!(moh = ast_calloc(1, sizeof(*moh))))
00734       return NULL;
00735    
00736    if (pipe(moh->pipe)) {
00737       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00738       ast_free(moh);
00739       return NULL;
00740    }
00741 
00742    /* Make entirely non-blocking */
00743    flags = fcntl(moh->pipe[0], F_GETFL);
00744    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00745    flags = fcntl(moh->pipe[1], F_GETFL);
00746    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00747 
00748    moh->f.frametype = AST_FRAME_VOICE;
00749    moh->f.subclass = cl->format;
00750    moh->f.offset = AST_FRIENDLY_OFFSET;
00751 
00752    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00753 
00754    ao2_lock(cl);
00755    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00756    ao2_unlock(cl);
00757    
00758    return moh;
00759 }

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

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

00518 {
00519 #define  MOH_MS_INTERVAL      100
00520 
00521    struct mohclass *class = data;
00522    struct mohdata *moh;
00523    char buf[8192];
00524    short sbuf[8192];
00525    int res, res2;
00526    int len;
00527    struct timeval deadline, tv_tmp;
00528 
00529    deadline.tv_sec = 0;
00530    deadline.tv_usec = 0;
00531    for(;/* ever */;) {
00532       pthread_testcancel();
00533       /* Spawn mp3 player if it's not there */
00534       if (class->srcfd < 0) {
00535          if ((class->srcfd = spawn_mp3(class)) < 0) {
00536             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00537             /* Try again later */
00538             sleep(500);
00539             pthread_testcancel();
00540          }
00541       }
00542       if (class->pseudofd > -1) {
00543 #ifdef SOLARIS
00544          thr_yield();
00545 #endif
00546          /* Pause some amount of time */
00547          res = read(class->pseudofd, buf, sizeof(buf));
00548          pthread_testcancel();
00549       } else {
00550          long delta;
00551          /* Reliable sleep */
00552          tv_tmp = ast_tvnow();
00553          if (ast_tvzero(deadline))
00554             deadline = tv_tmp;
00555          delta = ast_tvdiff_ms(tv_tmp, deadline);
00556          if (delta < MOH_MS_INTERVAL) {   /* too early */
00557             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00558             usleep(1000 * (MOH_MS_INTERVAL - delta));
00559             pthread_testcancel();
00560          } else {
00561             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00562             deadline = tv_tmp;
00563          }
00564          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00565       }
00566       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00567          continue;
00568       /* Read mp3 audio */
00569       len = ast_codec_get_len(class->format, res);
00570 
00571       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00572          if (!res2) {
00573             close(class->srcfd);
00574             class->srcfd = -1;
00575             pthread_testcancel();
00576             if (class->pid > 1) {
00577                killpg(class->pid, SIGHUP);
00578                usleep(100000);
00579                killpg(class->pid, SIGTERM);
00580                usleep(100000);
00581                killpg(class->pid, SIGKILL);
00582                class->pid = 0;
00583             }
00584          } else {
00585             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00586          }
00587          continue;
00588       }
00589 
00590       pthread_testcancel();
00591 
00592       ao2_lock(class);
00593       AST_LIST_TRAVERSE(&class->members, moh, list) {
00594          /* Write data */
00595          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00596             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00597          }
00598       }
00599       ao2_unlock(class);
00600    }
00601    return NULL;
00602 }

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

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

00605 {
00606    char *parse;
00607    char *class;
00608    int timeout = -1;
00609    int res;
00610    AST_DECLARE_APP_ARGS(args,
00611       AST_APP_ARG(class);
00612       AST_APP_ARG(duration);
00613    );
00614 
00615    parse = ast_strdupa(data);
00616 
00617    AST_STANDARD_APP_ARGS(args, parse);
00618 
00619    if (!ast_strlen_zero(args.duration)) {
00620       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00621          timeout *= 1000;
00622       } else {
00623          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00624       }
00625    }
00626 
00627    class = S_OR(args.class, NULL);
00628    if (ast_moh_start(chan, class, NULL)) {
00629       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00630       return 0;
00631    }
00632 
00633    if (timeout > 0)
00634       res = ast_safe_sleep(chan, timeout);
00635    else {
00636       while (!(res = ast_safe_sleep(chan, 10000)));
00637    }
00638 
00639    ast_moh_stop(chan);
00640 
00641    return res;
00642 }

static int reload ( void   )  [static]

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

Definition at line 667 of file res_musiconhold.c.

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

Referenced by load_module().

00668 {
00669    static int deprecation_warning = 0;
00670 
00671    if (!deprecation_warning) {
00672       deprecation_warning = 1;
00673       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00674    }
00675 
00676    if (ast_strlen_zero(data)) {
00677       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00678       return -1;
00679    }
00680    ast_string_field_set(chan, musicclass, data);
00681    return 0;
00682 }

static int spawn_mp3 ( struct mohclass class  )  [static]

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

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

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

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

00685 {
00686    char *parse;
00687    char *class;
00688    AST_DECLARE_APP_ARGS(args,
00689       AST_APP_ARG(class);
00690    );
00691 
00692    parse = ast_strdupa(data);
00693 
00694    AST_STANDARD_APP_ARGS(args, parse);
00695 
00696    class = S_OR(args.class, NULL);
00697    if (ast_moh_start(chan, class, NULL)) 
00698       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00699 
00700    return 0;
00701 }

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

Definition at line 703 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00704 {
00705    ast_moh_stop(chan);
00706 
00707    return 0;
00708 }

static int unload_module ( void   )  [static]

Definition at line 1695 of file res_musiconhold.c.

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

01696 {
01697    int res = 0;
01698    struct mohclass *class = NULL;
01699 
01700    /* XXX This check shouldn't be required if module ref counting was being used
01701     * properly ... */
01702    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01703       class = mohclass_unref(class, "unref of class from module unload callback");
01704       res = -1;
01705    }
01706 
01707    if (res < 0) {
01708       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01709       return res;
01710    }
01711 
01712    ast_uninstall_music_functions();
01713 
01714    ast_moh_destroy();
01715    res = ast_unregister_application(play_moh);
01716    res |= ast_unregister_application(wait_moh);
01717    res |= ast_unregister_application(set_moh);
01718    res |= ast_unregister_application(start_moh);
01719    res |= ast_unregister_application(stop_moh);
01720    ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01721    ast_unregister_atexit(ast_moh_destroy);
01722 
01723    return res;
01724 }

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

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

00645 {
00646    static int deprecation_warning = 0;
00647    int res;
00648 
00649    if (!deprecation_warning) {
00650       deprecation_warning = 1;
00651       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00652    }
00653 
00654    if (!data || !atoi(data)) {
00655       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00656       return -1;
00657    }
00658    if (ast_moh_start(chan, NULL, NULL)) {
00659       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00660       return 0;
00661    }
00662    res = ast_safe_sleep(chan, atoi(data) * 1000);
00663    ast_moh_stop(chan);
00664    return res;
00665 }


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

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

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

global MOH_ flags

Definition at line 140 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 368 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 180 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 847 of file res_musiconhold.c.

char* play_moh = "MusicOnHold" [static]

Definition at line 72 of file res_musiconhold.c.

char* play_moh_desc [static]

Definition at line 84 of file res_musiconhold.c.

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

Definition at line 78 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 120 of file res_musiconhold.c.

char* set_moh = "SetMusicOnHold" [static]

Definition at line 74 of file res_musiconhold.c.

char* set_moh_desc [static]

Definition at line 102 of file res_musiconhold.c.

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

Definition at line 80 of file res_musiconhold.c.

char* start_moh = "StartMusicOnHold" [static]

Definition at line 75 of file res_musiconhold.c.

char* start_moh_desc [static]

Initial value:

 "  StartMusicOnHold(class):\n"
"Starts playing music on hold, uses default music class for channel.\n"
"Starts playing music specified by class.  If omitted, the default\n"
"music source for the channel will be used.  Always returns 0.\n"

Definition at line 112 of file res_musiconhold.c.

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

Definition at line 81 of file res_musiconhold.c.

char* stop_moh = "StopMusicOnHold" [static]

Definition at line 76 of file res_musiconhold.c.

char* stop_moh_desc [static]

Initial value:

 "  StopMusicOnHold(): "
"Stops playing music on hold.\n"

Definition at line 117 of file res_musiconhold.c.

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

Definition at line 82 of file res_musiconhold.c.

char* wait_moh = "WaitMusicOnHold" [static]

Definition at line 73 of file res_musiconhold.c.

char* wait_moh_desc [static]

Definition at line 92 of file res_musiconhold.c.

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

Definition at line 79 of file res_musiconhold.c.


Generated on Wed Oct 28 11:53:08 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6