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