Thu Oct 11 06:44:15 2012

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 "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"

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_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 = AST_BUILDOPT_SUM, .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_info
ast_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 80 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 174 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 176 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CUSTOM   (1 << 2)

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

Referenced by init_app_class(), and spawn_mp3().

#define MOH_RANDOMIZE   (1 << 3)

#define MOH_SINGLE   (1 << 1)

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

Referenced by moh_files_alloc(), and mohalloc().

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

#define MPG_123   "/usr/bin/mpg123"

Definition at line 175 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1543 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1543 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1356 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01357 {
01358    if (option_verbose > 1) {
01359       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01360    }
01361 
01362    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01363 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 212 of file res_musiconhold.c.

References ast_closestream(), 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_DEBUG, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, ast_channel::name, mohclass::name, option_debug, 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().

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

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

Definition at line 1371 of file res_musiconhold.c.

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

01372 {
01373    struct mohclass *class;
01374    struct ao2_iterator i;
01375 
01376    i = ao2_iterator_init(mohclasses, 0);
01377 
01378    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01379       int x;
01380 
01381       if (!class->total_files) {
01382          continue;
01383       }
01384 
01385       ast_cli(fd, "Class: %s\n", class->name);
01386 
01387       for (x = 0; x < class->total_files; x++) {
01388          ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01389       }
01390    }
01391 
01392    ao2_iterator_destroy(&i);
01393 
01394    return 0;
01395 }

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

Definition at line 677 of file res_musiconhold.c.

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

Referenced by load_moh_classes(), local_ast_moh_start(), and moh_register().

00678 {
00679    struct mohclass *moh = NULL;
00680    struct mohclass tmp_class = {
00681       .flags = 0,
00682    };
00683 
00684    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00685 
00686    moh = ao2_find(mohclasses, &tmp_class, 0);
00687 
00688    if (!moh && warn) {
00689       ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
00690    }
00691 
00692    return moh;
00693 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 934 of file res_musiconhold.c.

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

Referenced by moh_register().

00935 {
00936 #ifdef HAVE_DAHDI
00937    int x;
00938 #endif
00939 
00940    if (!strcasecmp(class->mode, "custom")) {
00941       ast_set_flag(class, MOH_CUSTOM);
00942    } else if (!strcasecmp(class->mode, "mp3nb")) {
00943       ast_set_flag(class, MOH_SINGLE);
00944    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
00945       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
00946    } else if (!strcasecmp(class->mode, "quietmp3")) {
00947       ast_set_flag(class, MOH_QUIET);
00948    }
00949       
00950    class->srcfd = -1;
00951    class->pseudofd = -1;
00952 
00953 #ifdef HAVE_DAHDI
00954    /* Open /dev/zap/pseudo for timing...  Is
00955       there a better, yet reliable way to do this? */
00956    class->pseudofd = open(DAHDI_FILE_PSEUDO, O_RDONLY);
00957    if (class->pseudofd < 0) {
00958       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00959    } else {
00960       x = 320;
00961       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
00962    }
00963 #endif
00964 
00965    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
00966       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
00967       if (class->pseudofd > -1) {
00968          close(class->pseudofd);
00969          class->pseudofd = -1;
00970       }
00971       return -1;
00972    }
00973 
00974    return 0;
00975 }

static int init_files_class ( struct mohclass class  )  [static]

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

00909 {
00910    int res;
00911 
00912    res = moh_scan_files(class);
00913 
00914    if (res < 0) {
00915       return -1;
00916    }
00917 
00918    if (!res) {
00919       if (option_verbose > 2) {
00920          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00921                class->dir, class->name);
00922       }
00923       return -1;
00924    }
00925 
00926    if (strchr(class->args, 'r')) {
00927       ast_set_flag(class, MOH_RANDOMIZE);
00928    }
00929 
00930    return 0;
00931 }

static int load_module ( void   )  [static]

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

