Asterisk - The Open Source Telephony Project GIT-master-8ef3e4f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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/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 78 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 977 of file res_musiconhold.c.

◆ HANDLE_REF

#define HANDLE_REF   1

Definition at line 77 of file res_musiconhold.c.

◆ INITIAL_NUM_FILES

#define INITIAL_NUM_FILES   8

Definition at line 76 of file res_musiconhold.c.

◆ LOCAL_MPG_123

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

Definition at line 221 of file res_musiconhold.c.

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 223 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 162 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 161 of file res_musiconhold.c.

◆ moh_class_malloc

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

Definition at line 1639 of file res_musiconhold.c.

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

Definition at line 155 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 165 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 168 of file res_musiconhold.c.

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 163 of file res_musiconhold.c.

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 153 of file res_musiconhold.c.

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 156 of file res_musiconhold.c.

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 158 of file res_musiconhold.c.

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

Definition at line 169 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 1460 of file res_musiconhold.c.

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 154 of file res_musiconhold.c.

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 157 of file res_musiconhold.c.

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 159 of file res_musiconhold.c.

◆ moh_unregister

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

Definition at line 1528 of file res_musiconhold.c.

◆ mohclass_ref

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

Definition at line 228 of file res_musiconhold.c.

◆ mohclass_unref

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

Definition at line 231 of file res_musiconhold.c.

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 222 of file res_musiconhold.c.

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 173 of file res_musiconhold.c.

173 {
176};
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2367 of file res_musiconhold.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2367 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 979 of file res_musiconhold.c.

980{
981 struct mohclass *moh = NULL;
982 struct mohclass tmp_class = {
983 .flags = 0,
984 };
985
986 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
987
988 moh = __ao2_find(mohclasses, &tmp_class, flags,
989 "get_mohbyname", file, lineno, funcname);
990
991 if (!moh && warn) {
992 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
993 }
994
995 return moh;
996}
#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 1641 of file res_musiconhold.c.

1642{
1643 struct mohclass *class;
1644
1646 "Allocating new moh class", file, line, funcname);
1647 if (class) {
1648 class->format = ao2_bump(ast_format_slin);
1649 class->srcfd = -1;
1650 class->kill_delay = 100000;
1651
1652 /* We create an empty one by default */
1653 class->files = moh_file_vector_alloc(0);
1654 if (!class->files) {
1655 ao2_ref(class, -1);
1656 return NULL;
1657 }
1658 }
1659
1660 return class;
1661}
@ 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 1461 of file res_musiconhold.c.

1462{
1463 struct mohclass *mohclass = NULL;
1464
1465 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1466
1467 if (mohclass && !moh_diff(mohclass, moh)) {
1468 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1469 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1470 if (unref) {
1471 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1472 }
1473 return -1;
1474 } else if (mohclass) {
1475 /* Found a class, but it's different from the one being registered */
1476 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1477 }
1478
1479 time(&moh->start);
1480 moh->start -= respawn_time;
1481
1482 if (!strcasecmp(moh->mode, "files")) {
1483 if (init_files_class(moh)) {
1484 if (unref) {
1485 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1486 }
1487 return -1;
1488 }
1489 } else if (!strcasecmp(moh->mode, "playlist")) {
1490 size_t file_count;
1491
1492 ao2_lock(moh);
1493 file_count = AST_VECTOR_SIZE(moh->files);
1494 ao2_unlock(moh);
1495
1496 if (!file_count) {
1497 if (unref) {
1498 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1499 }
1500 return -1;
1501 }
1502 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1503 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1504 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1505 if (init_app_class(moh)) {
1506 if (unref) {
1507 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1508 }
1509 return -1;
1510 }
1511 } else {
1512 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1513 if (unref) {
1514 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1515 }
1516 return -1;
1517 }
1518
1519 ao2_t_link(mohclasses, moh, "Adding class to container");
1520
1521 if (unref) {
1522 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1523 }
1524
1525 return 0;
1526}
#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 1529 of file res_musiconhold.c.

1530{
1531 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1532 return 0;
1533}
#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 2367 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2135 of file res_musiconhold.c.

2136{
2137 ast_verb(2, "Destroying musiconhold processes\n");
2138 if (mohclasses) {
2140 ao2_ref(mohclasses, -1);
2141 mohclasses = NULL;
2142 }
2143}
@ 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 1566 of file res_musiconhold.c.

1567{
1568 int which=0;
1569 struct mohclass *cur;
1570 char *c = NULL;
1571 int wordlen = strlen(word);
1572 struct ao2_iterator i;
1573
1574 if (pos != 3) {
1575 return NULL;
1576 }
1577
1579 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1580 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1581 c = ast_strdup(cur->name);
1582 mohclass_unref(cur, "drop ref in iterator loop break");
1583 break;
1584 }
1585 mohclass_unref(cur, "drop ref in iterator loop");
1586 }
1588
1589 return c;
1590}
#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 600 of file res_musiconhold.c.

601{
602 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
603}
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 2145 of file res_musiconhold.c.

2146{
2147 switch (cmd) {
2148 case CLI_INIT:
2149 e->command = "moh reload";
2150 e->usage =
2151 "Usage: moh reload\n"
2152 " Reloads the MusicOnHold module.\n"
2153 " Alias for 'module reload res_musiconhold.so'\n";
2154 return NULL;
2155 case CLI_GENERATE:
2156 return NULL;
2157 }
2158
2159 if (a->argc != e->args)
2160 return CLI_SHOWUSAGE;
2161
2162 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2163 ast_module_reload("res_musiconhold");
2164
2165 return CLI_SUCCESS;
2166}
#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 2211 of file res_musiconhold.c.

