Asterisk - The Open Source Telephony Project GIT-master-2de1a68
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 int ast_moh_files_next (struct ast_channel *chan)
 
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 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 953 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 212 of file res_musiconhold.c.

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 214 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 153 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 152 of file res_musiconhold.c.

◆ moh_class_malloc

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

Definition at line 1610 of file res_musiconhold.c.

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

Definition at line 146 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 156 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 159 of file res_musiconhold.c.

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 154 of file res_musiconhold.c.

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 144 of file res_musiconhold.c.

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 147 of file res_musiconhold.c.

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 149 of file res_musiconhold.c.

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

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

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 145 of file res_musiconhold.c.

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 148 of file res_musiconhold.c.

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 150 of file res_musiconhold.c.

◆ moh_unregister

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

Definition at line 1505 of file res_musiconhold.c.

◆ mohclass_ref

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

Definition at line 219 of file res_musiconhold.c.

◆ mohclass_unref

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

Definition at line 222 of file res_musiconhold.c.

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 213 of file res_musiconhold.c.

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 164 of file res_musiconhold.c.

164 {
167};
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2330 of file res_musiconhold.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

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

956{
957 struct mohclass *moh = NULL;
958 struct mohclass tmp_class = {
959 .flags = 0,
960 };
961
962 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
963
964 moh = __ao2_find(mohclasses, &tmp_class, flags,
965 "get_mohbyname", file, lineno, funcname);
966
967 if (!moh && warn) {
968 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
969 }
970
971 return moh;
972}
#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 1612 of file res_musiconhold.c.

1613{
1614 struct mohclass *class;
1615
1617 "Allocating new moh class", file, line, funcname);
1618 if (class) {
1619 class->format = ao2_bump(ast_format_slin);
1620 class->srcfd = -1;
1621 class->kill_delay = 100000;
1622
1623 /* We create an empty one by default */
1624 class->files = moh_file_vector_alloc(0);
1625 if (!class->files) {
1626 ao2_ref(class, -1);
1627 return NULL;
1628 }
1629 }
1630
1631 return class;
1632}
@ 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 1438 of file res_musiconhold.c.

1439{
1440 struct mohclass *mohclass = NULL;
1441
1442 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1443
1444 if (mohclass && !moh_diff(mohclass, moh)) {
1445 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1446 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1447 if (unref) {
1448 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1449 }
1450 return -1;
1451 } else if (mohclass) {
1452 /* Found a class, but it's different from the one being registered */
1453 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1454 }
1455
1456 time(&moh->start);
1457 moh->start -= respawn_time;
1458
1459 if (!strcasecmp(moh->mode, "files")) {
1460 if (init_files_class(moh)) {
1461 if (unref) {
1462 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1463 }
1464 return -1;
1465 }
1466 } else if (!strcasecmp(moh->mode, "playlist")) {
1467 size_t file_count;
1468
1469 ao2_lock(moh);
1470 file_count = AST_VECTOR_SIZE(moh->files);
1471 ao2_unlock(moh);
1472
1473 if (!file_count) {
1474 if (unref) {
1475 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1476 }
1477 return -1;
1478 }
1479 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1480 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1481 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1482 if (init_app_class(moh)) {
1483 if (unref) {
1484 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1485 }
1486 return -1;
1487 }
1488 } else {
1489 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1490 if (unref) {
1491 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1492 }
1493 return -1;
1494 }
1495
1496 ao2_t_link(mohclasses, moh, "Adding class to container");
1497
1498 if (unref) {
1499 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1500 }
1501
1502 return 0;
1503}
#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:609

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

1507{
1508 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1509 return 0;
1510}
#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 2330 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2098 of file res_musiconhold.c.

2099{
2100 ast_verb(2, "Destroying musiconhold processes\n");
2101 if (mohclasses) {
2103 ao2_ref(mohclasses, -1);
2104 mohclasses = NULL;
2105 }
2106}
@ 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().

◆ ast_moh_files_next()

static int ast_moh_files_next ( struct ast_channel chan)
static

Definition at line 317 of file res_musiconhold.c.

318{
320 struct ast_vector_string *files;
321 int tries;
322 size_t file_count;
323
324 /* Discontinue a stream if it is running already */
325 if (ast_channel_stream(chan)) {
328 }
329
330 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
331 state->announcement = 1;
332 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
333 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
334 return 0;
335 }
336 } else {
337 state->announcement = 0;
338 }
339
340 ao2_lock(state->class);
341 files = ao2_bump(state->class->files);
342 ao2_unlock(state->class);
343
344 file_count = AST_VECTOR_SIZE(files);
345 if (!file_count) {
346 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
347 ao2_ref(files, -1);
348 return -1;
349 }
350
351 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
352 /* First time so lets play the file. */
353 state->save_pos = -1;
354 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
355 /* If a specific file has been saved confirm it still exists and that it is still valid */
356 state->pos = state->save_pos;
357 state->save_pos = -1;
358 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
359 /* Get a random file and ensure we can open it */
360 for (tries = 0; tries < 20; tries++) {
361 state->pos = ast_random() % file_count;
362 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
363 break;
364 }
365 }
366 state->save_pos = -1;
367 state->samples = 0;
368 } else {
369 /* This is easy, just increment our position and make sure we don't exceed the total file count */
370 state->pos++;
371 if (ast_test_flag(state->class, MOH_LOOPLAST)) {
372 state->pos = MIN(file_count - 1, state->pos);
373 } else {
374 state->pos %= file_count;
375 }
376 state->save_pos = -1;
377 state->samples = 0;
378 }
379
380 for (tries = 0; tries < file_count; ++tries) {
381 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
382 break;
383 }
384
385 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
386 state->pos++;
387 state->pos %= file_count;
388 }
389
390 if (tries == file_count) {
391 ao2_ref(files, -1);
392 return -1;
393 }
394
395 /* Record the pointer to the filename for position resuming later */
396 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
397
398 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
399
400 if (state->samples) {
401 size_t loc;
402 /* seek *SHOULD* be good since it's from a known location */
403 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
404 /* if the seek failed then recover because if there is not a valid read,
405 * moh_files_generate will return -1 and MOH will stop */
407 if (state->samples > loc && loc) {
408 /* seek one sample from the end for one guaranteed valid read */
409 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
410 }
411 }
412
413 ao2_ref(files, -1);
414 return 0;
415}
const char * ast_channel_name(const struct ast_channel *chan)
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
void * ast_channel_music_state(const struct ast_channel *chan)
const char * ast_channel_language(const struct ast_channel *chan)
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1085
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:795
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1075
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
#define ast_debug(level,...)
Log a DEBUG message.
int errno
#define MOH_LOOPLAST
#define MOH_SORTMODE
#define MOH_RANDOMIZE
#define MOH_ANNOUNCEMENT
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
String vector definitions.
Definition: vector.h:55
char name[TZ_STRLEN_MAX+1]
Definition: localtime.c:160
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define MIN(a, b)
Definition: utils.h:231
long int ast_random(void)
Definition: utils.c:2312
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680

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

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