01460 {
01461    int res;
01462 
01463    if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01464       return AST_MODULE_LOAD_DECLINE;
01465    }
01466 
01467    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01468       ast_log(LOG_WARNING, "No music on hold classes configured, "
01469             "disabling music on hold.\n");
01470    } else {
01471       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01472             local_ast_moh_cleanup);
01473    }
01474 
01475    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01476    ast_register_atexit(ast_moh_destroy);
01477    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01478    if (!res)
01479       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01480    if (!res)
01481       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01482    if (!res)
01483       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01484    if (!res)
01485       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01486 
01487    return AST_MODULE_LOAD_SUCCESS;
01488 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1190 of file res_musiconhold.c.

References ao2_callback(), mohclass::args, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_copy_string(), AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), mohclass::delete, mohclass::deprecated, get_mohbyname(), LOG_WARNING, moh_class_malloc(), moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_register(), mohclass_unref, ast_variable::name, ast_variable::next, ast_variable::value, and var.

Referenced by load_module(), and reload().

01191 {
01192    struct ast_config *cfg;
01193    struct ast_variable *var;
01194    struct mohclass *class; 
01195    char *data;
01196    char *args;
01197    char *cat;
01198    int numclasses = 0;
01199    static int dep_warning = 0;
01200 
01201    cfg = ast_config_load("musiconhold.conf");
01202 
01203    if (!cfg) {
01204       return 0;
01205    }
01206 
01207    if (reload) {
01208       ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
01209    }
01210 
01211    cat = ast_category_browse(cfg, NULL);
01212    for (; cat; cat = ast_category_browse(cfg, cat)) {
01213       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files")) {
01214          continue;
01215       }
01216 
01217       if (!(class = moh_class_malloc())) {
01218          break;
01219       }
01220 
01221       ast_copy_string(class->name, cat, sizeof(class->name));  
01222 
01223       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01224          if (!strcasecmp(var->name, "mode")) {
01225             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01226          } else if (!strcasecmp(var->name, "directory")) {
01227             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01228          } else if (!strcasecmp(var->name, "application")) {
01229             ast_copy_string(class->args, var->value, sizeof(class->args));
01230          } else if (!strcasecmp(var->name, "random")) {
01231             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01232          } else if (!strcasecmp(var->name, "format")) {
01233             class->format = ast_getformatbyname(var->value);
01234             if (!class->format) {
01235                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01236                class->format = AST_FORMAT_SLINEAR;
01237             }
01238          }
01239       }
01240 
01241       if (ast_strlen_zero(class->dir)) {
01242          if (!strcasecmp(class->mode, "custom")) {
01243             ast_copy_string(class->dir, "nodir", sizeof(class->dir));
01244          } else {
01245             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01246             class = mohclass_unref(class);
01247             continue;
01248          }
01249       }
01250 
01251       if (ast_strlen_zero(class->mode)) {
01252          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01253          class = mohclass_unref(class);
01254          continue;
01255       }
01256 
01257       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01258          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01259          class = mohclass_unref(class);
01260          continue;
01261       }
01262 
01263       /* Don't leak a class when it's already registered */
01264       if (!moh_register(class, reload)) {
01265          numclasses++;
01266       }
01267    }
01268    
01269 
01270    /* Deprecated Old-School Configuration */
01271    for (var = ast_variable_browse(cfg, "classes"); var; var = var->next) {
01272       struct mohclass *tmp_class;
01273 
01274       if (!dep_warning) {
01275          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");
01276          dep_warning = 1;
01277       }
01278 
01279       if (!(data = strchr(var->value, ':'))) {
01280          continue;
01281       }
01282       *data++ = '\0';
01283 
01284       if ((args = strchr(data, ','))) {
01285          *args++ = '\0';
01286       }
01287 
01288       /* Only skip if this is a duplicate of an above item */
01289       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01290          tmp_class = mohclass_unref(tmp_class);
01291          continue;
01292       }
01293 
01294       if (!(class = moh_class_malloc())) {
01295          break;
01296       }
01297 
01298       class->deprecated = 1;
01299       ast_copy_string(class->name, var->name, sizeof(class->name));
01300       ast_copy_string(class->dir, data, sizeof(class->dir));
01301       ast_copy_string(class->mode, var->value, sizeof(class->mode));
01302       if (args) {
01303          ast_copy_string(class->args, args, sizeof(class->args));
01304       }
01305 
01306       moh_register(class, reload);
01307       class = NULL;
01308 
01309       numclasses++;
01310    }
01311 
01312    for (var = ast_variable_browse(cfg, "moh_files"); var; var = var->next) {
01313       struct mohclass *tmp_class;
01314 
01315       if (!dep_warning) {
01316          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");
01317          dep_warning = 1;
01318       }
01319 
01320       /* Only skip if this is a duplicate of an above item */
01321       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01322          tmp_class = mohclass_unref(tmp_class);
01323          continue;
01324       }
01325 
01326       if ((args = strchr(var->value, ','))) {
01327          *args++ = '\0';
01328       }
01329 
01330       if (!(class = moh_class_malloc())) {
01331          break;
01332       }
01333 
01334       class->deprecated = 1;
01335       ast_copy_string(class->name, var->name, sizeof(class->name));
01336       ast_copy_string(class->dir, var->value, sizeof(class->dir));
01337       ast_copy_string(class->mode, "files", sizeof(class->mode));
01338       if (args) {
01339          ast_copy_string(class->args, args, sizeof(class->args));
01340       }
01341 
01342       moh_register(class, reload);
01343       class = NULL;
01344 
01345       numclasses++;
01346    }
01347 
01348    ast_config_destroy(cfg);
01349 
01350    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01351          moh_classes_delete_marked, NULL);
01352 
01353    return numclasses;
01354 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1029 of file res_musiconhold.c.