2212{
2213 struct mohclass *class;
2214 struct ao2_iterator i;
2215
2216 switch (cmd) {
2217 case CLI_INIT:
2218 e->command = "moh show classes";
2219 e->usage =
2220 "Usage: moh show classes\n"
2221 " Lists all MusicOnHold classes.\n";
2222 return NULL;
2223 case CLI_GENERATE:
2224 return NULL;
2225 }
2226
2227 if (a->argc != e->args)
2228 return CLI_SHOWUSAGE;
2229
2231 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2232 ast_cli(a->fd, "Class: %s\n", class->name);
2233 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2234 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2235 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2236 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2237 }
2238 if (ast_test_flag(class, MOH_CUSTOM)) {
2239 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2240 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2241 ast_cli(a->fd, "\tKill Method: %s\n",
2242 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2243 }
2244 if (strcasecmp(class->mode, "files")) {
2245 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2246 }
2247 }
2249
2250 return CLI_SUCCESS;
2251}
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 2168 of file res_musiconhold.c.

2169{
2170 struct mohclass *class;
2171 struct ao2_iterator i;
2172
2173 switch (cmd) {
2174 case CLI_INIT:
2175 e->command = "moh show files";
2176 e->usage =
2177 "Usage: moh show files\n"
2178 " Lists all loaded file-based MusicOnHold classes and their\n"
2179 " files.\n";
2180 return NULL;
2181 case CLI_GENERATE:
2182 return NULL;
2183 }
2184
2185 if (a->argc != e->args)
2186 return CLI_SHOWUSAGE;
2187
2189 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2190 struct ast_vector_string *files;
2191
2192 ao2_lock(class);
2193 files = ao2_bump(class->files);
2194 ao2_unlock(class);
2195
2196 if (AST_VECTOR_SIZE(files)) {
2197 int x;
2198 ast_cli(a->fd, "Class: %s\n", class->name);
2199 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2200 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2201 }
2202 }
2203
2204 ao2_ref(files, -1);
2205 }
2207
2208 return CLI_SUCCESS;
2209}
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 1592 of file res_musiconhold.c.

1593{
1594 struct mohclass *cur;
1595 int len;
1596 int found = 0;
1597 struct ao2_iterator i;
1598
1599 switch (cmd) {
1600 case CLI_INIT:
1601 e->command = "moh unregister class";
1602 e->usage =
1603 "Usage: moh unregister class <class>\n"
1604 " Unregisters a realtime moh class.\n";
1605 return NULL;
1606 case CLI_GENERATE:
1607 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1608 }
1609
1610 if (a->argc != 4)
1611 return CLI_SHOWUSAGE;
1612
1613 len = strlen(a->argv[3]);
1614
1616 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1617 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1618 found = 1;
1619 break;
1620 }
1621 mohclass_unref(cur, "drop ref in iterator loop");
1622 }
1624
1625 if (found) {
1626 moh_unregister(cur);
1627 mohclass_unref(cur, "drop ref after unregister");
1628 } else {
1629 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1630 }
1631
1632 return CLI_SUCCESS;
1633}
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 1421 of file res_musiconhold.c.

1422{
1423 if (!strcasecmp(class->mode, "custom")) {
1424 ast_set_flag(class, MOH_CUSTOM);
1425 } else if (!strcasecmp(class->mode, "mp3nb")) {
1426 ast_set_flag(class, MOH_SINGLE);
1427 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1429 } else if (!strcasecmp(class->mode, "quietmp3")) {
1430 ast_set_flag(class, MOH_QUIET);
1431 }
1432
1433 class->srcfd = -1;
1434
1435 if (!(class->timer = ast_timer_open())) {
1436 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1437 return -1;
1438 }
1439 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1440 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1441 ast_timer_close(class->timer);
1442 class->timer = NULL;
1443 }
1444
1445 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1446 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1447 if (class->timer) {
1448 ast_timer_close(class->timer);
1449 class->timer = NULL;
1450 }
1451 return -1;
1452 }
1453
1454 return 0;
1455}
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:598
#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 1367 of file res_musiconhold.c.

1368{
1369 int res;
1370
1371 res = moh_scan_files(class);
1372
1373 if (res < 0) {
1374 return -1;
1375 }
1376
1377 if (!res) {
1378 ast_verb(3, "Files not found in %s for moh class:%s\n",
1379 class->dir, class->name);
1380 return -1;
1381 }
1382
1383 return 0;
1384}
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 768 of file res_musiconhold.c.

769{
770 switch (kill_method) {
772 return killpg(pid, signum);
774 return kill(pid, signum);
775 }
776
777 return -1;
778}

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 780 of file res_musiconhold.c.

781{
782 if (killer(pid, SIGHUP, kill_method) < 0) {
783 if (errno == ESRCH) {
784 return;
785 }
786 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
787 } else {
788 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
789 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
790 }
791 usleep(delay);
792 if (killer(pid, SIGTERM, kill_method) < 0) {
793 if (errno == ESRCH) {
794 return;
795 }
796 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
797 } else {
798 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
799 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
800 }
801 usleep(delay);
802 if (killer(pid, SIGKILL, kill_method) < 0) {
803 if (errno == ESRCH) {
804 return;
805 }
806 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
807 } else {
808 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
809 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
810 }
811}
#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 2286 of file res_musiconhold.c.