1538{
1539 int which=0;
1540 struct mohclass *cur;
1541 char *c = NULL;
1542 int wordlen = strlen(word);
1543 struct ao2_iterator i;
1544
1545 if (pos != 3) {
1546 return NULL;
1547 }
1548
1550 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1551 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1552 c = ast_strdup(cur->name);
1553 mohclass_unref(cur, "drop ref in iterator loop break");
1554 break;
1555 }
1556 mohclass_unref(cur, "drop ref in iterator loop");
1557 }
1559
1560 return c;
1561}
#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 576 of file res_musiconhold.c.

577{
578 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
579}
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 2108 of file res_musiconhold.c.

2109{
2110 switch (cmd) {
2111 case CLI_INIT:
2112 e->command = "moh reload";
2113 e->usage =
2114 "Usage: moh reload\n"
2115 " Reloads the MusicOnHold module.\n"
2116 " Alias for 'module reload res_musiconhold.so'\n";
2117 return NULL;
2118 case CLI_GENERATE:
2119 return NULL;
2120 }
2121
2122 if (a->argc != e->args)
2123 return CLI_SHOWUSAGE;
2124
2125 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2126 ast_module_reload("res_musiconhold");
2127
2128 return CLI_SUCCESS;
2129}
#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:1567
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 2174 of file res_musiconhold.c.

2175{
2176 struct mohclass *class;
2177 struct ao2_iterator i;
2178
2179 switch (cmd) {
2180 case CLI_INIT:
2181 e->command = "moh show classes";
2182 e->usage =
2183 "Usage: moh show classes\n"
2184 " Lists all MusicOnHold classes.\n";
2185 return NULL;
2186 case CLI_GENERATE:
2187 return NULL;
2188 }
2189
2190 if (a->argc != e->args)
2191 return CLI_SHOWUSAGE;
2192
2194 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2195 ast_cli(a->fd, "Class: %s\n", class->name);
2196 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2197 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2198 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2199 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2200 }
2201 if (ast_test_flag(class, MOH_CUSTOM)) {
2202 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2203 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2204 ast_cli(a->fd, "\tKill Method: %s\n",
2205 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2206 }
2207 if (strcasecmp(class->mode, "files")) {
2208 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2209 }
2210 }
2212
2213 return CLI_SUCCESS;
2214}
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 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

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

2132{
2133 struct mohclass *class;
2134 struct ao2_iterator i;
2135
2136 switch (cmd) {
2137 case CLI_INIT:
2138 e->command = "moh show files";
2139 e->usage =
2140 "Usage: moh show files\n"
2141 " Lists all loaded file-based MusicOnHold classes and their\n"
2142 " files.\n";
2143 return NULL;
2144 case CLI_GENERATE:
2145 return NULL;
2146 }
2147
2148 if (a->argc != e->args)
2149 return CLI_SHOWUSAGE;
2150
2152 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2153 struct ast_vector_string *files;
2154
2155 ao2_lock(class);
2156 files = ao2_bump(class->files);
2157 ao2_unlock(class);
2158
2159 if (AST_VECTOR_SIZE(files)) {
2160 int x;
2161 ast_cli(a->fd, "Class: %s\n", class->name);
2162 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2163 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2164 }
2165 }
2166
2167 ao2_ref(files, -1);
2168 }
2170
2171 return CLI_SUCCESS;
2172}

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

1564{
1565 struct mohclass *cur;
1566 int len;
1567 int found = 0;
1568 struct ao2_iterator i;
1569
1570 switch (cmd) {
1571 case CLI_INIT:
1572 e->command = "moh unregister class";
1573 e->usage =
1574 "Usage: moh unregister class <class>\n"
1575 " Unregisters a realtime moh class.\n";
1576 return NULL;
1577 case CLI_GENERATE:
1578 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1579 }
1580
1581 if (a->argc != 4)
1582 return CLI_SHOWUSAGE;
1583
1584 len = strlen(a->argv[3]);
1585
1587 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1588 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1589 found = 1;
1590 break;
1591 }
1592 mohclass_unref(cur, "drop ref in iterator loop");
1593 }
1595
1596 if (found) {
1597 moh_unregister(cur);
1598 mohclass_unref(cur, "drop ref after unregister");
1599 } else {
1600 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1601 }
1602
1603 return CLI_SUCCESS;
1604}
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 1398 of file res_musiconhold.c.

