Thu May 14 14:50:14 2009

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include "asterisk/dahdi_compat.h"
#include <sys/capability.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/astobj2.h"

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

#define INITIAL_NUM_FILES   8
#define LOCAL_MPG_123   "/usr/local/bin/mpg123"
#define MAX_MP3S   256
#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define MOH_SINGLE   (1 << 1)
#define mohclass_ref(class)   (ao2_ref((class), +1), class)
#define mohclass_unref(class)   (ao2_ref((class), -1), (struct mohclass *) NULL)
#define MPG_123   "/usr/bin/mpg123"

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void ast_moh_destroy (void)
static int ast_moh_files_next (struct ast_channel *chan)
static int cli_files_show (int fd, int argc, char *argv[])
static struct mohclassget_mohbyname (const char *name, int warn)
static int init_app_class (struct mohclass *class)
static int init_files_class (struct mohclass *class)
static int load_module (void)
static int load_moh_classes (int reload)
static void local_ast_moh_cleanup (struct ast_channel *chan)
static int local_ast_moh_start (struct ast_channel *chan, const char *mclass, const char *interpclass)
static void local_ast_moh_stop (struct ast_channel *chan)
static int moh0_exec (struct ast_channel *chan, void *data)
static int moh1_exec (struct ast_channel *chan, void *data)
static int moh2_exec (struct ast_channel *chan, void *data)
static int moh3_exec (struct ast_channel *chan, void *data)
static int moh4_exec (struct ast_channel *chan, void *data)
static int moh_add_file (struct mohclass *class, const char *filepath)
static void * moh_alloc (struct ast_channel *chan, void *params)
static int moh_class_cmp (void *obj, void *arg, int flags)
static void moh_class_destructor (void *obj)
static int moh_class_hash (const void *obj, const int flags)
static int moh_class_inuse (void *obj, void *arg, int flags)
static struct mohclassmoh_class_malloc (void)
static int moh_class_mark (void *obj, void *arg, int flags)
static int moh_classes_delete_marked (void *obj, void *arg, int flags)
static int moh_classes_show (int fd, int argc, char *argv[])
static int moh_cli (int fd, int argc, char *argv[])
static void * moh_files_alloc (struct ast_channel *chan, void *params)
static int moh_files_generator (struct ast_channel *chan, void *data, int len, int samples)
static struct ast_framemoh_files_readframe (struct ast_channel *chan)
static void moh_files_release (struct ast_channel *chan, void *data)
static int moh_generate (struct ast_channel *chan, void *data, int len, int samples)
static int moh_register (struct mohclass *moh, int reload)
static void moh_release (struct ast_channel *chan, void *data)
static int moh_scan_files (struct mohclass *class)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int reload (void)
static int spawn_mp3 (struct mohclass *class)
static int unload_module (void)

Variables

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


Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define INITIAL_NUM_FILES   8

Definition at line 81 of file res_musiconhold.c.

Referenced by moh_add_file().

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

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

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

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_QUIET   (1 << 0)

Definition at line 132 of file res_musiconhold.c.

Referenced by init_app_class(), and spawn_mp3().

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 135 of file res_musiconhold.c.

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

#define MOH_SINGLE   (1 << 1)

Definition at line 133 of file res_musiconhold.c.

Referenced by init_app_class(), and spawn_mp3().

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

Definition at line 180 of file res_musiconhold.c.

Referenced by moh_files_alloc(), and mohalloc().

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

Definition at line 181 of file res_musiconhold.c.

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

#define MPG_123   "/usr/bin/mpg123"

Definition at line 175 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1493 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1493 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1310 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01311 {
01312    if (option_verbose > 1) {
01313       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01314    }
01315 
01316    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01317 }

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_test_flag, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

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

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

Definition at line 1325 of file res_musiconhold.c.

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

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

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

Definition at line 653 of file res_musiconhold.c.

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

Referenced by local_ast_moh_start(), and moh_register().

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

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 909 of file res_musiconhold.c.

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

Referenced by moh_register().

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

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 884 of file res_musiconhold.c.

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

Referenced by moh_register().

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

static int load_module ( void   )  [static]

Definition at line 1409 of file res_musiconhold.c.

References ao2_container_alloc(), ARRAY_LEN, ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application(), ast_register_atexit(), cli_moh, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh0_exec(), moh1_exec(), moh2_exec(), moh3_exec(), moh4_exec(), moh_class_cmp(), and moh_class_hash().

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

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1148 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

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

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 997 of file res_musiconhold.c.

References free, and ast_channel::music_state.

Referenced by load_module(), and reload().

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

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

Definition at line 1005 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

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

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1051 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

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

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

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

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

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

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

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

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

Definition at line 625 of file res_musiconhold.c.

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

Referenced by load_module().

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

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