2287{
2288 int res;
2289
2291 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2292 if (!mohclasses) {
2294 }
2295
2296 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2297 ast_log(LOG_WARNING, "No music on hold classes configured, "
2298 "disabling music on hold.\n");
2299 } else {
2302 }
2303
2307 if (!res)
2309 if (!res)
2311
2313}
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:3750
@ 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:7719
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:672

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 2041 of file res_musiconhold.c.

2042{
2043 struct ast_config *cfg;
2044 struct ast_variable *var;
2045 struct mohclass *class;
2046 char *cat;
2047 int numclasses = 0;
2048 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2049
2050 cfg = ast_config_load("musiconhold.conf", config_flags);
2051
2052 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2053 if (ast_check_realtime("musiconhold") && reload) {
2054 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2056 }
2058 return 0;
2059 }
2060
2062 if (ast_check_realtime("musiconhold") && reload) {
2063 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2065 }
2066 return 0;
2067 }
2068
2069 if (reload) {
2070 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2071 }
2072
2075
2076 cat = ast_category_browse(cfg, NULL);
2077 for (; cat; cat = ast_category_browse(cfg, cat)) {
2078 /* Setup common options from [general] section */
2079 if (!strcasecmp(cat, "general")) {
2080 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2081 if (!strcasecmp(var->name, "cachertclasses")) {
2083 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2085 } else {
2086 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2087 }
2088 }
2089 continue;
2090 }
2091
2092 if (!(class = moh_class_malloc())) {
2093 break;
2094 }
2095
2096 moh_parse_options(ast_variable_browse(cfg, cat), class);
2097 /* For compatibility with the past, we overwrite any name=name
2098 * with the context [name]. */
2099 ast_copy_string(class->name, cat, sizeof(class->name));
2100
2101 if (ast_strlen_zero(class->dir)) {
2102 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2103 strcpy(class->dir, "nodir");
2104 } else {
2105 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2106 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2107 continue;
2108 }
2109 }
2110 if (ast_strlen_zero(class->mode)) {
2111 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2112 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2113 continue;
2114 }
2115 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2116 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2117 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2118 continue;
2119 }
2120
2121 /* Don't leak a class when it's already registered */
2122 if (!moh_register(class, reload, HANDLE_REF)) {
2123 numclasses++;
2124 }
2125 }
2126
2127 ast_config_destroy(cfg);
2128
2130 moh_classes_delete_marked, NULL, "Purge marked classes");
2131
2132 return numclasses;
2133}
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:2199
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:199
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:196

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 1663 of file res_musiconhold.c.

1664{
1665 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1666
1667 if (var) {
1668 const char *mode = ast_variable_find_in_list(var, "mode");
1669 if (ast_strings_equal(mode, "playlist")) {
1670 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1671 char *category = NULL;
1672 size_t entry_count = 0;
1673
1674 /* entries is NULL if there are no results */
1675 if (entries) {
1676 while ((category = ast_category_browse(entries, category))) {
1677 const char *entry = ast_variable_retrieve(entries, category, "entry");
1678
1679 if (entry) {
1680 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1681 if (dup) {
1682 entry_count++;
1684 }
1685 }
1686 }
1687 ast_config_destroy(entries);
1688 }
1689
1690 if (entry_count == 0) {
1691 /* Behave as though this class doesn't exist */
1693 var = NULL;
1694 }
1695 }
1696 }
1697
1698 if (!var) {
1700 "Music on Hold class '%s' not found in memory/database. "
1701 "Verify your configuration.\n",
1702 name);
1703 }
1704 return var;
1705}
#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:3842
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:3726
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 1538 of file res_musiconhold.c.

1539{
1540 struct moh_files_state *state;
1541
1542 ast_channel_lock(chan);
1544 if (state) {
1546 if (state->class) {
1547 /* This should never happen. We likely just leaked some resource. */
1548 state->class =
1549 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1550 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1551 }
1552 ao2_cleanup(state->origwfmt);
1553 ao2_cleanup(state->mohwfmt);
1554 ast_free(state);
1555 /* Only held a module reference if we had a music state */
1557 }
1558 ast_channel_unlock(chan);
1559}
#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 1710 of file res_musiconhold.c.

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

1948{
1950
1951 ast_channel_lock(chan);
1953 if (ast_channel_music_state(chan)) {
1954 if (ast_channel_stream(chan)) {
1957 }
1958 }
1959 ast_channel_unlock(chan);
1960}
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2861
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1119

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 1067 of file res_musiconhold.c.

1068{
1069 struct mohdata *res;
1070 struct mohclass *class = params;
1071 struct moh_files_state *state;
1072
1073 /* Initiating music_state for current channel. Channel should know name of moh class */
1075 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1078 } else {
1079 if (!state) {
1080 return NULL;
1081 }
1082 if (state->class) {
1083 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1084 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1085 }
1086 ao2_cleanup(state->origwfmt);
1087 ao2_cleanup(state->mohwfmt);
1088 memset(state, 0, sizeof(*state));
1089 }
1090
1091 if ((res = mohalloc(class))) {
1093 if (ast_set_write_format(chan, class->format)) {
1094 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1096 moh_release(NULL, res);
1097 res = NULL;
1098 } else {
1099 state->class = mohclass_ref(class, "Placing reference into state container");
1100 moh_post_start(chan, class->name);
1101 }
1102 }
1103 return res;
1104}
#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:5758
#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 2267 of file res_musiconhold.c.

2268{
2269 struct mohclass *class = obj, *class2 = arg;
2270
2271 return strcasecmp(class->name, class2->name) ? 0 :
2272 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2274}
@ 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 1962 of file res_musiconhold.c.

