Asterisk - The Open Source Telephony Project GIT-master-27fb039
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | Functions | Variables
res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.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/cel.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/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.h"
Include dependency graph for res_musiconhold.c:

Go to the source code of this file.

Data Structures

struct  moh_files_state
 
struct  mohclass
 
struct  mohdata
 

Macros

#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_ANNOUNCEMENT   (1 << 6)
 
#define MOH_CACHERTCLASSES   (1 << 5)
 
#define moh_class_malloc()   _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
 
#define MOH_CUSTOM   (1 << 2)
 
#define MOH_LOOPLAST   (1 << 8)
 
#define MOH_MS_INTERVAL   100
 
#define MOH_NOTDELETED   (1 << 30)
 
#define MOH_PREFERCHANNELCLASS   (1 << 7)
 
#define MOH_QUIET   (1 << 0)
 
#define MOH_RANDOMIZE   (1 << 3)
 
#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)
 
#define MOH_REALTIME   (1 << 31)
 
#define moh_register(moh, reload, unref)   _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
#define MOH_SINGLE   (1 << 1)
 
#define MOH_SORTALPHA   (1 << 4)
 
#define MOH_SORTMODE   (3 << 3)
 
#define moh_unregister(a)   _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
 
#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"
 

Enumerations

enum  kill_methods { KILL_METHOD_PROCESS_GROUP = 0 , KILL_METHOD_PROCESS }
 

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 int _moh_unregister (struct mohclass *moh, const char *file, int line, const char *funcname)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static void ast_moh_destroy (void)
 
static char * complete_mohclass_realtime (const char *line, const char *word, int pos, int state)
 Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match a prefix in the given position. As many functions of this kind, each invokation has O(state) time complexity so be careful in using it.
 
static struct mohclassget_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 char * handle_cli_moh_unregister_class (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 killer (pid_t pid, int signum, enum kill_methods kill_method)
 
static void killpid (int pid, size_t delay, enum kill_methods kill_method)
 
static int load_module (void)
 Load the module.
 
static int load_moh_classes (int reload)
 
static struct ast_variableload_realtime_musiconhold (const char *name)
 
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 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 struct ast_vector_stringmoh_file_vector_alloc (int initial_capacity)
 
static void moh_file_vector_destructor (void *obj)
 
static int moh_filename_strcasecmp (const void *a, const void *b)
 
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 int moh_files_next (struct ast_channel *chan)
 
static struct ast_framemoh_files_readframe (struct ast_channel *chan)
 
static void moh_files_release (struct ast_channel *chan, void *data)
 
static void moh_files_write_format_change (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_parse_options (struct ast_variable *var, struct mohclass *mohclass)
 
static void moh_post_start (struct ast_channel *chan, const char *moh_class_name)
 
static void moh_post_stop (struct ast_channel *chan)
 
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 struct mohdatamohalloc (struct mohclass *cl)
 
static void * monmp3thread (void *data)
 
static int on_moh_file (const char *directory, const char *filename, void *obj)
 
static int play_moh_exec (struct ast_channel *chan, const char *data)
 
static int reload (void)
 
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)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
 
static const struct ast_module_infoast_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_containermohclasses
 
static struct ast_generator mohgen
 
static const char play_moh [] = "MusicOnHold"
 
static int respawn_time = 20
 
static const char start_moh [] = "StartMusicOnHold"
 
static const char stop_moh [] = "StopMusicOnHold"
 

Detailed Description

Routines implementing music on hold.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m

Definition in file res_musiconhold.c.

Macro Definition Documentation

◆ DONT_UNREF

#define DONT_UNREF   0

Definition at line 79 of file res_musiconhold.c.

◆ get_mohbyname

#define get_mohbyname (   a,
  b,
  c 
)    _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)

Definition at line 998 of file res_musiconhold.c.

◆ HANDLE_REF

#define HANDLE_REF   1

Definition at line 78 of file res_musiconhold.c.

◆ INITIAL_NUM_FILES

#define INITIAL_NUM_FILES   8

Definition at line 77 of file res_musiconhold.c.

◆ LOCAL_MPG_123

#define LOCAL_MPG_123   "/usr/local/bin/mpg123"

Definition at line 222 of file res_musiconhold.c.

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 224 of file res_musiconhold.c.

◆ MOH_ANNOUNCEMENT

#define MOH_ANNOUNCEMENT   (1 << 6)

Do we play announcement files between songs on this channel?

Definition at line 163 of file res_musiconhold.c.

◆ MOH_CACHERTCLASSES

#define MOH_CACHERTCLASSES   (1 << 5)

Should we use a separate instance of MOH for each user or not

Definition at line 162 of file res_musiconhold.c.

◆ moh_class_malloc

#define moh_class_malloc ( )    _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)

Definition at line 1660 of file res_musiconhold.c.

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

Definition at line 156 of file res_musiconhold.c.

◆ MOH_LOOPLAST

#define MOH_LOOPLAST   (1 << 8)

Whether to loop the last file in the music class when we reach the end, rather than starting over

Definition at line 166 of file res_musiconhold.c.

◆ MOH_MS_INTERVAL

#define MOH_MS_INTERVAL   100

◆ MOH_NOTDELETED

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 169 of file res_musiconhold.c.

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 164 of file res_musiconhold.c.

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 154 of file res_musiconhold.c.

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 157 of file res_musiconhold.c.

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 159 of file res_musiconhold.c.

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

Definition at line 170 of file res_musiconhold.c.

◆ moh_register

#define moh_register (   moh,
  reload,
  unref 
)    _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
Note
This function owns the reference it gets to moh if unref is true

Definition at line 1481 of file res_musiconhold.c.

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 155 of file res_musiconhold.c.

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 158 of file res_musiconhold.c.

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 160 of file res_musiconhold.c.

◆ moh_unregister

#define moh_unregister (   a)    _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)

Definition at line 1549 of file res_musiconhold.c.

◆ mohclass_ref

#define mohclass_ref (   class,
  string 
)    (ao2_t_ref((class), +1, (string)), class)

Definition at line 229 of file res_musiconhold.c.

◆ mohclass_unref

#define mohclass_unref (   class,
  string 
)    ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })

Definition at line 232 of file res_musiconhold.c.

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 223 of file res_musiconhold.c.

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 174 of file res_musiconhold.c.

174 {
177};
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2388 of file res_musiconhold.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2388 of file res_musiconhold.c.

◆ _get_mohbyname()

static struct mohclass * _get_mohbyname ( const char *  name,
int  warn,
int  flags,
const char *  file,
int  lineno,
const char *  funcname 
)
static

Definition at line 1000 of file res_musiconhold.c.

1001{
1002 struct mohclass *moh = NULL;
1003 struct mohclass tmp_class = {
1004 .flags = 0,
1005 };
1006
1007 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
1008
1010 "get_mohbyname", file, lineno, funcname);
1011
1012 if (!moh && warn) {
1013 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
1014 }
1015
1016 return moh;
1017}
#define ast_log
Definition astobj2.c:42
void * __ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
static const char name[]
Definition format_mp3.c:68
#define LOG_WARNING
static struct ao2_container * mohclasses
#define NULL
Definition resample.c:96
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
unsigned int flags

References __ao2_find(), ast_copy_string(), ast_log, mohclass::flags, LOG_WARNING, mohclasses, name, NULL, and mohclass::start.

Referenced by _moh_register().

◆ _moh_class_malloc()

static struct mohclass * _moh_class_malloc ( const char *  file,
int  line,
const char *  funcname 
)
static

Definition at line 1662 of file res_musiconhold.c.

1663{
1664 struct mohclass *class;
1665
1667 "Allocating new moh class", file, line, funcname);
1668 if (class) {
1670 class->srcfd = -1;
1671 class->kill_delay = 100000;
1672
1673 /* We create an empty one by default */
1674 class->files = moh_file_vector_alloc(0);
1675 if (!class->files) {
1676 ao2_ref(class, -1);
1677 return NULL;
1678 }
1679 }
1680
1681 return class;
1682}
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition astobj2.h:363
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
void * __ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result
Definition astobj2.c:768
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
static void moh_class_destructor(void *obj)
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)
struct ast_format * format

References __ao2_alloc(), AO2_ALLOC_OPT_LOCK_MUTEX, ao2_bump, ao2_ref, ast_format_slin, mohclass::format, moh_class_destructor(), moh_file_vector_alloc(), and NULL.

◆ _moh_register()

static int _moh_register ( struct mohclass moh,
int  reload,
int  unref,
const char *  file,
int  line,
const char *  funcname 
)
static

Definition at line 1482 of file res_musiconhold.c.

1483{
1484 struct mohclass *mohclass = NULL;
1485
1486 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1487
1488 if (mohclass && !moh_diff(mohclass, moh)) {
1489 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1490 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1491 if (unref) {
1492 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1493 }
1494 return -1;
1495 } else if (mohclass) {
1496 /* Found a class, but it's different from the one being registered */
1497 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1498 }
1499
1500 time(&moh->start);
1501 moh->start -= respawn_time;
1502
1503 if (!strcasecmp(moh->mode, "files")) {
1504 if (init_files_class(moh)) {
1505 if (unref) {
1506 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1507 }
1508 return -1;
1509 }
1510 } else if (!strcasecmp(moh->mode, "playlist")) {
1511 size_t file_count;
1512
1513 ao2_lock(moh);
1515 ao2_unlock(moh);
1516
1517 if (!file_count) {
1518 if (unref) {
1519 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1520 }
1521 return -1;
1522 }
1523 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1524 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1525 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1526 if (init_app_class(moh)) {
1527 if (unref) {
1528 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1529 }
1530 return -1;
1531 }
1532 } else {
1533 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1534 if (unref) {
1535 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1536 }
1537 return -1;
1538 }
1539
1540 ao2_t_link(mohclasses, moh, "Adding class to container");
1541
1542 if (unref) {
1543 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1544 }
1545
1546 return 0;
1547}
#define ao2_t_link(container, obj, tag)
Definition astobj2.h:1534
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
static int init_app_class(struct mohclass *class)
static int init_files_class(struct mohclass *class)
static int moh_diff(struct mohclass *old, struct mohclass *new)
#define MOH_NOTDELETED
static struct mohclass * _get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
static int respawn_time
#define mohclass_unref(class, string)
char name[MAX_MUSICCLASS]
struct ast_vector_string * files
char mode[80]
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620