References free, and ast_channel::music_state.

Referenced by load_module(), and reload().

01030 {
01031    if (chan->music_state) {
01032       free(chan->music_state);
01033       chan->music_state = NULL;
01034    }
01035 }

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

Definition at line 1037 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

01038 {
01039    struct mohclass *mohclass = NULL;
01040    int res;
01041 
01042    /* The following is the order of preference for which class to use:
01043     * 1) The channels explicitly set musicclass, which should *only* be
01044     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01045     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01046     *    result of receiving a HOLD control frame, this should be the
01047     *    payload that came with the frame.
01048     * 3) The interpclass argument. This would be from the mohinterpret
01049     *    option from channel drivers. This is the same as the old musicclass
01050     *    option.
01051     * 4) The default class.
01052     */
01053    if (!ast_strlen_zero(chan->musicclass)) {
01054       mohclass = get_mohbyname(chan->musicclass, 1);
01055    }
01056    if (!mohclass && !ast_strlen_zero(mclass)) {
01057       mohclass = get_mohbyname(mclass, 1);
01058    }
01059    if (!mohclass && !ast_strlen_zero(interpclass)) {
01060       mohclass = get_mohbyname(interpclass, 1);
01061    }
01062    if (!mohclass) {
01063       mohclass = get_mohbyname("default", 1);
01064    }
01065 
01066    if (!mohclass) {
01067       return -1;
01068    }
01069 
01070    ast_set_flag(chan, AST_FLAG_MOH);
01071 
01072    if (mohclass->total_files) {
01073       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01074    } else {
01075       res = ast_activate_generator(chan, &mohgen, mohclass);
01076    }
01077 
01078    mohclass = mohclass_unref(mohclass);
01079 
01080    return res;
01081 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1083 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, 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().

01084 {
01085    ast_clear_flag(chan, AST_FLAG_MOH);
01086    ast_deactivate_generator(chan);
01087 
01088    ast_channel_lock(chan);
01089    if (chan->music_state) {
01090       if (chan->stream) {
01091          ast_closestream(chan->stream);
01092          chan->stream = NULL;
01093       }
01094    }
01095    ast_channel_unlock(chan);
01096 }

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

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

00623 {
00624    if (ast_moh_start(chan, data, NULL)) {
00625       ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00626       return 0;
00627    }
00628    while (!ast_safe_sleep(chan, 10000));
00629    ast_moh_stop(chan);
00630    return -1;
00631 }

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

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

00634 {
00635    int res;
00636    if (!data || !atoi(data)) {
00637       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00638       return -1;
00639    }
00640    if (ast_moh_start(chan, NULL, NULL)) {
00641       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00642       return 0;
00643    }
00644    res = ast_safe_sleep(chan, atoi(data) * 1000);
00645    ast_moh_stop(chan);
00646    return res;
00647 }

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

Definition at line 649 of file res_musiconhold.c.

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

Referenced by load_module().

00650 {
00651    if (ast_strlen_zero(data)) {
00652       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00653       return -1;
00654    }
00655    ast_string_field_set(chan, musicclass, data);
00656    return 0;
00657 }

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

Definition at line 659 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), LOG_NOTICE, and ast_channel::name.