1963{
1964 struct mohclass *class = obj;
1965 struct mohdata *member;
1966 pthread_t tid = 0;
1967
1968 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1969
1970 ao2_lock(class);
1971 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1973 }
1974 ao2_cleanup(class->files);
1975 ao2_unlock(class);
1976
1977 /* Kill the thread first, so it cannot restart the child process while the
1978 * class is being destroyed */
1979 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1980 tid = class->thread;
1981 class->thread = AST_PTHREADT_NULL;
1982 pthread_cancel(tid);
1983 /* We'll collect the exit status later, after we ensure all the readers
1984 * are dead. */
1985 }
1986
1987 if (class->pid > 1) {
1988 char buff[8192];
1989 int bytes, tbytes = 0, stime = 0;
1990
1991 ast_debug(1, "killing %d!\n", class->pid);
1992
1993 stime = time(NULL) + 2;
1994 killpid(class->pid, class->kill_delay, class->kill_method);
1995
1996 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1997 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1998 tbytes = tbytes + bytes;
1999 }
2000
2001 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
2002 class->pid, tbytes);
2003
2004 class->pid = 0;
2005 close(class->srcfd);
2006 class->srcfd = -1;
2007 }
2008
2009 if (class->timer) {
2010 ast_timer_close(class->timer);
2011 class->timer = NULL;
2012 }
2013
2014 ao2_cleanup(class->format);
2015
2016 /* Finally, collect the exit status of the monitor thread */
2017 if (tid > 0) {
2018 pthread_join(tid, NULL);
2019 }
2020
2021}
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:1698

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 2260 of file res_musiconhold.c.

2261{
2262 const struct mohclass *class = obj;
2263
2264 return ast_str_case_hash(class->name);
2265}
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 2325 of file res_musiconhold.c.

2326{
2327 struct mohclass *class = obj;
2328
2329 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2330}
#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 2023 of file res_musiconhold.c.

2024{
2025 struct mohclass *class = obj;
2026
2027 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
2028 class->delete = 1;
2029 }
2030
2031 return 0;
2032}
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 2034 of file res_musiconhold.c.

2035{
2036 struct mohclass *class = obj;
2037
2038 return class->delete ? CMP_MATCH : 0;
2039}

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 1402 of file res_musiconhold.c.

1403{
1404 if (!old || !new) {
1405 return -1;
1406 }
1407
1408 if (strcmp(old->dir, new->dir)) {
1409 return -1;
1410 } else if (strcmp(old->mode, new->mode)) {
1411 return -1;
1412 } else if (strcmp(old->args, new->args)) {
1413 return -1;
1414 } else if (old->flags != new->flags) {
1415 return -1;
1416 }
1417
1418 return 0;
1419}

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 591 of file res_musiconhold.c.

592{
593 char *digit = arg;
594 struct mohclass *class = obj;
595
596 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
597}

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 1148 of file res_musiconhold.c.

1149{
1150 struct ast_vector_string *files = ao2_alloc_options(
1151 sizeof(struct ast_vector_string),
1154 if (files) {
1155 AST_VECTOR_INIT(files, initial_capacity);
1156 }
1157 return files;
1158}
@ 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 1141 of file res_musiconhold.c.

1142{
1143 struct ast_vector_string *files = obj;
1144 AST_VECTOR_RESET(files, ast_free);
1145 AST_VECTOR_FREE(files);
1146}
#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 1322 of file res_musiconhold.c.

1323{
1324 const char **s1 = (const char **) a;
1325 const char **s2 = (const char **) b;
1326 return strcasecmp(*s1, *s2);
1327}
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 543 of file res_musiconhold.c.

544{
545 struct moh_files_state *state;
546 struct mohclass *class = params;
547 size_t file_count;
548
550 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
553 } else {
554 if (!state) {
555 return NULL;
556 }
557 if (state->class) {
558 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
559 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
560 }
561 }
562
563 ao2_lock(class);
564 file_count = AST_VECTOR_SIZE(class->files);
565 ao2_unlock(class);
566
567 /* Resume MOH from where we left off last time or start from scratch? */
568 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
569 /* Start MOH from scratch. */
570 ao2_cleanup(state->origwfmt);
571 ao2_cleanup(state->mohwfmt);
572 memset(state, 0, sizeof(*state));
573 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
574 state->pos = ast_random() % file_count;
575 }
576 }
577
578 state->class = mohclass_ref(class, "Reffing music class for channel");
579 /* it's possible state is not a new allocation, don't leak old refs */
580 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
582 /* For comparison on restart of MOH (see above) */
583 ast_copy_string(state->name, class->name, sizeof(state->name));
584 state->save_total = file_count;
585
586 moh_post_start(chan, class->name);
587
588 return state;
589}
#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:2312

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 486 of file res_musiconhold.c.

487{
488 struct moh_files_state *state;
489 struct ast_frame *f = NULL;
490 int res = 0, sample_queue = 0;
491
492 ast_channel_lock(chan);
494 state->sample_queue += samples;
495 /* save the sample queue value for un-locked access */
496 sample_queue = state->sample_queue;
497 ast_channel_unlock(chan);
498
499 while (sample_queue > 0) {
500 ast_channel_lock(chan);
501 f = moh_files_readframe(chan);
502 if (!f) {
503 ast_channel_unlock(chan);
504 return -1;
505 }
506
507 /* Only track our offset within the current file if we are not in the
508 * the middle of an announcement */
509 if (!state->announcement) {
510 state->samples += f->samples;
511 }
512
513 state->sample_queue -= f->samples;
515 ao2_replace(state->mohwfmt, f->subclass.format);
516 }
517
518 /* We need to be sure that we unlock
519 * the channel prior to calling
520 * ast_write, but after our references to state
521 * as it refers to chan->music_state. Update
522 * sample_queue for our loop
523 * Otherwise, the recursive locking that occurs
524 * can cause deadlocks when using indirect
525 * channels, like local channels
526 */
527 sample_queue = state->sample_queue;
528 ast_channel_unlock(chan);
529
530 res = ast_write(chan, f);
531 ast_frfree(f);
532 if (res < 0) {
533 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
534 return -1;
535 }
536 }
537 return res;
538}
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:5099
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 332 of file res_musiconhold.c.

