Thu Oct 25 11:10:59 2018

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_CACHERTCLASSES   (1 << 4)
#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_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_flags global_flags [1] = {{0}}
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 179 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 181 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CACHERTCLASSES   (1 << 4)

Should we use a separate instance of MOH for each user or not

Definition at line 135 of file res_musiconhold.c.

Referenced by load_moh_classes().

#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 185 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 180 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1566 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1566 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1379 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01380 {
01381    if (option_verbose > 1) {
01382       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01383    }
01384 
01385    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01386 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 217 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, LOG_DEBUG, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, 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().

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

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

Definition at line 1394 of file res_musiconhold.c.

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

01395 {
01396    struct mohclass *class;
01397    struct ao2_iterator i;
01398 
01399    i = ao2_iterator_init(mohclasses, 0);
01400 
01401    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01402       int x;
01403 
01404       if (!class->total_files) {
01405          continue;
01406       }
01407 
01408       ast_cli(fd, "Class: %s\n", class->name);
01409 
01410       for (x = 0; x < class->total_files; x++) {
01411          ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01412       }
01413    }
01414 
01415    ao2_iterator_destroy(&i);
01416 
01417    return 0;
01418 }

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

Definition at line 682 of file res_musiconhold.c.

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

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

00683 {
00684    struct mohclass *moh = NULL;
00685    struct mohclass tmp_class = {
00686       .flags = 0,
00687    };
00688 
00689    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00690 
00691    moh = ao2_find(mohclasses, &tmp_class, 0);
00692 
00693    if (!moh && warn) {
00694       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name); 
00695    }
00696 
00697    return moh;
00698 }

static int init_app_class ( struct mohclass class  )  [static]

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

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

static int init_files_class ( struct mohclass class  )  [static]

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

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

static int load_module ( void   )  [static]

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

01483 {
01484    int res;
01485 
01486    if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01487       return AST_MODULE_LOAD_DECLINE;
01488    }
01489 
01490    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01491       ast_log(LOG_WARNING, "No music on hold classes configured, "
01492             "disabling music on hold.\n");
01493    } else {
01494       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01495             local_ast_moh_cleanup);
01496    }
01497 
01498    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01499    ast_register_atexit(ast_moh_destroy);
01500    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01501    if (!res)
01502       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01503    if (!res)
01504       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01505    if (!res)
01506       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01507    if (!res)
01508       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01509 
01510    return AST_MODULE_LOAD_SUCCESS;
01511 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1197 of file res_musiconhold.c.

References ao2_callback(), mohclass::args, ast_category_browse(), ast_clear_flag, ast_config_destroy(), ast_config_load(), ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), mohclass::delete, mohclass::deprecated, get_mohbyname(), LOG_WARNING, MOH_CACHERTCLASSES, 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().

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

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1035 of file res_musiconhold.c.

References free, and ast_channel::music_state.

Referenced by load_module(), and reload().

01036 {
01037    if (chan->music_state) {
01038       free(chan->music_state);
01039       chan->music_state = NULL;
01040    }
01041 }

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

Definition at line 1043 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

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

01091 {
01092    ast_clear_flag(chan, AST_FLAG_MOH);
01093    ast_deactivate_generator(chan);
01094 
01095    ast_channel_lock(chan);
01096    if (chan->music_state) {
01097       if (chan->stream) {
01098          ast_closestream(chan->stream);
01099          chan->stream = NULL;
01100       }
01101    }
01102    ast_channel_unlock(chan);
01103 }

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

Definition at line 627 of file res_musiconhold.c.

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

Referenced by load_module().

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

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

Definition at line 638 of file res_musiconhold.c.

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

Referenced by load_module().

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

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

Definition at line 654 of file res_musiconhold.c.

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

Referenced by load_module().

00655 {
00656    if (ast_strlen_zero(data)) {
00657       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00658       return -1;
00659    }
00660    ast_string_field_set(chan, musicclass, data);
00661    return 0;
00662 }

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

Definition at line 664 of file res_musiconhold.c.

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

Referenced by load_module().

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

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

Definition at line 675 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00676 {
00677    ast_moh_stop(chan);
00678 
00679    return 0;
00680 }

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

Definition at line 816 of file res_musiconhold.c.

References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.

Referenced by moh_scan_files().

00817 {
00818    if (!class->allowed_files) {
00819       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00820          return -1;
00821       class->allowed_files = INITIAL_NUM_FILES;
00822    } else if (class->total_files == class->allowed_files) {
00823       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00824          class->allowed_files = 0;
00825          class->total_files = 0;
00826          return -1;
00827       }
00828       class->allowed_files *= 2;
00829    }
00830 
00831    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00832       return -1;
00833 
00834    class->total_files++;
00835 
00836    return 0;
00837 }

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

Definition at line 764 of file res_musiconhold.c.

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

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

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

Definition at line 1475 of file res_musiconhold.c.

Referenced by load_module().

