#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 = "88eaa8f5c1bd988bedd71113385e0886" , .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" |
Definition in file res_musiconhold.c.
#define DONT_UNREF 0 |
#define get_mohbyname | ( | a, | |||
b, | |||||
c | ) | _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
#define HANDLE_REF 1 |
Definition at line 71 of file res_musiconhold.c.
#define INITIAL_NUM_FILES 8 |
#define LOCAL_MPG_123 "/usr/local/bin/mpg123" |
Definition at line 221 of file res_musiconhold.c.
#define MAX_MP3S 256 |
#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 1296 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 1221 of file res_musiconhold.c.
Referenced by 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(), and local_ast_moh_start().
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(), 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 1982 of file res_musiconhold.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 1982 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] |
Definition at line 843 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().
00844 { 00845 struct mohclass *moh = NULL; 00846 struct mohclass tmp_class = { 00847 .flags = 0, 00848 }; 00849 00850 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name)); 00851 00852 #ifdef REF_DEBUG 00853 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname); 00854 #else 00855 moh = __ao2_find(mohclasses, &tmp_class, flags); 00856 #endif 00857 00858 if (!moh && warn) { 00859 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name); 00860 } 00861 00862 return moh; 00863 }
static struct mohclass* _moh_class_malloc | ( | const char * | file, | |
int | line, | |||
const char * | funcname | |||
) | [static] |
Definition at line 1298 of file res_musiconhold.c.
References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().
01299 { 01300 struct mohclass *class; 01301 01302 if ((class = 01303 #ifdef REF_DEBUG 01304 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1) 01305 #elif defined(__AST_DEBUG_MALLOC) 01306 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0) 01307 #else 01308 ao2_alloc(sizeof(*class), moh_class_destructor) 01309 #endif 01310 )) { 01311 class->format = AST_FORMAT_SLINEAR; 01312 class->srcfd = -1; 01313 } 01314 01315 return class; 01316 }
static int _moh_register | ( | struct mohclass * | moh, | |
int | reload, | |||
int | unref, | |||
const char * | file, | |||
int | line, | |||
const char * | funcname | |||
) | [static] |
Definition at line 1222 of file res_musiconhold.c.
References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, moh, moh_diff(), MOH_NOTDELETED, and mohclass_unref.
01223 { 01224 struct mohclass *mohclass = NULL; 01225 01226 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname); 01227 01228 if (mohclass && !moh_diff(mohclass, moh)) { 01229 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); 01230 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01231 if (unref) { 01232 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)"); 01233 } 01234 return -1; 01235 } else if (mohclass) { 01236 /* Found a class, but it's different from the one being registered */ 01237 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01238 } 01239 01240 time(&moh->start); 01241 moh->start -= respawn_time; 01242 01243 if (!strcasecmp(moh->mode, "files")) { 01244 if (init_files_class(moh)) { 01245 if (unref) { 01246 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)"); 01247 } 01248 return -1; 01249 } 01250 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 01251 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 01252 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { 01253 if (init_app_class(moh)) { 01254 if (unref) { 01255 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)"); 01256 } 01257 return -1; 01258 } 01259 } else { 01260 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); 01261 if (unref) { 01262 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)"); 01263 } 01264 return -1; 01265 } 01266 01267 ao2_t_link(mohclasses, moh, "Adding class to container"); 01268 01269 if (unref) { 01270 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container"); 01271 } 01272 01273 return 0; 01274 }
static void ast_moh_destroy | ( | void | ) | [static] |
Definition at line 1774 of file res_musiconhold.c.
References ao2_t_callback, ast_verb, OBJ_MULTIPLE, OBJ_NODATA, and OBJ_UNLINK.
Referenced by load_module(), and unload_module().
01775 { 01776 ast_verb(2, "Destroying musiconhold processes\n"); 01777 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback"); 01778 }
static int ast_moh_files_next | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 283 of file res_musiconhold.c.
References ast_closestream(), ast_fileexists(), ast_log(), ast_random(), ast_strlen_zero(), ast_test_flag, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, state, and ast_channel::stream.
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] |
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 1780 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.
01781 { 01782 switch (cmd) { 01783 case CLI_INIT: 01784 e->command = "moh reload"; 01785 e->usage = 01786 "Usage: moh reload\n" 01787 " Reloads the MusicOnHold module.\n" 01788 " Alias for 'module reload res_musiconhold.so'\n"; 01789 return NULL; 01790 case CLI_GENERATE: 01791 return NULL; 01792 } 01793 01794 if (a->argc != e->args) 01795 return CLI_SHOWUSAGE; 01796 01797 reload(); 01798 01799 return CLI_SUCCESS; 01800 }
static char* handle_cli_moh_show_classes | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1840 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.
01841 { 01842 struct mohclass *class; 01843 struct ao2_iterator i; 01844 01845 switch (cmd) { 01846 case CLI_INIT: 01847 e->command = "moh show classes"; 01848 e->usage = 01849 "Usage: moh show classes\n" 01850 " Lists all MusicOnHold classes.\n"; 01851 return NULL; 01852 case CLI_GENERATE: 01853 return NULL; 01854 } 01855 01856 if (a->argc != e->args) 01857 return CLI_SHOWUSAGE; 01858 01859 i = ao2_iterator_init(mohclasses, 0); 01860 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) { 01861 ast_cli(a->fd, "Class: %s\n", class->name); 01862 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>")); 01863 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>")); 01864 if (ast_test_flag(class, MOH_CUSTOM)) { 01865 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>")); 01866 } 01867 if (strcasecmp(class->mode, "files")) { 01868 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); 01869 } 01870 } 01871 ao2_iterator_destroy(&i); 01872 01873 return CLI_SUCCESS; 01874 }
static char* handle_cli_moh_show_files | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1802 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.
01803 { 01804 struct mohclass *class; 01805 struct ao2_iterator i; 01806 01807 switch (cmd) { 01808 case CLI_INIT: 01809 e->command = "moh show files"; 01810 e->usage = 01811 "Usage: moh show files\n" 01812 " Lists all loaded file-based MusicOnHold classes and their\n" 01813 " files.\n"; 01814 return NULL; 01815 case CLI_GENERATE: 01816 return NULL; 01817 } 01818 01819 if (a->argc != e->args) 01820 return CLI_SHOWUSAGE; 01821 01822 i = ao2_iterator_init(mohclasses, 0); 01823 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) { 01824 int x; 01825 01826 if (!class->total_files) { 01827 continue; 01828 } 01829 01830 ast_cli(a->fd, "Class: %s\n", class->name); 01831 for (x = 0; x < class->total_files; x++) { 01832 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]); 01833 } 01834 } 01835 ao2_iterator_destroy(&i); 01836 01837 return CLI_SUCCESS; 01838 }
static int init_app_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1182 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, mohclass::mode, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), mohclass::thread, and mohclass::timer.
Referenced by _moh_register().
01183 { 01184 if (!strcasecmp(class->mode, "custom")) { 01185 ast_set_flag(class, MOH_CUSTOM); 01186 } else if (!strcasecmp(class->mode, "mp3nb")) { 01187 ast_set_flag(class, MOH_SINGLE); 01188 } else if (!strcasecmp(class->mode, "quietmp3nb")) { 01189 ast_set_flag(class, MOH_SINGLE | MOH_QUIET); 01190 } else if (!strcasecmp(class->mode, "quietmp3")) { 01191 ast_set_flag(class, MOH_QUIET); 01192 } 01193 01194 class->srcfd = -1; 01195 01196 if (!(class->timer = ast_timer_open())) { 01197 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01198 return -1; 01199 } 01200 if (class->timer && ast_timer_set_rate(class->timer, 25)) { 01201 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01202 ast_timer_close(class->timer); 01203 class->timer = NULL; 01204 } 01205 01206 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) { 01207 ast_log(LOG_WARNING, "Unable to create moh thread...\n"); 01208 if (class->timer) { 01209 ast_timer_close(class->timer); 01210 class->timer = NULL; 01211 } 01212 return -1; 01213 } 01214 01215 return 0; 01216 }
static int init_files_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1119 of file res_musiconhold.c.
References mohclass::args, ast_set_flag, ast_verbose, mohclass::dir, MOH_RANDOMIZE, moh_scan_files(), mohclass::name, option_verbose, and VERBOSE_PREFIX_3.
Referenced by _moh_register().
01120 { 01121 int res; 01122 01123 res = moh_scan_files(class); 01124 01125 if (res < 0) { 01126 return -1; 01127 } 01128 01129 if (!res) { 01130 if (option_verbose > 2) { 01131 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", 01132 class->dir, class->name); 01133 } 01134 return -1; 01135 } 01136 01137 #if 0 01138 /* XXX This isn't correct. Args is an application for custom mode. XXX */ 01139 if (strchr(class->args, 'r')) { 01140 ast_set_flag(class, MOH_RANDOMIZE); 01141 } 01142 #endif 01143 01144 return 0; 01145 }
static int load_module | ( | void | ) | [static] |
Definition at line 1898 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(), cli_moh, 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().
01899 { 01900 int res; 01901 01902 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) { 01903 return AST_MODULE_LOAD_DECLINE; 01904 } 01905 01906 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */ 01907 ast_log(LOG_WARNING, "No music on hold classes configured, " 01908 "disabling music on hold.\n"); 01909 } else { 01910 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, 01911 local_ast_moh_cleanup); 01912 } 01913 01914 res = ast_register_application_xml(play_moh, play_moh_exec); 01915 ast_register_atexit(ast_moh_destroy); 01916 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01917 if (!res) 01918 res = ast_register_application_xml(wait_moh, wait_moh_exec); 01919 if (!res) 01920 res = ast_register_application_xml(set_moh, set_moh_exec); 01921 if (!res) 01922 res = ast_register_application_xml(start_moh, start_moh_exec); 01923 if (!res) 01924 res = ast_register_application_xml(stop_moh, stop_moh_exec); 01925 01926 return AST_MODULE_LOAD_SUCCESS; 01927 }
static int load_moh_classes | ( | int | reload | ) | [static] |
Definition at line 1664 of file res_musiconhold.c.
References ao2_t_callback, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, global_flags, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_rescan_files(), MOH_SORTALPHA, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, and var.
Referenced by load_module().
01665 { 01666 struct ast_config *cfg; 01667 struct ast_variable *var; 01668 struct mohclass *class; 01669 char *cat; 01670 int numclasses = 0; 01671 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 01672 01673 cfg = ast_config_load("musiconhold.conf", config_flags); 01674 01675 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { 01676 if (ast_check_realtime("musiconhold") && reload) { 01677 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01678 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes"); 01679 } 01680 return 0; 01681 } 01682 if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 01683 moh_rescan_files(); 01684 return 0; 01685 } 01686 01687 if (reload) { 01688 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01689 } 01690 01691 ast_clear_flag(global_flags, AST_FLAGS_ALL); 01692 01693 cat = ast_category_browse(cfg, NULL); 01694 for (; cat; cat = ast_category_browse(cfg, cat)) { 01695 /* Setup common options from [general] section */ 01696 if (!strcasecmp(cat, "general")) { 01697 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01698 if (!strcasecmp(var->name, "cachertclasses")) { 01699 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES); 01700 } else { 01701 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name); 01702 } 01703 } 01704 } 01705 /* These names were deprecated in 1.4 and should not be used until after the next major release. */ 01706 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 01707 !strcasecmp(cat, "general")) { 01708 continue; 01709 } 01710 01711 if (!(class = moh_class_malloc())) { 01712 break; 01713 } 01714 01715 ast_copy_string(class->name, cat, sizeof(class->name)); 01716 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01717 if (!strcasecmp(var->name, "mode")) 01718 ast_copy_string(class->mode, var->value, sizeof(class->mode)); 01719 else if (!strcasecmp(var->name, "directory")) 01720 ast_copy_string(class->dir, var->value, sizeof(class->dir)); 01721 else if (!strcasecmp(var->name, "application")) 01722 ast_copy_string(class->args, var->value, sizeof(class->args)); 01723 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) 01724 class->digit = *var->value; 01725 else if (!strcasecmp(var->name, "random")) 01726 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); 01727 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) 01728 ast_set_flag(class, MOH_RANDOMIZE); 01729 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 01730 ast_set_flag(class, MOH_SORTALPHA); 01731 else if (!strcasecmp(var->name, "format")) { 01732 class->format = ast_getformatbyname(var->value); 01733 if (!class->format) { 01734 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); 01735 class->format = AST_FORMAT_SLINEAR; 01736 } 01737 } 01738 } 01739 01740 if (ast_strlen_zero(class->dir)) { 01741 if (!strcasecmp(class->mode, "custom")) { 01742 strcpy(class->dir, "nodir"); 01743 } else { 01744 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); 01745 class = mohclass_unref(class, "unreffing potential mohclass (no directory)"); 01746 continue; 01747 } 01748 } 01749 if (ast_strlen_zero(class->mode)) { 01750 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); 01751 class = mohclass_unref(class, "unreffing potential mohclass (no mode)"); 01752 continue; 01753 } 01754 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { 01755 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); 01756 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)"); 01757 continue; 01758 } 01759 01760 /* Don't leak a class when it's already registered */ 01761 if (!moh_register(class, reload, HANDLE_REF)) { 01762 numclasses++; 01763 } 01764 } 01765 01766 ast_config_destroy(cfg); 01767 01768 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 01769 moh_classes_delete_marked, NULL, "Purge marked classes"); 01770 01771 return numclasses; 01772 }
static void local_ast_moh_cleanup | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1276 of file res_musiconhold.c.
References ast_free, ast_log(), ast_module_unref(), LOG_WARNING, mohclass_unref, ast_channel::music_state, and state.
Referenced by load_module().
01277 { 01278 struct moh_files_state *state = chan->music_state; 01279 01280 if (state) { 01281 if (state->class) { 01282 /* This should never happen. We likely just leaked some resource. */ 01283 state->class = 01284 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class"); 01285 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n"); 01286 } 01287 ast_free(chan->music_state); 01288 chan->music_state = NULL; 01289 /* Only held a module reference if we had a music state */ 01290 ast_module_unref(ast_module_info->self); 01291 } 01292 }
static int local_ast_moh_start | ( | struct ast_channel * | chan, | |
const char * | mclass, | |||
const char * | interpclass | |||
) | [static] |
Definition at line 1318 of file res_musiconhold.c.
References mohclass::args, ast_check_realtime(), ast_copy_string(), AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), 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(), mohclass::digit, mohclass::dir, DONT_UNREF, errno, mohclass::format, get_mohbyname, global_flags, 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, ast_channel::musicclass, mohclass::name, ast_variable::name, ast_variable::next, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, state, mohclass::thread, mohclass::timer, ast_variable::value, and var.
Referenced by load_module().
01319 { 01320 struct mohclass *mohclass = NULL; 01321 struct moh_files_state *state = chan->music_state; 01322 struct ast_variable *var = NULL; 01323 int res; 01324 int realtime_possible = ast_check_realtime("musiconhold"); 01325 01326 /* The following is the order of preference for which class to use: 01327 * 1) The channels explicitly set musicclass, which should *only* be 01328 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan. 01329 * 2) The mclass argument. If a channel is calling ast_moh_start() as the 01330 * result of receiving a HOLD control frame, this should be the 01331 * payload that came with the frame. 01332 * 3) The interpclass argument. This would be from the mohinterpret 01333 * option from channel drivers. This is the same as the old musicclass 01334 * option. 01335 * 4) The default class. 01336 */ 01337 if (!ast_strlen_zero(chan->musicclass)) { 01338 mohclass = get_mohbyname(chan->musicclass, 1, 0); 01339 if (!mohclass && realtime_possible) { 01340 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL); 01341 } 01342 } 01343 if (!mohclass && !var && !ast_strlen_zero(mclass)) { 01344 mohclass = get_mohbyname(mclass, 1, 0); 01345 if (!mohclass && realtime_possible) { 01346 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL); 01347 } 01348 } 01349 if (!mohclass && !var && !ast_strlen_zero(interpclass)) { 01350 mohclass = get_mohbyname(interpclass, 1, 0); 01351 if (!mohclass && realtime_possible) { 01352 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL); 01353 } 01354 } 01355 01356 if (!mohclass && !var) { 01357 mohclass = get_mohbyname("default", 1, 0); 01358 if (!mohclass && realtime_possible) { 01359 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL); 01360 } 01361 } 01362 01363 /* If no moh class found in memory, then check RT. Note that the logic used 01364 * above guarantees that if var is non-NULL, then mohclass must be NULL. 01365 */ 01366 if (var) { 01367 struct ast_variable *tmp = NULL; 01368 01369 if ((mohclass = moh_class_malloc())) { 01370 mohclass->realtime = 1; 01371 for (tmp = var; tmp; tmp = tmp->next) { 01372 if (!strcasecmp(tmp->name, "name")) 01373 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name)); 01374 else if (!strcasecmp(tmp->name, "mode")) 01375 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 01376 else if (!strcasecmp(tmp->name, "directory")) 01377 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir)); 01378 else if (!strcasecmp(tmp->name, "application")) 01379 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args)); 01380 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value))) 01381 mohclass->digit = *tmp->value; 01382 else if (!strcasecmp(tmp->name, "random")) 01383 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE); 01384 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random")) 01385 ast_set_flag(mohclass, MOH_RANDOMIZE); 01386 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 01387 ast_set_flag(mohclass, MOH_SORTALPHA); 01388 else if (!strcasecmp(tmp->name, "format")) { 01389 mohclass->format = ast_getformatbyname(tmp->value); 01390 if (!mohclass->format) { 01391 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value); 01392 mohclass->format = AST_FORMAT_SLINEAR; 01393 } 01394 } 01395 } 01396 ast_variables_destroy(var); 01397 if (ast_strlen_zero(mohclass->dir)) { 01398 if (!strcasecmp(mohclass->mode, "custom")) { 01399 strcpy(mohclass->dir, "nodir"); 01400 } else { 01401 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name); 01402 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)"); 01403 return -1; 01404 } 01405 } 01406 if (ast_strlen_zero(mohclass->mode)) { 01407 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name); 01408 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)"); 01409 return -1; 01410 } 01411 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) { 01412 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name); 01413 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode"); 01414 return -1; 01415 } 01416 01417 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) { 01418 /* CACHERTCLASSES enabled, let's add this class to default tree */ 01419 if (state && state->class) { 01420 /* Class already exist for this channel */ 01421 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01422 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01423 /* we found RT class with the same name, seems like we should continue playing existing one */ 01424 /* XXX This code is impossible to reach */ 01425 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)"); 01426 mohclass = state->class; 01427 } 01428 } 01429 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well. 01430 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would 01431 * be that the destructor would be called when the generator on the channel is deactivated. The container then 01432 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading 01433 * invalid memory. 01434 */ 01435 if (moh_register(mohclass, 0, DONT_UNREF) == -1) { 01436 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register"); 01437 return -1; 01438 } 01439 } else { 01440 /* We don't register RT moh class, so let's init it manualy */ 01441 01442 time(&mohclass->start); 01443 mohclass->start -= respawn_time; 01444 01445 if (!strcasecmp(mohclass->mode, "files")) { 01446 if (!moh_scan_files(mohclass)) { 01447 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01448 return -1; 01449 } 01450 if (strchr(mohclass->args, 'r')) 01451 ast_set_flag(mohclass, MOH_RANDOMIZE); 01452 } 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")) { 01453 01454 if (!strcasecmp(mohclass->mode, "custom")) 01455 ast_set_flag(mohclass, MOH_CUSTOM); 01456 else if (!strcasecmp(mohclass->mode, "mp3nb")) 01457 ast_set_flag(mohclass, MOH_SINGLE); 01458 else if (!strcasecmp(mohclass->mode, "quietmp3nb")) 01459 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET); 01460 else if (!strcasecmp(mohclass->mode, "quietmp3")) 01461 ast_set_flag(mohclass, MOH_QUIET); 01462 01463 mohclass->srcfd = -1; 01464 if (!(mohclass->timer = ast_timer_open())) { 01465 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01466 } 01467 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) { 01468 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01469 ast_timer_close(mohclass->timer); 01470 mohclass->timer = NULL; 01471 } 01472 01473 /* Let's check if this channel already had a moh class before */ 01474 if (state && state->class) { 01475 /* Class already exist for this channel */ 01476 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01477 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01478 /* we found RT class with the same name, seems like we should continue playing existing one */ 01479 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)"); 01480 mohclass = state->class; 01481 } 01482 } else { 01483 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) { 01484 ast_log(LOG_WARNING, "Unable to create moh...\n"); 01485 if (mohclass->timer) { 01486 ast_timer_close(mohclass->timer); 01487 mohclass->timer = NULL; 01488 } 01489 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)"); 01490 return -1; 01491 } 01492 } 01493 } else { 01494 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode); 01495 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)"); 01496 return -1; 01497 } 01498 } 01499 } else { 01500 ast_variables_destroy(var); 01501 var = NULL; 01502 } 01503 } 01504 01505 if (!mohclass) { 01506 return -1; 01507 } 01508 01509 /* If we are using a cached realtime class with files, re-scan the files */ 01510 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) { 01511 if (!moh_scan_files(mohclass)) { 01512 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01513 return -1; 01514 } 01515 } 01516 01517 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01518 "State: Start\r\n" 01519 "Channel: %s\r\n" 01520 "UniqueID: %s\r\n" 01521 "Class: %s\r\n", 01522 chan->name, chan->uniqueid, 01523 mohclass->name); 01524 01525 ast_set_flag(chan, AST_FLAG_MOH); 01526 01527 if (mohclass->total_files) { 01528 res = ast_activate_generator(chan, &moh_file_stream, mohclass); 01529 } else { 01530 res = ast_activate_generator(chan, &mohgen, mohclass); 01531 } 01532 01533 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start"); 01534 01535 return res; 01536 }
static void local_ast_moh_stop | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1538 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, ast_channel::name, ast_channel::stream, and ast_channel::uniqueid.
Referenced by load_module().
01539 { 01540 ast_clear_flag(chan, AST_FLAG_MOH); 01541 ast_deactivate_generator(chan); 01542 01543 ast_channel_lock(chan); 01544 if (chan->music_state) { 01545 if (chan->stream) { 01546 ast_closestream(chan->stream); 01547 chan->stream = NULL; 01548 } 01549 } 01550 01551 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01552 "State: Stop\r\n" 01553 "Channel: %s\r\n" 01554 "UniqueID: %s\r\n", 01555 chan->name, chan->uniqueid); 01556 ast_channel_unlock(chan); 01557 }
static int moh_add_file | ( | struct mohclass * | class, | |
const char * | filepath | |||
) | [static] |
Definition at line 1004 of file res_musiconhold.c.
References mohclass::allowed_files, ast_calloc, ast_realloc, ast_strdup, mohclass::filearray, INITIAL_NUM_FILES, and mohclass::total_files.
Referenced by moh_scan_files().
01005 { 01006 if (!class->allowed_files) { 01007 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray)))) 01008 return -1; 01009 class->allowed_files = INITIAL_NUM_FILES; 01010 } else if (class->total_files == class->allowed_files) { 01011 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) { 01012 class->allowed_files = 0; 01013 class->total_files = 0; 01014 return -1; 01015 } 01016 class->allowed_files *= 2; 01017 } 01018 01019 if (!(class->filearray[class->total_files] = ast_strdup(filepath))) 01020 return -1; 01021 01022 class->total_files++; 01023 01024 return 0; 01025 }
static void* moh_alloc | ( | struct ast_channel * | chan, | |
void * | params | |||
) | [static] |
Definition at line 933 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, ast_channel::name, mohdata::origwfmt, state, and ast_channel::writeformat.
00934 { 00935 struct mohdata *res; 00936 struct mohclass *class = params; 00937 struct moh_files_state *state; 00938 00939 /* Initiating music_state for current channel. Channel should know name of moh class */ 00940 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { 00941 chan->music_state = state; 00942 ast_module_ref(ast_module_info->self); 00943 } else { 00944 state = chan->music_state; 00945 if (!state) { 00946 return NULL; 00947 } 00948 if (state->class) { 00949 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class"); 00950 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n"); 00951 } 00952 memset(state, 0, sizeof(*state)); 00953 } 00954 00955 if ((res = mohalloc(class))) { 00956 res->origwfmt = chan->writeformat; 00957 if (ast_set_write_format(chan, class->format)) { 00958 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format)); 00959 moh_release(NULL, res); 00960 res = NULL; 00961 } else { 00962 state->class = mohclass_ref(class, "Placing reference into state container"); 00963 } 00964 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name); 00965 } 00966 return res; 00967 }
static int moh_class_cmp | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1889 of file res_musiconhold.c.
References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.
Referenced by load_module().
01890 { 01891 struct mohclass *class = obj, *class2 = arg; 01892 01893 return strcasecmp(class->name, class2->name) ? 0 : 01894 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 : 01895 CMP_MATCH | CMP_STOP; 01896 }
static void moh_class_destructor | ( | void * | obj | ) | [static] |
Definition at line 1559 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, mohdata::list, LOG_DEBUG, and LOG_WARNING.
Referenced by _moh_class_malloc().
01560 { 01561 struct mohclass *class = obj; 01562 struct mohdata *member; 01563 pthread_t tid = 0; 01564 01565 ast_debug(1, "Destroying MOH class '%s'\n", class->name); 01566 01567 ao2_lock(class); 01568 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) { 01569 free(member); 01570 } 01571 ao2_unlock(class); 01572 01573 /* Kill the thread first, so it cannot restart the child process while the 01574 * class is being destroyed */ 01575 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) { 01576 tid = class->thread; 01577 class->thread = AST_PTHREADT_NULL; 01578 pthread_cancel(tid); 01579 /* We'll collect the exit status later, after we ensure all the readers 01580 * are dead. */ 01581 } 01582 01583 if (class->pid > 1) { 01584 char buff[8192]; 01585 int bytes, tbytes = 0, stime = 0, pid = 0; 01586 01587 ast_log(LOG_DEBUG, "killing %d!\n", class->pid); 01588 01589 stime = time(NULL) + 2; 01590 pid = class->pid; 01591 class->pid = 0; 01592 01593 /* Back when this was just mpg123, SIGKILL was fine. Now we need 01594 * to give the process a reason and time enough to kill off its 01595 * children. */ 01596 do { 01597 if (killpg(pid, SIGHUP) < 0) { 01598 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); 01599 } 01600 usleep(100000); 01601 if (killpg(pid, SIGTERM) < 0) { 01602 if (errno == ESRCH) { 01603 break; 01604 } 01605 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); 01606 } 01607 usleep(100000); 01608 if (killpg(pid, SIGKILL) < 0) { 01609 if (errno == ESRCH) { 01610 break; 01611 } 01612 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); 01613 } 01614 } while (0); 01615 01616 while ((ast_wait_for_input(class->srcfd, 100) > 0) && 01617 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) { 01618 tbytes = tbytes + bytes; 01619 } 01620 01621 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); 01622 01623 close(class->srcfd); 01624 class->srcfd = -1; 01625 } 01626 01627 if (class->filearray) { 01628 int i; 01629 for (i = 0; i < class->total_files; i++) { 01630 free(class->filearray[i]); 01631 } 01632 free(class->filearray); 01633 class->filearray = NULL; 01634 } 01635 01636 if (class->timer) { 01637 ast_timer_close(class->timer); 01638 class->timer = NULL; 01639 } 01640 01641 /* Finally, collect the exit status of the monitor thread */ 01642 if (tid > 0) { 01643 pthread_join(tid, NULL); 01644 } 01645 01646 }
static int moh_class_hash | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1882 of file res_musiconhold.c.
References ast_str_case_hash().
Referenced by load_module().
01883 { 01884 const struct mohclass *class = obj; 01885 01886 return ast_str_case_hash(class->name); 01887 }
static int moh_class_inuse | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1939 of file res_musiconhold.c.
References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.
Referenced by unload_module().
01940 { 01941 struct mohclass *class = obj; 01942 01943 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP; 01944 }
static int moh_class_mark | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1648 of file res_musiconhold.c.
Referenced by load_moh_classes().
01649 { 01650 struct mohclass *class = obj; 01651 01652 class->delete = 1; 01653 01654 return 0; 01655 }
static int moh_classes_delete_marked | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1657 of file res_musiconhold.c.
References CMP_MATCH.
Referenced by load_moh_classes().
01658 { 01659 struct mohclass *class = obj; 01660 01661 return class->delete ? CMP_MATCH : 0; 01662 }
Definition at line 1163 of file res_musiconhold.c.
References mohclass::args, mohclass::dir, mohclass::flags, and mohclass::mode.
Referenced by _moh_register().
01164 { 01165 if (!old || !new) { 01166 return -1; 01167 } 01168 01169 if (strcmp(old->dir, new->dir)) { 01170 return -1; 01171 } else if (strcmp(old->mode, new->mode)) { 01172 return -1; 01173 } else if (strcmp(old->args, new->args)) { 01174 return -1; 01175 } else if (old->flags != new->flags) { 01176 return -1; 01177 } 01178 01179 return 0; 01180 }
static int moh_digit_match | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 447 of file res_musiconhold.c.
References CMP_MATCH, and CMP_STOP.
Referenced by get_mohbydigit().
00448 { 00449 char *digit = arg; 00450 struct mohclass *class = obj; 00451 00452 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0; 00453 }
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, LOG_WARNING, MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::music_state, ast_channel::name, state, 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, and state.
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] |
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, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, state, 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 969 of file res_musiconhold.c.
References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, LOG_WARNING, moh, and ast_channel::name.
00970 { 00971 struct mohdata *moh = data; 00972 short buf[1280 + AST_FRIENDLY_OFFSET / 2]; 00973 int res; 00974 00975 len = ast_codec_get_len(moh->parent->format, samples); 00976 00977 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { 00978 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name); 00979 len = sizeof(buf) - AST_FRIENDLY_OFFSET; 00980 } 00981 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len); 00982 if (res <= 0) 00983 return 0; 00984 00985 moh->f.datalen = res; 00986 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2; 00987 moh->f.samples = ast_codec_get_samples(&moh->f); 00988 00989 if (ast_write(chan, &moh->f) < 0) { 00990 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); 00991 return -1; 00992 } 00993 00994 return 0; 00995 }
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 898 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, LOG_WARNING, moh, mohclass_unref, ast_channel::music_state, ast_channel::name, and state.
Referenced by moh_alloc().
00899 { 00900 struct mohdata *moh = data; 00901 struct mohclass *class = moh->parent; 00902 format_t oldwfmt; 00903 00904 ao2_lock(class); 00905 AST_LIST_REMOVE(&moh->parent->members, moh, list); 00906 ao2_unlock(class); 00907 00908 close(moh->pipe[0]); 00909 close(moh->pipe[1]); 00910 00911 oldwfmt = moh->origwfmt; 00912 00913 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator"); 00914 00915 ast_free(moh); 00916 00917 if (chan) { 00918 struct moh_files_state *state; 00919 00920 state = chan->music_state; 00921 if (state && state->class) { 00922 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator"); 00923 } 00924 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) { 00925 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 00926 chan->name, ast_getformatname(oldwfmt)); 00927 } 00928 00929 ast_verb(3, "Stopped music on hold on %s\n", chan->name); 00930 } 00931 }
static void moh_rescan_files | ( | void | ) | [static] |
Definition at line 1147 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().
01147 { 01148 struct ao2_iterator i; 01149 struct mohclass *c; 01150 01151 i = ao2_iterator_init(mohclasses, 0); 01152 01153 while ((c = ao2_iterator_next(&i))) { 01154 if (!strcasecmp(c->mode, "files")) { 01155 moh_scan_files(c); 01156 } 01157 ao2_ref(c, -1); 01158 } 01159 01160 ao2_iterator_destroy(&i); 01161 }
static int moh_scan_files | ( | struct mohclass * | class | ) | [static] |
Definition at line 1037 of file res_musiconhold.c.
References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_free, ast_log(), mohclass::dir, errno, ext, mohclass::filearray, LOG_WARNING, moh_add_file(), mohclass::name, and mohclass::total_files.
Referenced by init_files_class(), local_ast_moh_start(), and moh_rescan_files().
01037 { 01038 01039 DIR *files_DIR; 01040 struct dirent *files_dirent; 01041 char dir_path[PATH_MAX]; 01042 char path[PATH_MAX]; 01043 char filepath[PATH_MAX]; 01044 char *ext; 01045 struct stat statbuf; 01046 int i; 01047 01048 if (class->dir[0] != '/') { 01049 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path)); 01050 strncat(dir_path, "/", sizeof(dir_path) - 1); 01051 strncat(dir_path, class->dir, sizeof(dir_path) - 1); 01052 } else { 01053 ast_copy_string(dir_path, class->dir, sizeof(dir_path)); 01054 } 01055 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name); 01056 files_DIR = opendir(dir_path); 01057 if (!files_DIR) { 01058 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path); 01059 return -1; 01060 } 01061 01062 for (i = 0; i < class->total_files; i++) 01063 ast_free(class->filearray[i]); 01064 01065 class->total_files = 0; 01066 if (!getcwd(path, sizeof(path))) { 01067 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno)); 01068 return -1; 01069 } 01070 if (chdir(dir_path) < 0) { 01071 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01072 return -1; 01073 } 01074 while ((files_dirent = readdir(files_DIR))) { 01075 /* The file name must be at least long enough to have the file type extension */ 01076 if ((strlen(files_dirent->d_name) < 4)) 01077 continue; 01078 01079 /* Skip files that starts with a dot */ 01080 if (files_dirent->d_name[0] == '.') 01081 continue; 01082 01083 /* Skip files without extensions... they are not audio */ 01084 if (!strchr(files_dirent->d_name, '.')) 01085 continue; 01086 01087 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name); 01088 01089 if (stat(filepath, &statbuf)) 01090 continue; 01091 01092 if (!S_ISREG(statbuf.st_mode)) 01093 continue; 01094 01095 if ((ext = strrchr(filepath, '.'))) 01096 *ext = '\0'; 01097 01098 /* if the file is present in multiple formats, ensure we only put it into the list once */ 01099 for (i = 0; i < class->total_files; i++) 01100 if (!strcmp(filepath, class->filearray[i])) 01101 break; 01102 01103 if (i == class->total_files) { 01104 if (moh_add_file(class, filepath)) 01105 break; 01106 } 01107 } 01108 01109 closedir(files_DIR); 01110 if (chdir(path) < 0) { 01111 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01112 return -1; 01113 } 01114 if (ast_test_flag(class, MOH_SORTALPHA)) 01115 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare); 01116 return class->total_files; 01117 }
static int moh_sort_compare | ( | const void * | i1, | |
const void * | i2 | |||
) | [static] |
Definition at line 1027 of file res_musiconhold.c.
01028 { 01029 char *s1, *s2; 01030 01031 s1 = ((char **)i1)[0]; 01032 s2 = ((char **)i2)[0]; 01033 01034 return strcasecmp(s1, s2); 01035 }
Definition at line 865 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(), errno, mohclass::format, mohdata::list, LOG_WARNING, mohclass::members, moh, mohclass_ref, and mohdata::pipe.
Referenced by moh_alloc().
00866 { 00867 struct mohdata *moh; 00868 long flags; 00869 00870 if (!(moh = ast_calloc(1, sizeof(*moh)))) 00871 return NULL; 00872 00873 if (pipe(moh->pipe)) { 00874 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno)); 00875 ast_free(moh); 00876 return NULL; 00877 } 00878 00879 /* Make entirely non-blocking */ 00880 flags = fcntl(moh->pipe[0], F_GETFL); 00881 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK); 00882 flags = fcntl(moh->pipe[1], F_GETFL); 00883 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK); 00884 00885 moh->f.frametype = AST_FRAME_VOICE; 00886 moh->f.subclass.codec = cl->format; 00887 moh->f.offset = AST_FRIENDLY_OFFSET; 00888 00889 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent"); 00890 00891 ao2_lock(cl); 00892 AST_LIST_INSERT_HEAD(&cl->members, moh, list); 00893 ao2_unlock(cl); 00894 00895 return moh; 00896 }
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_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, 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 pthread_testcancel(); 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 ast_timer_ack(class->timer, 1); 00657 res = 320; 00658 } else { 00659 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); 00660 res = 0; 00661 } 00662 pthread_testcancel(); 00663 } else { 00664 long delta; 00665 /* Reliable sleep */ 00666 tv_tmp = ast_tvnow(); 00667 if (ast_tvzero(deadline)) 00668 deadline = tv_tmp; 00669 delta = ast_tvdiff_ms(tv_tmp, deadline); 00670 if (delta < MOH_MS_INTERVAL) { /* too early */ 00671 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */ 00672 usleep(1000 * (MOH_MS_INTERVAL - delta)); 00673 pthread_testcancel(); 00674 } else { 00675 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n"); 00676 deadline = tv_tmp; 00677 } 00678 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */ 00679 } 00680 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members)) 00681 continue; 00682 /* Read mp3 audio */ 00683 len = ast_codec_get_len(class->format, res); 00684 00685 if ((res2 = read(class->srcfd, sbuf, len)) != len) { 00686 if (!res2) { 00687 close(class->srcfd); 00688 class->srcfd = -1; 00689 pthread_testcancel(); 00690 if (class->pid > 1) { 00691 do { 00692 if (killpg(class->pid, SIGHUP) < 0) { 00693 if (errno == ESRCH) { 00694 break; 00695 } 00696 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); 00697 } 00698 usleep(100000); 00699 if (killpg(class->pid, SIGTERM) < 0) { 00700 if (errno == ESRCH) { 00701 break; 00702 } 00703 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); 00704 } 00705 usleep(100000); 00706 if (killpg(class->pid, SIGKILL) < 0) { 00707 if (errno == ESRCH) { 00708 break; 00709 } 00710 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); 00711 } 00712 } while (0); 00713 class->pid = 0; 00714 } 00715 } else { 00716 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len); 00717 } 00718 continue; 00719 } 00720 00721 pthread_testcancel(); 00722 00723 ao2_lock(class); 00724 AST_LIST_TRAVERSE(&class->members, moh, list) { 00725 /* Write data */ 00726 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) { 00727 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2); 00728 } 00729 } 00730 ao2_unlock(class); 00731 } 00732 return NULL; 00733 }
static int play_moh_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 735 of file res_musiconhold.c.
References 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, ast_channel::name, parse(), and S_OR.
Referenced by load_module().
00736 { 00737 char *parse; 00738 char *class; 00739 int timeout = -1; 00740 int res; 00741 AST_DECLARE_APP_ARGS(args, 00742 AST_APP_ARG(class); 00743 AST_APP_ARG(duration); 00744 ); 00745 00746 parse = ast_strdupa(data); 00747 00748 AST_STANDARD_APP_ARGS(args, parse); 00749 00750 if (!ast_strlen_zero(args.duration)) { 00751 if (sscanf(args.duration, "%30d", &timeout) == 1) { 00752 timeout *= 1000; 00753 } else { 00754 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration); 00755 } 00756 } 00757 00758 class = S_OR(args.class, NULL); 00759 if (ast_moh_start(chan, class, NULL)) { 00760 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name); 00761 return 0; 00762 } 00763 00764 if (timeout > 0) 00765 res = ast_safe_sleep(chan, timeout); 00766 else { 00767 while (!(res = ast_safe_sleep(chan, 10000))); 00768 } 00769 00770 ast_moh_stop(chan); 00771 00772 return res; 00773 }
static int reload | ( | void | ) | [static] |
static int set_moh_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 798 of file res_musiconhold.c.
References ast_log(), ast_string_field_set, ast_strlen_zero(), LOG_WARNING, and musicclass.
Referenced by load_module().
00799 { 00800 static int deprecation_warning = 0; 00801 00802 if (!deprecation_warning) { 00803 deprecation_warning = 1; 00804 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n"); 00805 } 00806 00807 if (ast_strlen_zero(data)) { 00808 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n"); 00809 return -1; 00810 } 00811 ast_string_field_set(chan, musicclass, data); 00812 return 0; 00813 }
static int spawn_mp3 | ( | struct mohclass * | class | ) | [static] |
Definition at line 483 of file res_musiconhold.c.
References mohclass::args, 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, MPG_123, mohclass::pid, mohclass::start, and strsep().
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 815 of file res_musiconhold.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, ast_channel::name, parse(), and S_OR.
Referenced by load_module().
00816 { 00817 char *parse; 00818 char *class; 00819 AST_DECLARE_APP_ARGS(args, 00820 AST_APP_ARG(class); 00821 ); 00822 00823 parse = ast_strdupa(data); 00824 00825 AST_STANDARD_APP_ARGS(args, parse); 00826 00827 class = S_OR(args.class, NULL); 00828 if (ast_moh_start(chan, class, NULL)) 00829 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name); 00830 00831 return 0; 00832 }
static int stop_moh_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 834 of file res_musiconhold.c.
References ast_moh_stop().
Referenced by load_module().
00835 { 00836 ast_moh_stop(chan); 00837 00838 return 0; 00839 }
static int unload_module | ( | void | ) | [static] |
Definition at line 1946 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(), cli_moh, LOG_WARNING, moh_class_inuse(), and mohclass_unref.
01947 { 01948 int res = 0; 01949 struct mohclass *class = NULL; 01950 01951 /* XXX This check shouldn't be required if module ref counting was being used 01952 * properly ... */ 01953 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) { 01954 class = mohclass_unref(class, "unref of class from module unload callback"); 01955 res = -1; 01956 } 01957 01958 if (res < 0) { 01959 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n"); 01960 return res; 01961 } 01962 01963 ast_uninstall_music_functions(); 01964 01965 ast_moh_destroy(); 01966 res = ast_unregister_application(play_moh); 01967 res |= ast_unregister_application(wait_moh); 01968 res |= ast_unregister_application(set_moh); 01969 res |= ast_unregister_application(start_moh); 01970 res |= ast_unregister_application(stop_moh); 01971 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01972 ast_unregister_atexit(ast_moh_destroy); 01973 01974 return res; 01975 }
static int wait_moh_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 775 of file res_musiconhold.c.
References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), LOG_WARNING, and ast_channel::name.
Referenced by load_module().
00776 { 00777 static int deprecation_warning = 0; 00778 int res; 00779 00780 if (!deprecation_warning) { 00781 deprecation_warning = 1; 00782 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n"); 00783 } 00784 00785 if (!data || !atoi(data)) { 00786 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n"); 00787 return -1; 00788 } 00789 if (ast_moh_start(chan, NULL, NULL)) { 00790 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name); 00791 return 0; 00792 } 00793 res = ast_safe_sleep(chan, atoi(data) * 1000); 00794 ast_moh_stop(chan); 00795 return res; 00796 }
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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static] |
Definition at line 1982 of file res_musiconhold.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1982 of file res_musiconhold.c.
struct ast_cli_entry cli_moh[] [static] |
Initial value:
{ { .handler = handle_cli_moh_reload , .summary = "Reload MusicOnHold" ,__VA_ARGS__ }, { .handler = handle_cli_moh_show_classes , .summary = "List MusicOnHold classes" ,__VA_ARGS__ }, { .handler = handle_cli_moh_show_files , .summary = "List MusicOnHold file-based classes" ,__VA_ARGS__ } }
Definition at line 1876 of file res_musiconhold.c.
Referenced by load_module(), and unload_module().
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 997 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.