Fri Aug 13 18:21:31 2010

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.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/dahdi_compat.h"
#include <sys/capability.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.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/astobj2.h"

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_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define MOH_SINGLE   (1 << 1)
#define mohclass_ref(class)   (ao2_ref((class), +1), class)
#define mohclass_unref(class)   (ao2_ref((class), -1), (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 int cli_files_show (int fd, int argc, char *argv[])
static struct mohclassget_mohbyname (const char *name, int warn)
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 moh0_exec (struct ast_channel *chan, void *data)
static int moh1_exec (struct ast_channel *chan, void *data)
static int moh2_exec (struct ast_channel *chan, void *data)
static int moh3_exec (struct ast_channel *chan, void *data)
static int moh4_exec (struct ast_channel *chan, void *data)
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_classes_show (int fd, int argc, char *argv[])
static int moh_cli (int fd, int argc, char *argv[])
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 int moh_register (struct mohclass *moh, int reload)
static void moh_release (struct ast_channel *chan, void *data)
static int moh_scan_files (struct mohclass *class)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int reload (void)
static int spawn_mp3 (struct mohclass *class)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app0 = "MusicOnHold"
static char * app1 = "WaitMusicOnHold"
static char * app2 = "SetMusicOnHold"
static char * app3 = "StartMusicOnHold"
static char * app4 = "StopMusicOnHold"
static const struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_moh []
static struct ast_cli_entry cli_moh_classes_show_deprecated
static struct ast_cli_entry cli_moh_files_show_deprecated
static char * descrip0
static char * descrip1
static char * descrip2
static char * descrip3
static char * descrip4
static struct ast_generator moh_file_stream
static struct ao2_containermohclasses
static struct ast_generator mohgen
static int respawn_time = 20
static char * synopsis0 = "Play Music On Hold indefinitely"
static char * synopsis1 = "Wait, playing Music On Hold"
static char * synopsis2 = "Set default Music On Hold class"
static char * synopsis3 = "Play Music On Hold"
static char * synopsis4 = "Stop 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 81 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 175 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 177 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CUSTOM   (1 << 2)

Definition at line 134 of file res_musiconhold.c.

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

#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(), and spawn_mp3().

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 135 of file res_musiconhold.c.

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

#define MOH_SINGLE   (1 << 1)

Definition at line 133 of file res_musiconhold.c.

Referenced by init_app_class(), and spawn_mp3().

#define mohclass_ref ( class   )     (ao2_ref((class), +1), class)

Definition at line 181 of file res_musiconhold.c.

Referenced by moh_files_alloc(), and mohalloc().

#define mohclass_unref ( class   )     (ao2_ref((class), -1), (struct mohclass *) NULL)

Definition at line 182 of file res_musiconhold.c.

Referenced by cli_files_show(), local_ast_moh_start(), moh_classes_show(), moh_files_release(), moh_register(), moh_release(), and unload_module().

#define MPG_123   "/usr/bin/mpg123"

Definition at line 176 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1503 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1503 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1316 of file res_musiconhold.c.

References ao2_callback(), ast_verbose(), option_verbose, and VERBOSE_PREFIX_2.

Referenced by load_module(), and unload_module().

01317 {
01318    if (option_verbose > 1) {
01319       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01320    }
01321 
01322    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01323 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 213 of file res_musiconhold.c.

References ast_closestream(), ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

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

static int cli_files_show ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1331 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ast_cli(), and mohclass_unref.

01332 {
01333    struct mohclass *class;
01334    struct ao2_iterator i;
01335 
01336    i = ao2_iterator_init(mohclasses, 0);
01337 
01338    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01339       int x;
01340 
01341       if (!class->total_files) {
01342          continue;
01343       }
01344 
01345       ast_cli(fd, "Class: %s\n", class->name);
01346 
01347       for (x = 0; x < class->total_files; x++) {
01348          ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01349       }
01350    }
01351 
01352    ao2_iterator_destroy(&i);
01353 
01354    return 0;
01355 }

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

Definition at line 654 of file res_musiconhold.c.

References ao2_find(), ast_copy_string(), ast_log(), mohclass::flags, LOG_WARNING, moh, and mohclass::name.

Referenced by local_ast_moh_start(), and moh_register().