1399{
1400 if (!strcasecmp(class->mode, "custom")) {
1401 ast_set_flag(class, MOH_CUSTOM);
1402 } else if (!strcasecmp(class->mode, "mp3nb")) {
1403 ast_set_flag(class, MOH_SINGLE);
1404 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1406 } else if (!strcasecmp(class->mode, "quietmp3")) {
1407 ast_set_flag(class, MOH_QUIET);
1408 }
1409
1410 class->srcfd = -1;
1411
1412 if (!(class->timer = ast_timer_open())) {
1413 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1414 return -1;
1415 }
1416 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1417 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1418 ast_timer_close(class->timer);
1419 class->timer = NULL;
1420 }
1421
1422 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1423 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1424 if (class->timer) {
1425 ast_timer_close(class->timer);
1426 class->timer = NULL;
1427 }
1428 return -1;
1429 }
1430
1431 return 0;
1432}
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:592
#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 1344 of file res_musiconhold.c.

1345{
1346 int res;
1347
1348 res = moh_scan_files(class);
1349
1350 if (res < 0) {
1351 return -1;
1352 }
1353
1354 if (!res) {
1355 ast_verb(3, "Files not found in %s for moh class:%s\n",
1356 class->dir, class->name);
1357 return -1;
1358 }
1359
1360 return 0;
1361}
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 744 of file res_musiconhold.c.

745{
746 switch (kill_method) {
748 return killpg(pid, signum);
750 return kill(pid, signum);
751 }
752
753 return -1;
754}

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

757{
758 if (killer(pid, SIGHUP, kill_method) < 0) {
759 if (errno == ESRCH) {
760 return;
761 }
762 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
763 } else {
764 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
765 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
766 }
767 usleep(delay);
768 if (killer(pid, SIGTERM, kill_method) < 0) {
769 if (errno == ESRCH) {
770 return;
771 }
772 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
773 } else {
774 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
775 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
776 }
777 usleep(delay);
778 if (killer(pid, SIGKILL, kill_method) < 0) {
779 if (errno == ESRCH) {
780 return;
781 }
782 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
783 } else {
784 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
785 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
786 }
787}
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 2249 of file res_musiconhold.c.

2250{
2251 int res;
2252
2254 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2255 if (!mohclasses) {
2257 }
2258
2259 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2260 ast_log(LOG_WARNING, "No music on hold classes configured, "
2261 "disabling music on hold.\n");
2262 } else {
2265 }
2266
2270 if (!res)
2272 if (!res)
2274
2276}
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:3530
@ 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:626
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:7749
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:666

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

2005{
2006 struct ast_config *cfg;
2007 struct ast_variable *var;
2008 struct mohclass *class;
2009 char *cat;
2010 int numclasses = 0;
2011 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2012
2013 cfg = ast_config_load("musiconhold.conf", config_flags);
2014
2015 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2016 if (ast_check_realtime("musiconhold") && reload) {
2017 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2019 }
2021 return 0;
2022 }
2023
2025 if (ast_check_realtime("musiconhold") && reload) {
2026 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2028 }
2029 return 0;
2030 }
2031
2032 if (reload) {
2033 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2034 }
2035
2038
2039 cat = ast_category_browse(cfg, NULL);
2040 for (; cat; cat = ast_category_browse(cfg, cat)) {
2041 /* Setup common options from [general] section */
2042 if (!strcasecmp(cat, "general")) {
2043 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2044 if (!strcasecmp(var->name, "cachertclasses")) {
2046 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2048 } else {
2049 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2050 }
2051 }
2052 continue;
2053 }
2054
2055 if (!(class = moh_class_malloc())) {
2056 break;
2057 }
2058
2059 moh_parse_options(ast_variable_browse(cfg, cat), class);
2060 /* For compatibility with the past, we overwrite any name=name
2061 * with the context [name]. */
2062 ast_copy_string(class->name, cat, sizeof(class->name));
2063
2064 if (ast_strlen_zero(class->dir)) {
2065 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2066 strcpy(class->dir, "nodir");
2067 } else {
2068 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2069 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2070 continue;
2071 }
2072 }
2073 if (ast_strlen_zero(class->mode)) {
2074 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2075 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2076 continue;
2077 }
2078 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2079 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2080 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2081 continue;
2082 }
2083
2084 /* Don't leak a class when it's already registered */
2085 if (!moh_register(class, reload, HANDLE_REF)) {
2086 numclasses++;
2087 }
2088 }
2089
2090 ast_config_destroy(cfg);
2091
2093 moh_classes_delete_marked, NULL, "Purge marked classes");
2094
2095 return numclasses;
2096}
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
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#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
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 1634 of file res_musiconhold.c.

1635{
1636 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1637
1638 if (var) {
1639 const char *mode = ast_variable_find_in_list(var, "mode");
1640 if (ast_strings_equal(mode, "playlist")) {
1641 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1642 char *category = NULL;
1643 size_t entry_count = 0;
1644
1645 /* entries is NULL if there are no results */
1646 if (entries) {
1647 while ((category = ast_category_browse(entries, category))) {
1648 const char *entry = ast_variable_retrieve(entries, category, "entry");
1649
1650 if (entry) {
1651 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1652 if (dup) {
1653 entry_count++;
1655 }
1656 }
1657 }
1658 ast_config_destroy(entries);
1659 }
1660
1661 if (entry_count == 0) {
1662 /* Behave as though this class doesn't exist */
1664 var = NULL;
1665 }
1666 }
1667 }
1668
1669 if (!var) {
1671 "Music on Hold class '%s' not found in memory/database. "
1672 "Verify your configuration.\n",
1673 name);
1674 }
1675 return var;
1676}
#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:3622
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:919
#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:783
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3506
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
Definition: search.h:40

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

