Routines implementing music on hold. More...
#include "asterisk.h"#include <ctype.h>#include <signal.h>#include <sys/time.h>#include <sys/signal.h>#include <netinet/in.h>#include <sys/stat.h>#include <dirent.h>#include "asterisk/lock.h"#include "asterisk/file.h"#include "asterisk/channel.h"#include "asterisk/pbx.h"#include "asterisk/app.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/manager.h"#include "asterisk/paths.h"#include "asterisk/astobj2.h"#include "asterisk/timing.h"#include "asterisk/time.h"#include "asterisk/poll-compat.h"Go to the source code of this file.
Data Structures | |
| struct | moh_files_state |
| struct | mohclass |
| struct | mohdata |
Defines | |
| #define | DONT_UNREF 0 |
| #define | get_mohbyname(a, b, c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
| #define | HANDLE_REF 1 |
| #define | INITIAL_NUM_FILES 8 |
| #define | LOCAL_MPG_123 "/usr/local/bin/mpg123" |
| #define | MAX_MP3S 256 |
| #define | MOH_CACHERTCLASSES (1 << 5) |
| #define | moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__) |
| #define | MOH_CUSTOM (1 << 2) |
| #define | MOH_MS_INTERVAL 100 |
| #define | MOH_NOTDELETED (1 << 30) |
| #define | MOH_QUIET (1 << 0) |
| #define | MOH_RANDOMIZE (1 << 3) |
| #define | moh_register(a, b, c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
| #define | MOH_SINGLE (1 << 1) |
| #define | MOH_SORTALPHA (1 << 4) |
| #define | mohclass_ref(class, string) (ao2_t_ref((class), +1, (string)), class) |
| #define | mohclass_unref(class, string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL) |
| #define | MPG_123 "/usr/bin/mpg123" |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static struct mohclass * | _get_mohbyname (const char *name, int warn, int flags, const char *file, int lineno, const char *funcname) |
| static struct mohclass * | _moh_class_malloc (const char *file, int line, const char *funcname) |
| static int | _moh_register (struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname) |
| static void | ast_moh_destroy (void) |
| static int | ast_moh_files_next (struct ast_channel *chan) |
| static struct mohclass * | get_mohbydigit (char digit) |
| static char * | handle_cli_moh_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_cli_moh_show_classes (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_cli_moh_show_files (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| 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 | 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 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_diff (struct mohclass *old, struct mohclass *new) |
| static int | moh_digit_match (void *obj, void *arg, int flags) |
| 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 void | moh_handle_digit (struct ast_channel *chan, char digit) |
| static void | moh_release (struct ast_channel *chan, void *data) |
| static void | moh_rescan_files (void) |
| static int | moh_scan_files (struct mohclass *class) |
| static int | moh_sort_compare (const void *i1, const void *i2) |
| static struct mohdata * | mohalloc (struct mohclass *cl) |
| static void * | monmp3thread (void *data) |
| static int | play_moh_exec (struct ast_channel *chan, const char *data) |
| static int | reload (void) |
| static int | set_moh_exec (struct ast_channel *chan, const char *data) |
| static int | spawn_mp3 (struct mohclass *class) |
| static int | start_moh_exec (struct ast_channel *chan, const char *data) |
| static int | stop_moh_exec (struct ast_channel *chan, const char *data) |
| static int | unload_module (void) |
| static int | wait_moh_exec (struct ast_channel *chan, const char *data) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = "95089850e3c922fa176f9bd274fd8109" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static struct ast_cli_entry | cli_moh [] |
| 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 const char | play_moh [] = "MusicOnHold" |
| static int | respawn_time = 20 |
| static const char | set_moh [] = "SetMusicOnHold" |
| static const char | start_moh [] = "StartMusicOnHold" |
| static const char | stop_moh [] = "StopMusicOnHold" |
| static const char | wait_moh [] = "WaitMusicOnHold" |
Routines implementing music on hold.
Definition in file res_musiconhold.c.
| #define DONT_UNREF 0 |
Definition at line 72 of file res_musiconhold.c.
Referenced by local_ast_moh_start().
| #define get_mohbyname | ( | a, | |||
| b, | |||||
| c | ) | _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
Definition at line 845 of file res_musiconhold.c.
Referenced by local_ast_moh_start().
| #define HANDLE_REF 1 |
Definition at line 71 of file res_musiconhold.c.
Referenced by load_moh_classes().
| #define INITIAL_NUM_FILES 8 |
Definition at line 70 of file res_musiconhold.c.
Referenced by moh_add_file().
| #define LOCAL_MPG_123 "/usr/local/bin/mpg123" |
Definition at line 221 of file res_musiconhold.c.
| #define MAX_MP3S 256 |
Definition at line 223 of file res_musiconhold.c.
Referenced by spawn_mp3().
| #define MOH_CACHERTCLASSES (1 << 5) |
Should we use a separate instance of MOH for each user or not
Definition at line 174 of file res_musiconhold.c.
Referenced by load_moh_classes(), and local_ast_moh_start().
| #define moh_class_malloc | ( | ) | _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__) |
Definition at line 1308 of file res_musiconhold.c.
Referenced by load_moh_classes(), and local_ast_moh_start().
| #define MOH_CUSTOM (1 << 2) |
Definition at line 170 of file res_musiconhold.c.
Referenced by handle_cli_moh_show_classes(), init_app_class(), local_ast_moh_start(), and spawn_mp3().
| #define MOH_MS_INTERVAL 100 |
Referenced by monmp3thread().
| #define MOH_NOTDELETED (1 << 30) |
Find only records that aren't deleted?
Definition at line 177 of file res_musiconhold.c.
Referenced by _moh_register(), and moh_class_cmp().
| #define MOH_QUIET (1 << 0) |
Definition at line 168 of file res_musiconhold.c.
Referenced by init_app_class(), local_ast_moh_start(), and spawn_mp3().
| #define MOH_RANDOMIZE (1 << 3) |
Definition at line 171 of file res_musiconhold.c.
Referenced by ast_moh_files_next(), init_files_class(), load_moh_classes(), local_ast_moh_start(), and moh_files_alloc().
| #define moh_register | ( | a, | |||
| b, | |||||
| c | ) | _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
Definition at line 1233 of file res_musiconhold.c.
Referenced by load_moh_classes(), and local_ast_moh_start().
| #define MOH_SINGLE (1 << 1) |
Definition at line 169 of file res_musiconhold.c.
Referenced by init_app_class(), local_ast_moh_start(), and spawn_mp3().
| #define MOH_SORTALPHA (1 << 4) |
Definition at line 172 of file res_musiconhold.c.
Referenced by load_moh_classes(), local_ast_moh_start(), and moh_scan_files().
Definition at line 227 of file res_musiconhold.c.
Referenced by moh_alloc(), moh_files_alloc(), and mohalloc().
| #define mohclass_unref | ( | class, | |||
| string | ) | (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL) |
Definition at line 230 of file res_musiconhold.c.
Referenced by _moh_register(), handle_cli_moh_show_classes(), handle_cli_moh_show_files(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), moh_alloc(), moh_files_alloc(), moh_files_release(), moh_handle_digit(), moh_release(), and unload_module().
| #define MPG_123 "/usr/bin/mpg123" |
Definition at line 222 of file res_musiconhold.c.
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1998 of file res_musiconhold.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1998 of file res_musiconhold.c.
| static struct mohclass* _get_mohbyname | ( | const char * | name, | |
| int | warn, | |||
| int | flags, | |||
| const char * | file, | |||
| int | lineno, | |||
| const char * | funcname | |||
| ) | [static, read] |
Definition at line 847 of file res_musiconhold.c.
References __ao2_find(), __ao2_find_debug(), ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, and mohclass::name.
Referenced by _moh_register().
00848 { 00849 struct mohclass *moh = NULL; 00850 struct mohclass tmp_class = { 00851 .flags = 0, 00852 }; 00853 00854 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name)); 00855 00856 #ifdef REF_DEBUG 00857 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname); 00858 #else 00859 moh = __ao2_find(mohclasses, &tmp_class, flags); 00860 #endif 00861 00862 if (!moh && warn) { 00863 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name); 00864 } 00865 00866 return moh; 00867 }
| static struct mohclass* _moh_class_malloc | ( | const char * | file, | |
| int | line, | |||
| const char * | funcname | |||
| ) | [static, read] |
Definition at line 1310 of file res_musiconhold.c.
References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().
01311 { 01312 struct mohclass *class; 01313 01314 if ((class = 01315 #ifdef REF_DEBUG 01316 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1) 01317 #elif defined(__AST_DEBUG_MALLOC) 01318 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0) 01319 #else 01320 ao2_alloc(sizeof(*class), moh_class_destructor) 01321 #endif 01322 )) { 01323 class->format = AST_FORMAT_SLINEAR; 01324 class->srcfd = -1; 01325 } 01326 01327 return class; 01328 }
| static int _moh_register | ( | struct mohclass * | moh, | |
| int | reload, | |||
| int | unref, | |||
| const char * | file, | |||
| int | line, | |||
| const char * | funcname | |||
| ) | [static] |
Definition at line 1234 of file res_musiconhold.c.
References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, mohclass::mode, moh_diff(), MOH_NOTDELETED, mohclass_unref, mohclass::name, and mohclass::start.
01235 { 01236 struct mohclass *mohclass = NULL; 01237 01238 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname); 01239 01240 if (mohclass && !moh_diff(mohclass, moh)) { 01241 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); 01242 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01243 if (unref) { 01244 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)"); 01245 } 01246 return -1; 01247 } else if (mohclass) { 01248 /* Found a class, but it's different from the one being registered */ 01249 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01250 } 01251 01252 time(&moh->start); 01253 moh->start -= respawn_time; 01254 01255 if (!strcasecmp(moh->mode, "files")) { 01256 if (init_files_class(moh)) { 01257 if (unref) { 01258 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)"); 01259 } 01260 return -1; 01261 } 01262 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 01263 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 01264 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { 01265 if (init_app_class(moh)) { 01266 if (unref) { 01267 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)"); 01268 } 01269 return -1; 01270 } 01271 } else { 01272 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); 01273 if (unref) { 01274 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)"); 01275 } 01276 return -1; 01277 } 01278 01279 ao2_t_link(mohclasses, moh, "Adding class to container"); 01280 01281 if (unref) { 01282 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container"); 01283 } 01284 01285 return 0; 01286 }
| static void ast_moh_destroy | ( | void | ) | [static] |
Definition at line 1786 of file res_musiconhold.c.
References ao2_ref, ao2_t_callback, ast_verb, OBJ_MULTIPLE, OBJ_NODATA, and OBJ_UNLINK.
Referenced by load_module(), and unload_module().
01787 { 01788 ast_verb(2, "Destroying musiconhold processes\n"); 01789 if (mohclasses) { 01790 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback"); 01791 ao2_ref(mohclasses, -1); 01792 mohclasses = NULL; 01793 } 01794 }
| static int ast_moh_files_next | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 284 of file res_musiconhold.c.
References ast_closestream(), ast_copy_string(), ast_debug, ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_seekstream(), ast_strlen_zero(), ast_tellstream(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, 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().
00285 { 00286 struct moh_files_state *state = chan->music_state; 00287 int tries; 00288 00289 /* Discontinue a stream if it is running already */ 00290 if (chan->stream) { 00291 ast_closestream(chan->stream); 00292 chan->stream = NULL; 00293 } 00294 00295 if (!state->class->total_files) { 00296 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name); 00297 return -1; 00298 } 00299 00300 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) { 00301 /* First time so lets play the file. */ 00302 state->save_pos = -1; 00303 } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) { 00304 /* If a specific file has been saved confirm it still exists and that it is still valid */ 00305 state->pos = state->save_pos; 00306 state->save_pos = -1; 00307 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) { 00308 /* Get a random file and ensure we can open it */ 00309 for (tries = 0; tries < 20; tries++) { 00310 state->pos = ast_random() % state->class->total_files; 00311 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) { 00312 break; 00313 } 00314 } 00315 state->save_pos = -1; 00316 state->samples = 0; 00317 } else { 00318 /* This is easy, just increment our position and make sure we don't exceed the total file count */ 00319 state->pos++; 00320 state->pos %= state->class->total_files; 00321 state->save_pos = -1; 00322 state->samples = 0; 00323 } 00324 00325 for (tries = 0; tries < state->class->total_files; ++tries) { 00326 if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) { 00327 break; 00328 } 00329 00330 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno)); 00331 state->pos++; 00332 state->pos %= state->class->total_files; 00333 } 00334 00335 if (tries == state->class->total_files) { 00336 return -1; 00337 } 00338 00339 /* Record the pointer to the filename for position resuming later */ 00340 ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename)); 00341 00342 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); 00343 00344 if (state->samples) { 00345 size_t loc; 00346 /* seek *SHOULD* be good since it's from a known location */ 00347 ast_seekstream(chan->stream, state->samples, SEEK_SET); 00348 /* if the seek failed then recover because if there is not a valid read, 00349 * moh_files_generate will return -1 and MOH will stop */ 00350 loc = ast_tellstream(chan->stream); 00351 if (state->samples > loc && loc) { 00352 /* seek one sample from the end for one guaranteed valid read */ 00353 ast_seekstream(chan->stream, 1, SEEK_END); 00354 } 00355 } 00356 00357 return 0; 00358 }
| static struct mohclass* get_mohbydigit | ( | char | digit | ) | [static, read] |
Definition at line 457 of file res_musiconhold.c.
References ao2_t_callback, and moh_digit_match().
Referenced by moh_handle_digit().
00458 { 00459 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback"); 00460 }
| static char* handle_cli_moh_reload | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 1796 of file res_musiconhold.c.
References ast_cli_args::argc, ast_cli_entry::args, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, reload(), and ast_cli_entry::usage.
01797 { 01798 switch (cmd) { 01799 case CLI_INIT: 01800 e->command = "moh reload"; 01801 e->usage = 01802 "Usage: moh reload\n" 01803 " Reloads the MusicOnHold module.\n" 01804 " Alias for 'module reload res_musiconhold.so'\n"; 01805 return NULL; 01806 case CLI_GENERATE: 01807 return NULL; 01808 } 01809 01810 if (a->argc != e->args) 01811 return CLI_SHOWUSAGE; 01812 01813 reload(); 01814 01815 return CLI_SUCCESS; 01816 }
| static char* handle_cli_moh_show_classes | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 1856 of file res_musiconhold.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), ast_getformatname(), ast_test_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, MOH_CUSTOM, mohclass_unref, S_OR, and ast_cli_entry::usage.
01857 { 01858 struct mohclass *class; 01859 struct ao2_iterator i; 01860 01861 switch (cmd) { 01862 case CLI_INIT: 01863 e->command = "moh show classes"; 01864 e->usage = 01865 "Usage: moh show classes\n" 01866 " Lists all MusicOnHold classes.\n"; 01867 return NULL; 01868 case CLI_GENERATE: 01869 return NULL; 01870 } 01871 01872 if (a->argc != e->args) 01873 return CLI_SHOWUSAGE; 01874 01875 i = ao2_iterator_init(mohclasses, 0); 01876 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) { 01877 ast_cli(a->fd, "Class: %s\n", class->name); 01878 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>")); 01879 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>")); 01880 if (ast_test_flag(class, MOH_CUSTOM)) { 01881 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>")); 01882 } 01883 if (strcasecmp(class->mode, "files")) { 01884 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); 01885 } 01886 } 01887 ao2_iterator_destroy(&i); 01888 01889 return CLI_SUCCESS; 01890 }
| static char* handle_cli_moh_show_files | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 1818 of file res_musiconhold.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, mohclass_unref, and ast_cli_entry::usage.
01819 { 01820 struct mohclass *class; 01821 struct ao2_iterator i; 01822 01823 switch (cmd) { 01824 case CLI_INIT: 01825 e->command = "moh show files"; 01826 e->usage = 01827 "Usage: moh show files\n" 01828 " Lists all loaded file-based MusicOnHold classes and their\n" 01829 " files.\n"; 01830 return NULL; 01831 case CLI_GENERATE: 01832 return NULL; 01833 } 01834 01835 if (a->argc != e->args) 01836 return CLI_SHOWUSAGE; 01837 01838 i = ao2_iterator_init(mohclasses, 0); 01839 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) { 01840 int x; 01841 01842 if (!class->total_files) { 01843 continue; 01844 } 01845 01846 ast_cli(a->fd, "Class: %s\n", class->name); 01847 for (x = 0; x < class->total_files; x++) { 01848 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]); 01849 } 01850 } 01851 ao2_iterator_destroy(&i); 01852 01853 return CLI_SUCCESS; 01854 }
| static int init_app_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1194 of file res_musiconhold.c.
References ast_log(), ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), errno, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and monmp3thread().
Referenced by _moh_register().
01195 { 01196 if (!strcasecmp(class->mode, "custom")) { 01197 ast_set_flag(class, MOH_CUSTOM); 01198 } else if (!strcasecmp(class->mode, "mp3nb")) { 01199 ast_set_flag(class, MOH_SINGLE); 01200 } else if (!strcasecmp(class->mode, "quietmp3nb")) { 01201 ast_set_flag(class, MOH_SINGLE | MOH_QUIET); 01202 } else if (!strcasecmp(class->mode, "quietmp3")) { 01203 ast_set_flag(class, MOH_QUIET); 01204 } 01205 01206 class->srcfd = -1; 01207 01208 if (!(class->timer = ast_timer_open())) { 01209 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01210 return -1; 01211 } 01212 if (class->timer && ast_timer_set_rate(class->timer, 25)) { 01213 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01214 ast_timer_close(class->timer); 01215 class->timer = NULL; 01216 } 01217 01218 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) { 01219 ast_log(LOG_WARNING, "Unable to create moh thread...\n"); 01220 if (class->timer) { 01221 ast_timer_close(class->timer); 01222 class->timer = NULL; 01223 } 01224 return -1; 01225 } 01226 01227 return 0; 01228 }
| static int init_files_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1131 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().
01132 { 01133 int res; 01134 01135 res = moh_scan_files(class); 01136 01137 if (res < 0) { 01138 return -1; 01139 } 01140 01141 if (!res) { 01142 if (option_verbose > 2) { 01143 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", 01144 class->dir, class->name); 01145 } 01146 return -1; 01147 } 01148 01149 #if 0 01150 /* XXX This isn't correct. Args is an application for custom mode. XXX */ 01151 if (strchr(class->args, 'r')) { 01152 ast_set_flag(class, MOH_RANDOMIZE); 01153 } 01154 #endif 01155 01156 return 0; 01157 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1914 of file res_musiconhold.c.
References ao2_t_container_alloc, ARRAY_LEN, ast_check_realtime(), ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application_xml, ast_register_atexit(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().
01915 { 01916 int res; 01917 01918 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) { 01919 return AST_MODULE_LOAD_DECLINE; 01920 } 01921 01922 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */ 01923 ast_log(LOG_WARNING, "No music on hold classes configured, " 01924 "disabling music on hold.\n"); 01925 } else { 01926 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, 01927 local_ast_moh_cleanup); 01928 } 01929 01930 res = ast_register_application_xml(play_moh, play_moh_exec); 01931 ast_register_atexit(ast_moh_destroy); 01932 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01933 if (!res) 01934 res = ast_register_application_xml(wait_moh, wait_moh_exec); 01935 if (!res) 01936 res = ast_register_application_xml(set_moh, set_moh_exec); 01937 if (!res) 01938 res = ast_register_application_xml(start_moh, start_moh_exec); 01939 if (!res) 01940 res = ast_register_application_xml(stop_moh, stop_moh_exec); 01941 01942 return AST_MODULE_LOAD_SUCCESS; 01943 }
| static int load_moh_classes | ( | int | reload | ) | [static] |
Definition at line 1676 of file res_musiconhold.c.
References ao2_t_callback, ast_category_browse(), ast_check_realtime(), 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_set_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, HANDLE_REF, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_register, moh_rescan_files(), MOH_SORTALPHA, mohclass_unref, ast_variable::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, ast_variable::value, and var.
Referenced by load_module(), and reload().
01677 { 01678 struct ast_config *cfg; 01679 struct ast_variable *var; 01680 struct mohclass *class; 01681 char *cat; 01682 int numclasses = 0; 01683 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 01684 01685 cfg = ast_config_load("musiconhold.conf", config_flags); 01686 01687 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { 01688 if (ast_check_realtime("musiconhold") && reload) { 01689 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01690 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes"); 01691 } 01692 return 0; 01693 } 01694 if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 01695 moh_rescan_files(); 01696 return 0; 01697 } 01698 01699 if (reload) { 01700 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01701 } 01702 01703 ast_clear_flag(global_flags, AST_FLAGS_ALL); 01704 01705 cat = ast_category_browse(cfg, NULL); 01706 for (; cat; cat = ast_category_browse(cfg, cat)) { 01707 /* Setup common options from [general] section */ 01708 if (!strcasecmp(cat, "general")) { 01709 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01710 if (!strcasecmp(var->name, "cachertclasses")) { 01711 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES); 01712 } else { 01713 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name); 01714 } 01715 } 01716 } 01717 /* These names were deprecated in 1.4 and should not be used until after the next major release. */ 01718 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 01719 !strcasecmp(cat, "general")) { 01720 continue; 01721 } 01722 01723 if (!(class = moh_class_malloc())) { 01724 break; 01725 } 01726 01727 ast_copy_string(class->name, cat, sizeof(class->name)); 01728 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01729 if (!strcasecmp(var->name, "mode")) 01730 ast_copy_string(class->mode, var->value, sizeof(class->mode)); 01731 else if (!strcasecmp(var->name, "directory")) 01732 ast_copy_string(class->dir, var->value, sizeof(class->dir)); 01733 else if (!strcasecmp(var->name, "application")) 01734 ast_copy_string(class->args, var->value, sizeof(class->args)); 01735 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) 01736 class->digit = *var->value; 01737 else if (!strcasecmp(var->name, "random")) 01738 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); 01739 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) 01740 ast_set_flag(class, MOH_RANDOMIZE); 01741 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 01742 ast_set_flag(class, MOH_SORTALPHA); 01743 else if (!strcasecmp(var->name, "format")) { 01744 class->format = ast_getformatbyname(var->value); 01745 if (!class->format) { 01746 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); 01747 class->format = AST_FORMAT_SLINEAR; 01748 } 01749 } 01750 } 01751 01752 if (ast_strlen_zero(class->dir)) { 01753 if (!strcasecmp(class->mode, "custom")) { 01754 strcpy(class->dir, "nodir"); 01755 } else { 01756 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); 01757 class = mohclass_unref(class, "unreffing potential mohclass (no directory)"); 01758 continue; 01759 } 01760 } 01761 if (ast_strlen_zero(class->mode)) { 01762 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); 01763 class = mohclass_unref(class, "unreffing potential mohclass (no mode)"); 01764 continue; 01765 } 01766 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { 01767 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); 01768 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)"); 01769 continue; 01770 } 01771 01772 /* Don't leak a class when it's already registered */ 01773 if (!moh_register(class, reload, HANDLE_REF)) { 01774 numclasses++; 01775 } 01776 } 01777 01778 ast_config_destroy(cfg); 01779 01780 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 01781 moh_classes_delete_marked, NULL, "Purge marked classes"); 01782 01783 return numclasses; 01784 }
| static void local_ast_moh_cleanup | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1288 of file res_musiconhold.c.
References ast_free, ast_log(), ast_module_unref(), moh_files_state::class, LOG_WARNING, mohclass_unref, and ast_channel::music_state.
Referenced by load_module(), and reload().
01289 { 01290 struct moh_files_state *state = chan->music_state; 01291 01292 if (state) { 01293 if (state->class) { 01294 /* This should never happen. We likely just leaked some resource. */ 01295 state->class = 01296 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class"); 01297 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n"); 01298 } 01299 ast_free(chan->music_state); 01300 chan->music_state = NULL; 01301 /* Only held a module reference if we had a music state */ 01302 ast_module_unref(ast_module_info->self); 01303 } 01304 }
| static int local_ast_moh_start | ( | struct ast_channel * | chan, | |
| const char * | mclass, | |||
| const char * | interpclass | |||
| ) | [static] |
Definition at line 1330 of file res_musiconhold.c.
References mohclass::args, ast_activate_generator(), ast_check_realtime(), ast_copy_string(), AST_FLAG_MOH, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), ast_manager_event, ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_true(), ast_variables_destroy(), moh_files_state::class, mohclass::digit, mohclass::dir, DONT_UNREF, errno, EVENT_FLAG_CALL, mohclass::format, get_mohbyname, LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, MOH_SORTALPHA, mohclass_unref, monmp3thread(), ast_channel::music_state, mohclass::name, ast_variable::name, ast_variable::next, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, mohclass::total_files, ast_variable::value, and var.
Referenced by load_module(), and reload().
01331 { 01332 struct mohclass *mohclass = NULL; 01333 struct moh_files_state *state = chan->music_state; 01334 struct ast_variable *var = NULL; 01335 int res; 01336 int realtime_possible = ast_check_realtime("musiconhold"); 01337 01338 /* The following is the order of preference for which class to use: 01339 * 1) The channels explicitly set musicclass, which should *only* be 01340 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan. 01341 * 2) The mclass argument. If a channel is calling ast_moh_start() as the 01342 * result of receiving a HOLD control frame, this should be the 01343 * payload that came with the frame. 01344 * 3) The interpclass argument. This would be from the mohinterpret 01345 * option from channel drivers. This is the same as the old musicclass 01346 * option. 01347 * 4) The default class. 01348 */ 01349 if (!ast_strlen_zero(chan->musicclass)) { 01350 mohclass = get_mohbyname(chan->musicclass, 1, 0); 01351 if (!mohclass && realtime_possible) { 01352 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL); 01353 } 01354 } 01355 if (!mohclass && !var && !ast_strlen_zero(mclass)) { 01356 mohclass = get_mohbyname(mclass, 1, 0); 01357 if (!mohclass && realtime_possible) { 01358 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL); 01359 } 01360 } 01361 if (!mohclass && !var && !ast_strlen_zero(interpclass)) { 01362 mohclass = get_mohbyname(interpclass, 1, 0); 01363 if (!mohclass && realtime_possible) { 01364 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL); 01365 } 01366 } 01367 01368 if (!mohclass && !var) { 01369 mohclass = get_mohbyname("default", 1, 0); 01370 if (!mohclass && realtime_possible) { 01371 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL); 01372 } 01373 } 01374 01375 /* If no moh class found in memory, then check RT. Note that the logic used 01376 * above guarantees that if var is non-NULL, then mohclass must be NULL. 01377 */ 01378 if (var) { 01379 struct ast_variable *tmp = NULL; 01380 01381 if ((mohclass = moh_class_malloc())) { 01382 mohclass->realtime = 1; 01383 for (tmp = var; tmp; tmp = tmp->next) { 01384 if (!strcasecmp(tmp->name, "name")) 01385 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name)); 01386 else if (!strcasecmp(tmp->name, "mode")) 01387 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 01388 else if (!strcasecmp(tmp->name, "directory")) 01389 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir)); 01390 else if (!strcasecmp(tmp->name, "application")) 01391 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args)); 01392 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value))) 01393 mohclass->digit = *tmp->value; 01394 else if (!strcasecmp(tmp->name, "random")) 01395 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE); 01396 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random")) 01397 ast_set_flag(mohclass, MOH_RANDOMIZE); 01398 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 01399 ast_set_flag(mohclass, MOH_SORTALPHA); 01400 else if (!strcasecmp(tmp->name, "format")) { 01401 mohclass->format = ast_getformatbyname(tmp->value); 01402 if (!mohclass->format) { 01403 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value); 01404 mohclass->format = AST_FORMAT_SLINEAR; 01405 } 01406 } 01407 } 01408 ast_variables_destroy(var); 01409 if (ast_strlen_zero(mohclass->dir)) { 01410 if (!strcasecmp(mohclass->mode, "custom")) { 01411 strcpy(mohclass->dir, "nodir"); 01412 } else { 01413 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name); 01414 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)"); 01415 return -1; 01416 } 01417 } 01418 if (ast_strlen_zero(mohclass->mode)) { 01419 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name); 01420 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)"); 01421 return -1; 01422 } 01423 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) { 01424 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name); 01425 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode"); 01426 return -1; 01427 } 01428 01429 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) { 01430 /* CACHERTCLASSES enabled, let's add this class to default tree */ 01431 if (state && state->class) { 01432 /* Class already exist for this channel */ 01433 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01434 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01435 /* we found RT class with the same name, seems like we should continue playing existing one */ 01436 /* XXX This code is impossible to reach */ 01437 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)"); 01438 mohclass = state->class; 01439 } 01440 } 01441 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well. 01442 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would 01443 * be that the destructor would be called when the generator on the channel is deactivated. The container then 01444 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading 01445 * invalid memory. 01446 */ 01447 if (moh_register(mohclass, 0, DONT_UNREF) == -1) { 01448 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register"); 01449 return -1; 01450 } 01451 } else { 01452 /* We don't register RT moh class, so let's init it manualy */ 01453 01454 time(&mohclass->start); 01455 mohclass->start -= respawn_time; 01456 01457 if (!strcasecmp(mohclass->mode, "files")) { 01458 if (!moh_scan_files(mohclass)) { 01459 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01460 return -1; 01461 } 01462 if (strchr(mohclass->args, 'r')) 01463 ast_set_flag(mohclass, MOH_RANDOMIZE); 01464 } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) { 01465 01466 if (!strcasecmp(mohclass->mode, "custom")) 01467 ast_set_flag(mohclass, MOH_CUSTOM); 01468 else if (!strcasecmp(mohclass->mode, "mp3nb")) 01469 ast_set_flag(mohclass, MOH_SINGLE); 01470 else if (!strcasecmp(mohclass->mode, "quietmp3nb")) 01471 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET); 01472 else if (!strcasecmp(mohclass->mode, "quietmp3")) 01473 ast_set_flag(mohclass, MOH_QUIET); 01474 01475 mohclass->srcfd = -1; 01476 if (!(mohclass->timer = ast_timer_open())) { 01477 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01478 } 01479 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) { 01480 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01481 ast_timer_close(mohclass->timer); 01482 mohclass->timer = NULL; 01483 } 01484 01485 /* Let's check if this channel already had a moh class before */ 01486 if (state && state->class) { 01487 /* Class already exist for this channel */ 01488 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01489 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01490 /* we found RT class with the same name, seems like we should continue playing existing one */ 01491 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)"); 01492 mohclass = state->class; 01493 } 01494 } else { 01495 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) { 01496 ast_log(LOG_WARNING, "Unable to create moh...\n"); 01497 if (mohclass->timer) { 01498 ast_timer_close(mohclass->timer); 01499 mohclass->timer = NULL; 01500 } 01501 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)"); 01502 return -1; 01503 } 01504 } 01505 } else { 01506 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode); 01507 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)"); 01508 return -1; 01509 } 01510 } 01511 } else { 01512 ast_variables_destroy(var); 01513 var = NULL; 01514 } 01515 } 01516 01517 if (!mohclass) { 01518 return -1; 01519 } 01520 01521 /* If we are using a cached realtime class with files, re-scan the files */ 01522 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) { 01523 if (!moh_scan_files(mohclass)) { 01524 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01525 return -1; 01526 } 01527 } 01528 01529 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01530 "State: Start\r\n" 01531 "Channel: %s\r\n" 01532 "UniqueID: %s\r\n" 01533 "Class: %s\r\n", 01534 chan->name, chan->uniqueid, 01535 mohclass->name); 01536 01537 ast_set_flag(chan, AST_FLAG_MOH); 01538 01539 if (mohclass->total_files) { 01540 res = ast_activate_generator(chan, &moh_file_stream, mohclass); 01541 } else { 01542 res = ast_activate_generator(chan, &mohgen, mohclass); 01543 } 01544 01545 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start"); 01546 01547 return res; 01548 }
| static void local_ast_moh_stop | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1550 of file res_musiconhold.c.
References ast_channel_lock, ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_manager_event, EVENT_FLAG_CALL, ast_channel::music_state, and ast_channel::stream.
Referenced by load_module(), and reload().
01551 { 01552 ast_clear_flag(chan, AST_FLAG_MOH); 01553 ast_deactivate_generator(chan); 01554 01555 ast_channel_lock(chan); 01556 if (chan->music_state) { 01557 if (chan->stream) { 01558 ast_closestream(chan->stream); 01559 chan->stream = NULL; 01560 } 01561 } 01562 01563 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01564 "State: Stop\r\n" 01565 "Channel: %s\r\n" 01566 "UniqueID: %s\r\n", 01567 chan->name, chan->uniqueid); 01568 ast_channel_unlock(chan); 01569 }
| static int moh_add_file | ( | struct mohclass * | class, | |
| const char * | filepath | |||
| ) | [static] |
Definition at line 1008 of file res_musiconhold.c.
References ast_calloc, ast_realloc, ast_strdup, and INITIAL_NUM_FILES.
Referenced by moh_scan_files().
01009 { 01010 if (!class->allowed_files) { 01011 class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray)); 01012 if (!class->filearray) { 01013 return -1; 01014 } 01015 class->allowed_files = INITIAL_NUM_FILES; 01016 } else if (class->total_files == class->allowed_files) { 01017 char **new_array; 01018 01019 new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2); 01020 if (!new_array) { 01021 return -1; 01022 } 01023 class->filearray = new_array; 01024 class->allowed_files *= 2; 01025 } 01026 01027 class->filearray[class->total_files] = ast_strdup(filepath); 01028 if (!class->filearray[class->total_files]) { 01029 return -1; 01030 } 01031 01032 class->total_files++; 01033 01034 return 0; 01035 }
| static void* moh_alloc | ( | struct ast_channel * | chan, | |
| void * | params | |||
| ) | [static] |
Definition at line 937 of file res_musiconhold.c.
References ast_calloc, ast_codec2str(), ast_log(), ast_module_ref(), ast_set_write_format(), ast_verb, moh_files_state::class, mohclass::format, LOG_WARNING, moh_release(), mohalloc(), mohclass_ref, mohclass_unref, ast_channel::music_state, mohclass::name, mohdata::origwfmt, and ast_channel::writeformat.
00938 { 00939 struct mohdata *res; 00940 struct mohclass *class = params; 00941 struct moh_files_state *state; 00942 00943 /* Initiating music_state for current channel. Channel should know name of moh class */ 00944 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { 00945 chan->music_state = state; 00946 ast_module_ref(ast_module_info->self); 00947 } else { 00948 state = chan->music_state; 00949 if (!state) { 00950 return NULL; 00951 } 00952 if (state->class) { 00953 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class"); 00954 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n"); 00955 } 00956 memset(state, 0, sizeof(*state)); 00957 } 00958 00959 if ((res = mohalloc(class))) { 00960 res->origwfmt = chan->writeformat; 00961 if (ast_set_write_format(chan, class->format)) { 00962 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format)); 00963 moh_release(NULL, res); 00964 res = NULL; 00965 } else { 00966 state->class = mohclass_ref(class, "Placing reference into state container"); 00967 } 00968 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name); 00969 } 00970 return res; 00971 }
| static int moh_class_cmp | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1905 of file res_musiconhold.c.
References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.
Referenced by load_module().
01906 { 01907 struct mohclass *class = obj, *class2 = arg; 01908 01909 return strcasecmp(class->name, class2->name) ? 0 : 01910 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 : 01911 CMP_MATCH | CMP_STOP; 01912 }
| static void moh_class_destructor | ( | void * | obj | ) | [static] |
Definition at line 1571 of file res_musiconhold.c.
References ao2_lock, ao2_unlock, ast_debug, AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, errno, free, LOG_DEBUG, LOG_WARNING, and mohclass::pid.
Referenced by _moh_class_malloc().
01572 { 01573 struct mohclass *class = obj; 01574 struct mohdata *member; 01575 pthread_t tid = 0; 01576 01577 ast_debug(1, "Destroying MOH class '%s'\n", class->name); 01578 01579 ao2_lock(class); 01580 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) { 01581 free(member); 01582 } 01583 ao2_unlock(class); 01584 01585 /* Kill the thread first, so it cannot restart the child process while the 01586 * class is being destroyed */ 01587 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) { 01588 tid = class->thread; 01589 class->thread = AST_PTHREADT_NULL; 01590 pthread_cancel(tid); 01591 /* We'll collect the exit status later, after we ensure all the readers 01592 * are dead. */ 01593 } 01594 01595 if (class->pid > 1) { 01596 char buff[8192]; 01597 int bytes, tbytes = 0, stime = 0, pid = 0; 01598 01599 ast_log(LOG_DEBUG, "killing %d!\n", class->pid); 01600 01601 stime = time(NULL) + 2; 01602 pid = class->pid; 01603 class->pid = 0; 01604 01605 /* Back when this was just mpg123, SIGKILL was fine. Now we need 01606 * to give the process a reason and time enough to kill off its 01607 * children. */ 01608 do { 01609 if (killpg(pid, SIGHUP) < 0) { 01610 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); 01611 } 01612 usleep(100000); 01613 if (killpg(pid, SIGTERM) < 0) { 01614 if (errno == ESRCH) { 01615 break; 01616 } 01617 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); 01618 } 01619 usleep(100000); 01620 if (killpg(pid, SIGKILL) < 0) { 01621 if (errno == ESRCH) { 01622 break; 01623 } 01624 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); 01625 } 01626 } while (0); 01627 01628 while ((ast_wait_for_input(class->srcfd, 100) > 0) && 01629 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) { 01630 tbytes = tbytes + bytes; 01631 } 01632 01633 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); 01634 01635 close(class->srcfd); 01636 class->srcfd = -1; 01637 } 01638 01639 if (class->filearray) { 01640 int i; 01641 for (i = 0; i < class->total_files; i++) { 01642 free(class->filearray[i]); 01643 } 01644 free(class->filearray); 01645 class->filearray = NULL; 01646 } 01647 01648 if (class->timer) { 01649 ast_timer_close(class->timer); 01650 class->timer = NULL; 01651 } 01652 01653 /* Finally, collect the exit status of the monitor thread */ 01654 if (tid > 0) { 01655 pthread_join(tid, NULL); 01656 } 01657 01658 }
| static int moh_class_hash | ( | const void * | obj, | |
| const int | flags | |||
| ) | [static] |
Definition at line 1898 of file res_musiconhold.c.
References ast_str_case_hash().
Referenced by load_module().
01899 { 01900 const struct mohclass *class = obj; 01901 01902 return ast_str_case_hash(class->name); 01903 }
| static int moh_class_inuse | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1955 of file res_musiconhold.c.
References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.
Referenced by unload_module().
01956 { 01957 struct mohclass *class = obj; 01958 01959 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP; 01960 }
| static int moh_class_mark | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1660 of file res_musiconhold.c.
Referenced by load_moh_classes().
01661 { 01662 struct mohclass *class = obj; 01663 01664 class->delete = 1; 01665 01666 return 0; 01667 }
| static int moh_classes_delete_marked | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1669 of file res_musiconhold.c.
References CMP_MATCH.
Referenced by load_moh_classes().
Definition at line 1175 of file res_musiconhold.c.
References mohclass::args, mohclass::dir, mohclass::flags, and mohclass::mode.
Referenced by _moh_register().
01176 { 01177 if (!old || !new) { 01178 return -1; 01179 } 01180 01181 if (strcmp(old->dir, new->dir)) { 01182 return -1; 01183 } else if (strcmp(old->mode, new->mode)) { 01184 return -1; 01185 } else if (strcmp(old->args, new->args)) { 01186 return -1; 01187 } else if (old->flags != new->flags) { 01188 return -1; 01189 } 01190 01191 return 0; 01192 }
| static int moh_digit_match | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 448 of file res_musiconhold.c.
References CMP_MATCH, CMP_STOP, and mohclass::digit.
Referenced by get_mohbydigit().
| static void* moh_files_alloc | ( | struct ast_channel * | chan, | |
| void * | params | |||
| ) | [static] |
Definition at line 406 of file res_musiconhold.c.
References ast_calloc, ast_copy_string(), ast_log(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, moh_files_state::class, LOG_WARNING, MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::music_state, moh_files_state::name, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_total, and ast_channel::writeformat.
00407 { 00408 struct moh_files_state *state; 00409 struct mohclass *class = params; 00410 00411 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { 00412 chan->music_state = state; 00413 ast_module_ref(ast_module_info->self); 00414 } else { 00415 state = chan->music_state; 00416 if (!state) { 00417 return NULL; 00418 } 00419 if (state->class) { 00420 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class"); 00421 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n"); 00422 } 00423 } 00424 00425 /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because 00426 * malloc may allocate a different class to the same memory block. This 00427 * might only happen when two reloads are generated in a short period of 00428 * time, but it's still important to protect against. 00429 * PROG: Compare the quick operation first, to save CPU. */ 00430 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) { 00431 memset(state, 0, sizeof(*state)); 00432 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) { 00433 state->pos = ast_random() % class->total_files; 00434 } 00435 } 00436 00437 state->class = mohclass_ref(class, "Reffing music class for channel"); 00438 state->origwfmt = chan->writeformat; 00439 /* For comparison on restart of MOH (see above) */ 00440 ast_copy_string(state->name, class->name, sizeof(state->name)); 00441 state->save_total = class->total_files; 00442 00443 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name); 00444 00445 return chan->music_state; 00446 }
| static int moh_files_generator | ( | struct ast_channel * | chan, | |
| void * | data, | |||
| int | len, | |||
| int | samples | |||
| ) | [static] |
Definition at line 372 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.
00373 { 00374 struct moh_files_state *state = chan->music_state; 00375 struct ast_frame *f = NULL; 00376 int res = 0; 00377 00378 state->sample_queue += samples; 00379 00380 while (state->sample_queue > 0) { 00381 ast_channel_lock(chan); 00382 if ((f = moh_files_readframe(chan))) { 00383 /* We need to be sure that we unlock 00384 * the channel prior to calling 00385 * ast_write. Otherwise, the recursive locking 00386 * that occurs can cause deadlocks when using 00387 * indirect channels, like local channels 00388 */ 00389 ast_channel_unlock(chan); 00390 state->samples += f->samples; 00391 state->sample_queue -= f->samples; 00392 res = ast_write(chan, f); 00393 ast_frfree(f); 00394 if (res < 0) { 00395 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); 00396 return -1; 00397 } 00398 } else { 00399 ast_channel_unlock(chan); 00400 return -1; 00401 } 00402 } 00403 return res; 00404 }
| static struct ast_frame* moh_files_readframe | ( | struct ast_channel * | chan | ) | [static, read] |
Definition at line 360 of file res_musiconhold.c.
References ast_moh_files_next(), ast_readframe(), f, and ast_channel::stream.
Referenced by moh_files_generator().
00361 { 00362 struct ast_frame *f = NULL; 00363 00364 if (!(chan->stream && (f = ast_readframe(chan->stream)))) { 00365 if (!ast_moh_files_next(chan)) 00366 f = ast_readframe(chan->stream); 00367 } 00368 00369 return f; 00370 }
| static void moh_files_release | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 256 of file res_musiconhold.c.
References ast_closestream(), ast_getformatname(), 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.
00257 { 00258 struct moh_files_state *state; 00259 00260 if (!chan || !chan->music_state) { 00261 return; 00262 } 00263 00264 state = chan->music_state; 00265 00266 if (chan->stream) { 00267 ast_closestream(chan->stream); 00268 chan->stream = NULL; 00269 } 00270 00271 if (option_verbose > 2) { 00272 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name); 00273 } 00274 00275 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) { 00276 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt)); 00277 } 00278 00279 state->save_pos = state->pos; 00280 00281 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator"); 00282 }
| static int moh_generate | ( | struct ast_channel * | chan, | |
| void * | data, | |||
| int | len, | |||
| int | samples | |||
| ) | [static] |
Definition at line 973 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, ast_frame::ptr, and ast_frame::samples.
00974 { 00975 struct mohdata *moh = data; 00976 short buf[1280 + AST_FRIENDLY_OFFSET / 2]; 00977 int res; 00978 00979 len = ast_codec_get_len(moh->parent->format, samples); 00980 00981 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { 00982 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name); 00983 len = sizeof(buf) - AST_FRIENDLY_OFFSET; 00984 } 00985 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len); 00986 if (res <= 0) 00987 return 0; 00988 00989 moh->f.datalen = res; 00990 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2; 00991 moh->f.samples = ast_codec_get_samples(&moh->f); 00992 00993 if (ast_write(chan, &moh->f) < 0) { 00994 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); 00995 return -1; 00996 } 00997 00998 return 0; 00999 }
| static void moh_handle_digit | ( | struct ast_channel * | chan, | |
| char | digit | |||
| ) | [static] |
Definition at line 462 of file res_musiconhold.c.
References ast_moh_start(), ast_moh_stop(), ast_strdupa, ast_string_field_set, get_mohbydigit(), mohclass_unref, and musicclass.
00463 { 00464 struct mohclass *class; 00465 const char *classname = NULL; 00466 00467 if ((class = get_mohbydigit(digit))) { 00468 classname = ast_strdupa(class->name); 00469 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit"); 00470 ast_string_field_set(chan,musicclass,classname); 00471 ast_moh_stop(chan); 00472 ast_moh_start(chan, classname, NULL); 00473 } 00474 }
| static void moh_release | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 902 of file res_musiconhold.c.
References ao2_lock, ao2_unlock, ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, moh_files_state::class, LOG_WARNING, mohclass::members, moh, mohclass_unref, ast_channel::music_state, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.
Referenced by moh_alloc().
00903 { 00904 struct mohdata *moh = data; 00905 struct mohclass *class = moh->parent; 00906 format_t oldwfmt; 00907 00908 ao2_lock(class); 00909 AST_LIST_REMOVE(&moh->parent->members, moh, list); 00910 ao2_unlock(class); 00911 00912 close(moh->pipe[0]); 00913 close(moh->pipe[1]); 00914 00915 oldwfmt = moh->origwfmt; 00916 00917 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator"); 00918 00919 ast_free(moh); 00920 00921 if (chan) { 00922 struct moh_files_state *state; 00923 00924 state = chan->music_state; 00925 if (state && state->class) { 00926 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator"); 00927 } 00928 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) { 00929 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 00930 chan->name, ast_getformatname(oldwfmt)); 00931 } 00932 00933 ast_verb(3, "Stopped music on hold on %s\n", chan->name); 00934 } 00935 }
| static void moh_rescan_files | ( | void | ) | [static] |
Definition at line 1159 of file res_musiconhold.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, mohclass::mode, and moh_scan_files().
Referenced by load_moh_classes().
01159 { 01160 struct ao2_iterator i; 01161 struct mohclass *c; 01162 01163 i = ao2_iterator_init(mohclasses, 0); 01164 01165 while ((c = ao2_iterator_next(&i))) { 01166 if (!strcasecmp(c->mode, "files")) { 01167 moh_scan_files(c); 01168 } 01169 ao2_ref(c, -1); 01170 } 01171 01172 ao2_iterator_destroy(&i); 01173 }
| static int moh_scan_files | ( | struct mohclass * | class | ) | [static] |
Definition at line 1047 of file res_musiconhold.c.
References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_free, ast_log(), ast_test_flag, errno, ext, LOG_WARNING, moh_add_file(), moh_sort_compare(), and MOH_SORTALPHA.
Referenced by init_files_class(), local_ast_moh_start(), and moh_rescan_files().
01047 { 01048 01049 DIR *files_DIR; 01050 struct dirent *files_dirent; 01051 char dir_path[PATH_MAX]; 01052 char path[PATH_MAX]; 01053 char filepath[PATH_MAX]; 01054 char *ext; 01055 struct stat statbuf; 01056 int i; 01057 01058 if (class->dir[0] != '/') { 01059 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path)); 01060 strncat(dir_path, "/", sizeof(dir_path) - 1); 01061 strncat(dir_path, class->dir, sizeof(dir_path) - 1); 01062 } else { 01063 ast_copy_string(dir_path, class->dir, sizeof(dir_path)); 01064 } 01065 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name); 01066 files_DIR = opendir(dir_path); 01067 if (!files_DIR) { 01068 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path); 01069 return -1; 01070 } 01071 01072 for (i = 0; i < class->total_files; i++) 01073 ast_free(class->filearray[i]); 01074 01075 class->total_files = 0; 01076 if (!getcwd(path, sizeof(path))) { 01077 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno)); 01078 closedir(files_DIR); 01079 return -1; 01080 } 01081 if (chdir(dir_path) < 0) { 01082 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01083 closedir(files_DIR); 01084 return -1; 01085 } 01086 while ((files_dirent = readdir(files_DIR))) { 01087 /* The file name must be at least long enough to have the file type extension */ 01088 if ((strlen(files_dirent->d_name) < 4)) 01089 continue; 01090 01091 /* Skip files that starts with a dot */ 01092 if (files_dirent->d_name[0] == '.') 01093 continue; 01094 01095 /* Skip files without extensions... they are not audio */ 01096 if (!strchr(files_dirent->d_name, '.')) 01097 continue; 01098 01099 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name); 01100 01101 if (stat(filepath, &statbuf)) 01102 continue; 01103 01104 if (!S_ISREG(statbuf.st_mode)) 01105 continue; 01106 01107 if ((ext = strrchr(filepath, '.'))) 01108 *ext = '\0'; 01109 01110 /* if the file is present in multiple formats, ensure we only put it into the list once */ 01111 for (i = 0; i < class->total_files; i++) 01112 if (!strcmp(filepath, class->filearray[i])) 01113 break; 01114 01115 if (i == class->total_files) { 01116 if (moh_add_file(class, filepath)) 01117 break; 01118 } 01119 } 01120 01121 closedir(files_DIR); 01122 if (chdir(path) < 0) { 01123 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01124 return -1; 01125 } 01126 if (ast_test_flag(class, MOH_SORTALPHA)) 01127 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare); 01128 return class->total_files; 01129 }
| static int moh_sort_compare | ( | const void * | i1, | |
| const void * | i2 | |||
| ) | [static] |
Definition at line 1037 of file res_musiconhold.c.
Referenced by moh_scan_files().
Definition at line 869 of file res_musiconhold.c.
References ao2_lock, ao2_unlock, ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), ast_frame_subclass::codec, errno, mohdata::f, mohclass::flags, mohclass::format, ast_frame::frametype, LOG_WARNING, mohclass::members, moh, mohclass_ref, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.
Referenced by moh_alloc().
00870 { 00871 struct mohdata *moh; 00872 long flags; 00873 00874 if (!(moh = ast_calloc(1, sizeof(*moh)))) 00875 return NULL; 00876 00877 if (pipe(moh->pipe)) { 00878 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno)); 00879 ast_free(moh); 00880 return NULL; 00881 } 00882 00883 /* Make entirely non-blocking */ 00884 flags = fcntl(moh->pipe[0], F_GETFL); 00885 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK); 00886 flags = fcntl(moh->pipe[1], F_GETFL); 00887 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK); 00888 00889 moh->f.frametype = AST_FRAME_VOICE; 00890 moh->f.subclass.codec = cl->format; 00891 moh->f.offset = AST_FRIENDLY_OFFSET; 00892 00893 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent"); 00894 00895 ao2_lock(cl); 00896 AST_LIST_INSERT_HEAD(&cl->members, moh, list); 00897 ao2_unlock(cl); 00898 00899 return moh; 00900 }
| static void* monmp3thread | ( | void * | data | ) | [static] |
Definition at line 625 of file res_musiconhold.c.
References ao2_lock, ao2_unlock, ast_codec_get_len(), ast_debug, AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_poll, ast_samp2tv(), ast_timer_ack(), ast_timer_fd(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), errno, len(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, mohdata::pipe, and spawn_mp3().
Referenced by init_app_class(), and local_ast_moh_start().
00626 { 00627 #define MOH_MS_INTERVAL 100 00628 00629 struct mohclass *class = data; 00630 struct mohdata *moh; 00631 short sbuf[8192]; 00632 int res = 0, res2; 00633 int len; 00634 struct timeval deadline, tv_tmp; 00635 00636 deadline.tv_sec = 0; 00637 deadline.tv_usec = 0; 00638 for(;/* ever */;) { 00639 pthread_testcancel(); 00640 /* Spawn mp3 player if it's not there */ 00641 if (class->srcfd < 0) { 00642 if ((class->srcfd = spawn_mp3(class)) < 0) { 00643 ast_log(LOG_WARNING, "Unable to spawn mp3player\n"); 00644 /* Try again later */ 00645 sleep(500); 00646 continue; 00647 } 00648 } 00649 if (class->timer) { 00650 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, }; 00651 00652 #ifdef SOLARIS 00653 thr_yield(); 00654 #endif 00655 /* Pause some amount of time */ 00656 if (ast_poll(&pfd, 1, -1) > 0) { 00657 if (ast_timer_ack(class->timer, 1) < 0) { 00658 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n"); 00659 return NULL; 00660 } 00661 res = 320; 00662 } else { 00663 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); 00664 res = 0; 00665 } 00666 pthread_testcancel(); 00667 } else { 00668 long delta; 00669 /* Reliable sleep */ 00670 tv_tmp = ast_tvnow(); 00671 if (ast_tvzero(deadline)) 00672 deadline = tv_tmp; 00673 delta = ast_tvdiff_ms(tv_tmp, deadline); 00674 if (delta < MOH_MS_INTERVAL) { /* too early */ 00675 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */ 00676 usleep(1000 * (MOH_MS_INTERVAL - delta)); 00677 pthread_testcancel(); 00678 } else { 00679 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n"); 00680 deadline = tv_tmp; 00681 } 00682 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */ 00683 } 00684 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members)) 00685 continue; 00686 /* Read mp3 audio */ 00687 len = ast_codec_get_len(class->format, res); 00688 00689 if ((res2 = read(class->srcfd, sbuf, len)) != len) { 00690 if (!res2) { 00691 close(class->srcfd); 00692 class->srcfd = -1; 00693 pthread_testcancel(); 00694 if (class->pid > 1) { 00695 do { 00696 if (killpg(class->pid, SIGHUP) < 0) { 00697 if (errno == ESRCH) { 00698 break; 00699 } 00700 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); 00701 } 00702 usleep(100000); 00703 if (killpg(class->pid, SIGTERM) < 0) { 00704 if (errno == ESRCH) { 00705 break; 00706 } 00707 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); 00708 } 00709 usleep(100000); 00710 if (killpg(class->pid, SIGKILL) < 0) { 00711 if (errno == ESRCH) { 00712 break; 00713 } 00714 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); 00715 } 00716 } while (0); 00717 class->pid = 0; 00718 } 00719 } else { 00720 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len); 00721 } 00722 continue; 00723 } 00724 00725 pthread_testcancel(); 00726 00727 ao2_lock(class); 00728 AST_LIST_TRAVERSE(&class->members, moh, list) { 00729 /* Write data */ 00730 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) { 00731 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2); 00732 } 00733 } 00734 ao2_unlock(class); 00735 } 00736 return NULL; 00737 }
| static int play_moh_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 739 of file res_musiconhold.c.
References mohclass::args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, parse(), and S_OR.
Referenced by load_module().
00740 { 00741 char *parse; 00742 char *class; 00743 int timeout = -1; 00744 int res; 00745 AST_DECLARE_APP_ARGS(args, 00746 AST_APP_ARG(class); 00747 AST_APP_ARG(duration); 00748 ); 00749 00750 parse = ast_strdupa(data); 00751 00752 AST_STANDARD_APP_ARGS(args, parse); 00753 00754 if (!ast_strlen_zero(args.duration)) { 00755 if (sscanf(args.duration, "%30d", &timeout) == 1) { 00756 timeout *= 1000; 00757 } else { 00758 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration); 00759 } 00760 } 00761 00762 class = S_OR(args.class, NULL); 00763 if (ast_moh_start(chan, class, NULL)) { 00764 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name); 00765 return 0; 00766 } 00767 00768 if (timeout > 0) 00769 res = ast_safe_sleep(chan, timeout); 00770 else { 00771 while (!(res = ast_safe_sleep(chan, 10000))); 00772 } 00773 00774 ast_moh_stop(chan); 00775 00776 return res; 00777 }
| static int reload | ( | void | ) | [static] |
Definition at line 1945 of file res_musiconhold.c.
References ast_install_music_functions(), AST_MODULE_LOAD_SUCCESS, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), and local_ast_moh_stop().
Referenced by handle_cli_moh_reload().
01946 { 01947 if (load_moh_classes(1)) { 01948 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, 01949 local_ast_moh_cleanup); 01950 } 01951 01952 return AST_MODULE_LOAD_SUCCESS; 01953 }
| static int set_moh_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 802 of file res_musiconhold.c.
References ast_log(), ast_string_field_set, ast_strlen_zero(), LOG_WARNING, and musicclass.
Referenced by load_module().
00803 { 00804 static int deprecation_warning = 0; 00805 00806 if (!deprecation_warning) { 00807 deprecation_warning = 1; 00808 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n"); 00809 } 00810 00811 if (ast_strlen_zero(data)) { 00812 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n"); 00813 return -1; 00814 } 00815 ast_string_field_set(chan, musicclass, data); 00816 return 0; 00817 }
| static int spawn_mp3 | ( | struct mohclass * | class | ) | [static] |
Definition at line 484 of file res_musiconhold.c.
References ast_close_fds_above_n(), ast_copy_string(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_strlen_zero(), ast_test_flag, mohclass::dir, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, and MPG_123.
Referenced by monmp3thread().
00485 { 00486 int fds[2]; 00487 int files = 0; 00488 char fns[MAX_MP3S][80]; 00489 char *argv[MAX_MP3S + 50]; 00490 char xargs[256]; 00491 char *argptr; 00492 int argc = 0; 00493 DIR *dir = NULL; 00494 struct dirent *de; 00495 00496 00497 if (!strcasecmp(class->dir, "nodir")) { 00498 files = 1; 00499 } else { 00500 dir = opendir(class->dir); 00501 if (!dir && strncasecmp(class->dir, "http://", 7)) { 00502 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir); 00503 return -1; 00504 } 00505 } 00506 00507 if (!ast_test_flag(class, MOH_CUSTOM)) { 00508 argv[argc++] = "mpg123"; 00509 argv[argc++] = "-q"; 00510 argv[argc++] = "-s"; 00511 argv[argc++] = "--mono"; 00512 argv[argc++] = "-r"; 00513 argv[argc++] = "8000"; 00514 00515 if (!ast_test_flag(class, MOH_SINGLE)) { 00516 argv[argc++] = "-b"; 00517 argv[argc++] = "2048"; 00518 } 00519 00520 argv[argc++] = "-f"; 00521 00522 if (ast_test_flag(class, MOH_QUIET)) 00523 argv[argc++] = "4096"; 00524 else 00525 argv[argc++] = "8192"; 00526 00527 /* Look for extra arguments and add them to the list */ 00528 ast_copy_string(xargs, class->args, sizeof(xargs)); 00529 argptr = xargs; 00530 while (!ast_strlen_zero(argptr)) { 00531 argv[argc++] = argptr; 00532 strsep(&argptr, ","); 00533 } 00534 } else { 00535 /* Format arguments for argv vector */ 00536 ast_copy_string(xargs, class->args, sizeof(xargs)); 00537 argptr = xargs; 00538 while (!ast_strlen_zero(argptr)) { 00539 argv[argc++] = argptr; 00540 strsep(&argptr, " "); 00541 } 00542 } 00543 00544 if (!strncasecmp(class->dir, "http://", 7)) { 00545 ast_copy_string(fns[files], class->dir, sizeof(fns[files])); 00546 argv[argc++] = fns[files]; 00547 files++; 00548 } else if (dir) { 00549 while ((de = readdir(dir)) && (files < MAX_MP3S)) { 00550 if ((strlen(de->d_name) > 3) && 00551 ((ast_test_flag(class, MOH_CUSTOM) && 00552 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 00553 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) || 00554 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) { 00555 ast_copy_string(fns[files], de->d_name, sizeof(fns[files])); 00556 argv[argc++] = fns[files]; 00557 files++; 00558 } 00559 } 00560 } 00561 argv[argc] = NULL; 00562 if (dir) { 00563 closedir(dir); 00564 } 00565 if (pipe(fds)) { 00566 ast_log(LOG_WARNING, "Pipe failed\n"); 00567 return -1; 00568 } 00569 if (!files) { 00570 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir); 00571 close(fds[0]); 00572 close(fds[1]); 00573 return -1; 00574 } 00575 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) { 00576 sleep(respawn_time - (time(NULL) - class->start)); 00577 } 00578 00579 time(&class->start); 00580 class->pid = ast_safe_fork(0); 00581 if (class->pid < 0) { 00582 close(fds[0]); 00583 close(fds[1]); 00584 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); 00585 return -1; 00586 } 00587 if (!class->pid) { 00588 if (ast_opt_high_priority) 00589 ast_set_priority(0); 00590 00591 close(fds[0]); 00592 /* Stdout goes to pipe */ 00593 dup2(fds[1], STDOUT_FILENO); 00594 00595 /* Close everything else */ 00596 ast_close_fds_above_n(STDERR_FILENO); 00597 00598 /* Child */ 00599 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) { 00600 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 00601 _exit(1); 00602 } 00603 setpgid(0, getpid()); 00604 if (ast_test_flag(class, MOH_CUSTOM)) { 00605 execv(argv[0], argv); 00606 } else { 00607 /* Default install is /usr/local/bin */ 00608 execv(LOCAL_MPG_123, argv); 00609 /* Many places have it in /usr/bin */ 00610 execv(MPG_123, argv); 00611 /* Check PATH as a last-ditch effort */ 00612 execvp("mpg123", argv); 00613 } 00614 /* Can't use logger, since log FDs are closed */ 00615 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno)); 00616 close(fds[1]); 00617 _exit(1); 00618 } else { 00619 /* Parent */ 00620 close(fds[1]); 00621 } 00622 return fds[0]; 00623 }
| static int start_moh_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 819 of file res_musiconhold.c.
References mohclass::args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, parse(), and S_OR.
Referenced by load_module().
00820 { 00821 char *parse; 00822 char *class; 00823 AST_DECLARE_APP_ARGS(args, 00824 AST_APP_ARG(class); 00825 ); 00826 00827 parse = ast_strdupa(data); 00828 00829 AST_STANDARD_APP_ARGS(args, parse); 00830 00831 class = S_OR(args.class, NULL); 00832 if (ast_moh_start(chan, class, NULL)) 00833 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name); 00834 00835 return 0; 00836 }
| static int stop_moh_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 838 of file res_musiconhold.c.
References ast_moh_stop().
Referenced by load_module().
00839 { 00840 ast_moh_stop(chan); 00841 00842 return 0; 00843 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1962 of file res_musiconhold.c.
References ao2_t_callback, ARRAY_LEN, ast_cli_unregister_multiple(), ast_log(), ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), ast_unregister_atexit(), LOG_WARNING, moh_class_inuse(), and mohclass_unref.
01963 { 01964 int res = 0; 01965 struct mohclass *class = NULL; 01966 01967 /* XXX This check shouldn't be required if module ref counting was being used 01968 * properly ... */ 01969 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) { 01970 class = mohclass_unref(class, "unref of class from module unload callback"); 01971 res = -1; 01972 } 01973 01974 if (res < 0) { 01975 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n"); 01976 return res; 01977 } 01978 01979 ast_uninstall_music_functions(); 01980 01981 ast_moh_destroy(); 01982 res = ast_unregister_application(play_moh); 01983 res |= ast_unregister_application(wait_moh); 01984 res |= ast_unregister_application(set_moh); 01985 res |= ast_unregister_application(start_moh); 01986 res |= ast_unregister_application(stop_moh); 01987 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01988 ast_unregister_atexit(ast_moh_destroy); 01989 01990 return res; 01991 }
| static int wait_moh_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 779 of file res_musiconhold.c.
References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), and LOG_WARNING.
Referenced by load_module().
00780 { 00781 static int deprecation_warning = 0; 00782 int res; 00783 00784 if (!deprecation_warning) { 00785 deprecation_warning = 1; 00786 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n"); 00787 } 00788 00789 if (!data || !atoi(data)) { 00790 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n"); 00791 return -1; 00792 } 00793 if (ast_moh_start(chan, NULL, NULL)) { 00794 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name); 00795 return 0; 00796 } 00797 res = ast_safe_sleep(chan, atoi(data) * 1000); 00798 ast_moh_stop(chan); 00799 return res; 00800 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = "95089850e3c922fa176f9bd274fd8109" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static] |
Definition at line 1998 of file res_musiconhold.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1998 of file res_musiconhold.c.
struct ast_cli_entry cli_moh[] [static] |
{
AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
}
Definition at line 1892 of file res_musiconhold.c.
struct ast_flags global_flags[1] = {{0}} [static] |
global MOH_ flags
Definition at line 179 of file res_musiconhold.c.
struct ast_generator moh_file_stream [static] |
Definition at line 476 of file res_musiconhold.c.
struct ao2_container* mohclasses [static] |
Definition at line 219 of file res_musiconhold.c.
struct ast_generator mohgen [static] |
Definition at line 1001 of file res_musiconhold.c.
const char play_moh[] = "MusicOnHold" [static] |
Definition at line 147 of file res_musiconhold.c.
int respawn_time = 20 [static] |
Definition at line 153 of file res_musiconhold.c.
const char set_moh[] = "SetMusicOnHold" [static] |
Definition at line 149 of file res_musiconhold.c.
const char start_moh[] = "StartMusicOnHold" [static] |
Definition at line 150 of file res_musiconhold.c.
const char stop_moh[] = "StopMusicOnHold" [static] |
Definition at line 151 of file res_musiconhold.c.
const char wait_moh[] = "WaitMusicOnHold" [static] |
Definition at line 148 of file res_musiconhold.c.
1.6.1