00655 {
00656    struct mohclass *moh = NULL;
00657    struct mohclass tmp_class = {
00658       .flags = 0,
00659    };
00660 
00661    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00662 
00663    moh = ao2_find(mohclasses, &tmp_class, 0);
00664 
00665    if (!moh && warn) {
00666       ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
00667    }
00668 
00669    return moh;
00670 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 910 of file res_musiconhold.c.

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

Referenced by moh_register().

00911 {
00912 #ifdef HAVE_DAHDI
00913    int x;
00914 #endif
00915 
00916    if (!strcasecmp(class->mode, "custom")) {
00917       ast_set_flag(class, MOH_CUSTOM);
00918    } else if (!strcasecmp(class->mode, "mp3nb")) {
00919       ast_set_flag(class, MOH_SINGLE);
00920    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
00921       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
00922    } else if (!strcasecmp(class->mode, "quietmp3")) {
00923       ast_set_flag(class, MOH_QUIET);
00924    }
00925       
00926    class->srcfd = -1;
00927    class->pseudofd = -1;
00928 
00929 #ifdef HAVE_DAHDI
00930    /* Open /dev/zap/pseudo for timing...  Is
00931       there a better, yet reliable way to do this? */
00932    class->pseudofd = open(DAHDI_FILE_PSEUDO, O_RDONLY);
00933    if (class->pseudofd < 0) {
00934       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00935    } else {
00936       x = 320;
00937       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
00938    }
00939 #endif
00940 
00941    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
00942       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
00943       if (class->pseudofd > -1) {
00944          close(class->pseudofd);
00945          class->pseudofd = -1;
00946       }
00947       return -1;
00948    }
00949 
00950    return 0;
00951 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 885 of file res_musiconhold.c.

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

Referenced by moh_register().

00886 {
00887    int res;
00888 
00889    res = moh_scan_files(class);
00890 
00891    if (res < 0) {
00892       return -1;
00893    }
00894 
00895    if (!res) {
00896       if (option_verbose > 2) {
00897          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00898                class->dir, class->name);
00899       }
00900       return -1;
00901    }
00902 
00903    if (strchr(class->args, 'r')) {
00904       ast_set_flag(class, MOH_RANDOMIZE);
00905    }
00906 
00907    return 0;
00908 }

static int load_module ( void   )  [static]

Definition at line 1419 of file res_musiconhold.c.

References ao2_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(), ast_register_atexit(), cli_moh, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh0_exec(), moh1_exec(), moh2_exec(), moh3_exec(), moh4_exec(), moh_class_cmp(), and moh_class_hash().

01420 {
01421    int res;
01422 
01423    if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01424       return AST_MODULE_LOAD_DECLINE;
01425    }
01426 
01427    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01428       ast_log(LOG_WARNING, "No music on hold classes configured, "
01429             "disabling music on hold.\n");
01430    } else {
01431       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01432             local_ast_moh_cleanup);
01433    }
01434 
01435    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01436    ast_register_atexit(ast_moh_destroy);
01437    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01438    if (!res)
01439       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01440    if (!res)
01441       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01442    if (!res)
01443       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01444    if (!res)
01445       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01446 
01447    return AST_MODULE_LOAD_SUCCESS;
01448 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1150 of file res_musiconhold.c.

References ao2_callback(), mohclass::args, ast_category_browse(), ast_config_load(), ast_copy_string(), ast_variable_browse(), moh_class_malloc(), moh_class_mark(), and var.

Referenced by load_module(), and reload().