333{
335 struct ast_vector_string *files;
336 int tries;
337 size_t file_count;
338
339 /* Discontinue a stream if it is running already */
340 if (ast_channel_stream(chan)) {
343 }
344
345 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
346 state->announcement = 1;
347 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
348 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
349 return 0;
350 }
351 } else {
352 state->announcement = 0;
353 }
354
355 ao2_lock(state->class);
356 files = ao2_bump(state->class->files);
357 ao2_unlock(state->class);
358
359 file_count = AST_VECTOR_SIZE(files);
360 if (!file_count) {
361 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
362 ao2_ref(files, -1);
363 return -1;
364 }
365
366 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
367 /* First time so lets play the file. */
368 state->save_pos = -1;
369 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
370 /* If a specific file has been saved confirm it still exists and that it is still valid */
371 state->pos = state->save_pos;
372 state->save_pos = -1;
373 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
374 /* Get a random file and ensure we can open it */
375 for (tries = 0; tries < 20; tries++) {
376 state->pos = ast_random() % file_count;
377 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
378 break;
379 }
380 }
381 state->save_pos = -1;
382 state->samples = 0;
383 } else {
384 /* This is easy, just increment our position and make sure we don't exceed the total file count */
385 state->pos++;
386 if (ast_test_flag(state->class, MOH_LOOPLAST)) {
387 state->pos = MIN(file_count - 1, state->pos);
388 } else {
389 state->pos %= file_count;
390 }
391 state->save_pos = -1;
392 state->samples = 0;
393 }
394
395 for (tries = 0; tries < file_count; ++tries) {
396 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
397 break;
398 }
399
400 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
401 state->pos++;
402 state->pos %= file_count;
403 }
404
405 if (tries == file_count) {
406 ao2_ref(files, -1);
407 return -1;
408 }
409
410 /* Record the pointer to the filename for position resuming later */
411 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
412
413 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
414
415 if (state->samples) {
416 size_t loc;
417 /* seek *SHOULD* be good since it's from a known location */
418 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
419 /* if the seek failed then recover because if there is not a valid read,
420 * moh_files_generate will return -1 and MOH will stop */
422 if (state->samples > loc && loc) {
423 /* seek one sample from the end for one guaranteed valid read */
424 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
425 }
426 }
427
428 ao2_ref(files, -1);
429 return 0;
430}
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:1093
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:850
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1083
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1137
#define MOH_LOOPLAST
#define MOH_SORTMODE
#define MIN(a, b)
Definition: utils.h:231

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 435 of file res_musiconhold.c.

436{
437 struct ast_frame *f;
438
440 if (!f) {
441 /* Either there was no file stream setup or we reached EOF. */
442 if (!moh_files_next(chan)) {
443 /*
444 * Either we resetup the previously saved file stream position
445 * or we started a new file stream.
446 */
448 if (!f) {
449 /*
450 * We can get here if we were very unlucky because the
451 * resetup file stream was saved at EOF when MOH was
452 * previously stopped.
453 */
454 if (!moh_files_next(chan)) {
456 }
457 }
458 }
459 }
460
461 return f;
462}
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:944
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 297 of file res_musiconhold.c.

298{
299 struct moh_files_state *state;
300
301 if (!chan || !ast_channel_music_state(chan)) {
302 return;
303 }
304
306
307 if (ast_channel_stream(chan)) {
310 }
311
312 moh_post_stop(chan);
313
314 ao2_ref(state->mohwfmt, -1);
315 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
316 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
317 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
318 ast_format_get_name(state->origwfmt));
319 }
320 ao2_cleanup(state->origwfmt);
321 state->origwfmt = NULL;
322
323 state->save_pos = state->pos;
324 state->announcement = 0;
325
326 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
327}
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 467 of file res_musiconhold.c.

468{
470
471 /* In order to prevent a recursive call to this function as a result
472 * of setting the moh write format back on the channel. Clear
473 * the moh write format before setting the write format on the channel.*/
474 if (state->origwfmt) {
475 struct ast_format *tmp;
476
478 ao2_replace(state->origwfmt, NULL);
479 if (state->mohwfmt) {
480 ast_set_write_format(chan, state->mohwfmt);
481 }
482 state->origwfmt = tmp;
483 }
484}
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 1106 of file res_musiconhold.c.

1107{
1108 struct mohdata *moh = data;
1109 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1110 int res;
1111
1112 len = ast_format_determine_length(moh->parent->format, samples);
1113
1114 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1115 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1116 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1117 }
1118 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1119 if (res <= 0)
1120 return 0;
1121
1122 moh->f.datalen = res;
1123 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1124 moh->f.samples = ast_codec_samples_count(&moh->f);
1125
1126 if (ast_write(chan, &moh->f) < 0) {
1127 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1128 return -1;
1129 }
1130
1131 return 0;
1132}
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 605 of file res_musiconhold.c.