References _get_mohbyname(), ao2_lock, ao2_t_link, ao2_unlock, ast_log, AST_VECTOR_SIZE, mohclass::files, init_app_class(), init_files_class(), LOG_WARNING, mohclass::mode, moh_diff(), MOH_NOTDELETED, mohclass_unref, mohclasses, mohclass::name, NULL, respawn_time, and mohclass::start.

◆ _moh_unregister()

static int _moh_unregister ( struct mohclass moh,
const char *  file,
int  line,
const char *  funcname 
)
static

Definition at line 1550 of file res_musiconhold.c.

1551{
1552 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1553 return 0;
1554}
#define ao2_t_unlink(container, obj, tag)
Definition astobj2.h:1580

References ao2_t_unlink, and mohclasses.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 2388 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2156 of file res_musiconhold.c.

2157{
2158 ast_verb(2, "Destroying musiconhold processes\n");
2159 if (mohclasses) {
2161 ao2_ref(mohclasses, -1);
2162 mohclasses = NULL;
2163 }
2164}
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
Definition astobj2.h:1696
#define ast_verb(level,...)

References ao2_ref, ao2_t_callback, ast_verb, mohclasses, NULL, OBJ_MULTIPLE, OBJ_NODATA, and OBJ_UNLINK.

Referenced by load_module(), and unload_module().

◆ complete_mohclass_realtime()

static char * complete_mohclass_realtime ( const char *  line,
const char *  word,
int  pos,
int  state 
)
static

Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match a prefix in the given position. As many functions of this kind, each invokation has O(state) time complexity so be careful in using it.

Definition at line 1587 of file res_musiconhold.c.

1588{
1589 int which=0;
1590 struct mohclass *cur;
1591 char *c = NULL;
1592 int wordlen = strlen(word);
1593 struct ao2_iterator i;
1594
1595 if (pos != 3) {
1596 return NULL;
1597 }
1598
1600 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1601 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1602 c = ast_strdup(cur->name);
1603 mohclass_unref(cur, "drop ref in iterator loop break");
1604 break;
1605 }
1606 mohclass_unref(cur, "drop ref in iterator loop");
1607 }
1609
1610 return c;
1611}
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ao2_t_iterator_next(iter, tag)
Definition astobj2.h:1909
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
short word
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
unsigned int realtime
static struct test_val c

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_strdup, c, mohclass_unref, mohclasses, mohclass::name, NULL, mohclass::realtime, and mohclass::start.

Referenced by handle_cli_moh_unregister_class().

◆ get_mohbydigit()

static struct mohclass * get_mohbydigit ( char  digit)
static
Note
This function should be called with the mohclasses list locked

Definition at line 621 of file res_musiconhold.c.

622{
623 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
624}
char digit
static int moh_digit_match(void *obj, void *arg, int flags)

References ao2_t_callback, digit, moh_digit_match(), and mohclasses.

Referenced by moh_handle_digit().

◆ handle_cli_moh_reload()

static char * handle_cli_moh_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 2166 of file res_musiconhold.c.

2167{
2168 switch (cmd) {
2169 case CLI_INIT:
2170 e->command = "moh reload";
2171 e->usage =
2172 "Usage: moh reload\n"
2173 " Reloads the MusicOnHold module.\n"
2174 " Alias for 'module reload res_musiconhold.so'\n";
2175 return NULL;
2176 case CLI_GENERATE:
2177 return NULL;
2178 }
2179
2180 if (a->argc != e->args)
2181 return CLI_SHOWUSAGE;
2182
2183 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2184 ast_module_reload("res_musiconhold");
2185
2186 return CLI_SUCCESS;
2187}
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition loader.c:1730
int args
This gets set in ast_cli_register()
Definition cli.h:185
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
static struct test_val a

References a, ast_cli_entry::args, ast_module_reload(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, NULL, and ast_cli_entry::usage.

◆ handle_cli_moh_show_classes()

static char * handle_cli_moh_show_classes ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 2232 of file res_musiconhold.c.

2233{
2234 struct mohclass *class;
2235 struct ao2_iterator i;
2236
2237 switch (cmd) {
2238 case CLI_INIT:
2239 e->command = "moh show classes";
2240 e->usage =
2241 "Usage: moh show classes\n"
2242 " Lists all MusicOnHold classes.\n";
2243 return NULL;
2244 case CLI_GENERATE:
2245 return NULL;
2246 }
2247
2248 if (a->argc != e->args)
2249 return CLI_SHOWUSAGE;
2250
2252 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2253 ast_cli(a->fd, "Class: %s\n", class->name);
2254 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2255 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2256 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2257 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2258 }
2259 if (ast_test_flag(class, MOH_CUSTOM)) {
2260 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2261 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2262 ast_cli(a->fd, "\tKill Method: %s\n",
2263 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2264 }
2265 if (strcasecmp(class->mode, "files")) {
2266 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2267 }
2268 }
2270
2271 return CLI_SUCCESS;
2272}
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition format.c:334
#define MOH_CUSTOM
#define MOH_ANNOUNCEMENT
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
const char * name
#define ast_test_flag(p, flag)
Definition utils.h:64

References a, ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_entry::args, ast_cli(), ast_format_get_name(), ast_test_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, KILL_METHOD_PROCESS, MOH_ANNOUNCEMENT, MOH_CUSTOM, mohclass_unref, mohclasses, test_val::name, NULL, S_OR, and ast_cli_entry::usage.

◆ handle_cli_moh_show_files()

static char * handle_cli_moh_show_files ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 2189 of file res_musiconhold.c.

2190{
2191 struct mohclass *class;
2192 struct ao2_iterator i;
2193
2194 switch (cmd) {
2195 case CLI_INIT:
2196 e->command = "moh show files";
2197 e->usage =
2198 "Usage: moh show files\n"
2199 " Lists all loaded file-based MusicOnHold classes and their\n"
2200 " files.\n";
2201 return NULL;
2202 case CLI_GENERATE:
2203 return NULL;
2204 }
2205
2206 if (a->argc != e->args)
2207 return CLI_SHOWUSAGE;
2208
2210 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2211 struct ast_vector_string *files;
2212
2213 ao2_lock(class);
2214 files = ao2_bump(class->files);
2215 ao2_unlock(class);
2216
2217 if (AST_VECTOR_SIZE(files)) {
2218 int x;
2219 ast_cli(a->fd, "Class: %s\n", class->name);
2220 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2221 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2222 }
2223 }
2224
2225 ao2_ref(files, -1);
2226 }
2228
2229 return CLI_SUCCESS;
2230}
String vector definitions.
Definition vector.h:55
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691

References a, ao2_bump, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_cli_entry::args, ast_cli(), AST_VECTOR_GET, AST_VECTOR_SIZE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, mohclass_unref, mohclasses, test_val::name, NULL, and ast_cli_entry::usage.

◆ handle_cli_moh_unregister_class()

static char * handle_cli_moh_unregister_class ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 1613 of file res_musiconhold.c.

1614{
1615 struct mohclass *cur;
1616 int len;
1617 int found = 0;
1618 struct ao2_iterator i;
1619
1620 switch (cmd) {
1621 case CLI_INIT:
1622 e->command = "moh unregister class";
1623 e->usage =
1624 "Usage: moh unregister class <class>\n"
1625 " Unregisters a realtime moh class.\n";
1626 return NULL;
1627 case CLI_GENERATE:
1628 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1629 }
1630
1631 if (a->argc != 4)
1632 return CLI_SHOWUSAGE;
1633
1634 len = strlen(a->argv[3]);
1635
1637 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1638 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1639 found = 1;
1640 break;
1641 }
1642 mohclass_unref(cur, "drop ref in iterator loop");
1643 }
1645
1646 if (found) {
1647 moh_unregister(cur);
1648 mohclass_unref(cur, "drop ref after unregister");
1649 } else {
1650 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1651 }
1652
1653 return CLI_SUCCESS;
1654}
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static char * complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match...
#define moh_unregister(a)

References a, ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_mohclass_realtime(), len(), moh_unregister, mohclass_unref, mohclasses, mohclass::name, NULL, mohclass::realtime, and ast_cli_entry::usage.

◆ init_app_class()

static int init_app_class ( struct mohclass class)
static

Definition at line 1442 of file res_musiconhold.c.

1443{
1444 if (!strcasecmp(class->mode, "custom")) {
1445 ast_set_flag(class, MOH_CUSTOM);
1446 } else if (!strcasecmp(class->mode, "mp3nb")) {
1447 ast_set_flag(class, MOH_SINGLE);
1448 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1450 } else if (!strcasecmp(class->mode, "quietmp3")) {
1451 ast_set_flag(class, MOH_QUIET);
1452 }
1453
1454 class->srcfd = -1;
1455
1456 if (!(class->timer = ast_timer_open())) {
1457 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1458 return -1;
1459 }
1460 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1461 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1462 ast_timer_close(class->timer);
1463 class->timer = NULL;
1464 }
1465
1466 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1467 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1468 if (class->timer) {
1469 ast_timer_close(class->timer);
1470 class->timer = NULL;
1471 }
1472 return -1;
1473 }
1474
1475 return 0;
1476}
int errno
static void * monmp3thread(void *data)
#define MOH_QUIET
#define MOH_SINGLE
pthread_t thread
struct ast_timer * timer
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition timing.c:154
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition timing.c:166
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition timing.c:122
#define ast_pthread_create_background(a, b, c, d)
Definition utils.h:632
#define ast_set_flag(p, flag)
Definition utils.h:71

References ast_log, ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), errno, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), NULL, and mohclass::start.

Referenced by _moh_register().

◆ init_files_class()

static int init_files_class ( struct mohclass class)
static

Definition at line 1388 of file res_musiconhold.c.

1389{
1390 int res;
1391
1392 res = moh_scan_files(class);
1393
1394 if (res < 0) {
1395 return -1;
1396 }
1397
1398 if (!res) {
1399 ast_verb(3, "Files not found in %s for moh class:%s\n",
1400 class->dir, class->name);
1401 return -1;
1402 }
1403
1404 return 0;
1405}
static int moh_scan_files(struct mohclass *class)
char dir[256]

References ast_verb, and moh_scan_files().

Referenced by _moh_register().

◆ killer()

static int killer ( pid_t  pid,
int  signum,
enum kill_methods  kill_method 
)
static

Definition at line 789 of file res_musiconhold.c.

790{
791 switch (kill_method) {
793 return killpg(pid, signum);
795 return kill(pid, signum);
796 }
797
798 return -1;
799}