01151 {
01152    struct ast_config *cfg;
01153    struct ast_variable *var;
01154    struct mohclass *class; 
01155    char *data;
01156    char *args;
01157    char *cat;
01158    int numclasses = 0;
01159    static int dep_warning = 0;
01160 
01161    cfg = ast_config_load("musiconhold.conf");
01162 
01163    if (!cfg) {
01164       return 0;
01165    }
01166 
01167    if (reload) {
01168       ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
01169    }
01170 
01171    cat = ast_category_browse(cfg, NULL);
01172    for (; cat; cat = ast_category_browse(cfg, cat)) {
01173       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files")) {
01174          continue;
01175       }
01176 
01177       if (!(class = moh_class_malloc())) {
01178          break;
01179       }
01180 
01181       ast_copy_string(class->name, cat, sizeof(class->name));  
01182 
01183       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01184          if (!strcasecmp(var->name, "mode")) {
01185             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01186          } else if (!strcasecmp(var->name, "directory")) {
01187             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01188          } else if (!strcasecmp(var->name, "application")) {
01189             ast_copy_string(class->args, var->value, sizeof(class->args));
01190          } else if (!strcasecmp(var->name, "random")) {
01191             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01192          } else if (!strcasecmp(var->name, "format")) {
01193             class->format = ast_getformatbyname(var->value);
01194             if (!class->format) {
01195                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01196                class->format = AST_FORMAT_SLINEAR;
01197             }
01198          }
01199       }
01200 
01201       if (ast_strlen_zero(class->dir)) {
01202          if (!strcasecmp(class->mode, "custom")) {
01203             ast_copy_string(class->dir, "nodir", sizeof(class->dir));
01204          } else {
01205             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01206             class = mohclass_unref(class);
01207             continue;
01208          }
01209       }
01210 
01211       if (ast_strlen_zero(class->mode)) {
01212          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01213          class = mohclass_unref(class);
01214          continue;
01215       }
01216 
01217       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01218          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01219          class = mohclass_unref(class);
01220          continue;
01221       }
01222 
01223       /* Don't leak a class when it's already registered */
01224       if (!moh_register(class, reload)) {
01225          numclasses++;
01226       }
01227    }
01228    
01229 
01230    /* Deprecated Old-School Configuration */
01231    for (var = ast_variable_browse(cfg, "classes"); var; var = var->next) {
01232       struct mohclass *tmp_class;
01233 
01234       if (!dep_warning) {
01235          ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
01236          dep_warning = 1;
01237       }
01238 
01239       if (!(data = strchr(var->value, ':'))) {
01240          continue;
01241       }
01242       *data++ = '\0';
01243 
01244       if ((args = strchr(data, ','))) {
01245          *args++ = '\0';
01246       }
01247 
01248       /* Only skip if this is a duplicate of an above item */
01249       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01250          tmp_class = mohclass_unref(tmp_class);
01251          continue;
01252       }
01253 
01254       if (!(class = moh_class_malloc())) {
01255          break;
01256       }
01257 
01258       class->deprecated = 1;
01259       ast_copy_string(class->name, var->name, sizeof(class->name));
01260       ast_copy_string(class->dir, data, sizeof(class->dir));
01261       ast_copy_string(class->mode, var->value, sizeof(class->mode));
01262       if (args) {
01263          ast_copy_string(class->args, args, sizeof(class->args));
01264       }
01265 
01266       moh_register(class, reload);
01267       class = NULL;
01268 
01269       numclasses++;
01270    }
01271 
01272    for (var = ast_variable_browse(cfg, "moh_files"); var; var = var->next) {
01273       struct mohclass *tmp_class;
01274 
01275       if (!dep_warning) {
01276          ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
01277          dep_warning = 1;
01278       }
01279 
01280       /* Only skip if this is a duplicate of an above item */
01281       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01282          tmp_class = mohclass_unref(tmp_class);
01283          continue;
01284       }
01285 
01286       if ((args = strchr(var->value, ','))) {
01287          *args++ = '\0';
01288       }
01289 
01290       if (!(class = moh_class_malloc())) {
01291          break;
01292       }
01293 
01294       class->deprecated = 1;
01295       ast_copy_string(class->name, var->name, sizeof(class->name));
01296       ast_copy_string(class->dir, var->value, sizeof(class->dir));
01297       ast_copy_string(class->mode, "files", sizeof(class->mode));
01298       if (args) {
01299          ast_copy_string(class->args, args, sizeof(class->args));
01300       }
01301 
01302       moh_register(class, reload);
01303       class = NULL;
01304 
01305       numclasses++;
01306    }
01307 
01308    ast_config_destroy(cfg);
01309 
01310    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01311          moh_classes_delete_marked, NULL);
01312 
01313    return numclasses;
01314 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 998 of file res_musiconhold.c.

References free, and ast_channel::music_state.

Referenced by load_module(), and reload().

00999 {
01000    if (chan->music_state) {
01001       free(chan->music_state);
01002       chan->music_state = NULL;
01003    }
01004 }

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

Definition at line 1006 of file res_musiconhold.c.

References ast_activate_generator(), AST_FLAG_MOH, ast_set_flag, ast_strlen_zero(), get_mohbyname(), moh_file_stream, mohclass_unref, mohgen, ast_channel::musicclass, and mohclass::total_files.

Referenced by load_module(), and reload().