606{
607 struct mohclass *class;
608 const char *classname = NULL;
609
610 if ((class = get_mohbydigit(digit))) {
611 classname = ast_strdupa(class->name);
612 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
613 ast_channel_musicclass_set(chan, classname);
614 ast_moh_stop(chan);
615 ast_moh_start(chan, classname, NULL);
616 }
617}
#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:7735
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7745
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 1160 of file res_musiconhold.c.

1161{
1162 struct ast_vector_string *playlist_entries = NULL;
1163
1164 for (; var; var = var->next) {
1165 if (!strcasecmp(var->name, "name")) {
1166 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1167 } else if (!strcasecmp(var->name, "mode")) {
1168 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1169 } else if (!strcasecmp(var->name, "entry")) {
1170 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1171 char *dup;
1172
1173 if (!playlist_entries) {
1174 playlist_entries = moh_file_vector_alloc(16);
1175 if (!playlist_entries) {
1176 continue;
1177 }
1178 }
1179
1180 dup = ast_strdup(var->value);
1181 if (!dup) {
1182 continue;
1183 }
1184
1185 if (ast_begins_with(dup, "/")) {
1186 char *last_pos_dot = strrchr(dup, '.');
1187 char *last_pos_slash = strrchr(dup, '/');
1188 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1189 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1190 dup);
1191 }
1192 }
1193
1194 AST_VECTOR_APPEND(playlist_entries, dup);
1195 } else {
1196 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1197 }
1198 } else if (!strcasecmp(var->name, "directory")) {
1199 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1200 } else if (!strcasecmp(var->name, "application")) {
1201 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1202 } else if (!strcasecmp(var->name, "announcement")) {
1205 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1206 mohclass->digit = *var->value;
1207 } else if (!strcasecmp(var->name, "sort")) {
1208 if (!strcasecmp(var->value, "random")) {
1210 } else if (!strcasecmp(var->value, "alpha")) {
1212 } else if (!strcasecmp(var->value, "randstart")) {
1214 }
1215 } else if (!strcasecmp(var->name, "loop_last")) {
1216 if (ast_true(var->value)) {
1218 } else {
1220 }
1221 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1224 if (!mohclass->format) {
1225 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1227 }
1228 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1229 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1230 mohclass->kill_delay *= 1000;
1231 } else {
1232 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1233 mohclass->kill_delay = 100000;
1234 }
1235 } else if (!strcasecmp(var->name, "kill_method")) {
1236 if (!strcasecmp(var->value, "process")) {
1238 } else if (!strcasecmp(var->value, "process_group")) {
1240 } else {
1241 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1243 }
1244 } else if (!strcasecmp(var->name, "answeredonly")) {
1245 mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1246 }
1247 }
1248
1249 if (playlist_entries) {
1250 /* If we aren't in playlist mode, drop any list we may have already built */
1251 if (strcasecmp(mohclass->mode, "playlist")) {
1252 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1253 mohclass->mode);
1254 ao2_ref(playlist_entries, -1);
1255 return;
1256 }
1257
1258 AST_VECTOR_COMPACT(playlist_entries);
1259
1260 /* We don't need to lock here because we are the thread that
1261 * created this mohclass and we haven't published it yet */
1262 ao2_ref(mohclass->files, -1);
1263 mohclass->files = playlist_entries;
1264 }
1265}
#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 252 of file res_musiconhold.c.

253{
254 struct stasis_message *message;
255 struct ast_json *json_object;
256
257 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
258 moh_class_name, ast_channel_name(chan));
259
260 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
261 if (!json_object) {
262 return;
263 }
264
266 ast_channel_moh_start_type(), json_object);
267 if (message) {
268 /* A channel snapshot must have been in the cache. */
269 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
270
272 }
274 ast_json_unref(json_object);
275}
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:745

References ao2_cleanup, ast_assert, 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_verb, 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 277 of file res_musiconhold.c.

278{
279 struct stasis_message *message;
280
281 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
282
285 if (message) {
286 /* A channel snapshot must have been in the cache. */
287 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
288
290 }
292}
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_channel_blob_create_from_cache(), ast_channel_moh_stop_type(), ast_channel_name(), ast_channel_topic(), ast_channel_uniqueid(), 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 1027 of file res_musiconhold.c.

1028{
1029 struct mohdata *moh = data;
1030 struct mohclass *class = moh->parent;
1031 struct ast_format *oldwfmt;
1032
1033 ao2_lock(class);
1034 AST_LIST_REMOVE(&moh->parent->members, moh, list);
1035 ao2_unlock(class);
1036
1037 close(moh->pipe[0]);
1038 close(moh->pipe[1]);
1039
1040 oldwfmt = moh->origwfmt;
1041
1042 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1043
1044 ast_free(moh);
1045
1046 if (chan) {
1047 struct moh_files_state *state;
1048
1050 if (state && state->class) {
1051 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1052 }
1053 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1054 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1055 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1056 }
1057
1058 moh_post_stop(chan);
1059 }
1060
1061 ao2_cleanup(oldwfmt);
1062}
#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 1386 of file res_musiconhold.c.

1386 {
1387 struct ao2_iterator i;
1388 struct mohclass *c;
1389
1391
1392 while ((c = ao2_iterator_next(&i))) {
1393 if (!strcasecmp(c->mode, "files")) {
1395 }
1396 ao2_ref(c, -1);
1397 }
1398
1400}
#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 1329 of file res_musiconhold.c.