References KILL_METHOD_PROCESS, and KILL_METHOD_PROCESS_GROUP.

Referenced by killpid().

◆ killpid()

static void killpid ( int  pid,
size_t  delay,
enum kill_methods  kill_method 
)
static

Definition at line 801 of file res_musiconhold.c.

802{
803 if (killer(pid, SIGHUP, kill_method) < 0) {
804 if (errno == ESRCH) {
805 return;
806 }
807 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
808 } else {
809 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
810 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
811 }
812 usleep(delay);
813 if (killer(pid, SIGTERM, kill_method) < 0) {
814 if (errno == ESRCH) {
815 return;
816 }
817 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
818 } else {
819 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
820 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
821 }
822 usleep(delay);
823 if (killer(pid, SIGKILL, kill_method) < 0) {
824 if (errno == ESRCH) {
825 return;
826 }
827 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
828 } else {
829 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
830 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
831 }
832}
#define ast_debug(level,...)
Log a DEBUG message.
static int killer(pid_t pid, int signum, enum kill_methods kill_method)

References ast_debug, ast_log, errno, KILL_METHOD_PROCESS_GROUP, killer(), and LOG_WARNING.

Referenced by moh_class_destructor(), and monmp3thread().

◆ load_module()

static int load_module ( void  )
static

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 2307 of file res_musiconhold.c.

2308{
2309 int res;
2310
2312 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2313 if (!mohclasses) {
2315 }
2316
2317 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2318 ast_log(LOG_WARNING, "No music on hold classes configured, "
2319 "disabling music on hold.\n");
2320 } else {
2323 }
2324
2328 if (!res)
2330 if (!res)
2332
2334}
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition clicompat.c:13
#define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag)
Definition astobj2.h:1306
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition module.h:640
void ast_install_music_functions(int(*start_ptr)(struct ast_channel *, const char *, const char *), void(*stop_ptr)(struct ast_channel *), void(*cleanup_ptr)(struct ast_channel *))
Definition channel.c:7762
static struct ast_cli_entry cli_moh[]
static void ast_moh_destroy(void)
static int play_moh_exec(struct ast_channel *chan, const char *data)
static void local_ast_moh_stop(struct ast_channel *chan)
static int load_moh_classes(int reload)
static int stop_moh_exec(struct ast_channel *chan, const char *data)
static int start_moh_exec(struct ast_channel *chan, const char *data)
static int moh_class_cmp(void *obj, void *arg, int flags)
static const char play_moh[]
static int moh_class_hash(const void *obj, const int flags)
static const char start_moh[]
static const char stop_moh[]
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)
#define ARRAY_LEN(a)
Definition utils.h:706

References AO2_ALLOC_OPT_LOCK_MUTEX, ao2_t_container_alloc_hash, 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(), mohclasses, NULL, play_moh, play_moh_exec(), start_moh, start_moh_exec(), stop_moh, and stop_moh_exec().

◆ load_moh_classes()

static int load_moh_classes ( int  reload)
static

Definition at line 2062 of file res_musiconhold.c.

2063{
2064 struct ast_config *cfg;
2065 struct ast_variable *var;
2066 struct mohclass *class;
2067 char *cat;
2068 int numclasses = 0;
2069 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2070
2071 cfg = ast_config_load("musiconhold.conf", config_flags);
2072
2073 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2074 if (ast_check_realtime("musiconhold") && reload) {
2075 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2077 }
2079 return 0;
2080 }
2081
2083 if (ast_check_realtime("musiconhold") && reload) {
2084 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2086 }
2087 return 0;
2088 }
2089
2090 if (reload) {
2091 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2092 }
2093
2096
2097 cat = ast_category_browse(cfg, NULL);
2098 for (; cat; cat = ast_category_browse(cfg, cat)) {
2099 /* Setup common options from [general] section */
2100 if (!strcasecmp(cat, "general")) {
2101 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2102 if (!strcasecmp(var->name, "cachertclasses")) {
2104 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2106 } else {
2107 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2108 }
2109 }
2110 continue;
2111 }
2112
2113 if (!(class = moh_class_malloc())) {
2114 break;
2115 }
2116
2117 moh_parse_options(ast_variable_browse(cfg, cat), class);
2118 /* For compatibility with the past, we overwrite any name=name
2119 * with the context [name]. */
2120 ast_copy_string(class->name, cat, sizeof(class->name));
2121
2122 if (ast_strlen_zero(class->dir)) {
2123 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2124 strcpy(class->dir, "nodir");
2125 } else {
2126 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2127 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2128 continue;
2129 }
2130 }
2131 if (ast_strlen_zero(class->mode)) {
2132 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2133 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2134 continue;
2135 }
2136 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2137 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2138 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2139 continue;
2140 }
2141
2142 /* Don't leak a class when it's already registered */
2143 if (!moh_register(class, reload, HANDLE_REF)) {
2144 numclasses++;
2145 }
2146 }
2147
2148 ast_config_destroy(cfg);
2149
2151 moh_classes_delete_marked, NULL, "Purge marked classes");
2152
2153 return numclasses;
2154}
#define var
Definition ast_expr2f.c:605
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition extconf.c:3324
#define CONFIG_STATUS_FILEMISSING
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
@ CONFIG_FLAG_FILEUNCHANGED
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
#define HANDLE_REF
static int moh_class_mark(void *obj, void *arg, int flags)
static void moh_rescan_files(void)
#define moh_register(moh, reload, unref)
#define moh_class_malloc()
#define MOH_PREFERCHANNELCLASS
#define MOH_CACHERTCLASSES
#define MOH_REALTIME
static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
static int reload(void)
static struct ast_flags global_flags[1]
static int moh_classes_delete_marked(void *obj, void *arg, int flags)
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2235
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
Structure used to handle boolean flags.
Definition utils.h:220
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
#define ast_set2_flag(p, value, flag)
Definition utils.h:95
#define ast_clear_flag(p, flag)
Definition utils.h:78
#define AST_FLAGS_ALL
Definition utils.h:217

References ao2_t_callback, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_destroy(), ast_config_load, ast_copy_string(), AST_FLAGS_ALL, ast_log, ast_set2_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, global_flags, HANDLE_REF, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_REALTIME, moh_register, moh_rescan_files(), mohclass_unref, mohclasses, ast_variable::next, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, reload(), mohclass::start, and var.

Referenced by load_module(), and reload().

◆ load_realtime_musiconhold()

static struct ast_variable * load_realtime_musiconhold ( const char *  name)
static

Definition at line 1684 of file res_musiconhold.c.

1685{
1686 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1687
1688 if (var) {
1689 const char *mode = ast_variable_find_in_list(var, "mode");
1690 if (ast_strings_equal(mode, "playlist")) {
1691 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1692 char *category = NULL;
1693 size_t entry_count = 0;
1694
1695 /* entries is NULL if there are no results */
1696 if (entries) {
1697 while ((category = ast_category_browse(entries, category))) {
1698 const char *entry = ast_variable_retrieve(entries, category, "entry");
1699
1700 if (entry) {
1701 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1702 if (dup) {
1703 entry_count++;
1705 }
1706 }
1707 }
1708 ast_config_destroy(entries);
1709 }
1710
1711 if (entry_count == 0) {
1712 /* Behave as though this class doesn't exist */
1714 var = NULL;
1715 }
1716 }
1717 }
1718
1719 if (!var) {
1721 "Music on Hold class '%s' not found in memory/database. "
1722 "Verify your configuration.\n",
1723 name);
1724 }
1725 return var;
1726}
#define SENTINEL
Definition compiler.h:87
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238

