#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/manager.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.h"
Go to the source code of this file.
Data Structures | |
struct | moh_files_state |
struct | mohclass |
struct | mohdata |
Defines | |
#define | DONT_UNREF 0 |
#define | get_mohbyname(a, b, c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
#define | HANDLE_REF 1 |
#define | INITIAL_NUM_FILES 8 |
#define | LOCAL_MPG_123 "/usr/local/bin/mpg123" |
#define | MAX_MP3S 256 |
#define | MOH_CACHERTCLASSES (1 << 5) |
#define | moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__) |
#define | MOH_CUSTOM (1 << 2) |
#define | MOH_MS_INTERVAL 100 |
#define | MOH_NOTDELETED (1 << 30) |
#define | MOH_QUIET (1 << 0) |
#define | MOH_RANDOMIZE (1 << 3) |
#define | moh_register(a, b, c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__) |
#define | MOH_SINGLE (1 << 1) |
#define | MOH_SORTALPHA (1 << 4) |
#define | mohclass_ref(class, string) (ao2_t_ref((class), +1, (string)), class) |
#define | mohclass_unref(class, string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL) |
#define | MPG_123 "/usr/bin/mpg123" |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static struct mohclass * | _get_mohbyname (const char *name, int warn, int flags, const char *file, int lineno, const char *funcname) |
static struct mohclass * | _moh_class_malloc (const char *file, int line, const char *funcname) |
static int | _moh_register (struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname) |
static void | ast_moh_destroy (void) |
static int | ast_moh_files_next (struct ast_channel *chan) |
static struct mohclass * | get_mohbydigit (char digit) |
static char * | handle_cli_moh_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_cli_moh_show_classes (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_cli_moh_show_files (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static int | init_app_class (struct mohclass *class) |
static int | init_files_class (struct mohclass *class) |
static int | load_module (void) |
static int | load_moh_classes (int reload) |
static void | local_ast_moh_cleanup (struct ast_channel *chan) |
static int | local_ast_moh_start (struct ast_channel *chan, const char *mclass, const char *interpclass) |
static void | local_ast_moh_stop (struct ast_channel *chan) |
static int | moh_add_file (struct mohclass *class, const char *filepath) |
static void * | moh_alloc (struct ast_channel *chan, void *params) |
static int | moh_class_cmp (void *obj, void *arg, int flags) |
static void | moh_class_destructor (void *obj) |
static int | moh_class_hash (const void *obj, const int flags) |
static int | moh_class_inuse (void *obj, void *arg, int flags) |
static int | moh_class_mark (void *obj, void *arg, int flags) |
static int | moh_classes_delete_marked (void *obj, void *arg, int flags) |
static int | moh_diff (struct mohclass *old, struct mohclass *new) |
static int | moh_digit_match (void *obj, void *arg, int flags) |
static void * | moh_files_alloc (struct ast_channel *chan, void *params) |
static int | moh_files_generator (struct ast_channel *chan, void *data, int len, int samples) |
static struct ast_frame * | moh_files_readframe (struct ast_channel *chan) |
static void | moh_files_release (struct ast_channel *chan, void *data) |
static int | moh_generate (struct ast_channel *chan, void *data, int len, int samples) |
static void | moh_handle_digit (struct ast_channel *chan, char digit) |
static void | moh_release (struct ast_channel *chan, void *data) |
static void | moh_rescan_files (void) |
static int | moh_scan_files (struct mohclass *class) |
static int | moh_sort_compare (const void *i1, const void *i2) |
static struct mohdata * | mohalloc (struct mohclass *cl) |
static void * | monmp3thread (void *data) |
static int | play_moh_exec (struct ast_channel *chan, const char *data) |
static int | reload (void) |
static int | set_moh_exec (struct ast_channel *chan, const char *data) |
static int | spawn_mp3 (struct mohclass *class) |
static int | start_moh_exec (struct ast_channel *chan, const char *data) |
static int | stop_moh_exec (struct ast_channel *chan, const char *data) |
static int | unload_module (void) |
static int | wait_moh_exec (struct ast_channel *chan, const char *data) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } |
static struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_cli_entry | cli_moh [] |
static struct ast_flags | global_flags [1] = {{0}} |
static struct ast_generator | moh_file_stream |
static struct ao2_container * | mohclasses |
static struct ast_generator | mohgen |
static const char | play_moh [] = "MusicOnHold" |
static int | respawn_time = 20 |
static const char | set_moh [] = "SetMusicOnHold" |
static const char | start_moh [] = "StartMusicOnHold" |
static const char | stop_moh [] = "StopMusicOnHold" |
static const char | wait_moh [] = "WaitMusicOnHold" |
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 1298 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 1223 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 1984 of file res_musiconhold.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 1984 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 1300 of file res_musiconhold.c.
References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().
01301 { 01302 struct mohclass *class; 01303 01304 if ((class = 01305 #ifdef REF_DEBUG 01306 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1) 01307 #elif defined(__AST_DEBUG_MALLOC) 01308 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0) 01309 #else 01310 ao2_alloc(sizeof(*class), moh_class_destructor) 01311 #endif 01312 )) { 01313 class->format = AST_FORMAT_SLINEAR; 01314 class->srcfd = -1; 01315 } 01316 01317 return class; 01318 }
static int _moh_register | ( | struct mohclass * | moh, | |
int | reload, | |||
int | unref, | |||
const char * | file, | |||
int | line, | |||
const char * | funcname | |||
) | [static] |
Definition at line 1224 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.
01225 { 01226 struct mohclass *mohclass = NULL; 01227 01228 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname); 01229 01230 if (mohclass && !moh_diff(mohclass, moh)) { 01231 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); 01232 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01233 if (unref) { 01234 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)"); 01235 } 01236 return -1; 01237 } else if (mohclass) { 01238 /* Found a class, but it's different from the one being registered */ 01239 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name"); 01240 } 01241 01242 time(&moh->start); 01243 moh->start -= respawn_time; 01244 01245 if (!strcasecmp(moh->mode, "files")) { 01246 if (init_files_class(moh)) { 01247 if (unref) { 01248 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)"); 01249 } 01250 return -1; 01251 } 01252 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 01253 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 01254 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { 01255 if (init_app_class(moh)) { 01256 if (unref) { 01257 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)"); 01258 } 01259 return -1; 01260 } 01261 } else { 01262 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); 01263 if (unref) { 01264 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)"); 01265 } 01266 return -1; 01267 } 01268 01269 ao2_t_link(mohclasses, moh, "Adding class to container"); 01270 01271 if (unref) { 01272 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container"); 01273 } 01274 01275 return 0; 01276 }
static void ast_moh_destroy | ( | void | ) | [static] |
Definition at line 1776 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().
01777 { 01778 ast_verb(2, "Destroying musiconhold processes\n"); 01779 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback"); 01780 }
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 1782 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.
01783 { 01784 switch (cmd) { 01785 case CLI_INIT: 01786 e->command = "moh reload"; 01787 e->usage = 01788 "Usage: moh reload\n" 01789 " Reloads the MusicOnHold module.\n" 01790 " Alias for 'module reload res_musiconhold.so'\n"; 01791 return NULL; 01792 case CLI_GENERATE: 01793 return NULL; 01794 } 01795 01796 if (a->argc != e->args) 01797 return CLI_SHOWUSAGE; 01798 01799 reload(); 01800 01801 return CLI_SUCCESS; 01802 }
static char* handle_cli_moh_show_classes | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1842 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.
01843 { 01844 struct mohclass *class; 01845 struct ao2_iterator i; 01846 01847 switch (cmd) { 01848 case CLI_INIT: 01849 e->command = "moh show classes"; 01850 e->usage = 01851 "Usage: moh show classes\n" 01852 " Lists all MusicOnHold classes.\n"; 01853 return NULL; 01854 case CLI_GENERATE: 01855 return NULL; 01856 } 01857 01858 if (a->argc != e->args) 01859 return CLI_SHOWUSAGE; 01860 01861 i = ao2_iterator_init(mohclasses, 0); 01862 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) { 01863 ast_cli(a->fd, "Class: %s\n", class->name); 01864 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>")); 01865 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>")); 01866 if (ast_test_flag(class, MOH_CUSTOM)) { 01867 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>")); 01868 } 01869 if (strcasecmp(class->mode, "files")) { 01870 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); 01871 } 01872 } 01873 ao2_iterator_destroy(&i); 01874 01875 return CLI_SUCCESS; 01876 }
static char* handle_cli_moh_show_files | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1804 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.
01805 { 01806 struct mohclass *class; 01807 struct ao2_iterator i; 01808 01809 switch (cmd) { 01810 case CLI_INIT: 01811 e->command = "moh show files"; 01812 e->usage = 01813 "Usage: moh show files\n" 01814 " Lists all loaded file-based MusicOnHold classes and their\n" 01815 " files.\n"; 01816 return NULL; 01817 case CLI_GENERATE: 01818 return NULL; 01819 } 01820 01821 if (a->argc != e->args) 01822 return CLI_SHOWUSAGE; 01823 01824 i = ao2_iterator_init(mohclasses, 0); 01825 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) { 01826 int x; 01827 01828 if (!class->total_files) { 01829 continue; 01830 } 01831 01832 ast_cli(a->fd, "Class: %s\n", class->name); 01833 for (x = 0; x < class->total_files; x++) { 01834 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]); 01835 } 01836 } 01837 ao2_iterator_destroy(&i); 01838 01839 return CLI_SUCCESS; 01840 }
static int init_app_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1184 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().
01185 { 01186 if (!strcasecmp(class->mode, "custom")) { 01187 ast_set_flag(class, MOH_CUSTOM); 01188 } else if (!strcasecmp(class->mode, "mp3nb")) { 01189 ast_set_flag(class, MOH_SINGLE); 01190 } else if (!strcasecmp(class->mode, "quietmp3nb")) { 01191 ast_set_flag(class, MOH_SINGLE | MOH_QUIET); 01192 } else if (!strcasecmp(class->mode, "quietmp3")) { 01193 ast_set_flag(class, MOH_QUIET); 01194 } 01195 01196 class->srcfd = -1; 01197 01198 if (!(class->timer = ast_timer_open())) { 01199 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01200 return -1; 01201 } 01202 if (class->timer && ast_timer_set_rate(class->timer, 25)) { 01203 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01204 ast_timer_close(class->timer); 01205 class->timer = NULL; 01206 } 01207 01208 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) { 01209 ast_log(LOG_WARNING, "Unable to create moh thread...\n"); 01210 if (class->timer) { 01211 ast_timer_close(class->timer); 01212 class->timer = NULL; 01213 } 01214 return -1; 01215 } 01216 01217 return 0; 01218 }
static int init_files_class | ( | struct mohclass * | class | ) | [static] |
Definition at line 1121 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().
01122 { 01123 int res; 01124 01125 res = moh_scan_files(class); 01126 01127 if (res < 0) { 01128 return -1; 01129 } 01130 01131 if (!res) { 01132 if (option_verbose > 2) { 01133 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", 01134 class->dir, class->name); 01135 } 01136 return -1; 01137 } 01138 01139 #if 0 01140 /* XXX This isn't correct. Args is an application for custom mode. XXX */ 01141 if (strchr(class->args, 'r')) { 01142 ast_set_flag(class, MOH_RANDOMIZE); 01143 } 01144 #endif 01145 01146 return 0; 01147 }
static int load_module | ( | void | ) | [static] |
Definition at line 1900 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().
01901 { 01902 int res; 01903 01904 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) { 01905 return AST_MODULE_LOAD_DECLINE; 01906 } 01907 01908 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */ 01909 ast_log(LOG_WARNING, "No music on hold classes configured, " 01910 "disabling music on hold.\n"); 01911 } else { 01912 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, 01913 local_ast_moh_cleanup); 01914 } 01915 01916 res = ast_register_application_xml(play_moh, play_moh_exec); 01917 ast_register_atexit(ast_moh_destroy); 01918 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01919 if (!res) 01920 res = ast_register_application_xml(wait_moh, wait_moh_exec); 01921 if (!res) 01922 res = ast_register_application_xml(set_moh, set_moh_exec); 01923 if (!res) 01924 res = ast_register_application_xml(start_moh, start_moh_exec); 01925 if (!res) 01926 res = ast_register_application_xml(stop_moh, stop_moh_exec); 01927 01928 return AST_MODULE_LOAD_SUCCESS; 01929 }
static int load_moh_classes | ( | int | reload | ) | [static] |
Definition at line 1666 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_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().
01667 { 01668 struct ast_config *cfg; 01669 struct ast_variable *var; 01670 struct mohclass *class; 01671 char *cat; 01672 int numclasses = 0; 01673 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 01674 01675 cfg = ast_config_load("musiconhold.conf", config_flags); 01676 01677 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { 01678 if (ast_check_realtime("musiconhold") && reload) { 01679 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01680 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes"); 01681 } 01682 return 0; 01683 } 01684 if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 01685 moh_rescan_files(); 01686 return 0; 01687 } 01688 01689 if (reload) { 01690 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); 01691 } 01692 01693 ast_clear_flag(global_flags, AST_FLAGS_ALL); 01694 01695 cat = ast_category_browse(cfg, NULL); 01696 for (; cat; cat = ast_category_browse(cfg, cat)) { 01697 /* Setup common options from [general] section */ 01698 if (!strcasecmp(cat, "general")) { 01699 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01700 if (!strcasecmp(var->name, "cachertclasses")) { 01701 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES); 01702 } else { 01703 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name); 01704 } 01705 } 01706 } 01707 /* These names were deprecated in 1.4 and should not be used until after the next major release. */ 01708 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 01709 !strcasecmp(cat, "general")) { 01710 continue; 01711 } 01712 01713 if (!(class = moh_class_malloc())) { 01714 break; 01715 } 01716 01717 ast_copy_string(class->name, cat, sizeof(class->name)); 01718 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 01719 if (!strcasecmp(var->name, "mode")) 01720 ast_copy_string(class->mode, var->value, sizeof(class->mode)); 01721 else if (!strcasecmp(var->name, "directory")) 01722 ast_copy_string(class->dir, var->value, sizeof(class->dir)); 01723 else if (!strcasecmp(var->name, "application")) 01724 ast_copy_string(class->args, var->value, sizeof(class->args)); 01725 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) 01726 class->digit = *var->value; 01727 else if (!strcasecmp(var->name, "random")) 01728 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); 01729 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) 01730 ast_set_flag(class, MOH_RANDOMIZE); 01731 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 01732 ast_set_flag(class, MOH_SORTALPHA); 01733 else if (!strcasecmp(var->name, "format")) { 01734 class->format = ast_getformatbyname(var->value); 01735 if (!class->format) { 01736 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); 01737 class->format = AST_FORMAT_SLINEAR; 01738 } 01739 } 01740 } 01741 01742 if (ast_strlen_zero(class->dir)) { 01743 if (!strcasecmp(class->mode, "custom")) { 01744 strcpy(class->dir, "nodir"); 01745 } else { 01746 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); 01747 class = mohclass_unref(class, "unreffing potential mohclass (no directory)"); 01748 continue; 01749 } 01750 } 01751 if (ast_strlen_zero(class->mode)) { 01752 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); 01753 class = mohclass_unref(class, "unreffing potential mohclass (no mode)"); 01754 continue; 01755 } 01756 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { 01757 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); 01758 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)"); 01759 continue; 01760 } 01761 01762 /* Don't leak a class when it's already registered */ 01763 if (!moh_register(class, reload, HANDLE_REF)) { 01764 numclasses++; 01765 } 01766 } 01767 01768 ast_config_destroy(cfg); 01769 01770 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 01771 moh_classes_delete_marked, NULL, "Purge marked classes"); 01772 01773 return numclasses; 01774 }
static void local_ast_moh_cleanup | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1278 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().
01279 { 01280 struct moh_files_state *state = chan->music_state; 01281 01282 if (state) { 01283 if (state->class) { 01284 /* This should never happen. We likely just leaked some resource. */ 01285 state->class = 01286 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class"); 01287 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n"); 01288 } 01289 ast_free(chan->music_state); 01290 chan->music_state = NULL; 01291 /* Only held a module reference if we had a music state */ 01292 ast_module_unref(ast_module_info->self); 01293 } 01294 }
static int local_ast_moh_start | ( | struct ast_channel * | chan, | |
const char * | mclass, | |||
const char * | interpclass | |||
) | [static] |
Definition at line 1320 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().
01321 { 01322 struct mohclass *mohclass = NULL; 01323 struct moh_files_state *state = chan->music_state; 01324 struct ast_variable *var = NULL; 01325 int res; 01326 int realtime_possible = ast_check_realtime("musiconhold"); 01327 01328 /* The following is the order of preference for which class to use: 01329 * 1) The channels explicitly set musicclass, which should *only* be 01330 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan. 01331 * 2) The mclass argument. If a channel is calling ast_moh_start() as the 01332 * result of receiving a HOLD control frame, this should be the 01333 * payload that came with the frame. 01334 * 3) The interpclass argument. This would be from the mohinterpret 01335 * option from channel drivers. This is the same as the old musicclass 01336 * option. 01337 * 4) The default class. 01338 */ 01339 if (!ast_strlen_zero(chan->musicclass)) { 01340 mohclass = get_mohbyname(chan->musicclass, 1, 0); 01341 if (!mohclass && realtime_possible) { 01342 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL); 01343 } 01344 } 01345 if (!mohclass && !var && !ast_strlen_zero(mclass)) { 01346 mohclass = get_mohbyname(mclass, 1, 0); 01347 if (!mohclass && realtime_possible) { 01348 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL); 01349 } 01350 } 01351 if (!mohclass && !var && !ast_strlen_zero(interpclass)) { 01352 mohclass = get_mohbyname(interpclass, 1, 0); 01353 if (!mohclass && realtime_possible) { 01354 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL); 01355 } 01356 } 01357 01358 if (!mohclass && !var) { 01359 mohclass = get_mohbyname("default", 1, 0); 01360 if (!mohclass && realtime_possible) { 01361 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL); 01362 } 01363 } 01364 01365 /* If no moh class found in memory, then check RT. Note that the logic used 01366 * above guarantees that if var is non-NULL, then mohclass must be NULL. 01367 */ 01368 if (var) { 01369 struct ast_variable *tmp = NULL; 01370 01371 if ((mohclass = moh_class_malloc())) { 01372 mohclass->realtime = 1; 01373 for (tmp = var; tmp; tmp = tmp->next) { 01374 if (!strcasecmp(tmp->name, "name")) 01375 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name)); 01376 else if (!strcasecmp(tmp->name, "mode")) 01377 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 01378 else if (!strcasecmp(tmp->name, "directory")) 01379 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir)); 01380 else if (!strcasecmp(tmp->name, "application")) 01381 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args)); 01382 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value))) 01383 mohclass->digit = *tmp->value; 01384 else if (!strcasecmp(tmp->name, "random")) 01385 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE); 01386 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random")) 01387 ast_set_flag(mohclass, MOH_RANDOMIZE); 01388 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 01389 ast_set_flag(mohclass, MOH_SORTALPHA); 01390 else if (!strcasecmp(tmp->name, "format")) { 01391 mohclass->format = ast_getformatbyname(tmp->value); 01392 if (!mohclass->format) { 01393 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value); 01394 mohclass->format = AST_FORMAT_SLINEAR; 01395 } 01396 } 01397 } 01398 ast_variables_destroy(var); 01399 if (ast_strlen_zero(mohclass->dir)) { 01400 if (!strcasecmp(mohclass->mode, "custom")) { 01401 strcpy(mohclass->dir, "nodir"); 01402 } else { 01403 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name); 01404 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)"); 01405 return -1; 01406 } 01407 } 01408 if (ast_strlen_zero(mohclass->mode)) { 01409 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name); 01410 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)"); 01411 return -1; 01412 } 01413 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) { 01414 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name); 01415 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode"); 01416 return -1; 01417 } 01418 01419 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) { 01420 /* CACHERTCLASSES enabled, let's add this class to default tree */ 01421 if (state && state->class) { 01422 /* Class already exist for this channel */ 01423 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01424 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01425 /* we found RT class with the same name, seems like we should continue playing existing one */ 01426 /* XXX This code is impossible to reach */ 01427 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)"); 01428 mohclass = state->class; 01429 } 01430 } 01431 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well. 01432 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would 01433 * be that the destructor would be called when the generator on the channel is deactivated. The container then 01434 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading 01435 * invalid memory. 01436 */ 01437 if (moh_register(mohclass, 0, DONT_UNREF) == -1) { 01438 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register"); 01439 return -1; 01440 } 01441 } else { 01442 /* We don't register RT moh class, so let's init it manualy */ 01443 01444 time(&mohclass->start); 01445 mohclass->start -= respawn_time; 01446 01447 if (!strcasecmp(mohclass->mode, "files")) { 01448 if (!moh_scan_files(mohclass)) { 01449 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01450 return -1; 01451 } 01452 if (strchr(mohclass->args, 'r')) 01453 ast_set_flag(mohclass, MOH_RANDOMIZE); 01454 } 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")) { 01455 01456 if (!strcasecmp(mohclass->mode, "custom")) 01457 ast_set_flag(mohclass, MOH_CUSTOM); 01458 else if (!strcasecmp(mohclass->mode, "mp3nb")) 01459 ast_set_flag(mohclass, MOH_SINGLE); 01460 else if (!strcasecmp(mohclass->mode, "quietmp3nb")) 01461 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET); 01462 else if (!strcasecmp(mohclass->mode, "quietmp3")) 01463 ast_set_flag(mohclass, MOH_QUIET); 01464 01465 mohclass->srcfd = -1; 01466 if (!(mohclass->timer = ast_timer_open())) { 01467 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); 01468 } 01469 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) { 01470 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); 01471 ast_timer_close(mohclass->timer); 01472 mohclass->timer = NULL; 01473 } 01474 01475 /* Let's check if this channel already had a moh class before */ 01476 if (state && state->class) { 01477 /* Class already exist for this channel */ 01478 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); 01479 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { 01480 /* we found RT class with the same name, seems like we should continue playing existing one */ 01481 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)"); 01482 mohclass = state->class; 01483 } 01484 } else { 01485 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) { 01486 ast_log(LOG_WARNING, "Unable to create moh...\n"); 01487 if (mohclass->timer) { 01488 ast_timer_close(mohclass->timer); 01489 mohclass->timer = NULL; 01490 } 01491 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)"); 01492 return -1; 01493 } 01494 } 01495 } else { 01496 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode); 01497 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)"); 01498 return -1; 01499 } 01500 } 01501 } else { 01502 ast_variables_destroy(var); 01503 var = NULL; 01504 } 01505 } 01506 01507 if (!mohclass) { 01508 return -1; 01509 } 01510 01511 /* If we are using a cached realtime class with files, re-scan the files */ 01512 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) { 01513 if (!moh_scan_files(mohclass)) { 01514 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); 01515 return -1; 01516 } 01517 } 01518 01519 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01520 "State: Start\r\n" 01521 "Channel: %s\r\n" 01522 "UniqueID: %s\r\n" 01523 "Class: %s\r\n", 01524 chan->name, chan->uniqueid, 01525 mohclass->name); 01526 01527 ast_set_flag(chan, AST_FLAG_MOH); 01528 01529 if (mohclass->total_files) { 01530 res = ast_activate_generator(chan, &moh_file_stream, mohclass); 01531 } else { 01532 res = ast_activate_generator(chan, &mohgen, mohclass); 01533 } 01534 01535 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start"); 01536 01537 return res; 01538 }
static void local_ast_moh_stop | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 1540 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().
01541 { 01542 ast_clear_flag(chan, AST_FLAG_MOH); 01543 ast_deactivate_generator(chan); 01544 01545 ast_channel_lock(chan); 01546 if (chan->music_state) { 01547 if (chan->stream) { 01548 ast_closestream(chan->stream); 01549 chan->stream = NULL; 01550 } 01551 } 01552 01553 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold", 01554 "State: Stop\r\n" 01555 "Channel: %s\r\n" 01556 "UniqueID: %s\r\n", 01557 chan->name, chan->uniqueid); 01558 ast_channel_unlock(chan); 01559 }
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 1891 of file res_musiconhold.c.
References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.
Referenced by load_module().
01892 { 01893 struct mohclass *class = obj, *class2 = arg; 01894 01895 return strcasecmp(class->name, class2->name) ? 0 : 01896 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 : 01897 CMP_MATCH | CMP_STOP; 01898 }
static void moh_class_destructor | ( | void * | obj | ) | [static] |
Definition at line 1561 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().
01562 { 01563 struct mohclass *class = obj; 01564 struct mohdata *member; 01565 pthread_t tid = 0; 01566 01567 ast_debug(1, "Destroying MOH class '%s'\n", class->name); 01568 01569 ao2_lock(class); 01570 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) { 01571 free(member); 01572 } 01573 ao2_unlock(class); 01574 01575 /* Kill the thread first, so it cannot restart the child process while the 01576 * class is being destroyed */ 01577 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) { 01578 tid = class->thread; 01579 class->thread = AST_PTHREADT_NULL; 01580 pthread_cancel(tid); 01581 /* We'll collect the exit status later, after we ensure all the readers 01582 * are dead. */ 01583 } 01584 01585 if (class->pid > 1) { 01586 char buff[8192]; 01587 int bytes, tbytes = 0, stime = 0, pid = 0; 01588 01589 ast_log(LOG_DEBUG, "killing %d!\n", class->pid); 01590 01591 stime = time(NULL) + 2; 01592 pid = class->pid; 01593 class->pid = 0; 01594 01595 /* Back when this was just mpg123, SIGKILL was fine. Now we need 01596 * to give the process a reason and time enough to kill off its 01597 * children. */ 01598 do { 01599 if (killpg(pid, SIGHUP) < 0) { 01600 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno)); 01601 } 01602 usleep(100000); 01603 if (killpg(pid, SIGTERM) < 0) { 01604 if (errno == ESRCH) { 01605 break; 01606 } 01607 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno)); 01608 } 01609 usleep(100000); 01610 if (killpg(pid, SIGKILL) < 0) { 01611 if (errno == ESRCH) { 01612 break; 01613 } 01614 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno)); 01615 } 01616 } while (0); 01617 01618 while ((ast_wait_for_input(class->srcfd, 100) > 0) && 01619 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) { 01620 tbytes = tbytes + bytes; 01621 } 01622 01623 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); 01624 01625 close(class->srcfd); 01626 class->srcfd = -1; 01627 } 01628 01629 if (class->filearray) { 01630 int i; 01631 for (i = 0; i < class->total_files; i++) { 01632 free(class->filearray[i]); 01633 } 01634 free(class->filearray); 01635 class->filearray = NULL; 01636 } 01637 01638 if (class->timer) { 01639 ast_timer_close(class->timer); 01640 class->timer = NULL; 01641 } 01642 01643 /* Finally, collect the exit status of the monitor thread */ 01644 if (tid > 0) { 01645 pthread_join(tid, NULL); 01646 } 01647 01648 }
static int moh_class_hash | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1884 of file res_musiconhold.c.
References ast_str_case_hash().
Referenced by load_module().
01885 { 01886 const struct mohclass *class = obj; 01887 01888 return ast_str_case_hash(class->name); 01889 }
static int moh_class_inuse | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1941 of file res_musiconhold.c.
References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.
Referenced by unload_module().
01942 { 01943 struct mohclass *class = obj; 01944 01945 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP; 01946 }
static int moh_class_mark | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1650 of file res_musiconhold.c.
Referenced by load_moh_classes().
01651 { 01652 struct mohclass *class = obj; 01653 01654 class->delete = 1; 01655 01656 return 0; 01657 }
static int moh_classes_delete_marked | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1659 of file res_musiconhold.c.
References CMP_MATCH.
Referenced by load_moh_classes().
01660 { 01661 struct mohclass *class = obj; 01662 01663 return class->delete ? CMP_MATCH : 0; 01664 }
Definition at line 1165 of file res_musiconhold.c.
References mohclass::args, mohclass::dir, mohclass::flags, and mohclass::mode.
Referenced by _moh_register().
01166 { 01167 if (!old || !new) { 01168 return -1; 01169 } 01170 01171 if (strcmp(old->dir, new->dir)) { 01172 return -1; 01173 } else if (strcmp(old->mode, new->mode)) { 01174 return -1; 01175 } else if (strcmp(old->args, new->args)) { 01176 return -1; 01177 } else if (old->flags != new->flags) { 01178 return -1; 01179 } 01180 01181 return 0; 01182 }
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 1149 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().
01149 { 01150 struct ao2_iterator i; 01151 struct mohclass *c; 01152 01153 i = ao2_iterator_init(mohclasses, 0); 01154 01155 while ((c = ao2_iterator_next(&i))) { 01156 if (!strcasecmp(c->mode, "files")) { 01157 moh_scan_files(c); 01158 } 01159 ao2_ref(c, -1); 01160 } 01161 01162 ao2_iterator_destroy(&i); 01163 }
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 closedir(files_DIR); 01069 return -1; 01070 } 01071 if (chdir(dir_path) < 0) { 01072 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01073 closedir(files_DIR); 01074 return -1; 01075 } 01076 while ((files_dirent = readdir(files_DIR))) { 01077 /* The file name must be at least long enough to have the file type extension */ 01078 if ((strlen(files_dirent->d_name) < 4)) 01079 continue; 01080 01081 /* Skip files that starts with a dot */ 01082 if (files_dirent->d_name[0] == '.') 01083 continue; 01084 01085 /* Skip files without extensions... they are not audio */ 01086 if (!strchr(files_dirent->d_name, '.')) 01087 continue; 01088 01089 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name); 01090 01091 if (stat(filepath, &statbuf)) 01092 continue; 01093 01094 if (!S_ISREG(statbuf.st_mode)) 01095 continue; 01096 01097 if ((ext = strrchr(filepath, '.'))) 01098 *ext = '\0'; 01099 01100 /* if the file is present in multiple formats, ensure we only put it into the list once */ 01101 for (i = 0; i < class->total_files; i++) 01102 if (!strcmp(filepath, class->filearray[i])) 01103 break; 01104 01105 if (i == class->total_files) { 01106 if (moh_add_file(class, filepath)) 01107 break; 01108 } 01109 } 01110 01111 closedir(files_DIR); 01112 if (chdir(path) < 0) { 01113 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno)); 01114 return -1; 01115 } 01116 if (ast_test_flag(class, MOH_SORTALPHA)) 01117 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare); 01118 return class->total_files; 01119 }
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 continue; 00646 } 00647 } 00648 if (class->timer) { 00649 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, }; 00650 00651 #ifdef SOLARIS 00652 thr_yield(); 00653 #endif 00654 /* Pause some amount of time */ 00655 if (ast_poll(&pfd, 1, -1) > 0) { 00656 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 1948 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.
01949 { 01950 int res = 0; 01951 struct mohclass *class = NULL; 01952 01953 /* XXX This check shouldn't be required if module ref counting was being used 01954 * properly ... */ 01955 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) { 01956 class = mohclass_unref(class, "unref of class from module unload callback"); 01957 res = -1; 01958 } 01959 01960 if (res < 0) { 01961 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n"); 01962 return res; 01963 } 01964 01965 ast_uninstall_music_functions(); 01966 01967 ast_moh_destroy(); 01968 res = ast_unregister_application(play_moh); 01969 res |= ast_unregister_application(wait_moh); 01970 res |= ast_unregister_application(set_moh); 01971 res |= ast_unregister_application(start_moh); 01972 res |= ast_unregister_application(stop_moh); 01973 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh)); 01974 ast_unregister_atexit(ast_moh_destroy); 01975 01976 return res; 01977 }
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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static] |
Definition at line 1984 of file res_musiconhold.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1984 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 1878 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.