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 mohclass * | get_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 mohclass * | moh_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_frame * | moh_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 mohdata * | mohalloc (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_container * | mohclasses |
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" |
Routines implementing music on hold.
Definition in file res_musiconhold.c.
#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) |
Definition at line 134 of file res_musiconhold.c.
Referenced by ast_moh_files_next(), init_files_class(), load_moh_classes(), and moh_files_alloc().
#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) |
Definition at line 186 of file res_musiconhold.c.
Referenced by cli_files_show(), load_moh_classes(), 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 180 of file res_musiconhold.c.
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] |
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 }
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] |
Definition at line 1513 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().
Referenced by moh_cli().
01514 { 01515 if (load_moh_classes(1)) { 01516 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, 01517 local_ast_moh_cleanup); 01518 } 01519 01520 return 0; 01521 }
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 }
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.
struct ast_cli_entry cli_moh_classes_show_deprecated [static] |
{ { "moh", "classes", "show"}, moh_classes_show, NULL, NULL }
Definition at line 1444 of file res_musiconhold.c.
struct ast_cli_entry cli_moh_files_show_deprecated [static] |
{ { "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] |
"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] |
"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] |
"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] |
"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] |
{ .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] |
{ .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.