1513{
1515
1516 if (state) {
1518 if (state->class) {
1519 /* This should never happen. We likely just leaked some resource. */
1520 state->class =
1521 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1522 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1523 }
1524 ao2_cleanup(state->origwfmt);
1525 ao2_cleanup(state->mohwfmt);
1526 ast_free(state);
1527 /* Only held a module reference if we had a music state */
1529 }
1530}
#define ast_free(a)
Definition: astmm.h:180
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
struct ast_module * self
Definition: module.h:342

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

Referenced by load_module(), and reload().

◆ local_ast_moh_start()

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

Definition at line 1678 of file res_musiconhold.c.

1679{
1680 struct mohclass *mohclass = NULL;
1682 struct ast_variable *var = NULL;
1683 int res = 0;
1684 int i;
1685 int realtime_possible = ast_check_realtime("musiconhold");
1686 int warn_if_not_in_memory = !realtime_possible;
1687 const char *classes[] = {NULL, NULL, interpclass, "default"};
1688
1690 classes[0] = ast_channel_musicclass(chan);
1691 classes[1] = mclass;
1692 } else {
1693 classes[0] = mclass;
1694 classes[1] = ast_channel_musicclass(chan);
1695 }
1696
1697 /* The following is the order of preference for which class to use:
1698 * 1) The channels explicitly set musicclass, which should *only* be
1699 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1700 * Unless preferchannelclass in musiconhold.conf is false
1701 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1702 * result of receiving a HOLD control frame, this should be the
1703 * payload that came with the frame.
1704 * 3) The channels explicitly set musicclass, which should *only* be
1705 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1706 * 4) The interpclass argument. This would be from the mohinterpret
1707 * option from channel drivers. This is the same as the old musicclass
1708 * option.
1709 * 5) The default class.
1710 */
1711
1712 for (i = 0; i < ARRAY_LEN(classes); ++i) {
1713 if (!ast_strlen_zero(classes[i])) {
1714 mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1715 if (!mohclass && realtime_possible) {
1716 var = load_realtime_musiconhold(classes[i]);
1717 }
1718 if (mohclass || var) {
1719 break;
1720 }
1721 }
1722 }
1723
1724 /* If no moh class found in memory, then check RT. Note that the logic used
1725 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1726 */
1727 if (var) {
1728 if ((mohclass = moh_class_malloc())) {
1729 mohclass->realtime = 1;
1730
1733
1735 if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1736 strcpy(mohclass->dir, "nodir");
1737 } else {
1738 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1739 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1740 return -1;
1741 }
1742 }
1744 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1745 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1746 return -1;
1747 }
1748 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1749 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1750 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1751 return -1;
1752 }
1753
1755 /* CACHERTCLASSES enabled, let's add this class to default tree */
1756 if (state && state->class) {
1757 /* Class already exist for this channel */
1758 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1759 }
1760 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1761 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1762 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1763 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1764 * invalid memory.
1765 */
1766 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1767 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1768 return -1;
1769 }
1770 } else {
1771 /* We don't register RT moh class, so let's init it manually */
1772
1773 time(&mohclass->start);
1775
1776 if (!strcasecmp(mohclass->mode, "files")) {
1777 /*
1778 * XXX moh_scan_files returns -1 if it is unable to open the
1779 * configured directory or there is a memory allocation
1780 * failure. Otherwise it returns the number of files for this music
1781 * class. This check is only checking if the number of files is zero
1782 * and it ignores the -1 case. To avoid a behavior change we keep this
1783 * as-is, but we should address what the 'correct' behavior should be.
1784 */
1785 if (!moh_scan_files(mohclass)) {
1786 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1787 return -1;
1788 }
1789 if (strchr(mohclass->args, 'r')) {
1790 static int deprecation_warning = 0;
1791 if (!deprecation_warning) {
1792 ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1793 deprecation_warning = 1;
1794 }
1796 }
1797 } else if (!strcasecmp(mohclass->mode, "playlist")) {
1798 size_t file_count;
1799
1801 file_count = AST_VECTOR_SIZE(mohclass->files);
1803
1804 if (!file_count) {
1805 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1806 return -1;
1807 }
1808 } 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")) {
1809
1810 if (!strcasecmp(mohclass->mode, "custom"))
1812 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1814 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1816 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1818
1819 mohclass->srcfd = -1;
1820 if (!(mohclass->timer = ast_timer_open())) {
1821 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1822 }
1824 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1826 mohclass->timer = NULL;
1827 }
1828
1829 /* Let's check if this channel already had a moh class before */
1830 if (state && state->class) {
1831 /* Class already exist for this channel */
1832 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1833 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1834 /* we found RT class with the same name, seems like we should continue playing existing one */
1835 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1836 mohclass = mohclass_ref(state->class, "using existing class from state");
1837 }
1838 } else {
1840 ast_log(LOG_WARNING, "Unable to create moh...\n");
1841 if (mohclass->timer) {
1843 mohclass->timer = NULL;
1844 }
1845 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1846 return -1;
1847 }
1848 }
1849 } else {
1850 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1851 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1852 return -1;
1853 }
1854 }
1855 } else {
1857 var = NULL;
1858 }
1859 }
1860
1861 if (!mohclass) {
1862 return -1;
1863 }
1864
1866 ast_verb(3, "The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1867 return -1;
1868 }
1869
1870 /* If we are using a cached realtime class with files, re-scan the files */
1871 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1872 /*
1873 * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1874 * or there is a memory allocation failure. Otherwise it returns the number of
1875 * files for this music class. This check is only checking if the number of files
1876 * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1877 * as-is, but we should address what the 'correct' behavior should be.
1878 */
1879 if (!moh_scan_files(mohclass)) {
1880 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1881 return -1;
1882 }
1883 }
1884
1885 if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1886 size_t file_count;
1887
1889 file_count = AST_VECTOR_SIZE(mohclass->files);
1891
1892 if (file_count) {
1894 } else {
1895 res = ast_activate_generator(chan, &mohgen, mohclass);
1896 }
1897 }
1898 if (!res) {
1899 ast_channel_lock(chan);
1900 ast_channel_latest_musicclass_set(chan, mohclass->name);
1902 ast_channel_unlock(chan);
1903 }
1904
1905 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1906
1907 return res;
1908}
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2951
const char * ast_channel_musicclass(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2922
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
@ AST_FLAG_MOH
Definition: channel.h:991
#define ast_channel_unlock(chan)
Definition: channel.h:2923
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]

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_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, mohclass_ref, mohclass_unref, mohgen, monmp3thread(), mohclass::name, state::name, NULL, mohclass::realtime, respawn_time, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, and var.