Definition at line 635 of file res_musiconhold.c.

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

Referenced by load_module().

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

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

Definition at line 646 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

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

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

Definition at line 786 of file res_musiconhold.c.

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

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

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

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

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

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

Definition at line 1402 of file res_musiconhold.c.

Referenced by load_module().

01403 {
01404    struct mohclass *class = obj, *class2 = arg;
01405 
01406    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01407 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1064 of file res_musiconhold.c.

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

Referenced by moh_class_malloc().

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

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

Definition at line 1395 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01396 {
01397    const struct mohclass *class = obj;
01398 
01399    return ast_str_case_hash(class->name);
01400 }

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

Definition at line 1450 of file res_musiconhold.c.

References AST_LIST_EMPTY.

Referenced by unload_module().

01451 {
01452    struct mohclass *class = obj;
01453 
01454    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01455 }

static struct mohclass* moh_class_malloc ( void   )  [static]

Definition at line 1121 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

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

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

Definition at line 1132 of file res_musiconhold.c.

Referenced by load_moh_classes().

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

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

Definition at line 1141 of file res_musiconhold.c.

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

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

Definition at line 1349 of file res_musiconhold.c.

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

01350 {
01351    struct mohclass *class;
01352    struct ao2_iterator i;
01353 
01354    i = ao2_iterator_init(mohclasses, 0);
01355 
01356    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01357       ast_cli(fd, "Class: %s\n", class->name);
01358       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01359       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01360       if (ast_test_flag(class, MOH_CUSTOM)) {
01361          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01362       }
01363       if (strcasecmp(class->mode, "files")) {
01364          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01365       }
01366    }
01367 
01368    return 0;
01369 }

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

Definition at line 1319 of file res_musiconhold.c.

References reload().

01320 {
01321    reload();
01322    return 0;
01323 }

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

Definition at line 306 of file res_musiconhold.c.

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

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

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

Definition at line 281 of file res_musiconhold.c.

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

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

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

Definition at line 269 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

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

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

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

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

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

Note:
This function owns the reference it gets to moh

Definition at line 955 of file res_musiconhold.c.

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

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

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

Definition at line 704 of file res_musiconhold.c.

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

Referenced by moh_alloc().

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

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 809 of file res_musiconhold.c.

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

Referenced by init_files_class().

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

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

Definition at line 671 of file res_musiconhold.c.

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

Referenced by moh_alloc().

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

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

Definition at line 511 of file res_musiconhold.c.

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

Referenced by init_app_class().

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

static int reload ( void   )  [static]

Definition at line 1440 of file res_musiconhold.c.

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

01441 {
01442    if (load_moh_classes(1)) {
01443       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01444             local_ast_moh_cleanup);
01445    }
01446 
01447    return 0;
01448 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 345 of file res_musiconhold.c.

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

Referenced by monmp3thread().

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

static int unload_module ( void   )  [static]

Definition at line 1457 of file res_musiconhold.c.

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

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


Variable Documentation

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

Definition at line 1493 of file res_musiconhold.c.

char* app0 = "MusicOnHold" [static]

Definition at line 83 of file res_musiconhold.c.

char* app1 = "WaitMusicOnHold" [static]

Definition at line 84 of file res_musiconhold.c.

char* app2 = "SetMusicOnHold" [static]

Definition at line 85 of file res_musiconhold.c.

char* app3 = "StartMusicOnHold" [static]

Definition at line 86 of file res_musiconhold.c.

char* app4 = "StopMusicOnHold" [static]

Definition at line 87 of file res_musiconhold.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1493 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Definition at line 1381 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_moh_classes_show_deprecated [static]

Initial value:

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

Definition at line 1371 of file res_musiconhold.c.

struct ast_cli_entry cli_moh_files_show_deprecated [static]

Initial value:

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

Definition at line 1376 of file res_musiconhold.c.

char* descrip0 [static]

Definition at line 95 of file res_musiconhold.c.

char* descrip1 [static]

Initial value:

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

Definition at line 102 of file res_musiconhold.c.

char* descrip2 [static]

Initial value:

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

Definition at line 107 of file res_musiconhold.c.

char* descrip3 [static]

Initial value:

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

Definition at line 112 of file res_musiconhold.c.

char* descrip4 [static]

Initial value:

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

Definition at line 117 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Initial value:

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

Definition at line 339 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

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

Referenced by local_ast_moh_start().

int respawn_time = 20 [static]

Definition at line 120 of file res_musiconhold.c.

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

Definition at line 89 of file res_musiconhold.c.

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

Definition at line 90 of file res_musiconhold.c.

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

Definition at line 91 of file res_musiconhold.c.

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

Definition at line 92 of file res_musiconhold.c.

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

Definition at line 93 of file res_musiconhold.c.


Generated on Thu May 14 14:50:14 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7