Referenced by load_module().

00660 {
00661    char *class = NULL;
00662    if (data && strlen(data))
00663       class = data;
00664    if (ast_moh_start(chan, class, NULL)) 
00665       ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00666 
00667    return 0;
00668 }

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

Definition at line 670 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00671 {
00672    ast_moh_stop(chan);
00673 
00674    return 0;
00675 }

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

Definition at line 810 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

00811 {
00812    if (!class->allowed_files) {
00813       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00814          return -1;
00815       class->allowed_files = INITIAL_NUM_FILES;
00816    } else if (class->total_files == class->allowed_files) {
00817       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00818          class->allowed_files = 0;
00819          class->total_files = 0;
00820          return -1;
00821       }
00822       class->allowed_files *= 2;
00823    }
00824 
00825    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00826       return -1;
00827 
00828    class->total_files++;
00829 
00830    return 0;
00831 }

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

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

00759 {
00760    struct mohdata *res;
00761    struct mohclass *class = params;
00762 
00763    if ((res = mohalloc(class))) {
00764       res->origwfmt = chan->writeformat;
00765       if (ast_set_write_format(chan, class->format)) {
00766          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00767          moh_release(NULL, res);
00768          res = NULL;
00769       }
00770       if (option_verbose > 2)
00771          ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00772    }
00773    return res;
00774 }

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

Definition at line 1452 of file res_musiconhold.c.

Referenced by load_module().

01453 {
01454    struct mohclass *class = obj, *class2 = arg;
01455 
01456    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01457 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1098 of file res_musiconhold.c.

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

Referenced by moh_class_malloc().

01099 {
01100    struct mohclass *class = obj;
01101    struct mohdata *member;
01102 
01103    if (option_debug) {
01104       ast_log(LOG_DEBUG, "Destroying MOH class '%s'\n", class->name);
01105    }
01106 
01107    if (class->pid > 1) {
01108       char buff[8192];
01109       int bytes, tbytes = 0, stime = 0, pid = 0;
01110 
01111       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01112 
01113       stime = time(NULL) + 2;
01114       pid = class->pid;
01115       class->pid = 0;
01116 
01117       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01118        * to give the process a reason and time enough to kill off its
01119        * children. */
01120       killpg(pid, SIGHUP);
01121       usleep(100000);
01122       killpg(pid, SIGTERM);
01123       usleep(100000);
01124       killpg(pid, SIGKILL);
01125 
01126       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01127             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01128          tbytes = tbytes + bytes;
01129       }
01130 
01131       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01132 
01133       close(class->srcfd);
01134    }
01135 
01136    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01137       free(member);
01138    }
01139 
01140    if (class->thread) {
01141       pthread_cancel(class->thread);
01142       pthread_join(class->thread, NULL);
01143       class->thread = AST_PTHREADT_NULL;
01144    }
01145 
01146    if (class->pseudofd > -1) {
01147       close(class->pseudofd);
01148       class->pseudofd = -1;
01149    }
01150 
01151    if (class->filearray) {
01152       int i;
01153       for (i = 0; i < class->total_files; i++) {
01154          free(class->filearray[i]);
01155       }
01156       free(class->filearray);
01157       class->filearray = NULL;
01158    }
01159 }

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

Definition at line 1445 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01446 {
01447    const struct mohclass *class = obj;
01448 
01449    return ast_str_case_hash(class->name);
01450 }

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

Definition at line 1500 of file res_musiconhold.c.

References AST_LIST_EMPTY.

Referenced by unload_module().

01501 {
01502    struct mohclass *class = obj;
01503 
01504    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01505 }

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

Definition at line 1161 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01162 {
01163    struct mohclass *class;
01164 
01165    if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01166       class->format = AST_FORMAT_SLINEAR;
01167       class->srcfd = -1;
01168       class->pseudofd = -1;
01169    }
01170 
01171    return class;
01172 }

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