References ast_category_browse(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log, ast_strings_equal(), ast_variable_find_in_list(), ast_variable_list_append, ast_variable_new, ast_variable_retrieve(), ast_variables_destroy(), LOG_WARNING, name, NULL, SENTINEL, and var.

Referenced by local_ast_moh_start().

◆ local_ast_moh_cleanup()

static void local_ast_moh_cleanup ( struct ast_channel chan)
static

Definition at line 1559 of file res_musiconhold.c.

1560{
1561 struct moh_files_state *state;
1562
1563 ast_channel_lock(chan);
1565 if (state) {
1567 if (state->class) {
1568 /* This should never happen. We likely just leaked some resource. */
1569 state->class =
1570 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1571 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1572 }
1573 ao2_cleanup(state->origwfmt);
1574 ao2_cleanup(state->mohwfmt);
1575 ast_free(state);
1576 /* Only held a module reference if we had a music state */
1578 }
1579 ast_channel_unlock(chan);
1580}
#define ast_free(a)
Definition astmm.h:180
#define ao2_cleanup(obj)
Definition astobj2.h:1934
void * ast_channel_music_state(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition channel.h:2982
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define ast_channel_unlock(chan)
Definition channel.h:2983
#define ast_module_unref(mod)
Release a reference to the module.
Definition module.h:483
struct ast_module * self
Definition module.h:356

References ao2_cleanup, ast_channel_lock, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_unlock, ast_free, ast_log, ast_module_unref, LOG_WARNING, mohclass_unref, NULL, and ast_module_info::self.

Referenced by load_module(), and reload().

◆ local_ast_moh_start()

static int local_ast_moh_start ( struct ast_channel chan,
const char *  mclass,
const char *  interpclass 
)
static

Definition at line 1731 of file res_musiconhold.c.

1732{
1733 struct mohclass *mohclass = NULL;
1734 struct moh_files_state *state;
1735 struct ast_variable *var = NULL;
1736 int res = 0;
1737 int i;
1738 int realtime_possible = ast_check_realtime("musiconhold");
1739 int warn_if_not_in_memory = !realtime_possible;
1740 const char *classes[] = {NULL, NULL, interpclass, "default"};
1741
1743 classes[0] = ast_channel_musicclass(chan);
1744 classes[1] = mclass;
1745 } else {
1746 classes[0] = mclass;
1747 classes[1] = ast_channel_musicclass(chan);
1748 }
1749
1750 /* The following is the order of preference for which class to use:
1751 * 1) The channels explicitly set musicclass, which should *only* be
1752 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1753 * Unless preferchannelclass in musiconhold.conf is false
1754 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1755 * result of receiving a HOLD control frame, this should be the
1756 * payload that came with the frame.
1757 * 3) The channels explicitly set musicclass, which should *only* be
1758 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1759 * 4) The interpclass argument. This would be from the mohinterpret
1760 * option from channel drivers. This is the same as the old musicclass
1761 * option.
1762 * 5) The default class.
1763 */
1764
1765 for (i = 0; i < ARRAY_LEN(classes); ++i) {
1766 if (!ast_strlen_zero(classes[i])) {
1767 mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1768 if (!mohclass && realtime_possible) {
1769 var = load_realtime_musiconhold(classes[i]);
1770 }
1771 if (mohclass || var) {
1772 break;
1773 }
1774 }
1775 }
1776
1777 /* If no moh class found in memory, then check RT. Note that the logic used
1778 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1779 */
1780 if (var) {
1781 if ((mohclass = moh_class_malloc())) {
1782 mohclass->realtime = 1;
1783
1786
1788 if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1789 strcpy(mohclass->dir, "nodir");
1790 } else {
1791 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1792 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1793 return -1;
1794 }
1795 }
1797 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1798 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1799 return -1;
1800 }
1801 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1802 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1803 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1804 return -1;
1805 }
1806
1808 /* CACHERTCLASSES enabled, let's add this class to default tree */
1809 ast_channel_lock(chan);
1811 if (state && state->class) {
1812 /* Class already exist for this channel */
1813 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1814 }
1815 ast_channel_unlock(chan);
1816 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1817 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1818 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1819 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1820 * invalid memory.
1821 */
1822 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1823 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1824 return -1;
1825 }
1826 } else {
1827 /* We don't register RT moh class, so let's init it manually */
1828
1829 time(&mohclass->start);
1831
1832 if (!strcasecmp(mohclass->mode, "files")) {
1833 /*
1834 * XXX moh_scan_files returns -1 if it is unable to open the
1835 * configured directory or there is a memory allocation
1836 * failure. Otherwise it returns the number of files for this music
1837 * class. This check is only checking if the number of files is zero
1838 * and it ignores the -1 case. To avoid a behavior change we keep this
1839 * as-is, but we should address what the 'correct' behavior should be.
1840 */
1841 if (!moh_scan_files(mohclass)) {
1842 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1843 return -1;
1844 }
1845 } else if (!strcasecmp(mohclass->mode, "playlist")) {
1846 size_t file_count;
1847
1849 file_count = AST_VECTOR_SIZE(mohclass->files);
1851
1852 if (!file_count) {
1853 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1854 return -1;
1855 }
1856 } 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")) {
1857
1858 if (!strcasecmp(mohclass->mode, "custom"))
1860 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1862 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1864 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1866
1867 mohclass->srcfd = -1;
1868 if (!(mohclass->timer = ast_timer_open())) {
1869 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1870 }
1872 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1874 mohclass->timer = NULL;
1875 }
1876
1877 /* Let's check if this channel already had a moh class before */
1878 ast_channel_lock(chan);
1880 if (state && state->class) {
1881 /* Class already exist for this channel */
1882 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1883 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1884 /* we found RT class with the same name, seems like we should continue playing existing one */
1885 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1886 mohclass = mohclass_ref(state->class, "using existing class from state");
1887 }
1888 ast_channel_unlock(chan);
1889 } else {
1890 ast_channel_unlock(chan);
1892 ast_log(LOG_WARNING, "Unable to create moh...\n");
1893 if (mohclass->timer) {
1895 mohclass->timer = NULL;
1896 }
1897 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1898 return -1;
1899 }
1900 }
1901 } else {
1902 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1903 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1904 return -1;
1905 }
1906 }
1907 } else {
1909 var = NULL;
1910 }
1911 }
1912
1913 if (!mohclass) {
1914 return -1;
1915 }
1916
1918 ast_verb(3, "The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1919 return -1;
1920 }
1921
1922 /* If we are using a cached realtime class with files, re-scan the files */
1923 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1924 /*
1925 * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1926 * or there is a memory allocation failure. Otherwise it returns the number of
1927 * files for this music class. This check is only checking if the number of files
1928 * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1929 * as-is, but we should address what the 'correct' behavior should be.
1930 */
1931 if (!moh_scan_files(mohclass)) {
1932 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1933 return -1;
1934 }
1935 }
1936
1937 ast_channel_lock(chan);
1939 if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1940 size_t file_count;
1941
1943 file_count = AST_VECTOR_SIZE(mohclass->files);
1945
1946 if (file_count) {
1948 } else {
1949 res = ast_activate_generator(chan, &mohgen, mohclass);
1950 }
1951 }
1952 ast_channel_unlock(chan);
1953 if (!res) {
1954 ast_channel_lock(chan);
1955 ast_channel_latest_musicclass_set(chan, mohclass->name);
1957 ast_channel_unlock(chan);
1958 }
1959
1960 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1961
1962 return res;
1963}
const char * ast_channel_name(const struct ast_channel *chan)
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition channel.c:2948
const char * ast_channel_musicclass(const struct ast_channel *chan)
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
@ AST_FLAG_MOH
Definition channel.h:1011
ast_channel_state
ast_channel states
@ AST_STATE_UP
#define LOG_NOTICE
static struct ast_generator mohgen
static struct ast_variable * load_realtime_musiconhold(const char *name)
#define get_mohbyname(a, b, c)
#define mohclass_ref(class, string)
static struct ast_generator moh_file_stream
#define DONT_UNREF
char args[256]
char name[TZ_STRLEN_MAX+1]
Definition localtime.c:160

References mohclass::answeredonly, ao2_lock, ao2_unlock, mohclass::args, ARRAY_LEN, ast_activate_generator(), ast_channel_flags(), ast_channel_lock, ast_channel_music_state(), ast_channel_musicclass(), ast_channel_name(), ast_channel_unlock, ast_check_realtime(), AST_FLAG_MOH, ast_log, ast_pthread_create_background, ast_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_variables_destroy(), AST_VECTOR_SIZE, ast_verb, mohclass::dir, DONT_UNREF, errno, mohclass::files, get_mohbyname, global_flags, load_realtime_musiconhold(), LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, moh_file_stream, moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_QUIET, moh_register, moh_scan_files(), MOH_SINGLE, mohclass_ref, mohclass_unref, mohgen, monmp3thread(), mohclass::name, state::name, NULL, mohclass::realtime, respawn_time, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, and var.

Referenced by load_module(), and reload().

◆ local_ast_moh_stop()

static void local_ast_moh_stop ( struct ast_channel chan)
static

Definition at line 1968 of file res_musiconhold.c.

1969{
1971
1972 ast_channel_lock(chan);
1974 if (ast_channel_music_state(chan)) {
1975 if (ast_channel_stream(chan)) {
1978 }
1979 }
1980 ast_channel_unlock(chan);
1981}
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
void ast_deactivate_generator(struct ast_channel *chan)
Definition channel.c:2890
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition file.c:1130

References ast_channel_flags(), ast_channel_lock, ast_channel_music_state(), ast_channel_stream(), ast_channel_stream_set(), ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, and NULL.

Referenced by load_module(), and reload().

◆ moh_alloc()

static void * moh_alloc ( struct ast_channel chan,
void *  params 
)
static

Definition at line 1088 of file res_musiconhold.c.

1089{
1090 struct mohdata *res;
1091 struct mohclass *class = params;
1092 struct moh_files_state *state;
1093
1094 /* Initiating music_state for current channel. Channel should know name of moh class */
1096 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1099 } else {
1100 if (!state) {
1101 return NULL;
1102 }
1103 if (state->class) {
1104 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1105 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1106 }
1107 ao2_cleanup(state->origwfmt);
1108 ao2_cleanup(state->mohwfmt);
1109 memset(state, 0, sizeof(*state));
1110 }
1111
1112 if ((res = mohalloc(class))) {
1114 if (ast_set_write_format(chan, class->format)) {
1115 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1117 moh_release(NULL, res);
1118 res = NULL;
1119 } else {
1120 state->class = mohclass_ref(class, "Placing reference into state container");
1121 moh_post_start(chan, class->name);
1122 }
1123 }
1124 return res;
1125}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition channel.c:5798
#define ast_module_ref(mod)
Hold a reference to the module.
Definition module.h:457
static void moh_release(struct ast_channel *chan, void *data)
static struct mohdata * mohalloc(struct mohclass *cl)
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
struct mohclass * class
struct ast_format * origwfmt

References ao2_bump, ao2_cleanup, ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_name(), ast_channel_writeformat(), ast_format_get_name(), ast_log, ast_module_ref, ast_set_write_format(), moh_files_state::class, mohclass::format, LOG_WARNING, moh_post_start(), moh_release(), mohalloc(), mohclass_ref, mohclass_unref, mohclass::name, NULL, mohdata::origwfmt, and ast_module_info::self.

◆ moh_class_cmp()

static int moh_class_cmp ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 2288 of file res_musiconhold.c.

2289{
2290 struct mohclass *class = obj, *class2 = arg;
2291
2292 return strcasecmp(class->name, class2->name) ? 0 :
2293 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2295}
@ CMP_MATCH
Definition astobj2.h:1027
@ CMP_STOP
Definition astobj2.h:1028

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

◆ moh_class_destructor()

static void moh_class_destructor ( void *  obj)
static

Definition at line 1983 of file res_musiconhold.c.

1984{
1985 struct mohclass *class = obj;
1986 struct mohdata *member;
1987 pthread_t tid = 0;
1988
1989 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1990
1991 ao2_lock(class);
1992 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1994 }
1995 ao2_cleanup(class->files);
1996 ao2_unlock(class);
1997
1998 /* Kill the thread first, so it cannot restart the child process while the
1999 * class is being destroyed */
2000 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
2001 tid = class->thread;
2002 class->thread = AST_PTHREADT_NULL;
2003 pthread_cancel(tid);
2004 /* We'll collect the exit status later, after we ensure all the readers
2005 * are dead. */
2006 }
2007
2008 if (class->pid > 1) {
2009 char buff[8192];
2010 int bytes, tbytes = 0, stime = 0;
2011
2012 ast_debug(1, "killing %d!\n", class->pid);
2013
2014 stime = time(NULL) + 2;
2015 killpid(class->pid, class->kill_delay, class->kill_method);
2016
2017 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
2018 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
2019 tbytes = tbytes + bytes;
2020 }
2021
2022 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
2023 class->pid, tbytes);
2024
2025 class->pid = 0;
2026 close(class->srcfd);
2027 class->srcfd = -1;
2028 }
2029
2030 if (class->timer) {
2031 ast_timer_close(class->timer);
2032 class->timer = NULL;
2033 }
2034
2035 ao2_cleanup(class->format);
2036
2037 /* Finally, collect the exit status of the monitor thread */
2038 if (tid > 0) {
2039 pthread_join(tid, NULL);
2040 }
2041
2042}
static unsigned char * buff
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_PTHREADT_NULL
Definition lock.h:73
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
struct mohdata::@477 list
int ast_wait_for_input(int fd, int ms)
Definition utils.c:1734