01007 {
01008    struct mohclass *mohclass = NULL;
01009    int res;
01010 
01011    /* The following is the order of preference for which class to use:
01012     * 1) The channels explicitly set musicclass, which should *only* be
01013     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01014     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01015     *    result of receiving a HOLD control frame, this should be the
01016     *    payload that came with the frame.
01017     * 3) The interpclass argument. This would be from the mohinterpret
01018     *    option from channel drivers. This is the same as the old musicclass
01019     *    option.
01020     * 4) The default class.
01021     */
01022    if (!ast_strlen_zero(chan->musicclass)) {
01023       mohclass = get_mohbyname(chan->musicclass, 1);
01024    }
01025    if (!mohclass && !ast_strlen_zero(mclass)) {
01026       mohclass = get_mohbyname(mclass, 1);
01027    }
01028    if (!mohclass && !ast_strlen_zero(interpclass)) {
01029       mohclass = get_mohbyname(interpclass, 1);
01030    }
01031    if (!mohclass) {
01032       mohclass = get_mohbyname("default", 1);
01033    }
01034 
01035    if (!mohclass) {
01036       return -1;
01037    }
01038 
01039    ast_set_flag(chan, AST_FLAG_MOH);
01040 
01041    if (mohclass->total_files) {
01042       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01043    } else {
01044       res = ast_activate_generator(chan, &mohgen, mohclass);
01045    }
01046 
01047    mohclass = mohclass_unref(mohclass);
01048 
01049    return res;
01050 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1052 of file res_musiconhold.c.

References ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_channel::music_state, and ast_channel::stream.

Referenced by load_module(), and reload().

01053 {
01054    ast_clear_flag(chan, AST_FLAG_MOH);
01055    ast_deactivate_generator(chan);
01056 
01057    if (chan->music_state) {
01058       if (chan->stream) {
01059          ast_closestream(chan->stream);
01060          chan->stream = NULL;
01061       }
01062    }
01063 }

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

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

00600 {
00601    if (ast_moh_start(chan, data, NULL)) {
00602       ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00603       return 0;
00604    }
00605    while (!ast_safe_sleep(chan, 10000));
00606    ast_moh_stop(chan);
00607    return -1;
00608 }

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

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

00611 {
00612    int res;
00613    if (!data || !atoi(data)) {
00614       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00615       return -1;
00616    }
00617    if (ast_moh_start(chan, NULL, NULL)) {
00618       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00619       return 0;
00620    }
00621    res = ast_safe_sleep(chan, atoi(data) * 1000);
00622    ast_moh_stop(chan);
00623    return res;
00624 }

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

Definition at line 626 of file res_musiconhold.c.

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

Referenced by load_module().

00627 {
00628    if (ast_strlen_zero(data)) {
00629       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00630       return -1;
00631    }
00632    ast_string_field_set(chan, musicclass, data);
00633    return 0;
00634 }

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

Definition at line 636 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), and LOG_NOTICE.

Referenced by load_module().

00637 {
00638    char *class = NULL;
00639    if (data && strlen(data))
00640       class = data;
00641    if (ast_moh_start(chan, class, NULL)) 
00642       ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00643 
00644    return 0;
00645 }

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

Definition at line 647 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00648 {
00649    ast_moh_stop(chan);
00650 
00651    return 0;
00652 }

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

Definition at line 787 of file res_musiconhold.c.

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

00788 {
00789    if (!class->allowed_files) {
00790       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00791          return -1;
00792       class->allowed_files = INITIAL_NUM_FILES;
00793    } else if (class->total_files == class->allowed_files) {
00794       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00795          class->allowed_files = 0;
00796          class->total_files = 0;
00797          return -1;
00798       }
00799       class->allowed_files *= 2;
00800    }
00801 
00802    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00803       return -1;
00804 
00805    class->total_files++;
00806 
00807    return 0;
00808 }

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

Definition at line 735 of file res_musiconhold.c.

References ast_codec2str(), ast_log(), ast_set_write_format(), ast_verbose(), LOG_WARNING, moh_release(), mohalloc(), ast_channel::name, option_verbose, mohdata::origwfmt, VERBOSE_PREFIX_3, and ast_channel::writeformat.

00736 {
00737    struct mohdata *res;
00738    struct mohclass *class = params;
00739 
00740    if ((res = mohalloc(class))) {
00741       res->origwfmt = chan->writeformat;
00742       if (ast_set_write_format(chan, class->format)) {
00743          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00744          moh_release(NULL, res);
00745          res = NULL;
00746       }
00747       if (option_verbose > 2)
00748          ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00749    }
00750    return res;
00751 }

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

Definition at line 1412 of file res_musiconhold.c.

Referenced by load_module().

01413 {
01414    struct mohclass *class = obj, *class2 = arg;
01415 
01416    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01417 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1065 of file res_musiconhold.c.

References AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_wait_for_input(), free, mohdata::list, and option_debug.

Referenced by moh_class_malloc().

01066 {
01067    struct mohclass *class = obj;
01068    struct mohdata *member;
01069 
01070    if (option_debug) {
01071       ast_log(LOG_DEBUG, "Destroying MOH class '%s'\n", class->name);
01072    }
01073 
01074    if (class->pid > 1) {
01075       char buff[8192];
01076       int bytes, tbytes = 0, stime = 0, pid = 0;
01077 
01078       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01079 
01080       stime = time(NULL) + 2;
01081       pid = class->pid;
01082       class->pid = 0;
01083 
01084       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01085        * to give the process a reason and time enough to kill off its
01086        * children. */
01087       killpg(pid, SIGHUP);
01088       usleep(100000);
01089       killpg(pid, SIGTERM);
01090       usleep(100000);
01091       killpg(pid, SIGKILL);
01092 
01093       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01094             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01095          tbytes = tbytes + bytes;
01096       }
01097 
01098       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01099 
01100       close(class->srcfd);
01101    }
01102 
01103    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01104       free(member);
01105    }
01106 
01107    if (class->thread) {
01108       pthread_cancel(class->thread);
01109       pthread_join(class->thread, NULL);
01110       class->thread = AST_PTHREADT_NULL;
01111    }
01112 
01113    if (class->filearray) {
01114       int i;
01115       for (i = 0; i < class->total_files; i++) {
01116          free(class->filearray[i]);
01117       }
01118       free(class->filearray);
01119       class->filearray = NULL;
01120    }
01121 }

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