Referenced by load_module(), and reload().

◆ local_ast_moh_stop()

static void local_ast_moh_stop ( struct ast_channel chan)
static

◆ moh_alloc()

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

Definition at line 1037 of file res_musiconhold.c.

1038{
1039 struct mohdata *res;
1040 struct mohclass *class = params;
1041 struct moh_files_state *state;
1042
1043 /* Initiating music_state for current channel. Channel should know name of moh class */
1045 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1048 } else {
1049 if (!state) {
1050 return NULL;
1051 }
1052 if (state->class) {
1053 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1054 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1055 }
1056 ao2_cleanup(state->origwfmt);
1057 ao2_cleanup(state->mohwfmt);
1058 memset(state, 0, sizeof(*state));
1059 }
1060
1061 if ((res = mohalloc(class))) {
1063 if (ast_set_write_format(chan, class->format)) {
1064 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1066 moh_release(NULL, res);
1067 res = NULL;
1068 } else {
1069 state->class = mohclass_ref(class, "Placing reference into state container");
1070 moh_post_start(chan, class->name);
1071 }
1072 }
1073 return res;
1074}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
enum cc_state state
Definition: ccss.c:393
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:5802
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
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 2230 of file res_musiconhold.c.

2231{
2232 struct mohclass *class = obj, *class2 = arg;
2233
2234 return strcasecmp(class->name, class2->name) ? 0 :
2235 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2237}
@ 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 1925 of file res_musiconhold.c.

1926{
1927 struct mohclass *class = obj;
1928 struct mohdata *member;
1929 pthread_t tid = 0;
1930
1931 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1932
1933 ao2_lock(class);
1934 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1936 }
1937 ao2_cleanup(class->files);
1938 ao2_unlock(class);
1939
1940 /* Kill the thread first, so it cannot restart the child process while the
1941 * class is being destroyed */
1942 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1943 tid = class->thread;
1944 class->thread = AST_PTHREADT_NULL;
1945 pthread_cancel(tid);
1946 /* We'll collect the exit status later, after we ensure all the readers
1947 * are dead. */
1948 }
1949
1950 if (class->pid > 1) {
1951 char buff[8192];
1952 int bytes, tbytes = 0, stime = 0;
1953
1954 ast_debug(1, "killing %d!\n", class->pid);
1955
1956 stime = time(NULL) + 2;
1957 killpid(class->pid, class->kill_delay, class->kill_method);
1958
1959 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1960 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1961 tbytes = tbytes + bytes;
1962 }
1963
1964 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1965 class->pid, tbytes);
1966
1967 class->pid = 0;
1968 close(class->srcfd);
1969 class->srcfd = -1;
1970 }
1971
1972 if (class->timer) {
1973 ast_timer_close(class->timer);
1974 class->timer = NULL;
1975 }
1976
1977 ao2_cleanup(class->format);
1978
1979 /* Finally, collect the exit status of the monitor thread */
1980 if (tid > 0) {
1981 pthread_join(tid, NULL);
1982 }
1983
1984}
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:66
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
struct mohdata::@440 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 2223 of file res_musiconhold.c.

2224{
2225 const struct mohclass *class = obj;
2226
2227 return ast_str_case_hash(class->name);
2228}
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 2288 of file res_musiconhold.c.

2289{
2290 struct mohclass *class = obj;
2291
2292 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2293}
#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 1986 of file res_musiconhold.c.

1987{
1988 struct mohclass *class = obj;
1989
1990 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1991 class->delete = 1;
1992 }
1993
1994 return 0;
1995}
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 1997 of file res_musiconhold.c.

1998{
1999 struct mohclass *class = obj;
2000
2001 return class->delete ? CMP_MATCH : 0;
2002}

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

1380{
1381 if (!old || !new) {
1382 return -1;
1383 }
1384
1385 if (strcmp(old->dir, new->dir)) {
1386 return -1;
1387 } else if (strcmp(old->mode, new->mode)) {
1388 return -1;
1389 } else if (strcmp(old->args, new->args)) {
1390 return -1;
1391 } else if (old->flags != new->flags) {
1392 return -1;
1393 }
1394
1395 return 0;
1396}

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

568{
569 char *digit = arg;
570 struct mohclass *class = obj;
571
572 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
573}

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