Definition at line 1174 of file res_musiconhold.c.

Referenced by load_moh_classes().

01175 {
01176    struct mohclass *class = obj;
01177 
01178    class->delete = 1;
01179 
01180    return 0;
01181 }

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

Definition at line 1183 of file res_musiconhold.c.

Referenced by load_moh_classes().

01184 {
01185    struct mohclass *class = obj;
01186 
01187    return class->delete ? CMP_MATCH : 0;
01188 }

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

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

01398 {
01399    struct mohclass *class;
01400    struct ao2_iterator i;
01401 
01402    i = ao2_iterator_init(mohclasses, 0);
01403 
01404    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01405       ast_cli(fd, "Class: %s\n", class->name);
01406       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01407       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01408       if (ast_test_flag(class, MOH_CUSTOM)) {
01409          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01410       }
01411       if (strcasecmp(class->mode, "files")) {
01412          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01413       }
01414    }
01415 
01416    ao2_iterator_destroy(&i);
01417 
01418    return 0;
01419 }

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

Definition at line 1365 of file res_musiconhold.c.

References reload().

01366 {
01367    reload();
01368    return 0;
01369 }

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

Definition at line 328 of file res_musiconhold.c.

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

00329 {
00330    struct moh_files_state *state;
00331    struct mohclass *class = params;
00332 
00333    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00334       chan->music_state = state;
00335    } else {
00336       state = chan->music_state;
00337    }
00338 
00339    if (!state) {
00340       return NULL;
00341    }
00342 
00343    if (state->class != class) {
00344       memset(state, 0, sizeof(*state));
00345       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00346          state->pos = ast_random() % class->total_files;
00347       }
00348    }
00349 
00350    state->class = mohclass_ref(class);
00351    state->origwfmt = chan->writeformat;
00352 
00353    if (option_verbose > 2) {
00354       ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", 
00355             class->name, chan->name);
00356    }
00357    
00358    return chan->music_state;
00359 }

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

Definition at line 293 of file res_musiconhold.c.

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

00294 {
00295    struct moh_files_state *state = chan->music_state;
00296    struct ast_frame *f = NULL;
00297    int res = 0;
00298 
00299    state->sample_queue += samples;
00300 
00301    while (state->sample_queue > 0) {
00302       ast_channel_lock(chan);
00303       if ((f = moh_files_readframe(chan))) {
00304          /* We need to be sure that we unlock
00305           * the channel prior to calling
00306           * ast_write. Otherwise, the recursive locking
00307           * that occurs can cause deadlocks when using
00308           * indirect channels, like local channels
00309           */
00310          ast_channel_unlock(chan);
00311          state->samples += f->samples;
00312          state->sample_queue -= f->samples;
00313          res = ast_write(chan, f);
00314          ast_frfree(f);
00315          if (res < 0) {
00316             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00317             return -1;
00318          }
00319       } else {
00320          ast_channel_unlock(chan);
00321          return -1;  
00322       }
00323    }
00324    return res;
00325 }

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

Definition at line 281 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00282 {
00283    struct ast_frame *f = NULL;
00284    
00285    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00286       if (!ast_moh_files_next(chan))
00287          f = ast_readframe(chan->stream);
00288    }
00289 
00290    return f;
00291 }

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

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

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

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

Definition at line 776 of file res_musiconhold.c.

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

00777 {
00778    struct mohdata *moh = data;
00779    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00780    int res;
00781 
00782    len = ast_codec_get_len(moh->parent->format, samples);
00783 
00784    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00785       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00786       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00787    }
00788    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00789    if (res <= 0)
00790       return 0;
00791 
00792    moh->f.datalen = res;
00793    moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00794    moh->f.samples = ast_codec_get_samples(&moh->f);
00795 
00796    if (ast_write(chan, &moh->f) < 0) {
00797       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00798       return -1;
00799    }
00800 
00801    return 0;
00802 }

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

Note:
This function owns the reference it gets to moh

Definition at line 981 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