Definition at line 1405 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01406 {
01407    const struct mohclass *class = obj;
01408 
01409    return ast_str_case_hash(class->name);
01410 }

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

Definition at line 1460 of file res_musiconhold.c.

References AST_LIST_EMPTY.

Referenced by unload_module().

01461 {
01462    struct mohclass *class = obj;
01463 
01464    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01465 }

static struct mohclass* moh_class_malloc ( void   )  [static]

Definition at line 1123 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01124 {
01125    struct mohclass *class;
01126 
01127    if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01128       class->format = AST_FORMAT_SLINEAR;
01129    }
01130 
01131    return class;
01132 }

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

Definition at line 1134 of file res_musiconhold.c.

Referenced by load_moh_classes().

01135 {
01136    struct mohclass *class = obj;
01137 
01138    class->delete = 1;
01139 
01140    return 0;
01141 }

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

Definition at line 1143 of file res_musiconhold.c.

01144 {
01145    struct mohclass *class = obj;
01146 
01147    return class->delete ? CMP_MATCH : 0;
01148 }

static int moh_classes_show ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1357 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ast_cli(), ast_getformatname(), ast_test_flag, MOH_CUSTOM, mohclass_unref, and S_OR.

01358 {
01359    struct mohclass *class;
01360    struct ao2_iterator i;
01361 
01362    i = ao2_iterator_init(mohclasses, 0);
01363 
01364    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01365       ast_cli(fd, "Class: %s\n", class->name);
01366       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01367       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01368       if (ast_test_flag(class, MOH_CUSTOM)) {
01369          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01370       }
01371       if (strcasecmp(class->mode, "files")) {
01372          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01373       }
01374    }
01375 
01376    ao2_iterator_destroy(&i);
01377 
01378    return 0;
01379 }

static int moh_cli ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1325 of file res_musiconhold.c.

References reload().

01326 {
01327    reload();
01328    return 0;
01329 }

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

Definition at line 307 of file res_musiconhold.c.

References ast_calloc, ast_random(), ast_test_flag, ast_verbose(), MOH_RANDOMIZE, mohclass_ref, ast_channel::music_state, ast_channel::name, option_verbose, VERBOSE_PREFIX_3, and ast_channel::writeformat.

00308 {
00309    struct moh_files_state *state;
00310    struct mohclass *class = params;
00311 
00312    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00313       chan->music_state = state;
00314    } else {
00315       state = chan->music_state;
00316    }
00317 
00318    if (!state) {
00319       return NULL;
00320    }
00321 
00322    if (state->class != class) {
00323       memset(state, 0, sizeof(*state));
00324       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00325          state->pos = ast_random() % class->total_files;
00326       }
00327    }
00328 
00329    state->class = mohclass_ref(class);
00330    state->origwfmt = chan->writeformat;
00331 
00332    if (option_verbose > 2) {
00333       ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", 
00334             class->name, chan->name);
00335    }
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 282 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, and moh_files_state::samples.

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

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

Definition at line 270 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00271 {
00272    struct ast_frame *f = NULL;
00273    
00274    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00275       if (!ast_moh_files_next(chan))
00276          f = ast_readframe(chan->stream);
00277    }
00278 
00279    return f;
00280 }

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

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

00185 {
00186    struct moh_files_state *state;
00187 
00188    if (!chan || !chan->music_state) {
00189       return;
00190    }
00191 
00192    state = chan->music_state;
00193 
00194    if (chan->stream) {
00195       ast_closestream(chan->stream);
00196       chan->stream = NULL;
00197    }
00198    
00199    if (option_verbose > 2) {
00200       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00201    }
00202 
00203    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00204       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00205    }
00206 
00207    state->save_pos = state->pos;
00208 
00209    mohclass_unref(state->class);
00210 }

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

Definition at line 753 of file res_musiconhold.c.

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

00754 {
00755    struct mohdata *moh = data;
00756    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00757    int res;
00758 
00759    len = ast_codec_get_len(moh->parent->format, samples);
00760 
00761    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00762       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00763       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00764    }
00765    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00766    if (res <= 0)
00767       return 0;
00768 
00769    moh->f.datalen = res;
00770    moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00771    moh->f.samples = ast_codec_get_samples(&moh->f);
00772 
00773    if (ast_write(chan, &moh->f) < 0) {
00774       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00775       return -1;
00776    }
00777 
00778    return 0;
00779 }

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

Note:
This function owns the reference it gets to moh

Definition at line 956 of file res_musiconhold.c.

References ast_log(), mohclass::delete, get_mohbyname(), init_app_class(), init_files_class(), LOG_WARNING, moh, and mohclass_unref.