References ao2_cleanup, ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, killpid(), mohdata::list, and NULL.

Referenced by _moh_class_malloc().

◆ moh_class_hash()

static int moh_class_hash ( const void *  obj,
const int  flags 
)
static

Definition at line 2281 of file res_musiconhold.c.

2282{
2283 const struct mohclass *class = obj;
2284
2285 return ast_str_case_hash(class->name);
2286}
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition strings.h:1303

References ast_str_case_hash().

Referenced by load_module().

◆ moh_class_inuse()

static int moh_class_inuse ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 2346 of file res_musiconhold.c.

2347{
2348 struct mohclass *class = obj;
2349
2350 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2351}
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

◆ moh_class_mark()

static int moh_class_mark ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 2044 of file res_musiconhold.c.

2045{
2046 struct mohclass *class = obj;
2047
2048 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
2049 class->delete = 1;
2050 }
2051
2052 return 0;
2053}
unsigned int delete

References mohclass::delete, and MOH_REALTIME.

Referenced by load_moh_classes().

◆ moh_classes_delete_marked()

static int moh_classes_delete_marked ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 2055 of file res_musiconhold.c.

2056{
2057 struct mohclass *class = obj;
2058
2059 return class->delete ? CMP_MATCH : 0;
2060}

References CMP_MATCH, and mohclass::delete.

Referenced by load_moh_classes().

◆ moh_diff()

static int moh_diff ( struct mohclass old,
struct mohclass new 
)
static

Definition at line 1423 of file res_musiconhold.c.

1424{
1425 if (!old || !new) {
1426 return -1;
1427 }
1428
1429 if (strcmp(old->dir, new->dir)) {
1430 return -1;
1431 } else if (strcmp(old->mode, new->mode)) {
1432 return -1;
1433 } else if (strcmp(old->args, new->args)) {
1434 return -1;
1435 } else if (old->flags != new->flags) {
1436 return -1;
1437 }
1438
1439 return 0;
1440}

References mohclass::args, mohclass::dir, mohclass::flags, mohclass::mode, and mohclass::start.

Referenced by _moh_register().

◆ moh_digit_match()

static int moh_digit_match ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 612 of file res_musiconhold.c.

613{
614 char *digit = arg;
615 struct mohclass *class = obj;
616
617 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
618}

References CMP_MATCH, CMP_STOP, and digit.

Referenced by get_mohbydigit().

◆ moh_file_vector_alloc()

static struct ast_vector_string * moh_file_vector_alloc ( int  initial_capacity)
static

Definition at line 1169 of file res_musiconhold.c.

1170{
1171 struct ast_vector_string *files = ao2_alloc_options(
1172 sizeof(struct ast_vector_string),
1175 if (files) {
1176 AST_VECTOR_INIT(files, initial_capacity);
1177 }
1178 return files;
1179}
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
static void moh_file_vector_destructor(void *obj)
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, AST_VECTOR_INIT, and moh_file_vector_destructor().

Referenced by _moh_class_malloc(), moh_parse_options(), and moh_scan_files().

◆ moh_file_vector_destructor()

static void moh_file_vector_destructor ( void *  obj)
static

Definition at line 1162 of file res_musiconhold.c.

1163{
1164 struct ast_vector_string *files = obj;
1165 AST_VECTOR_RESET(files, ast_free);
1166 AST_VECTOR_FREE(files);
1167}
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition vector.h:636
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185

References ast_free, AST_VECTOR_FREE, and AST_VECTOR_RESET.

Referenced by moh_file_vector_alloc().

◆ moh_filename_strcasecmp()

static int moh_filename_strcasecmp ( const void *  a,
const void *  b 
)
static

Definition at line 1343 of file res_musiconhold.c.

1344{
1345 const char **s1 = (const char **) a;
1346 const char **s2 = (const char **) b;
1347 return strcasecmp(*s1, *s2);
1348}
static struct test_val b

References a, and b.

Referenced by moh_scan_files().

◆ moh_files_alloc()

static void * moh_files_alloc ( struct ast_channel chan,
void *  params 
)
static

Definition at line 564 of file res_musiconhold.c.

565{
566 struct moh_files_state *state;
567 struct mohclass *class = params;
568 size_t file_count;
569
571 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
574 } else {
575 if (!state) {
576 return NULL;
577 }
578 if (state->class) {
579 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
580 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
581 }
582 }
583
584 ao2_lock(class);
585 file_count = AST_VECTOR_SIZE(class->files);
586 ao2_unlock(class);
587
588 /* Resume MOH from where we left off last time or start from scratch? */
589 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
590 /* Start MOH from scratch. */
591 ao2_cleanup(state->origwfmt);
592 ao2_cleanup(state->mohwfmt);
593 memset(state, 0, sizeof(*state));
594 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
595 state->pos = ast_random() % file_count;
596 }
597 }
598
599 state->class = mohclass_ref(class, "Reffing music class for channel");
600 /* it's possible state is not a new allocation, don't leak old refs */
601 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
603 /* For comparison on restart of MOH (see above) */
604 ast_copy_string(state->name, class->name, sizeof(state->name));
605 state->save_total = file_count;
606
607 moh_post_start(chan, class->name);
608
609 return state;
610}
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition astobj2.h:501
#define MOH_RANDOMIZE
long int ast_random(void)
Definition utils.c:2348

References ao2_cleanup, ao2_lock, ao2_replace, ao2_unlock, ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_writeformat(), ast_copy_string(), ast_log, ast_module_ref, ast_random(), ast_test_flag, AST_VECTOR_SIZE, LOG_WARNING, moh_post_start(), MOH_RANDOMIZE, mohclass_ref, mohclass_unref, state::name, NULL, and ast_module_info::self.

◆ moh_files_generator()

static int moh_files_generator ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
)
static

Definition at line 507 of file res_musiconhold.c.

508{
509 struct moh_files_state *state;
510 struct ast_frame *f = NULL;
511 int res = 0, sample_queue = 0;
512
513 ast_channel_lock(chan);
515 state->sample_queue += samples;
516 /* save the sample queue value for un-locked access */
517 sample_queue = state->sample_queue;
518 ast_channel_unlock(chan);
519
520 while (sample_queue > 0) {
521 ast_channel_lock(chan);
522 f = moh_files_readframe(chan);
523 if (!f) {
524 ast_channel_unlock(chan);
525 return -1;
526 }
527
528 /* Only track our offset within the current file if we are not in the
529 * the middle of an announcement */
530 if (!state->announcement) {
531 state->samples += f->samples;
532 }
533
534 state->sample_queue -= f->samples;
536 ao2_replace(state->mohwfmt, f->subclass.format);
537 }
538
539 /* We need to be sure that we unlock
540 * the channel prior to calling
541 * ast_write, but after our references to state
542 * as it refers to chan->music_state. Update
543 * sample_queue for our loop
544 * Otherwise, the recursive locking that occurs
545 * can cause deadlocks when using indirect
546 * channels, like local channels
547 */
548 sample_queue = state->sample_queue;
549 ast_channel_unlock(chan);
550
551 res = ast_write(chan, f);
552 ast_frfree(f);
553 if (res < 0) {
554 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
555 return -1;
556 }
557 }
558 return res;
559}
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition channel.c:5139
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition format.h:38
#define ast_frfree(fr)
static struct ast_frame * moh_files_readframe(struct ast_channel *chan)
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass

References ao2_replace, ast_channel_lock, ast_channel_music_state(), ast_channel_name(), ast_channel_unlock, ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_frfree, ast_log, ast_write(), errno, ast_frame_subclass::format, LOG_WARNING, moh_files_readframe(), NULL, ast_frame::samples, and ast_frame::subclass.

◆ moh_files_next()

static int moh_files_next ( struct ast_channel chan)
static

Definition at line 353 of file res_musiconhold.c.

354{
356 struct ast_vector_string *files;
357 int tries;
358 size_t file_count;
359
360 /* Discontinue a stream if it is running already */
361 if (ast_channel_stream(chan)) {
364 }
365
366 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
367 state->announcement = 1;
368 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
369 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
370 return 0;
371 }
372 } else {
373 state->announcement = 0;
374 }
375
376 ao2_lock(state->class);
377 files = ao2_bump(state->class->files);
378 ao2_unlock(state->class);
379
380 file_count = AST_VECTOR_SIZE(files);
381 if (!file_count) {
382 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
383 ao2_ref(files, -1);
384 return -1;
385 }
386
387 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
388 /* First time so lets play the file. */
389 state->save_pos = -1;
390 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
391 /* If a specific file has been saved confirm it still exists and that it is still valid */
392 state->pos = state->save_pos;
393 state->save_pos = -1;
394 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
395 /* Get a random file and ensure we can open it */
396 for (tries = 0; tries < 20; tries++) {
397 state->pos = ast_random() % file_count;
398 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
399 break;
400 }
401 }
402 state->save_pos = -1;
403 state->samples = 0;
404 } else {
405 /* This is easy, just increment our position and make sure we don't exceed the total file count */
406 state->pos++;
407 if (ast_test_flag(state->class, MOH_LOOPLAST)) {
408 state->pos = MIN(file_count - 1, state->pos);
409 } else {
410 state->pos %= file_count;
411 }
412 state->save_pos = -1;
413 state->samples = 0;
414 }
415
416 for (tries = 0; tries < file_count; ++tries) {
417 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
418 break;
419 }
420
421 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
422 state->pos++;
423 state->pos %= file_count;
424 }
425
426 if (tries == file_count) {
427 ao2_ref(files, -1);
428 return -1;
429 }
430
431 /* Record the pointer to the filename for position resuming later */
432 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
433
434 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
435
436 if (state->samples) {
437 size_t loc;
438 /* seek *SHOULD* be good since it's from a known location */
439 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
440 /* if the seek failed then recover because if there is not a valid read,
441 * moh_files_generate will return -1 and MOH will stop */
443 if (state->samples > loc && loc) {
444 /* seek one sample from the end for one guaranteed valid read */
445 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
446 }
447 }
448
449 ao2_ref(files, -1);
450 return 0;
451}
const char * ast_channel_language(const struct ast_channel *chan)
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition file.c:1104
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition file.c:861
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition file.c:1094
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1148
#define MOH_LOOPLAST
#define MOH_SORTMODE
#define MIN(a, b)
Definition utils.h:252