1119{
1120 struct ast_vector_string *files = ao2_alloc_options(
1121 sizeof(struct ast_vector_string),
1124 if (files) {
1125 AST_VECTOR_INIT(files, initial_capacity);
1126 }
1127 return files;
1128}
@ 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:113

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

1112{
1113 struct ast_vector_string *files = obj;
1114 AST_VECTOR_RESET(files, ast_free);
1115 AST_VECTOR_FREE(files);
1116}
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:625
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174

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

1300{
1301 const char **s1 = (const char **) a;
1302 const char **s2 = (const char **) b;
1303 return strcasecmp(*s1, *s2);
1304}
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 519 of file res_musiconhold.c.

520{
521 struct moh_files_state *state;
522 struct mohclass *class = params;
523 size_t file_count;
524
526 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
529 } else {
530 if (!state) {
531 return NULL;
532 }
533 if (state->class) {
534 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
535 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
536 }
537 }
538
539 ao2_lock(class);
540 file_count = AST_VECTOR_SIZE(class->files);
541 ao2_unlock(class);
542
543 /* Resume MOH from where we left off last time or start from scratch? */
544 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
545 /* Start MOH from scratch. */
546 ao2_cleanup(state->origwfmt);
547 ao2_cleanup(state->mohwfmt);
548 memset(state, 0, sizeof(*state));
549 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
550 state->pos = ast_random() % file_count;
551 }
552 }
553
554 state->class = mohclass_ref(class, "Reffing music class for channel");
555 /* it's possible state is not a new allocation, don't leak old refs */
556 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
558 /* For comparison on restart of MOH (see above) */
559 ast_copy_string(state->name, class->name, sizeof(state->name));
560 state->save_total = file_count;
561
562 moh_post_start(chan, class->name);
563
564 return state;
565}
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition: astobj2.h:501

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, ast_channel::name, 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 465 of file res_musiconhold.c.

466{
467 struct moh_files_state *state;
468 struct ast_frame *f = NULL;
469 int res = 0, sample_queue = 0;
470
471 ast_channel_lock(chan);
473 state->sample_queue += samples;
474 /* save the sample queue value for un-locked access */
475 sample_queue = state->sample_queue;
476 ast_channel_unlock(chan);
477
478 while (sample_queue > 0) {
479 ast_channel_lock(chan);
480 f = moh_files_readframe(chan);
481 if (!f) {
482 ast_channel_unlock(chan);
483 return -1;
484 }
485
486 /* Only track our offset within the current file if we are not in the
487 * the middle of an announcement */
488 if (!state->announcement) {
489 state->samples += f->samples;
490 }
491
492 state->sample_queue -= f->samples;
494 ao2_replace(state->mohwfmt, f->subclass.format);
495 }
496
497 /* We need to be sure that we unlock
498 * the channel prior to calling
499 * ast_write, but after our references to state
500 * as it refers to chan->music_state. Update
501 * sample_queue for our loop
502 * Otherwise, the recursive locking that occurs
503 * can cause deadlocks when using indirect
504 * channels, like local channels
505 */
506 sample_queue = state->sample_queue;
507 ast_channel_unlock(chan);
508
509 res = ast_write(chan, f);
510 ast_frfree(f);
511 if (res < 0) {
512 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
513 return -1;
514 }
515 }
516 return res;
517}
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:5143
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_readframe()

static struct ast_frame * moh_files_readframe ( struct ast_channel chan)
static

Definition at line 417 of file res_musiconhold.c.

418{
419 struct ast_frame *f;
420
422 if (!f) {
423 /* Either there was no file stream setup or we reached EOF. */
424 if (!ast_moh_files_next(chan)) {
425 /*
426 * Either we resetup the previously saved file stream position
427 * or we started a new file stream.
428 */
430 if (!f) {
431 /*
432 * We can get here if we were very unlucky because the
433 * resetup file stream was saved at EOF when MOH was
434 * previously stopped.
435 */
436 if (!ast_moh_files_next(chan)) {
438 }
439 }
440 }
441 }
442
443 return f;
444}
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:936
static int ast_moh_files_next(struct ast_channel *chan)

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

Referenced by moh_files_generator().

◆ moh_files_release()

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

Definition at line 285 of file res_musiconhold.c.

286{
287 struct moh_files_state *state;
288
289 if (!chan || !ast_channel_music_state(chan)) {
290 return;
291 }
292
294
295 if (ast_channel_stream(chan)) {
298 }
299
300 moh_post_stop(chan);
301
302 ao2_ref(state->mohwfmt, -1);
303 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
304 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
305 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
306 ast_format_get_name(state->origwfmt));
307 }
308 ao2_cleanup(state->origwfmt);
309 state->origwfmt = NULL;
310
311 state->save_pos = state->pos;
312 state->announcement = 0;
313
314 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
315}
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 446 of file res_musiconhold.c.

447{
449
450 /* In order to prevent a recursive call to this function as a result
451 * of setting the moh write format back on the channel. Clear
452 * the moh write format before setting the write format on the channel.*/
453 if (state->origwfmt) {
454 struct ast_format *tmp;
455
457 ao2_replace(state->origwfmt, NULL);
458 if (state->mohwfmt) {
459 ast_set_write_format(chan, state->mohwfmt);
460 }
461 state->origwfmt = tmp;
462 }
463}
static int tmp()
Definition: bt_open.c:389
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(), NULL, and tmp().

◆ moh_generate()

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

Definition at line 1076 of file res_musiconhold.c.