01476 {
01477    struct mohclass *class = obj, *class2 = arg;
01478 
01479    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01480 }

static void moh_class_destructor ( void *  obj  )  [static]

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

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

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

Definition at line 1468 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01469 {
01470    const struct mohclass *class = obj;
01471 
01472    return ast_str_case_hash(class->name);
01473 }

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

Definition at line 1523 of file res_musiconhold.c.

References AST_LIST_EMPTY.

Referenced by unload_module().

01524 {
01525    struct mohclass *class = obj;
01526 
01527    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01528 }

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

Definition at line 1168 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01169 {
01170    struct mohclass *class;
01171 
01172    if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01173       class->format = AST_FORMAT_SLINEAR;
01174       class->srcfd = -1;
01175       class->pseudofd = -1;
01176    }
01177 
01178    return class;
01179 }

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

Definition at line 1181 of file res_musiconhold.c.

Referenced by load_moh_classes().

01182 {
01183    struct mohclass *class = obj;
01184 
01185    class->delete = 1;
01186 
01187    return 0;
01188 }

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

Definition at line 1190 of file res_musiconhold.c.

Referenced by load_moh_classes().

01191 {
01192    struct mohclass *class = obj;
01193 
01194    return class->delete ? CMP_MATCH : 0;
01195 }

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

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

01421 {
01422    struct mohclass *class;
01423    struct ao2_iterator i;
01424 
01425    i = ao2_iterator_init(mohclasses, 0);
01426 
01427    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01428       ast_cli(fd, "Class: %s\n", class->name);
01429       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01430       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01431       if (ast_test_flag(class, MOH_CUSTOM)) {
01432          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01433       }
01434       if (strcasecmp(class->mode, "files")) {
01435          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01436       }
01437    }
01438 
01439    ao2_iterator_destroy(&i);
01440 
01441    return 0;
01442 }

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

Definition at line 1388 of file res_musiconhold.c.

References reload().

01389 {
01390    reload();
01391    return 0;
01392 }

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

Definition at line 333 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, option_verbose, moh_files_state::origwfmt, moh_files_state::pos, VERBOSE_PREFIX_3, and ast_channel::writeformat.

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

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

Definition at line 298 of file res_musiconhold.c.

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

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

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

Definition at line 286 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

00287 {
00288    struct ast_frame *f = NULL;
00289    
00290    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00291       if (!ast_moh_files_next(chan))
00292          f = ast_readframe(chan->stream);
00293    }
00294 
00295    return f;
00296 }

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

Definition at line 188 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, option_verbose, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_pos, ast_channel::stream, and VERBOSE_PREFIX_3.

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

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

Definition at line 782 of file res_musiconhold.c.

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

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

static int moh_register ( struct mohclass moh,
int  reload 
) [static]
Note:
This function owns the reference it gets to moh

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

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

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

Definition at line 733 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::music_state, option_verbose, mohdata::origwfmt, mohdata::parent, mohdata::pipe, and VERBOSE_PREFIX_3.

Referenced by moh_alloc().

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

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 839 of file res_musiconhold.c.

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

Referenced by init_files_class().

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

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

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

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

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

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

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

static int reload ( void   )  [static]
static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 373 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, MPG_123, and strsep().

Referenced by monmp3thread().

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

static int unload_module ( void   )  [static]

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

01531 {
01532    int res = 0;
01533    struct mohclass *class = NULL;
01534 
01535    /* XXX This check shouldn't be required if module ref counting was being used
01536     * properly ... */
01537    if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01538       class = mohclass_unref(class);
01539       res = -1;
01540    }
01541 
01542    if (res < 0) {
01543       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01544       return res;
01545    }
01546 
01547    ast_uninstall_music_functions();
01548 
01549    ast_moh_destroy();
01550 
01551    res = ast_unregister_application(app0);
01552    res |= ast_unregister_application(app1);
01553    res |= ast_unregister_application(app2);
01554    res |= ast_unregister_application(app3);
01555    res |= ast_unregister_application(app4);
01556 
01557    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01558 
01559    return res;
01560 }


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

struct ast_cli_entry cli_moh[] [static]

Definition at line 1454 of file res_musiconhold.c.

Initial value:
 {
   { "moh", "classes", "show"},
   moh_classes_show, NULL,
   NULL }

Definition at line 1444 of file res_musiconhold.c.

Initial value:
 {
   { "moh", "files", "show"},
   cli_files_show, NULL,
   NULL }

Definition at line 1449 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_flags global_flags[1] = {{0}} [static]

global MOH_ flags

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

struct ao2_container* mohclasses [static]

Definition at line 177 of file res_musiconhold.c.

struct ast_generator mohgen [static]
Initial value:
 {
   .alloc    = moh_alloc,
   .release  = moh_release,
   .generate = moh_generate,
}

Definition at line 810 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 25 Oct 2018 for Asterisk - the Open Source PBX by  doxygen 1.6.1