1329 {
1330
1331 char dir_path[PATH_MAX - sizeof(class->dir)];
1332 struct ast_vector_string *files;
1333
1334 if (class->dir[0] != '/') {
1335 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1336 } else {
1337 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1338 }
1339
1340 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1341
1342 /* 16 seems like a reasonable default */
1343 files = moh_file_vector_alloc(16);
1344 if (!files) {
1345 return -1;
1346 }
1347
1348 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1349 ao2_ref(files, -1);
1350 return -1;
1351 }
1352
1353 if (ast_test_flag(class, MOH_SORTALPHA)) {
1355 }
1356
1357 AST_VECTOR_COMPACT(files);
1358
1359 ao2_lock(class);
1360 ao2_ref(class->files, -1);
1361 class->files = files;
1362 ao2_unlock(class);
1363
1364 return AST_VECTOR_SIZE(files);
1365}
#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 998 of file res_musiconhold.c.

999{
1000 struct mohdata *moh;
1001
1002 if (!(moh = ast_calloc(1, sizeof(*moh))))
1003 return NULL;
1004
1005 if (ast_pipe_nonblock(moh->pipe)) {
1006 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
1007 ast_free(moh);
1008 return NULL;
1009 }
1010
1012 moh->f.subclass.format = cl->format;
1014
1015 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
1016
1017 ao2_lock(cl);
1018 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
1019 ao2_unlock(cl);
1020
1021 return moh;
1022}
@ 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:1096

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 813 of file res_musiconhold.c.

814{
815#define MOH_MS_INTERVAL 100
816
817 struct mohclass *class = data;
818 struct mohdata *moh;
819 short sbuf[8192];
820 int res = 0, res2;
821 int len;
822 struct timeval deadline, tv_tmp;
823
824 deadline.tv_sec = 0;
825 deadline.tv_usec = 0;
826 for(;/* ever */;) {
827 pthread_testcancel();
828 /* Spawn mp3 player if it's not there */
829 if (class->srcfd < 0) {
830 if ((class->srcfd = spawn_mp3(class)) < 0) {
831 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
832 /* Try again later */
833 sleep(500);
834 continue;
835 }
836 }
837 if (class->timer) {
838 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
839
840#ifdef SOLARIS
841 thr_yield();
842#endif
843 /* Pause some amount of time */
844 if (ast_poll(&pfd, 1, -1) > 0) {
845 if (ast_timer_ack(class->timer, 1) < 0) {
846 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
847 return NULL;
848 }
849 /* 25 samples per second => 40ms framerate => 320 samples */
850 res = 320; /* 320/40 = 8 samples/ms */
851 } else {
852 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
853 res = 0;
854 }
855 pthread_testcancel();
856 } else {
857 long delta;
858 /* Reliable sleep */
859 tv_tmp = ast_tvnow();
860 if (ast_tvzero(deadline))
861 deadline = tv_tmp;
862 delta = ast_tvdiff_ms(tv_tmp, deadline);
863 if (delta < MOH_MS_INTERVAL) { /* too early */
864 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
865 usleep(1000 * (MOH_MS_INTERVAL - delta));
866 pthread_testcancel();
867 } else {
868 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
869 deadline = tv_tmp;
870 }
871 /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
872 res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
873 }
874 /* For non-8000Hz formats, we need to alter the resolution */
875 res = res * ast_format_get_sample_rate(class->format) / 8000;
876
877 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
878 continue;
879 /* Read mp3 audio */
880 len = ast_format_determine_length(class->format, res);
881
882 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
883 if (!res2) {
884 close(class->srcfd);
885 class->srcfd = -1;
886 pthread_testcancel();
887 if (class->pid > 1) {
888 killpid(class->pid, class->kill_delay, class->kill_method);
889 class->pid = 0;
890 }
891 } else {
892 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
893 }
894 continue;
895 }
896
897 pthread_testcancel();
898
899 ao2_lock(class);
900 AST_LIST_TRAVERSE(&class->members, moh, list) {
901 /* Write data */
902 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
903 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
904 }
905 }
906 ao2_unlock(class);
907 }
908 return NULL;
909}
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 1267 of file res_musiconhold.c.

1268{
1269 struct ast_vector_string *files = obj;
1270 char *full_path;
1271 char *extension;
1272
1273 /* Skip files that starts with a dot */
1274 if (*filename == '.') {
1275 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1276 directory, filename);
1277 return 0;
1278 }
1279
1280 /* We can't do anything with files that don't have an extension,
1281 * so check that first and punt if we can't find something */
1282 extension = strrchr(filename, '.');
1283 if (!extension) {
1284 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1285 directory, filename);
1286 return 0;
1287 }
1288
1289 /* The extension needs at least two characters (after the .) to be useful */
1290 if (strlen(extension) < 3) {
1291 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1292 "character extension\n", directory, filename);
1293 return 0;
1294 }
1295
1296 /* Build the full path (excluding the extension) */
1297 if (ast_asprintf(&full_path, "%s/%.*s",
1298 directory,
1299 (int) (extension - filename), filename) < 0) {
1300 /* If we don't have enough memory to build this path, there is no
1301 * point in continuing */
1302 return 1;
1303 }
1304
1305 /* If the file is present in multiple formats, ensure we only put it
1306 * into the list once. Pretty sure this is O(n^2). */
1307 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1308 ast_free(full_path);
1309 return 0;
1310 }
1311
1312 if (AST_VECTOR_APPEND(files, full_path)) {
1313 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1314 * we stop iterating */
1315 ast_free(full_path);
1316 return 1;
1317 }
1318
1319 return 0;
1320}
#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 911 of file res_musiconhold.c.