1077{
1078 struct mohdata *moh = data;
1079 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1080 int res;
1081
1082 len = ast_format_determine_length(moh->parent->format, samples);
1083
1084 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1085 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1086 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1087 }
1088 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1089 if (res <= 0)
1090 return 0;
1091
1092 moh->f.datalen = res;
1093 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1094 moh->f.samples = ast_codec_samples_count(&moh->f);
1095
1096 if (ast_write(chan, &moh->f) < 0) {
1097 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1098 return -1;
1099 }
1100
1101 return 0;
1102}
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::@226 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 581 of file res_musiconhold.c.

582{
583 struct mohclass *class;
584 const char *classname = NULL;
585
586 if ((class = get_mohbydigit(digit))) {
587 classname = ast_strdupa(class->name);
588 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
589 ast_channel_musicclass_set(chan, classname);
590 ast_moh_stop(chan);
591 ast_moh_start(chan, classname, NULL);
592 }
593}
#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:7765
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7775
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 1130 of file res_musiconhold.c.

1131{
1132 struct ast_vector_string *playlist_entries = NULL;
1133
1134 for (; var; var = var->next) {
1135 if (!strcasecmp(var->name, "name")) {
1136 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1137 } else if (!strcasecmp(var->name, "mode")) {
1138 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1139 } else if (!strcasecmp(var->name, "entry")) {
1140 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1141 char *dup;
1142
1143 if (!playlist_entries) {
1144 playlist_entries = moh_file_vector_alloc(16);
1145 if (!playlist_entries) {
1146 continue;
1147 }
1148 }
1149
1150 dup = ast_strdup(var->value);
1151 if (!dup) {
1152 continue;
1153 }
1154
1155 if (ast_begins_with(dup, "/")) {
1156 char *last_pos_dot = strrchr(dup, '.');
1157 char *last_pos_slash = strrchr(dup, '/');
1158 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1159 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1160 dup);
1161 }
1162 }
1163
1164 AST_VECTOR_APPEND(playlist_entries, dup);
1165 } else {
1166 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1167 }
1168 } else if (!strcasecmp(var->name, "directory")) {
1169 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1170 } else if (!strcasecmp(var->name, "application")) {
1171 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1172 } else if (!strcasecmp(var->name, "announcement")) {
1175 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1176 mohclass->digit = *var->value;
1177 } else if (!strcasecmp(var->name, "random")) {
1178 static int deprecation_warning = 0;
1179 if (!deprecation_warning) {
1180 ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1181 deprecation_warning = 1;
1182 }
1184 } else if (!strcasecmp(var->name, "sort")) {
1185 if (!strcasecmp(var->value, "random")) {
1187 } else if (!strcasecmp(var->value, "alpha")) {
1189 } else if (!strcasecmp(var->value, "randstart")) {
1191 }
1192 } else if (!strcasecmp(var->name, "loop_last")) {
1193 if (ast_true(var->value)) {
1195 } else {
1197 }
1198 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1201 if (!mohclass->format) {
1202 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1204 }
1205 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1206 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1207 mohclass->kill_delay *= 1000;
1208 } else {
1209 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1210 mohclass->kill_delay = 100000;
1211 }
1212 } else if (!strcasecmp(var->name, "kill_method")) {
1213 if (!strcasecmp(var->value, "process")) {
1215 } else if (!strcasecmp(var->value, "process_group")) {
1217 } else {
1218 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1220 }
1221 } else if (!strcasecmp(var->name, "answeredonly")) {
1222 mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1223 }
1224 }
1225
1226 if (playlist_entries) {
1227 /* If we aren't in playlist mode, drop any list we may have already built */
1228 if (strcasecmp(mohclass->mode, "playlist")) {
1229 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1230 mohclass->mode);
1231 ao2_ref(playlist_entries, -1);
1232 return;
1233 }
1234
1235 AST_VECTOR_COMPACT(playlist_entries);
1236
1237 /* We don't need to lock here because we are the thread that
1238 * created this mohclass and we haven't published it yet */
1239 ao2_ref(mohclass->files, -1);
1240 mohclass->files = playlist_entries;
1241 }
1242}
#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:256
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:638

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_set2_flag, 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 243 of file res_musiconhold.c.

244{
245 struct stasis_message *message;
246 struct ast_json *json_object;
247
248 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
249 moh_class_name, ast_channel_name(chan));
250
251 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
252 if (!json_object) {
253 return;
254 }
255
257 ast_channel_moh_start_type(), json_object);
258 if (message) {
259 /* A channel snapshot must have been in the cache. */
260 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
261
263 }
265 ast_json_unref(json_object);
266}
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:1511
Blob of data associated with a channel.
Abstract JSON element (object, array, string, int, ...).
#define ast_assert(a)
Definition: utils.h:739

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

269{
270 struct stasis_message *message;
271
272 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
273
276 if (message) {
277 /* A channel snapshot must have been in the cache. */
278 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
279
281 }
283}
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 1000 of file res_musiconhold.c.

1001{
1002 struct mohdata *moh = data;
1003 struct mohclass *class = moh->parent;
1004 struct ast_format *oldwfmt;
1005
1006 ao2_lock(class);
1007 AST_LIST_REMOVE(&moh->parent->members, moh, list);
1008 ao2_unlock(class);
1009
1010 close(moh->pipe[0]);
1011 close(moh->pipe[1]);
1012
1013 oldwfmt = moh->origwfmt;
1014
1015 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1016
1017 ast_free(moh);
1018
1019 if (chan) {
1020 struct moh_files_state *state;
1021
1023 if (state && state->class) {
1024 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1025 }
1026 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1027 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1028 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1029 }
1030
1031 moh_post_stop(chan);
1032 }
1033
1034 ao2_cleanup(oldwfmt);
1035}
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
struct mohclass::@438 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 1363 of file res_musiconhold.c.

