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