References ao2_bump, ao2_lock, ao2_ref, ao2_unlock, ast_channel_language(), ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_copy_string(), ast_debug, ast_fileexists(), ast_log, ast_openstream_full(), ast_random(), ast_seekstream(), ast_strlen_zero(), ast_tellstream(), ast_test_flag, AST_VECTOR_GET, AST_VECTOR_SIZE, errno, LOG_WARNING, MIN, MOH_ANNOUNCEMENT, MOH_LOOPLAST, MOH_RANDOMIZE, MOH_SORTMODE, state::name, and NULL.

Referenced by moh_files_readframe().

◆ moh_files_readframe()

static struct ast_frame * moh_files_readframe ( struct ast_channel chan)
static

Definition at line 456 of file res_musiconhold.c.

457{
458 struct ast_frame *f;
459
461 if (!f) {
462 /* Either there was no file stream setup or we reached EOF. */
463 if (!moh_files_next(chan)) {
464 /*
465 * Either we resetup the previously saved file stream position
466 * or we started a new file stream.
467 */
469 if (!f) {
470 /*
471 * We can get here if we were very unlucky because the
472 * resetup file stream was saved at EOF when MOH was
473 * previously stopped.
474 */
475 if (!moh_files_next(chan)) {
477 }
478 }
479 }
480 }
481
482 return f;
483}
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition file.c:955
static int moh_files_next(struct ast_channel *chan)

References ast_channel_stream(), ast_readframe(), and moh_files_next().

Referenced by moh_files_generator().

◆ moh_files_release()

static void moh_files_release ( struct ast_channel chan,
void *  data 
)
static

Definition at line 318 of file res_musiconhold.c.

319{
320 struct moh_files_state *state;
321
322 if (!chan || !ast_channel_music_state(chan)) {
323 return;
324 }
325
327
328 if (ast_channel_stream(chan)) {
331 }
332
333 moh_post_stop(chan);
334
335 ao2_ref(state->mohwfmt, -1);
336 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
337 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
338 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
339 ast_format_get_name(state->origwfmt));
340 }
341 ao2_cleanup(state->origwfmt);
342 state->origwfmt = NULL;
343
344 state->save_pos = state->pos;
345 state->announcement = 0;
346
347 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
348}
static void moh_post_stop(struct ast_channel *chan)

References ao2_cleanup, ao2_ref, ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_format_get_name(), ast_log, ast_set_write_format(), LOG_WARNING, moh_post_stop(), mohclass_unref, and NULL.

◆ moh_files_write_format_change()

static void moh_files_write_format_change ( struct ast_channel chan,
void *  data 
)
static

Definition at line 488 of file res_musiconhold.c.

489{
491
492 /* In order to prevent a recursive call to this function as a result
493 * of setting the moh write format back on the channel. Clear
494 * the moh write format before setting the write format on the channel.*/
495 if (state->origwfmt) {
496 struct ast_format *tmp;
497
499 ao2_replace(state->origwfmt, NULL);
500 if (state->mohwfmt) {
501 ast_set_write_format(chan, state->mohwfmt);
502 }
503 state->origwfmt = tmp;
504 }
505}
Definition of a media format.
Definition format.c:43

References ao2_bump, ao2_replace, ast_channel_music_state(), ast_channel_writeformat(), ast_set_write_format(), and NULL.

◆ moh_generate()

static int moh_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
)
static

Definition at line 1127 of file res_musiconhold.c.

1128{
1129 struct mohdata *moh = data;
1130 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1131 int res;
1132
1133 len = ast_format_determine_length(moh->parent->format, samples);
1134
1135 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1136 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1137 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1138 }
1139 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1140 if (res <= 0)
1141 return 0;
1142
1143 moh->f.datalen = res;
1144 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1145 moh->f.samples = ast_codec_samples_count(&moh->f);
1146
1147 if (ast_write(chan, &moh->f) < 0) {
1148 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1149 return -1;
1150 }
1151
1152 return 0;
1153}
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition codec.c:379
char buf[BUFSIZE]
Definition eagi_proxy.c:66
unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples)
Get the length (in milliseconds) for the format with a given number of samples.
Definition format.c:384
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
union ast_frame::@239 data
struct mohclass * parent
struct ast_frame f

References ast_channel_name(), ast_codec_samples_count(), ast_format_determine_length(), AST_FRIENDLY_OFFSET, ast_log, ast_write(), buf, ast_frame::data, ast_frame::datalen, errno, mohdata::f, mohclass::format, len(), LOG_WARNING, mohdata::parent, mohdata::pipe, ast_frame::ptr, and ast_frame::samples.

◆ moh_handle_digit()

static void moh_handle_digit ( struct ast_channel chan,
char  digit 
)
static

Definition at line 626 of file res_musiconhold.c.

627{
628 struct mohclass *class;
629 const char *classname = NULL;
630
631 if ((class = get_mohbydigit(digit))) {
632 classname = ast_strdupa(class->name);
633 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
634 ast_channel_musicclass_set(chan, classname);
635 ast_moh_stop(chan);
636 ast_moh_start(chan, classname, NULL);
637 }
638}
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition channel.c:7778
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition channel.c:7788
static struct mohclass * get_mohbydigit(char digit)

References ast_moh_start(), ast_moh_stop(), ast_strdupa, digit, get_mohbydigit(), mohclass_unref, NULL, and mohclass::start.

◆ moh_parse_options()

static void moh_parse_options ( struct ast_variable var,
struct mohclass mohclass 
)
static

Definition at line 1181 of file res_musiconhold.c.

1182{
1183 struct ast_vector_string *playlist_entries = NULL;
1184
1185 for (; var; var = var->next) {
1186 if (!strcasecmp(var->name, "name")) {
1187 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1188 } else if (!strcasecmp(var->name, "mode")) {
1189 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1190 } else if (!strcasecmp(var->name, "entry")) {
1191 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1192 char *dup;
1193
1194 if (!playlist_entries) {
1195 playlist_entries = moh_file_vector_alloc(16);
1196 if (!playlist_entries) {
1197 continue;
1198 }
1199 }
1200
1201 dup = ast_strdup(var->value);
1202 if (!dup) {
1203 continue;
1204 }
1205
1206 if (ast_begins_with(dup, "/")) {
1207 char *last_pos_dot = strrchr(dup, '.');
1208 char *last_pos_slash = strrchr(dup, '/');
1209 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1210 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1211 dup);
1212 }
1213 }
1214
1215 AST_VECTOR_APPEND(playlist_entries, dup);
1216 } else {
1217 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1218 }
1219 } else if (!strcasecmp(var->name, "directory")) {
1220 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1221 } else if (!strcasecmp(var->name, "application")) {
1222 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1223 } else if (!strcasecmp(var->name, "announcement")) {
1226 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1227 mohclass->digit = *var->value;
1228 } else if (!strcasecmp(var->name, "sort")) {
1229 if (!strcasecmp(var->value, "random")) {
1231 } else if (!strcasecmp(var->value, "alpha")) {
1233 } else if (!strcasecmp(var->value, "randstart")) {
1235 }
1236 } else if (!strcasecmp(var->name, "loop_last")) {
1237 if (ast_true(var->value)) {
1239 } else {
1241 }
1242 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1245 if (!mohclass->format) {
1246 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1248 }
1249 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1250 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1251 mohclass->kill_delay *= 1000;
1252 } else {
1253 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1254 mohclass->kill_delay = 100000;
1255 }
1256 } else if (!strcasecmp(var->name, "kill_method")) {
1257 if (!strcasecmp(var->value, "process")) {
1259 } else if (!strcasecmp(var->value, "process_group")) {
1261 } else {
1262 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1264 }
1265 } else if (!strcasecmp(var->name, "answeredonly")) {
1266 mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1267 }
1268 }
1269
1270 if (playlist_entries) {
1271 /* If we aren't in playlist mode, drop any list we may have already built */
1272 if (strcasecmp(mohclass->mode, "playlist")) {
1273 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1274 mohclass->mode);
1275 ao2_ref(playlist_entries, -1);
1276 return;
1277 }
1278
1279 AST_VECTOR_COMPACT(playlist_entries);
1280
1281 /* We don't need to lock here because we are the thread that
1282 * created this mohclass and we haven't published it yet */
1283 ao2_ref(mohclass->files, -1);
1284 mohclass->files = playlist_entries;
1285 }
1286}
#define ast_format_cache_get(name)
Retrieve a named format from the cache.
#define LOG_ERROR
#define MOH_RANDSTART
#define MOH_SORTALPHA
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition strings.h:97
char announcement[256]
enum kill_methods kill_method
size_t kill_delay
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition vector.h:649

References mohclass::announcement, mohclass::answeredonly, ao2_bump, ao2_cleanup, ao2_ref, mohclass::args, ast_begins_with(), ast_clear_flag, ast_copy_string(), ast_format_cache_get, ast_format_slin, ast_log, ast_set_flag, ast_strdup, ast_strlen_zero(), ast_true(), AST_VECTOR_APPEND, AST_VECTOR_COMPACT, mohclass::digit, mohclass::dir, mohclass::files, mohclass::format, mohclass::kill_delay, mohclass::kill_method, KILL_METHOD_PROCESS, KILL_METHOD_PROCESS_GROUP, LOG_ERROR, LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_ANNOUNCEMENT, moh_file_vector_alloc(), MOH_LOOPLAST, MOH_RANDOMIZE, MOH_RANDSTART, MOH_SORTALPHA, mohclass::name, NULL, and var.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_post_start()

static void moh_post_start ( struct ast_channel chan,
const char *  moh_class_name 
)
static

Definition at line 253 of file res_musiconhold.c.