00957 {
00958    struct mohclass *mohclass = NULL;
00959 
00960    if ((mohclass = get_mohbyname(moh->name, 0))) {
00961       if (!mohclass->delete) {
00962          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00963          mohclass = mohclass_unref(mohclass);
00964          moh = mohclass_unref(moh);
00965          return -1;
00966       }
00967       mohclass = mohclass_unref(mohclass);
00968    }
00969 
00970    time(&moh->start);
00971    moh->start -= respawn_time;
00972    
00973    if (!strcasecmp(moh->mode, "files")) {
00974       if (init_files_class(moh)) {
00975          moh = mohclass_unref(moh);
00976          return -1;
00977       }
00978    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
00979          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
00980          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
00981       if (init_app_class(moh)) {
00982          moh = mohclass_unref(moh);
00983          return -1;
00984       }
00985    } else {
00986       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00987       moh = mohclass_unref(moh);
00988       return -1;
00989    }
00990 
00991    ao2_link(mohclasses, moh);
00992 
00993    moh = mohclass_unref(moh);
00994    
00995    return 0;
00996 }

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

Definition at line 705 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verbose(), free, LOG_WARNING, moh, mohclass_unref, ast_channel::name, option_verbose, and VERBOSE_PREFIX_3.

Referenced by moh_alloc().

00706 {
00707    struct mohdata *moh = data;
00708    struct mohclass *class = moh->parent;
00709    int oldwfmt;
00710 
00711    ao2_lock(class);
00712    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00713    ao2_unlock(class);
00714    
00715    close(moh->pipe[0]);
00716    close(moh->pipe[1]);
00717 
00718    oldwfmt = moh->origwfmt;
00719 
00720    moh->parent = class = mohclass_unref(class);
00721 
00722    free(moh);
00723 
00724    if (chan) {
00725       if (oldwfmt && ast_set_write_format(chan, oldwfmt))  {
00726          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 
00727                chan->name, ast_getformatname(oldwfmt));
00728       }
00729       if (option_verbose > 2) {
00730          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00731       }
00732    }
00733 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 810 of file res_musiconhold.c.

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

Referenced by init_files_class().

00810                                                   {
00811 
00812    DIR *files_DIR;
00813    struct dirent *files_dirent;
00814    char path[PATH_MAX];
00815    char filepath[PATH_MAX];
00816    char *ext;
00817    struct stat statbuf;
00818    int dirnamelen;
00819    int i;
00820    
00821    files_DIR = opendir(class->dir);
00822    if (!files_DIR) {
00823       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00824       return -1;
00825    }
00826 
00827    for (i = 0; i < class->total_files; i++)
00828       free(class->filearray[i]);
00829 
00830    class->total_files = 0;
00831    dirnamelen = strlen(class->dir) + 2;
00832    if (!getcwd(path, sizeof(path))) {
00833       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00834       return -1;
00835    }
00836    if (chdir(class->dir) < 0) {
00837       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00838       return -1;
00839    }
00840    while ((files_dirent = readdir(files_DIR))) {
00841       /* The file name must be at least long enough to have the file type extension */
00842       if ((strlen(files_dirent->d_name) < 4))
00843          continue;
00844 
00845       /* Skip files that starts with a dot */
00846       if (files_dirent->d_name[0] == '.')
00847          continue;
00848 
00849       /* Skip files without extensions... they are not audio */
00850       if (!strchr(files_dirent->d_name, '.'))
00851          continue;
00852 
00853       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00854 
00855       if (stat(filepath, &statbuf))
00856          continue;
00857 
00858       if (!S_ISREG(statbuf.st_mode))
00859          continue;
00860 
00861       if ((ext = strrchr(filepath, '.'))) {
00862          *ext = '\0';
00863          ext++;
00864       }
00865 
00866       /* if the file is present in multiple formats, ensure we only put it into the list once */
00867       for (i = 0; i < class->total_files; i++)
00868          if (!strcmp(filepath, class->filearray[i]))
00869             break;
00870 
00871       if (i == class->total_files) {
00872          if (moh_add_file(class, filepath))
00873             break;
00874       }
00875    }
00876 
00877    closedir(files_DIR);
00878    if (chdir(path) < 0) {
00879       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00880       return -1;
00881    }
00882    return class->total_files;
00883 }

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

Definition at line 672 of file res_musiconhold.c.

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

Referenced by moh_alloc().

