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