254{
255 struct stasis_message *message;
256 struct ast_json *json_object;
257 struct ast_json *cel_event;
258
259 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
260 moh_class_name, ast_channel_name(chan));
261
262 cel_event = ast_json_pack("{ s: s, s: {s: s }}",
263 "event", "MOH_STREAM_BEGIN",
264 "extra",
265 "class", moh_class_name
266 );
267 if (cel_event) {
269 } else {
270 ast_log(LOG_WARNING, "Unable to build extradata for music on hold STREAM_BEGIN event on channel %s", ast_channel_name(chan));
271 }
272 ast_json_unref(cel_event);
273
274 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
275 if (!json_object) {
276 return;
277 }
278
280 ast_channel_moh_start_type(), json_object);
281 if (message) {
282 /* A channel snapshot must have been in the cache. */
283 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
284
286 }
288 ast_json_unref(json_object);
289}
void ast_cel_publish_event(struct ast_channel *chan, enum ast_cel_event_type event_type, struct ast_json *blob)
Publish a CEL event.
Definition cel.c:1735
@ AST_CEL_STREAM_BEGIN
A stream started.
Definition cel.h:81
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition json.c:612
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition stasis.c:1578
Blob of data associated with a channel.
Abstract JSON element (object, array, string, int, ...).
#define ast_assert(a)
Definition utils.h:779

References ao2_cleanup, ast_assert, ast_cel_publish_event(), AST_CEL_STREAM_BEGIN, ast_channel_blob_create_from_cache(), ast_channel_moh_start_type(), ast_channel_name(), ast_channel_topic(), ast_channel_uniqueid(), ast_json_pack(), ast_json_unref(), ast_log, ast_verb, LOG_WARNING, NULL, stasis_message_data(), and stasis_publish().

Referenced by moh_alloc(), and moh_files_alloc().

◆ moh_post_stop()

static void moh_post_stop ( struct ast_channel chan)
static

Definition at line 291 of file res_musiconhold.c.

292{
293 struct stasis_message *message;
294 struct ast_json *cel_event;
295
296 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
297
298 cel_event = ast_json_pack("{ s: s }", "event", "MOH_STREAM_END");
299 if (cel_event) {
301 }
302 ast_json_unref(cel_event);
303
306 if (message) {
307 /* A channel snapshot must have been in the cache. */
308 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
309
311 }
313}
@ AST_CEL_STREAM_END
A stream ended.
Definition cel.h:83
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.

References ao2_cleanup, ast_assert, ast_cel_publish_event(), AST_CEL_STREAM_END, ast_channel_blob_create_from_cache(), ast_channel_moh_stop_type(), ast_channel_name(), ast_channel_topic(), ast_channel_uniqueid(), ast_json_pack(), ast_json_unref(), ast_verb, NULL, stasis_message_data(), and stasis_publish().

Referenced by moh_files_release(), and moh_release().

◆ moh_release()

static void moh_release ( struct ast_channel chan,
void *  data 
)
static

Definition at line 1048 of file res_musiconhold.c.

1049{
1050 struct mohdata *moh = data;
1051 struct mohclass *class = moh->parent;
1052 struct ast_format *oldwfmt;
1053
1054 ao2_lock(class);
1055 AST_LIST_REMOVE(&moh->parent->members, moh, list);
1056 ao2_unlock(class);
1057
1058 close(moh->pipe[0]);
1059 close(moh->pipe[1]);
1060
1061 oldwfmt = moh->origwfmt;
1062
1063 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1064
1065 ast_free(moh);
1066
1067 if (chan) {
1068 struct moh_files_state *state;
1069
1071 if (state && state->class) {
1072 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1073 }
1074 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1075 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1076 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1077 }
1078
1079 moh_post_stop(chan);
1080 }
1081
1082 ao2_cleanup(oldwfmt);
1083}
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
struct mohclass::@475 members

References ao2_cleanup, ao2_lock, ao2_unlock, ast_channel_music_state(), ast_channel_name(), ast_format_get_name(), ast_free, AST_LIST_REMOVE, ast_log, ast_set_write_format(), LOG_WARNING, mohclass::members, moh_post_stop(), mohclass_unref, mohdata::origwfmt, mohdata::parent, and mohdata::pipe.

Referenced by moh_alloc().

◆ moh_rescan_files()

static void moh_rescan_files ( void  )
static

Definition at line 1407 of file res_musiconhold.c.

1407 {
1408 struct ao2_iterator i;
1409 struct mohclass *c;
1410
1412
1413 while ((c = ao2_iterator_next(&i))) {
1414 if (!strcasecmp(c->mode, "files")) {
1416 }
1417 ao2_ref(c, -1);
1418 }
1419
1421}
#define ao2_iterator_next(iter)
Definition astobj2.h:1911

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, c, moh_scan_files(), mohclasses, and mohclass::start.

Referenced by load_moh_classes().

◆ moh_scan_files()

static int moh_scan_files ( struct mohclass class)
static

Definition at line 1350 of file res_musiconhold.c.

1350 {
1351
1352 char dir_path[PATH_MAX - sizeof(class->dir)];
1353 struct ast_vector_string *files;
1354
1355 if (class->dir[0] != '/') {
1356 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1357 } else {
1358 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1359 }
1360
1361 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1362
1363 /* 16 seems like a reasonable default */
1364 files = moh_file_vector_alloc(16);
1365 if (!files) {
1366 return -1;
1367 }
1368
1369 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1370 ao2_ref(files, -1);
1371 return -1;
1372 }
1373
1374 if (ast_test_flag(class, MOH_SORTALPHA)) {
1376 }
1377
1378 AST_VECTOR_COMPACT(files);
1379
1380 ao2_lock(class);
1381 ao2_ref(class->files, -1);
1382 class->files = files;
1383 ao2_unlock(class);
1384
1385 return AST_VECTOR_SIZE(files);
1386}
#define PATH_MAX
Definition asterisk.h:40
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition file.h:203
const char * ast_config_AST_DATA_DIR
Definition options.c:159
static int moh_filename_strcasecmp(const void *a, const void *b)
static int on_moh_file(const char *directory, const char *filename, void *obj)
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition vector.h:407

References ao2_lock, ao2_ref, ao2_unlock, ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_file_read_dir, ast_test_flag, AST_VECTOR_COMPACT, AST_VECTOR_SIZE, AST_VECTOR_SORT, moh_file_vector_alloc(), moh_filename_strcasecmp(), MOH_SORTALPHA, on_moh_file(), and PATH_MAX.

Referenced by init_files_class(), local_ast_moh_start(), and moh_rescan_files().

◆ mohalloc()

static struct mohdata * mohalloc ( struct mohclass cl)
static

Definition at line 1019 of file res_musiconhold.c.

1020{
1021 struct mohdata *moh;
1022
1023 if (!(moh = ast_calloc(1, sizeof(*moh))))
1024 return NULL;
1025
1026 if (ast_pipe_nonblock(moh->pipe)) {
1027 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
1028 ast_free(moh);
1029 return NULL;
1030 }
1031
1033 moh->f.subclass.format = cl->format;
1035
1036 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
1037
1038 ao2_lock(cl);
1039 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
1040 ao2_unlock(cl);
1041
1042 return moh;
1043}
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
enum ast_frame_type frametype
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition utils.h:1130

References ao2_lock, ao2_unlock, ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log, ast_pipe_nonblock, errno, mohdata::f, ast_frame_subclass::format, mohclass::format, ast_frame::frametype, mohdata::list, LOG_WARNING, mohclass::members, mohclass_ref, NULL, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

◆ monmp3thread()

static void * monmp3thread ( void *  data)
static

Definition at line 834 of file res_musiconhold.c.

835{
836#define MOH_MS_INTERVAL 100
837
838 struct mohclass *class = data;
839 struct mohdata *moh;
840 short sbuf[8192];
841 int res = 0, res2;
842 int len;
843 struct timeval deadline, tv_tmp;
844
845 deadline.tv_sec = 0;
846 deadline.tv_usec = 0;
847 for(;/* ever */;) {
848 pthread_testcancel();
849 /* Spawn mp3 player if it's not there */
850 if (class->srcfd < 0) {
851 if ((class->srcfd = spawn_mp3(class)) < 0) {
852 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
853 /* Try again later */
854 sleep(500);
855 continue;
856 }
857 }
858 if (class->timer) {
859 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
860
861#ifdef SOLARIS
862 thr_yield();
863#endif
864 /* Pause some amount of time */
865 if (ast_poll(&pfd, 1, -1) > 0) {
866 if (ast_timer_ack(class->timer, 1) < 0) {
867 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
868 return NULL;
869 }
870 /* 25 samples per second => 40ms framerate => 320 samples */
871 res = 320; /* 320/40 = 8 samples/ms */
872 } else {
873 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
874 res = 0;
875 }
876 pthread_testcancel();
877 } else {
878 long delta;
879 /* Reliable sleep */
880 tv_tmp = ast_tvnow();
881 if (ast_tvzero(deadline))
882 deadline = tv_tmp;
883 delta = ast_tvdiff_ms(tv_tmp, deadline);
884 if (delta < MOH_MS_INTERVAL) { /* too early */
885 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
886 usleep(1000 * (MOH_MS_INTERVAL - delta));
887 pthread_testcancel();
888 } else {
889 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
890 deadline = tv_tmp;
891 }
892 /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
893 res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
894 }
895 /* For non-8000Hz formats, we need to alter the resolution */
896 res = res * ast_format_get_sample_rate(class->format) / 8000;
897
898 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
899 continue;
900 /* Read mp3 audio */
901 len = ast_format_determine_length(class->format, res);
902
903 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
904 if (!res2) {
905 close(class->srcfd);
906 class->srcfd = -1;
907 pthread_testcancel();
908 if (class->pid > 1) {
909 killpid(class->pid, class->kill_delay, class->kill_method);
910 class->pid = 0;
911 }
912 } else {
913 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
914 }
915 continue;
916 }
917
918 pthread_testcancel();
919
920 ao2_lock(class);
921 AST_LIST_TRAVERSE(&class->members, moh, list) {
922 /* Write data */
923 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
924 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
925 }
926 }
927 ao2_unlock(class);
928 }
929 return NULL;
930}
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition format.c:379
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define ast_poll(a, b, c)
Definition poll-compat.h:88
#define MOH_MS_INTERVAL
static int spawn_mp3(struct mohclass *class)
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition time.h:282
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition time.h:117
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition extconf.c:2280
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition timing.c:171
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition timing.c:161