00673 {
00674    struct mohdata *moh;
00675    long flags; 
00676    
00677    if (!(moh = ast_calloc(1, sizeof(*moh))))
00678       return NULL;
00679    
00680    if (pipe(moh->pipe)) {
00681       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00682       free(moh);
00683       return NULL;
00684    }
00685 
00686    /* Make entirely non-blocking */
00687    flags = fcntl(moh->pipe[0], F_GETFL);
00688    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00689    flags = fcntl(moh->pipe[1], F_GETFL);
00690    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00691 
00692    moh->f.frametype = AST_FRAME_VOICE;
00693    moh->f.subclass = cl->format;
00694    moh->f.offset = AST_FRIENDLY_OFFSET;
00695 
00696    moh->parent = mohclass_ref(cl);
00697 
00698    ao2_lock(cl);
00699    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00700    ao2_unlock(cl);
00701    
00702    return moh;
00703 }

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

Definition at line 512 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_codec_get_len(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_samp2tv(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), len(), mohclass::list, LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, option_debug, and spawn_mp3().

Referenced by init_app_class().

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

static int reload ( void   )  [static]

Definition at line 1450 of file res_musiconhold.c.

References ast_install_music_functions(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), and local_ast_moh_stop().

01451 {
01452    if (load_moh_classes(1)) {
01453       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01454             local_ast_moh_cleanup);
01455    }
01456 
01457    return 0;
01458 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 346 of file res_musiconhold.c.

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

Referenced by monmp3thread().

00347 {
00348    int fds[2];
00349    int files = 0;
00350    char fns[MAX_MP3S][80];
00351    char *argv[MAX_MP3S + 50];
00352    char xargs[256];
00353    char *argptr;
00354    int argc = 0;
00355    DIR *dir = NULL;
00356    struct dirent *de;
00357    sigset_t signal_set, old_set;
00358 
00359    
00360    if (!strcasecmp(class->dir, "nodir")) {
00361       files = 1;
00362    } else {
00363       dir = opendir(class->dir);
00364       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00365          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00366          return -1;
00367       }
00368    }
00369 
00370    if (!ast_test_flag(class, MOH_CUSTOM)) {
00371       argv[argc++] = "mpg123";
00372       argv[argc++] = "-q";
00373       argv[argc++] = "-s";
00374       argv[argc++] = "--mono";
00375       argv[argc++] = "-r";
00376       argv[argc++] = "8000";
00377       
00378       if (!ast_test_flag(class, MOH_SINGLE)) {
00379          argv[argc++] = "-b";
00380          argv[argc++] = "2048";
00381       }
00382       
00383       argv[argc++] = "-f";
00384       
00385       if (ast_test_flag(class, MOH_QUIET))
00386          argv[argc++] = "4096";
00387       else
00388          argv[argc++] = "8192";
00389       
00390       /* Look for extra arguments and add them to the list */
00391       ast_copy_string(xargs, class->args, sizeof(xargs));
00392       argptr = xargs;
00393       while (!ast_strlen_zero(argptr)) {
00394          argv[argc++] = argptr;
00395          strsep(&argptr, ",");
00396       }
00397    } else  {
00398       /* Format arguments for argv vector */
00399       ast_copy_string(xargs, class->args, sizeof(xargs));
00400       argptr = xargs;
00401       while (!ast_strlen_zero(argptr)) {
00402          argv[argc++] = argptr;
00403          strsep(&argptr, " ");
00404       }
00405    }
00406 
00407    if (!strncasecmp(class->dir, "http://", 7)) {
00408       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00409       argv[argc++] = fns[files];
00410       files++;
00411    } else if (dir) {
00412       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00413          if ((strlen(de->d_name) > 3) && 
00414              ((ast_test_flag(class, MOH_CUSTOM) && 
00415                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00416                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00417               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00418             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00419             argv[argc++] = fns[files];
00420             files++;
00421          }
00422       }
00423    }
00424    argv[argc] = NULL;
00425    if (dir) {
00426       closedir(dir);
00427    }
00428    if (pipe(fds)) {  
00429       ast_log(LOG_WARNING, "Pipe failed\n");
00430       return -1;
00431    }
00432    if (!files) {
00433       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00434       close(fds[0]);
00435       close(fds[1]);
00436       return -1;
00437    }
00438    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00439       sleep(respawn_time - (time(NULL) - class->start));
00440    }
00441 
00442    /* Block signals during the fork() */
00443    sigfillset(&signal_set);
00444    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00445 
00446    time(&class->start);
00447    class->pid = fork();
00448    if (class->pid < 0) {
00449       close(fds[0]);
00450       close(fds[1]);
00451       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00452       return -1;
00453    }
00454    if (!class->pid) {
00455       /* Child */
00456       int x;
00457 #ifdef HAVE_CAP
00458       cap_t cap;
00459 #endif
00460       if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00461          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00462          _exit(1);
00463       }
00464 
00465       if (ast_opt_high_priority)
00466          ast_set_priority(0);
00467 
00468       /* Reset ignored signals back to default */
00469       signal(SIGPIPE, SIG_DFL);
00470       pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
00471 
00472 #ifdef HAVE_CAP
00473       cap = cap_from_text("cap_net_admin-eip");
00474 
00475       if (cap_set_proc(cap)) {
00476          ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00477       }
00478       cap_free(cap);
00479 #endif
00480       close(fds[0]);
00481       /* Stdout goes to pipe */
00482       dup2(fds[1], STDOUT_FILENO);
00483       /* Close unused file descriptors */
00484       for (x=3;x<8192;x++) {
00485          if (-1 != fcntl(x, F_GETFL)) {
00486             close(x);
00487          }
00488       }
00489       setpgid(0, getpid());
00490 
00491       if (ast_test_flag(class, MOH_CUSTOM)) {
00492          execv(argv[0], argv);
00493       } else {
00494          /* Default install is /usr/local/bin */
00495          execv(LOCAL_MPG_123, argv);
00496          /* Many places have it in /usr/bin */
00497          execv(MPG_123, argv);
00498          /* Check PATH as a last-ditch effort */
00499          execvp("mpg123", argv);
00500       }
00501       ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00502       close(fds[1]);
00503       _exit(1);
00504    } else {
00505       /* Parent */
00506       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00507       close(fds[1]);
00508    }
00509    return fds[0];
00510 }