912{
913 char *parse;
914 char *class;
915 int timeout = -1;
916 int res;
918 AST_APP_ARG(class);
919 AST_APP_ARG(duration);
920 );
921
922 parse = ast_strdupa(data);
923
925
926 if (!ast_strlen_zero(args.duration)) {
927 if (sscanf(args.duration, "%30d", &timeout) == 1) {
928 timeout *= 1000;
929 } else {
930 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
931 }
932 }
933
934 class = S_OR(args.class, NULL);
935 if (ast_moh_start(chan, class, NULL)) {
936 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
937 return 0;
938 }
939
940 if (timeout > 0)
941 res = ast_safe_sleep(chan, timeout);
942 else {
943 while (!(res = ast_safe_sleep(chan, 10000)));
944 }
945
946 ast_moh_stop(chan);
947
948 return res;
949}
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1541
#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 627 of file res_musiconhold.c.

628{
629 int fds[2];
630 int files = 0;
631 char fns[MAX_MP3S][80];
632 char *argv[MAX_MP3S + 50];
633 char xargs[256];
634 char *argptr;
635 int argc = 0;
636 DIR *dir = NULL;
637 struct dirent *de;
638
639
640 if (!strcasecmp(class->dir, "nodir")) {
641 files = 1;
642 } else {
643 dir = opendir(class->dir);
644 if (!dir && strncasecmp(class->dir, "http://", 7)) {
645 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
646 return -1;
647 }
648 }
649
650 if (!ast_test_flag(class, MOH_CUSTOM)) {
651 argv[argc++] = "mpg123";
652 argv[argc++] = "-q";
653 argv[argc++] = "-s";
654 argv[argc++] = "--mono";
655 argv[argc++] = "-r";
656 argv[argc++] = "8000";
657
658 if (!ast_test_flag(class, MOH_SINGLE)) {
659 argv[argc++] = "-b";
660 argv[argc++] = "2048";
661 }
662
663 argv[argc++] = "-f";
664
665 if (ast_test_flag(class, MOH_QUIET))
666 argv[argc++] = "4096";
667 else
668 argv[argc++] = "8192";
669
670 /* Look for extra arguments and add them to the list */
671 ast_copy_string(xargs, class->args, sizeof(xargs));
672 argptr = xargs;
673 while (!ast_strlen_zero(argptr)) {
674 argv[argc++] = argptr;
675 strsep(&argptr, ",");
676 }
677 } else {
678 /* Format arguments for argv vector */
679 ast_copy_string(xargs, class->args, sizeof(xargs));
680 argptr = xargs;
681 while (!ast_strlen_zero(argptr)) {
682 argv[argc++] = argptr;
683 strsep(&argptr, " ");
684 }
685 }
686
687 if (!strncasecmp(class->dir, "http://", 7)) {
688 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
689 argv[argc++] = fns[files];
690 files++;
691 } else if (dir) {
692 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
693 if ((strlen(de->d_name) > 3) &&
694 ((ast_test_flag(class, MOH_CUSTOM) &&
695 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
696 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
697 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
698 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
699 argv[argc++] = fns[files];
700 files++;
701 }
702 }
703 }
704 argv[argc] = NULL;
705 if (dir) {
706 closedir(dir);
707 }
708 if (pipe(fds)) {
709 ast_log(LOG_WARNING, "Pipe failed\n");
710 return -1;
711 }
712 if (!files) {
713 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
714 close(fds[0]);
715 close(fds[1]);
716 return -1;
717 }
718 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
719 sleep(respawn_time - (time(NULL) - class->start));
720 }
721
722 time(&class->start);
723 class->pid = ast_safe_fork(0);
724 if (class->pid < 0) {
725 close(fds[0]);
726 close(fds[1]);
727 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
728 return -1;
729 }
730 if (!class->pid) {
733
734 close(fds[0]);
735 /* Stdout goes to pipe */
736 dup2(fds[1], STDOUT_FILENO);
737
738 /* Close everything else */
739 ast_close_fds_above_n(STDERR_FILENO);
740
741 /* Child */
742 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
743 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
744 _exit(1);
745 }
746 setpgid(0, getpid());
747 if (ast_test_flag(class, MOH_CUSTOM)) {
748 execv(argv[0], argv);
749 } else {
750 /* Default install is /usr/local/bin */
751 execv(LOCAL_MPG_123, argv);
752 /* Many places have it in /usr/bin */
753 execv(MPG_123, argv);
754 /* Check PATH as a last-ditch effort */
755 execvp("mpg123", argv);
756 }
757 /* Can't use logger, since log FDs are closed */
758 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
759 close(fds[1]);
760 _exit(1);
761 } else {
762 /* Parent */
763 close(fds[1]);
764 }
765 return fds[0];
766}
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:113
#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 951 of file res_musiconhold.c.

952{
953 char *parse;
954 char *class;
956 AST_APP_ARG(class);
957 );
958
959 parse = ast_strdupa(data);
960
962
963 class = S_OR(args.class, NULL);
964 if (ast_moh_start(chan, class, NULL))
965 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
966
967 return 0;
968}

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 970 of file res_musiconhold.c.

971{
972 ast_moh_stop(chan);
973
974 return 0;
975}

References ast_moh_stop().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2332 of file res_musiconhold.c.

2333{
2334 int res = 0;
2335 struct mohclass *class = NULL;
2336
2337 /* XXX This check shouldn't be required if module ref counting was being used
2338 * properly ... */
2339 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2340 class = mohclass_unref(class, "unref of class from module unload callback");
2341 res = -1;
2342 }
2343
2344 if (res < 0) {
2345 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2346 return res;
2347 }
2348
2350
2357
2358 return res;
2359}
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:7728
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 2367 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2367 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2253 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 171 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 619 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 1134 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 132 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ respawn_time

int respawn_time = 20
static

Definition at line 136 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 133 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 134 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().