1363 {
1364 struct ao2_iterator i;
1365 struct mohclass *c;
1366
1368
1369 while ((c = ao2_iterator_next(&i))) {
1370 if (!strcasecmp(c->mode, "files")) {
1372 }
1373 ao2_ref(c, -1);
1374 }
1375
1377}
#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 1306 of file res_musiconhold.c.

1306 {
1307
1308 char dir_path[PATH_MAX - sizeof(class->dir)];
1309 struct ast_vector_string *files;
1310
1311 if (class->dir[0] != '/') {
1312 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1313 } else {
1314 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1315 }
1316
1317 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1318
1319 /* 16 seems like a reasonable default */
1320 files = moh_file_vector_alloc(16);
1321 if (!files) {
1322 return -1;
1323 }
1324
1325 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1326 ao2_ref(files, -1);
1327 return -1;
1328 }
1329
1330 if (ast_test_flag(class, MOH_SORTALPHA)) {
1332 }
1333
1334 AST_VECTOR_COMPACT(files);
1335
1336 ao2_lock(class);
1337 ao2_ref(class->files, -1);
1338 class->files = files;
1339 ao2_unlock(class);
1340
1341 return AST_VECTOR_SIZE(files);
1342}
#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:202
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
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:396

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

975{
976 struct mohdata *moh;
977
978 if (!(moh = ast_calloc(1, sizeof(*moh))))
979 return NULL;
980
981 if (ast_pipe_nonblock(moh->pipe)) {
982 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
983 ast_free(moh);
984 return NULL;
985 }
986
988 moh->f.subclass.format = cl->format;
990
991 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
992
993 ao2_lock(cl);
995 ao2_unlock(cl);
996
997 return moh;
998}
@ 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:1090

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

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

1245{
1246 struct ast_vector_string *files = obj;
1247 char *full_path;
1248 char *extension;
1249
1250 /* Skip files that starts with a dot */
1251 if (*filename == '.') {
1252 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1253 directory, filename);
1254 return 0;
1255 }
1256
1257 /* We can't do anything with files that don't have an extension,
1258 * so check that first and punt if we can't find something */
1259 extension = strrchr(filename, '.');
1260 if (!extension) {
1261 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1262 directory, filename);
1263 return 0;
1264 }
1265
1266 /* The extension needs at least two characters (after the .) to be useful */
1267 if (strlen(extension) < 3) {
1268 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1269 "character extension\n", directory, filename);
1270 return 0;
1271 }
1272
1273 /* Build the full path (excluding the extension) */
1274 if (ast_asprintf(&full_path, "%s/%.*s",
1275 directory,
1276 (int) (extension - filename), filename) < 0) {
1277 /* If we don't have enough memory to build this path, there is no
1278 * point in continuing */
1279 return 1;
1280 }
1281
1282 /* If the file is present in multiple formats, ensure we only put it
1283 * into the list once. Pretty sure this is O(n^2). */
1284 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1285 ast_free(full_path);
1286 return 0;
1287 }
1288
1289 if (AST_VECTOR_APPEND(files, full_path)) {
1290 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1291 * we stop iterating */
1292 ast_free(full_path);
1293 return 1;
1294 }
1295
1296 return 0;
1297}
#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:731

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

888{
889 char *parse;
890 char *class;
891 int timeout = -1;
892 int res;
894 AST_APP_ARG(class);
895 AST_APP_ARG(duration);
896 );
897
898 parse = ast_strdupa(data);
899
901
902 if (!ast_strlen_zero(args.duration)) {
903 if (sscanf(args.duration, "%30d", &timeout) == 1) {
904 timeout *= 1000;
905 } else {
906 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
907 }
908 }
909
910 class = S_OR(args.class, NULL);
911 if (ast_moh_start(chan, class, NULL)) {
912 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
913 return 0;
914 }
915
916 if (timeout > 0)
917 res = ast_safe_sleep(chan, timeout);
918 else {
919 while (!(res = ast_safe_sleep(chan, 10000)));
920 }
921
922 ast_moh_stop(chan);
923
924 return res;
925}
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
#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 603 of file res_musiconhold.c.

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

928{
929 char *parse;
930 char *class;
932 AST_APP_ARG(class);
933 );
934
935 parse = ast_strdupa(data);
936
938
939 class = S_OR(args.class, NULL);
940 if (ast_moh_start(chan, class, NULL))
941 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
942
943 return 0;
944}

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

947{
948 ast_moh_stop(chan);
949
950 return 0;
951}

References ast_moh_stop().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2295 of file res_musiconhold.c.

2296{
2297 int res = 0;
2298 struct mohclass *class = NULL;
2299
2300 /* XXX This check shouldn't be required if module ref counting was being used
2301 * properly ... */
2302 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2303 class = mohclass_unref(class, "unref of class from module unload callback");
2304 res = -1;
2305 }
2306
2307 if (res < 0) {
2308 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2309 return res;
2310 }
2311
2313
2320
2321 return res;
2322}
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1060
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:7758
static int moh_class_inuse(void *obj, void *arg, int flags)

References ao2_t_callback, ARRAY_LEN, ast_cli_unregister_multiple(), ast_log, ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), ast_unregister_atexit(), cli_moh, LOG_WARNING, moh_class_inuse(), mohclass_unref, mohclasses, NULL, play_moh, start_moh, and stop_moh.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "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 2330 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2330 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2216 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 162 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 595 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 1104 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 123 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ respawn_time

int respawn_time = 20
static

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

Referenced by load_module(), and unload_module().

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 125 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().