static int unload_module ( void   )  [static]

Definition at line 1467 of file res_musiconhold.c.

References ao2_callback(), ARRAY_LEN, ast_cli_unregister_multiple(), ast_log(), ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), cli_moh, LOG_WARNING, moh_class_inuse(), and mohclass_unref.

01468 {
01469    int res = 0;
01470    struct mohclass *class = NULL;
01471 
01472    /* XXX This check shouldn't be required if module ref counting was being used
01473     * properly ... */
01474    if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01475       class = mohclass_unref(class);
01476       res = -1;
01477    }
01478 
01479    if (res < 0) {
01480       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01481       return res;
01482    }
01483 
01484    ast_uninstall_music_functions();
01485 
01486    ast_moh_destroy();
01487 
01488    res = ast_unregister_application(app0);
01489    res |= ast_unregister_application(app1);
01490    res |= ast_unregister_application(app2);
01491    res |= ast_unregister_application(app3);
01492    res |= ast_unregister_application(app4);
01493 
01494    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01495 
01496    return res;
01497 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1503 of file res_musiconhold.c.

char* app0 = "MusicOnHold" [static]

Definition at line 83 of file res_musiconhold.c.

char* app1 = "WaitMusicOnHold" [static]

Definition at line 84 of file res_musiconhold.c.

char* app2 = "SetMusicOnHold" [static]

Definition at line 85 of file res_musiconhold.c.

char* app3 = "StartMusicOnHold" [static]

Definition at line 86 of file res_musiconhold.c.

char* app4 = "StopMusicOnHold" [static]

Definition at line 87 of file res_musiconhold.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1503 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Definition at line 1391 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_moh_classes_show_deprecated [static]

Initial value:

 {
   { "moh", "classes", "show"},
   moh_classes_show, NULL,
   NULL }

Definition at line 1381 of file res_musiconhold.c.

struct ast_cli_entry cli_moh_files_show_deprecated [static]

Initial value:

 {
   { "moh", "files", "show"},
   cli_files_show, NULL,
   NULL }

Definition at line 1386 of file res_musiconhold.c.

char* descrip0 [static]

Definition at line 95 of file res_musiconhold.c.

char* descrip1 [static]

Initial value:

 "WaitMusicOnHold(delay): "
"Plays hold music specified number of seconds.  Returns 0 when\n"
"done, or -1 on hangup.  If no hold music is available, the delay will\n"
"still occur with no sound.\n"

Definition at line 102 of file res_musiconhold.c.

char* descrip2 [static]

Initial value:

 "SetMusicOnHold(class): "
"Sets the default class for music on hold for a given channel.  When\n"
"music on hold is activated, this class will be used to select which\n"
"music is played.\n"

Definition at line 107 of file res_musiconhold.c.

char* descrip3 [static]

Initial value:

 "StartMusicOnHold(class): "
"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* descrip4 [static]

Initial value:

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

Definition at line 117 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Initial value:

 {
   .alloc    = moh_files_alloc,
   .release  = moh_files_release,
   .generate = moh_files_generator,
}

Definition at line 340 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

struct ao2_container* mohclasses [static]

Definition at line 173 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Initial value:

 {
   .alloc    = moh_alloc,
   .release  = moh_release,
   .generate = moh_generate,
}

Definition at line 781 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

int respawn_time = 20 [static]

Definition at line 120 of file res_musiconhold.c.

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

Definition at line 89 of file res_musiconhold.c.

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

Definition at line 90 of file res_musiconhold.c.

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

Definition at line 91 of file res_musiconhold.c.

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

Definition at line 92 of file res_musiconhold.c.

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

Definition at line 93 of file res_musiconhold.c.


Generated on Fri Aug 13 18:21:31 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7