00982 {
00983    struct mohclass *mohclass = NULL;
00984 
00985    if ((mohclass = get_mohbyname(moh->name, 0))) {
00986       if (!mohclass->delete) {
00987          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00988          mohclass = mohclass_unref(mohclass);
00989          moh = mohclass_unref(moh);
00990          return -1;
00991       }
00992       mohclass = mohclass_unref(mohclass);
00993    }
00994 
00995    time(&moh->start);
00996    moh->start -= respawn_time;
00997    
00998    if (!strcasecmp(moh->mode, "files")) {
00999       if (init_files_class(moh)) {
01000          moh = mohclass_unref(moh);
01001          return -1;
01002       }
01003    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01004          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
01005          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01006 #ifdef HAVE_WORKING_FORK
01007       if (init_app_class(moh)) {
01008          moh = mohclass_unref(moh);
01009          return -1;
01010       }
01011 #else
01012       ast_log(LOG_WARNING, "Cannot use mode '%s' music on hold, as there is no working fork().\n", moh->mode);
01013       moh = mohclass_unref(moh);
01014       return -1;
01015 #endif
01016    } else {
01017       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01018       moh = mohclass_unref(moh);
01019       return -1;
01020    }
01021 
01022    ao2_link(mohclasses, moh);
01023 
01024    moh = mohclass_unref(moh);
01025    
01026    return 0;
01027 }

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

Definition at line 728 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, mohclass::members, moh, mohclass_unref, ast_channel::name, option_verbose, mohdata::origwfmt, mohdata::parent, mohdata::pipe, and VERBOSE_PREFIX_3.

Referenced by moh_alloc().

00729 {
00730    struct mohdata *moh = data;
00731    struct mohclass *class = moh->parent;
00732    int oldwfmt;
00733 
00734    ao2_lock(class);
00735    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00736    ao2_unlock(class);
00737    
00738    close(moh->pipe[0]);
00739    close(moh->pipe[1]);
00740 
00741    oldwfmt = moh->origwfmt;
00742 
00743    moh->parent = class = mohclass_unref(class);
00744 
00745    free(moh);
00746 
00747    if (chan) {
00748       if (oldwfmt && ast_set_write_format(chan, oldwfmt))  {
00749          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 
00750                chan->name, ast_getformatname(oldwfmt));
00751       }
00752       if (option_verbose > 2) {
00753          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00754       }
00755    }
00756 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 833 of file res_musiconhold.c.

References ast_log(), errno, ext, free, LOG_WARNING, and moh_add_file().

Referenced by init_files_class().

00833                                                   {
00834 
00835    DIR *files_DIR;
00836    struct dirent *files_dirent;
00837    char path[PATH_MAX];
00838    char filepath[PATH_MAX];
00839    char *ext;
00840    struct stat statbuf;
00841    int dirnamelen;
00842    int i;
00843    
00844    files_DIR = opendir(class->dir);
00845    if (!files_DIR) {
00846       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00847       return -1;
00848    }
00849 
00850    for (i = 0; i < class->total_files; i++)
00851       free(class->filearray[i]);
00852 
00853    class->total_files = 0;
00854    dirnamelen = strlen(class->dir) + 2;
00855    if (!getcwd(path, sizeof(path))) {
00856       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00857       return -1;
00858    }
00859    if (chdir(class->dir) < 0) {
00860       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00861       return -1;
00862    }
00863    while ((files_dirent = readdir(files_DIR))) {
00864       /* The file name must be at least long enough to have the file type extension */
00865       if ((strlen(files_dirent->d_name) < 4))
00866          continue;
00867 
00868       /* Skip files that starts with a dot */
00869       if (files_dirent->d_name[0] == '.')
00870          continue;
00871 
00872       /* Skip files without extensions... they are not audio */
00873       if (!strchr(files_dirent->d_name, '.'))
00874          continue;
00875 
00876       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00877 
00878       if (stat(filepath, &statbuf))
00879          continue;
00880 
00881       if (!S_ISREG(statbuf.st_mode))
00882          continue;
00883 
00884       if ((ext = strrchr(filepath, '.'))) {
00885          *ext = '\0';
00886          ext++;
00887       }
00888 
00889       /* if the file is present in multiple formats, ensure we only put it into the list once */
00890       for (i = 0; i < class->total_files; i++)
00891          if (!strcmp(filepath, class->filearray[i]))
00892             break;
00893 
00894       if (i == class->total_files) {
00895          if (moh_add_file(class, filepath))
00896             break;
00897       }
00898    }
00899 
00900    closedir(files_DIR);
00901    if (chdir(path) < 0) {
00902       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00903       return -1;
00904    }
00905    return class->total_files;
00906 }

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