References ao2_lock, ao2_unlock, ast_debug, ast_format_determine_length(), ast_format_get_sample_rate(), 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, killpid(), len(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, MOH_MS_INTERVAL, NULL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

◆ on_moh_file()

static int on_moh_file ( const char *  directory,
const char *  filename,
void *  obj 
)
static

Definition at line 1288 of file res_musiconhold.c.

1289{
1290 struct ast_vector_string *files = obj;
1291 char *full_path;
1292 char *extension;
1293
1294 /* Skip files that starts with a dot */
1295 if (*filename == '.') {
1296 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1297 directory, filename);
1298 return 0;
1299 }
1300
1301 /* We can't do anything with files that don't have an extension,
1302 * so check that first and punt if we can't find something */
1303 extension = strrchr(filename, '.');
1304 if (!extension) {
1305 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1306 directory, filename);
1307 return 0;
1308 }
1309
1310 /* The extension needs at least two characters (after the .) to be useful */
1311 if (strlen(extension) < 3) {
1312 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1313 "character extension\n", directory, filename);
1314 return 0;
1315 }
1316
1317 /* Build the full path (excluding the extension) */
1318 if (ast_asprintf(&full_path, "%s/%.*s",
1319 directory,
1320 (int) (extension - filename), filename) < 0) {
1321 /* If we don't have enough memory to build this path, there is no
1322 * point in continuing */
1323 return 1;
1324 }
1325
1326 /* If the file is present in multiple formats, ensure we only put it
1327 * into the list once. Pretty sure this is O(n^2). */
1328 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1329 ast_free(full_path);
1330 return 0;
1331 }
1332
1333 if (AST_VECTOR_APPEND(files, full_path)) {
1334 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1335 * we stop iterating */
1336 ast_free(full_path);
1337 return 1;
1338 }
1339
1340 return 0;
1341}
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
structure to hold extensions
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition vector.h:742

References ast_asprintf, ast_debug, ast_free, AST_VECTOR_APPEND, and AST_VECTOR_GET_CMP.

Referenced by moh_scan_files().

◆ play_moh_exec()

static int play_moh_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 932 of file res_musiconhold.c.

933{
934 char *parse;
935 char *class;
936 int timeout = -1;
937 int res;
939 AST_APP_ARG(class);
940 AST_APP_ARG(duration);
941 );
942
943 parse = ast_strdupa(data);
944
946
947 if (!ast_strlen_zero(args.duration)) {
948 if (sscanf(args.duration, "%30d", &timeout) == 1) {
949 timeout *= 1000;
950 } else {
951 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
952 }
953 }
954
955 class = S_OR(args.class, NULL);
956 if (ast_moh_start(chan, class, NULL)) {
957 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
958 return 0;
959 }
960
961 if (timeout > 0)
962 res = ast_safe_sleep(chan, timeout);
963 else {
964 while (!(res = ast_safe_sleep(chan, 10000)));
965 }
966
967 ast_moh_stop(chan);
968
969 return res;
970}
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition channel.c:1560
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
static struct @519 args

References args, AST_APP_ARG, ast_channel_name(), 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, NULL, and S_OR.

Referenced by load_module().

◆ reload()

static int reload ( void  )
static

◆ spawn_mp3()

static int spawn_mp3 ( struct mohclass class)
static

Definition at line 648 of file res_musiconhold.c.

649{
650 int fds[2];
651 int files = 0;
652 char fns[MAX_MP3S][80];
653 char *argv[MAX_MP3S + 50];
654 char xargs[256];
655 char *argptr;
656 int argc = 0;
657 DIR *dir = NULL;
658 struct dirent *de;
659
660
661 if (!strcasecmp(class->dir, "nodir")) {
662 files = 1;
663 } else {
664 dir = opendir(class->dir);
665 if (!dir && strncasecmp(class->dir, "http://", 7)) {
666 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
667 return -1;
668 }
669 }
670
671 if (!ast_test_flag(class, MOH_CUSTOM)) {
672 argv[argc++] = "mpg123";
673 argv[argc++] = "-q";
674 argv[argc++] = "-s";
675 argv[argc++] = "--mono";
676 argv[argc++] = "-r";
677 argv[argc++] = "8000";
678
679 if (!ast_test_flag(class, MOH_SINGLE)) {
680 argv[argc++] = "-b";
681 argv[argc++] = "2048";
682 }
683
684 argv[argc++] = "-f";
685
686 if (ast_test_flag(class, MOH_QUIET))
687 argv[argc++] = "4096";
688 else
689 argv[argc++] = "8192";
690
691 /* Look for extra arguments and add them to the list */
692 ast_copy_string(xargs, class->args, sizeof(xargs));
693 argptr = xargs;
694 while (!ast_strlen_zero(argptr)) {
695 argv[argc++] = argptr;
696 strsep(&argptr, ",");
697 }
698 } else {
699 /* Format arguments for argv vector */
700 ast_copy_string(xargs, class->args, sizeof(xargs));
701 argptr = xargs;
702 while (!ast_strlen_zero(argptr)) {
703 argv[argc++] = argptr;
704 strsep(&argptr, " ");
705 }
706 }
707
708 if (!strncasecmp(class->dir, "http://", 7)) {
709 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
710 argv[argc++] = fns[files];
711 files++;
712 } else if (dir) {
713 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
714 if ((strlen(de->d_name) > 3) &&
715 ((ast_test_flag(class, MOH_CUSTOM) &&
716 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
717 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
718 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
719 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
720 argv[argc++] = fns[files];
721 files++;
722 }
723 }
724 }
725 argv[argc] = NULL;
726 if (dir) {
727 closedir(dir);
728 }
729 if (pipe(fds)) {
730 ast_log(LOG_WARNING, "Pipe failed\n");
731 return -1;
732 }
733 if (!files) {
734 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
735 close(fds[0]);
736 close(fds[1]);
737 return -1;
738 }
739 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
740 sleep(respawn_time - (time(NULL) - class->start));
741 }
742
743 time(&class->start);
744 class->pid = ast_safe_fork(0);
745 if (class->pid < 0) {
746 close(fds[0]);
747 close(fds[1]);
748 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
749 return -1;
750 }
751 if (!class->pid) {
754
755 close(fds[0]);
756 /* Stdout goes to pipe */
757 dup2(fds[1], STDOUT_FILENO);
758
759 /* Close everything else */
760 ast_close_fds_above_n(STDERR_FILENO);
761
762 /* Child */
763 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
764 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
765 _exit(1);
766 }
767 setpgid(0, getpid());
768 if (ast_test_flag(class, MOH_CUSTOM)) {
769 execv(argv[0], argv);
770 } else {
771 /* Default install is /usr/local/bin */
772 execv(LOCAL_MPG_123, argv);
773 /* Many places have it in /usr/bin */
774 execv(MPG_123, argv);
775 /* Check PATH as a last-ditch effort */
776 execvp("mpg123", argv);
777 }
778 /* Can't use logger, since log FDs are closed */
779 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
780 close(fds[1]);
781 _exit(1);
782 } else {
783 /* Parent */
784 close(fds[1]);
785 }
786 return fds[0];
787}
char * strsep(char **str, const char *delims)
int ast_set_priority(int)
We set ourselves to a high priority, that we might pre-empt everything else. If your PBX has heavy ac...
Definition asterisk.c:1853
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child.
Definition main/app.c:3207
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition main/app.c:3202
#define ast_opt_high_priority
Definition options.h:122
#define MPG_123
#define LOCAL_MPG_123
#define MAX_MP3S

References ast_close_fds_above_n(), ast_copy_string(), ast_log, ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_strlen_zero(), ast_test_flag, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, NULL, respawn_time, and strsep().

Referenced by monmp3thread().

◆ start_moh_exec()

static int start_moh_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 972 of file res_musiconhold.c.

973{
974 char *parse;
975 char *class;
977 AST_APP_ARG(class);
978 );
979
980 parse = ast_strdupa(data);
981
983
984 class = S_OR(args.class, NULL);
985 if (ast_moh_start(chan, class, NULL))
986 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
987
988 return 0;
989}

References args, AST_APP_ARG, ast_channel_name(), AST_DECLARE_APP_ARGS, ast_log, ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, NULL, and S_OR.

Referenced by load_module().

◆ stop_moh_exec()

static int stop_moh_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 991 of file res_musiconhold.c.

992{
993 ast_moh_stop(chan);
994
995 return 0;
996}

References ast_moh_stop().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2353 of file res_musiconhold.c.

2354{
2355 int res = 0;
2356 struct mohclass *class = NULL;
2357
2358 /* XXX This check shouldn't be required if module ref counting was being used
2359 * properly ... */
2360 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2361 class = mohclass_unref(class, "unref of class from module unload callback");
2362 res = -1;
2363 }
2364
2365 if (res < 0) {
2366 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2367 return res;
2368 }
2369
2371
2378
2379 return res;
2380}
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition asterisk.c:1072
int ast_unregister_application(const char *app)
Unregister an application.
Definition pbx_app.c:392
void ast_uninstall_music_functions(void)
Definition channel.c:7771
static int moh_class_inuse(void *obj, void *arg, int flags)

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(), mohclass_unref, mohclasses, NULL, play_moh, start_moh, and stop_moh.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static

Definition at line 2388 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2388 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2274 of file res_musiconhold.c.

2274 {
2275 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2276 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2277 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2278 AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2279};
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
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 char * handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)

Referenced by load_module(), and unload_module().

◆ global_flags

struct ast_flags global_flags[1] = {{0}}
static

global MOH_ flags

Definition at line 172 of file res_musiconhold.c.

172{{0}}; /*!< global MOH_ flags */

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_file_stream

struct ast_generator moh_file_stream
static

Definition at line 640 of file res_musiconhold.c.

640 {
641 .alloc = moh_files_alloc,
642 .release = moh_files_release,
643 .generate = moh_files_generator,
644 .digit = moh_handle_digit,
645 .write_format_change = moh_files_write_format_change,
646};
static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
static void moh_handle_digit(struct ast_channel *chan, char digit)
static void * moh_files_alloc(struct ast_channel *chan, void *params)
static void moh_files_release(struct ast_channel *chan, void *data)
static void moh_files_write_format_change(struct ast_channel *chan, void *data)

Referenced by local_ast_moh_start().

◆ mohclasses

struct ao2_container* mohclasses
static

◆ mohgen

struct ast_generator mohgen
static

Definition at line 1155 of file res_musiconhold.c.

1155 {
1156 .alloc = moh_alloc,
1157 .release = moh_release,
1158 .generate = moh_generate,
1159 .digit = moh_handle_digit,
1160};
static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
static void * moh_alloc(struct ast_channel *chan, void *params)

Referenced by local_ast_moh_start().

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 133 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ respawn_time

int respawn_time = 20
static

Definition at line 137 of file res_musiconhold.c.

Referenced by _moh_register(), local_ast_moh_start(), and spawn_mp3().

◆ start_moh

const char start_moh[] = "StartMusicOnHold"
static

Definition at line 134 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 135 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().