Asterisk - The Open Source Telephony Project GIT-master-0a46be9
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. More...
 
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. More...
 
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 = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = 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
1009 moh = __ao2_find(mohclasses, &tmp_class, flags,
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
char name[MAX_MUSICCLASS]
unsigned int flags

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

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) {
1669 class->format = ao2_bump(ast_format_slin);
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.
Definition: format_cache.c:41
static void moh_class_destructor(void *obj)
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)

References __ao2_alloc(), AO2_ALLOC_OPT_LOCK_MUTEX, ao2_bump, ao2_ref, ast_format_slin, make_ari_stubs::file, 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);
1514 file_count = AST_VECTOR_SIZE(moh->files);
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)
struct ast_vector_string * files
char mode[80]
time_t start
#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, make_ari_stubs::file, 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, and mohclass::realtime.

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:63

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:629
#define ast_set_flag(p, flag)
Definition: utils.h:70

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(), and NULL.

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.
Definition: main/config.c:3762
@ 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:7732
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:703

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}
static const char app[]
Definition: app_adsiprog.c:56
#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:3326
#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:1289
@ CONFIG_FLAG_FILEUNCHANGED
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#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:217
Structure for variables, used for configurations and for channel variables.
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define AST_FLAGS_ALL
Definition: utils.h:214

References ao2_t_callback, app, 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, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, reload(), 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.
Definition: main/config.c:3854
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.
Definition: main/config.c:1013
#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)
Definition: main/config.c:869
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3738
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
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
enum cc_state state
Definition: ccss.c:399
void * ast_channel_music_state(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2972
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define ast_channel_unlock(chan)
Definition: channel.h:2973
#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, ast_module_info::self, and state.

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:2921
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
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
#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, state, 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:2863
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:5771
#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 * format
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, ast_module_info::self, and state.

◆ 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
Definition: chan_unistim.c:259
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_PTHREADT_NULL
Definition: lock.h:73
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
struct mohdata::@446 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.
Definition: linkedlists.h:450

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, and mohclass::mode.

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, ast_module_info::self, and state.

◆ 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:5112
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, state, 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:249

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, NULL, and state.

◆ 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::@231 data
struct mohclass * parent
int pipe[2]
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
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
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:7748
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7758
static struct mohclass * get_mohbydigit(char digit)

References ao2_find, ast_moh_start(), ast_moh_stop(), ast_strdupa, digit, get_mohbydigit(), mohclass_unref, and NULL.

◆ 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.
Definition: format_cache.h:278
#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:1538
Blob of data associated with a channel.
Abstract JSON element (object, array, string, int, ...).
#define ast_assert(a)
Definition: utils.h:776

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.
Definition: linkedlists.h:856
struct mohclass::@444 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, mohdata::pipe, and state.

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(), and mohclasses.

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}
@ AST_FRAME_VOICE
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
enum ast_frame_type frametype
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition: utils.h:1127

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.
Definition: linkedlists.h:491
#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:2282
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:1542
#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.
const char * 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_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1072
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
void ast_uninstall_music_functions(void)
Definition: channel.c:7741
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(), callback(), 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 = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = 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.

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.

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.

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.

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().