Definition at line 695 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, mohdata::f, mohclass::flags, mohclass::format, ast_frame::frametype, free, LOG_WARNING, mohclass::members, moh, mohclass_ref, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

00696 {
00697    struct mohdata *moh;
00698    long flags; 
00699    
00700    if (!(moh = ast_calloc(1, sizeof(*moh))))
00701       return NULL;
00702    
00703    if (pipe(moh->pipe)) {
00704       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00705       free(moh);
00706       return NULL;
00707    }
00708 
00709    /* Make entirely non-blocking */
00710    flags = fcntl(moh->pipe[0], F_GETFL);
00711    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00712    flags = fcntl(moh->pipe[1], F_GETFL);
00713    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00714 
00715    moh->f.frametype = AST_FRAME_VOICE;
00716    moh->f.subclass = cl->format;
00717    moh->f.offset = AST_FRIENDLY_OFFSET;
00718 
00719    moh->parent = mohclass_ref(cl);
00720 
00721    ao2_lock(cl);
00722    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00723    ao2_unlock(cl);
00724    
00725    return moh;
00726 }

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

Definition at line 534 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(), LOG_DEBUG, LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, option_debug, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class().

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

static int reload ( void   )  [static]

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 368 of file res_musiconhold.c.

References 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, and MPG_123.

Referenced by monmp3thread().

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

static int unload_module ( void   )  [static]

Definition at line 1507 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(), LOG_WARNING, moh_class_inuse(), and mohclass_unref.

01508 {
01509    int res = 0;
01510    struct mohclass *class = NULL;
01511 
01512    /* XXX This check shouldn't be required if module ref counting was being used
01513     * properly ... */
01514    if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01515       class = mohclass_unref(class);
01516       res = -1;
01517    }
01518 
01519    if (res < 0) {
01520       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01521       return res;
01522    }
01523 
01524    ast_uninstall_music_functions();
01525 
01526    ast_moh_destroy();
01527 
01528    res = ast_unregister_application(app0);
01529    res |= ast_unregister_application(app1);
01530    res |= ast_unregister_application(app2);
01531    res |= ast_unregister_application(app3);
01532    res |= ast_unregister_application(app4);
01533 
01534    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01535 
01536    return res;
01537 }


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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1543 of file res_musiconhold.c.

char* app0 = "MusicOnHold" [static]

Definition at line 82 of file res_musiconhold.c.

char* app1 = "WaitMusicOnHold" [static]

Definition at line 83 of file res_musiconhold.c.

char* app2 = "SetMusicOnHold" [static]

Definition at line 84 of file res_musiconhold.c.

char* app3 = "StartMusicOnHold" [static]

Definition at line 85 of file res_musiconhold.c.

char* app4 = "StopMusicOnHold" [static]

Definition at line 86 of file res_musiconhold.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1543 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Definition at line 1431 of file res_musiconhold.c.

Initial value:

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

Definition at line 1421 of file res_musiconhold.c.

Initial value:

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

Definition at line 1426 of file res_musiconhold.c.

char* descrip0 [static]

Definition at line 94 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 101 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 106 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 111 of file res_musiconhold.c.

char* descrip4 [static]

Initial value:

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

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

struct ao2_container* mohclasses [static]

Definition at line 172 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Initial value:

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

Definition at line 804 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 119 of file res_musiconhold.c.

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

Definition at line 88 of file res_musiconhold.c.

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

Definition at line 89 of file res_musiconhold.c.

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

Definition at line 90 of file res_musiconhold.c.

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

Definition at line 91 of file res_musiconhold.c.

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

Definition at line 92 of file res_musiconhold.c.


Generated on Thu Oct 11 06:44:15 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6