Asterisk - The Open Source Telephony Project GIT-master-66c01d8
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 962 of file res_musiconhold.c.

◆ HANDLE_REF

#define HANDLE_REF   1

Definition at line 77 of file res_musiconhold.c.

◆ INITIAL_NUM_FILES

#define INITIAL_NUM_FILES   8

Definition at line 76 of file res_musiconhold.c.

◆ LOCAL_MPG_123

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

Definition at line 221 of file res_musiconhold.c.

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 223 of file res_musiconhold.c.

◆ MOH_ANNOUNCEMENT

#define MOH_ANNOUNCEMENT   (1 << 6)

Do we play announcement files between songs on this channel?

Definition at line 162 of file res_musiconhold.c.

◆ MOH_CACHERTCLASSES

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 161 of file res_musiconhold.c.

◆ moh_class_malloc

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

Definition at line 1619 of file res_musiconhold.c.

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

Definition at line 155 of file res_musiconhold.c.

◆ MOH_LOOPLAST

#define MOH_LOOPLAST   (1 << 8)

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

Definition at line 165 of file res_musiconhold.c.

◆ MOH_MS_INTERVAL

#define MOH_MS_INTERVAL   100

◆ MOH_NOTDELETED

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 168 of file res_musiconhold.c.

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 163 of file res_musiconhold.c.

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 153 of file res_musiconhold.c.

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 156 of file res_musiconhold.c.

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 158 of file res_musiconhold.c.

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

Definition at line 169 of file res_musiconhold.c.

◆ moh_register

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

Definition at line 1446 of file res_musiconhold.c.

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 154 of file res_musiconhold.c.

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 157 of file res_musiconhold.c.

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 159 of file res_musiconhold.c.

◆ moh_unregister

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

Definition at line 1514 of file res_musiconhold.c.

◆ mohclass_ref

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

Definition at line 228 of file res_musiconhold.c.

◆ mohclass_unref

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

Definition at line 231 of file res_musiconhold.c.

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 222 of file res_musiconhold.c.

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 173 of file res_musiconhold.c.

173 {
176};
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2339 of file res_musiconhold.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

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

965{
966 struct mohclass *moh = NULL;
967 struct mohclass tmp_class = {
968 .flags = 0,
969 };
970
971 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
972
973 moh = __ao2_find(mohclasses, &tmp_class, flags,
974 "get_mohbyname", file, lineno, funcname);
975
976 if (!moh && warn) {
977 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
978 }
979
980 return moh;
981}
#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 1621 of file res_musiconhold.c.

1622{
1623 struct mohclass *class;
1624
1626 "Allocating new moh class", file, line, funcname);
1627 if (class) {
1628 class->format = ao2_bump(ast_format_slin);
1629 class->srcfd = -1;
1630 class->kill_delay = 100000;
1631
1632 /* We create an empty one by default */
1633 class->files = moh_file_vector_alloc(0);
1634 if (!class->files) {
1635 ao2_ref(class, -1);
1636 return NULL;
1637 }
1638 }
1639
1640 return class;
1641}
@ 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 1447 of file res_musiconhold.c.

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

1516{
1517 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1518 return 0;
1519}
#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 2339 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2107 of file res_musiconhold.c.

2108{
2109 ast_verb(2, "Destroying musiconhold processes\n");
2110 if (mohclasses) {
2112 ao2_ref(mohclasses, -1);
2113 mohclasses = NULL;
2114 }
2115}
@ 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 326 of file res_musiconhold.c.

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

1547{
1548 int which=0;
1549 struct mohclass *cur;
1550 char *c = NULL;
1551 int wordlen = strlen(word);
1552 struct ao2_iterator i;
1553
1554 if (pos != 3) {
1555 return NULL;
1556 }
1557
1559 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1560 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1561 c = ast_strdup(cur->name);
1562 mohclass_unref(cur, "drop ref in iterator loop break");
1563 break;
1564 }
1565 mohclass_unref(cur, "drop ref in iterator loop");
1566 }
1568
1569 return c;
1570}
#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 585 of file res_musiconhold.c.

586{
587 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
588}
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 2117 of file res_musiconhold.c.

2118{
2119 switch (cmd) {
2120 case CLI_INIT:
2121 e->command = "moh reload";
2122 e->usage =
2123 "Usage: moh reload\n"
2124 " Reloads the MusicOnHold module.\n"
2125 " Alias for 'module reload res_musiconhold.so'\n";
2126 return NULL;
2127 case CLI_GENERATE:
2128 return NULL;
2129 }
2130
2131 if (a->argc != e->args)
2132 return CLI_SHOWUSAGE;
2133
2134 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2135 ast_module_reload("res_musiconhold");
2136
2137 return CLI_SUCCESS;
2138}
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1730
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
static struct test_val a

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

◆ handle_cli_moh_show_classes()

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

Definition at line 2183 of file res_musiconhold.c.

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

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

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

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

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

1354{
1355 int res;
1356
1357 res = moh_scan_files(class);
1358
1359 if (res < 0) {
1360 return -1;
1361 }
1362
1363 if (!res) {
1364 ast_verb(3, "Files not found in %s for moh class:%s\n",
1365 class->dir, class->name);
1366 return -1;
1367 }
1368
1369 return 0;
1370}
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 753 of file res_musiconhold.c.

754{
755 switch (kill_method) {
757 return killpg(pid, signum);
759 return kill(pid, signum);
760 }
761
762 return -1;
763}

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

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

2259{
2260 int res;
2261
2263 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2264 if (!mohclasses) {
2266 }
2267
2268 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2269 ast_log(LOG_WARNING, "No music on hold classes configured, "
2270 "disabling music on hold.\n");
2271 } else {
2274 }
2275
2279 if (!res)
2281 if (!res)
2283
2285}
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
#define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag)
Definition: astobj2.h:1306
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3750
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
void ast_install_music_functions(int(*start_ptr)(struct ast_channel *, const char *, const char *), void(*stop_ptr)(struct ast_channel *), void(*cleanup_ptr)(struct ast_channel *))
Definition: channel.c:7771
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 2013 of file res_musiconhold.c.

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

1644{
1645 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1646
1647 if (var) {
1648 const char *mode = ast_variable_find_in_list(var, "mode");
1649 if (ast_strings_equal(mode, "playlist")) {
1650 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1651 char *category = NULL;
1652 size_t entry_count = 0;
1653
1654 /* entries is NULL if there are no results */
1655 if (entries) {
1656 while ((category = ast_category_browse(entries, category))) {
1657 const char *entry = ast_variable_retrieve(entries, category, "entry");
1658
1659 if (entry) {
1660 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1661 if (dup) {
1662 entry_count++;
1664 }
1665 }
1666 }
1667 ast_config_destroy(entries);
1668 }
1669
1670 if (entry_count == 0) {
1671 /* Behave as though this class doesn't exist */
1673 var = NULL;
1674 }
1675 }
1676 }
1677
1678 if (!var) {
1680 "Music on Hold class '%s' not found in memory/database. "
1681 "Verify your configuration.\n",
1682 name);
1683 }
1684 return var;
1685}
#define SENTINEL
Definition: compiler.h:87
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3842
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:1013
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:869
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3726
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238

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

Referenced by local_ast_moh_start().

◆ local_ast_moh_cleanup()

static void local_ast_moh_cleanup ( struct ast_channel chan)
static

Definition at line 1521 of file res_musiconhold.c.

1522{
1524
1525 if (state) {
1527 if (state->class) {
1528 /* This should never happen. We likely just leaked some resource. */
1529 state->class =
1530 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1531 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1532 }
1533 ao2_cleanup(state->origwfmt);
1534 ao2_cleanup(state->mohwfmt);
1535 ast_free(state);
1536 /* Only held a module reference if we had a music state */
1538 }
1539}
#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:483
struct ast_module * self
Definition: module.h:356

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

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

1047{
1048 struct mohdata *res;
1049 struct mohclass *class = params;
1050 struct moh_files_state *state;
1051
1052 /* Initiating music_state for current channel. Channel should know name of moh class */
1054 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1057 } else {
1058 if (!state) {
1059 return NULL;
1060 }
1061 if (state->class) {
1062 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1063 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1064 }
1065 ao2_cleanup(state->origwfmt);
1066 ao2_cleanup(state->mohwfmt);
1067 memset(state, 0, sizeof(*state));
1068 }
1069
1070 if ((res = mohalloc(class))) {
1072 if (ast_set_write_format(chan, class->format)) {
1073 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1075 moh_release(NULL, res);
1076 res = NULL;
1077 } else {
1078 state->class = mohclass_ref(class, "Placing reference into state container");
1079 moh_post_start(chan, class->name);
1080 }
1081 }
1082 return res;
1083}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
enum cc_state state
Definition: ccss.c:399
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:5820
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:457
static void moh_release(struct ast_channel *chan, void *data)
static struct mohdata * mohalloc(struct mohclass *cl)
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
struct mohclass * class
struct ast_format * format
struct ast_format * origwfmt

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

◆ moh_class_cmp()

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

Definition at line 2239 of file res_musiconhold.c.

2240{
2241 struct mohclass *class = obj, *class2 = arg;
2242
2243 return strcasecmp(class->name, class2->name) ? 0 :
2244 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2246}
@ 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 1934 of file res_musiconhold.c.

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

2233{
2234 const struct mohclass *class = obj;
2235
2236 return ast_str_case_hash(class->name);
2237}
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 2297 of file res_musiconhold.c.

2298{
2299 struct mohclass *class = obj;
2300
2301 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2302}
#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 1995 of file res_musiconhold.c.

1996{
1997 struct mohclass *class = obj;
1998
1999 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
2000 class->delete = 1;
2001 }
2002
2003 return 0;
2004}
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 2006 of file res_musiconhold.c.

2007{
2008 struct mohclass *class = obj;
2009
2010 return class->delete ? CMP_MATCH : 0;
2011}

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

1389{
1390 if (!old || !new) {
1391 return -1;
1392 }
1393
1394 if (strcmp(old->dir, new->dir)) {
1395 return -1;
1396 } else if (strcmp(old->mode, new->mode)) {
1397 return -1;
1398 } else if (strcmp(old->args, new->args)) {
1399 return -1;
1400 } else if (old->flags != new->flags) {
1401 return -1;
1402 }
1403
1404 return 0;
1405}

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

577{
578 char *digit = arg;
579 struct mohclass *class = obj;
580
581 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
582}

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

1128{
1129 struct ast_vector_string *files = ao2_alloc_options(
1130 sizeof(struct ast_vector_string),
1133 if (files) {
1134 AST_VECTOR_INIT(files, initial_capacity);
1135 }
1136 return files;
1137}
@ 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 1120 of file res_musiconhold.c.

1121{
1122 struct ast_vector_string *files = obj;
1123 AST_VECTOR_RESET(files, ast_free);
1124 AST_VECTOR_FREE(files);
1125}
#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 1308 of file res_musiconhold.c.

1309{
1310 const char **s1 = (const char **) a;
1311 const char **s2 = (const char **) b;
1312 return strcasecmp(*s1, *s2);
1313}
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 528 of file res_musiconhold.c.

529{
530 struct moh_files_state *state;
531 struct mohclass *class = params;
532 size_t file_count;
533
535 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
538 } else {
539 if (!state) {
540 return NULL;
541 }
542 if (state->class) {
543 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
544 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
545 }
546 }
547
548 ao2_lock(class);
549 file_count = AST_VECTOR_SIZE(class->files);
550 ao2_unlock(class);
551
552 /* Resume MOH from where we left off last time or start from scratch? */
553 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
554 /* Start MOH from scratch. */
555 ao2_cleanup(state->origwfmt);
556 ao2_cleanup(state->mohwfmt);
557 memset(state, 0, sizeof(*state));
558 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
559 state->pos = ast_random() % file_count;
560 }
561 }
562
563 state->class = mohclass_ref(class, "Reffing music class for channel");
564 /* it's possible state is not a new allocation, don't leak old refs */
565 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
567 /* For comparison on restart of MOH (see above) */
568 ast_copy_string(state->name, class->name, sizeof(state->name));
569 state->save_total = file_count;
570
571 moh_post_start(chan, class->name);
572
573 return state;
574}
#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 474 of file res_musiconhold.c.

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

427{
428 struct ast_frame *f;
429
431 if (!f) {
432 /* Either there was no file stream setup or we reached EOF. */
433 if (!ast_moh_files_next(chan)) {
434 /*
435 * Either we resetup the previously saved file stream position
436 * or we started a new file stream.
437 */
439 if (!f) {
440 /*
441 * We can get here if we were very unlucky because the
442 * resetup file stream was saved at EOF when MOH was
443 * previously stopped.
444 */
445 if (!ast_moh_files_next(chan)) {
447 }
448 }
449 }
450 }
451
452 return f;
453}
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:944
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 294 of file res_musiconhold.c.

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

456{
458
459 /* In order to prevent a recursive call to this function as a result
460 * of setting the moh write format back on the channel. Clear
461 * the moh write format before setting the write format on the channel.*/
462 if (state->origwfmt) {
463 struct ast_format *tmp;
464
466 ao2_replace(state->origwfmt, NULL);
467 if (state->mohwfmt) {
468 ast_set_write_format(chan, state->mohwfmt);
469 }
470 state->origwfmt = tmp;
471 }
472}
Definition of a media format.
Definition: format.c:43

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

◆ moh_generate()

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

Definition at line 1085 of file res_musiconhold.c.

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

591{
592 struct mohclass *class;
593 const char *classname = NULL;
594
595 if ((class = get_mohbydigit(digit))) {
596 classname = ast_strdupa(class->name);
597 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
598 ast_channel_musicclass_set(chan, classname);
599 ast_moh_stop(chan);
600 ast_moh_start(chan, classname, NULL);
601 }
602}
#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:7787
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7797
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 1139 of file res_musiconhold.c.

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

253{
254 struct stasis_message *message;
255 struct ast_json *json_object;
256
257 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
258 moh_class_name, ast_channel_name(chan));
259
260 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
261 if (!json_object) {
262 return;
263 }
264
266 ast_channel_moh_start_type(), json_object);
267 if (message) {
268 /* A channel snapshot must have been in the cache. */
269 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
270
272 }
274 ast_json_unref(json_object);
275}
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1538
Blob of data associated with a channel.
Abstract JSON element (object, array, string, int, ...).
#define ast_assert(a)
Definition: utils.h: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 277 of file res_musiconhold.c.

278{
279 struct stasis_message *message;
280
281 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
282
285 if (message) {
286 /* A channel snapshot must have been in the cache. */
287 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
288
290 }
292}
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.

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

Referenced by moh_files_release(), and moh_release().

◆ moh_release()

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

Definition at line 1009 of file res_musiconhold.c.

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

1372 {
1373 struct ao2_iterator i;
1374 struct mohclass *c;
1375
1377
1378 while ((c = ao2_iterator_next(&i))) {
1379 if (!strcasecmp(c->mode, "files")) {
1381 }
1382 ao2_ref(c, -1);
1383 }
1384
1386}
#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 1315 of file res_musiconhold.c.

1315 {
1316
1317 char dir_path[PATH_MAX - sizeof(class->dir)];
1318 struct ast_vector_string *files;
1319
1320 if (class->dir[0] != '/') {
1321 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1322 } else {
1323 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1324 }
1325
1326 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1327
1328 /* 16 seems like a reasonable default */
1329 files = moh_file_vector_alloc(16);
1330 if (!files) {
1331 return -1;
1332 }
1333
1334 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1335 ao2_ref(files, -1);
1336 return -1;
1337 }
1338
1339 if (ast_test_flag(class, MOH_SORTALPHA)) {
1341 }
1342
1343 AST_VECTOR_COMPACT(files);
1344
1345 ao2_lock(class);
1346 ao2_ref(class->files, -1);
1347 class->files = files;
1348 ao2_unlock(class);
1349
1350 return AST_VECTOR_SIZE(files);
1351}
#define PATH_MAX
Definition: asterisk.h:40
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition: file.h:203
const char * ast_config_AST_DATA_DIR
Definition: options.c: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 983 of file res_musiconhold.c.

984{
985 struct mohdata *moh;
986
987 if (!(moh = ast_calloc(1, sizeof(*moh))))
988 return NULL;
989
990 if (ast_pipe_nonblock(moh->pipe)) {
991 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
992 ast_free(moh);
993 return NULL;
994 }
995
997 moh->f.subclass.format = cl->format;
999
1000 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
1001
1002 ao2_lock(cl);
1003 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
1004 ao2_unlock(cl);
1005
1006 return moh;
1007}
@ 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 798 of file res_musiconhold.c.

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

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

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

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

937{
938 char *parse;
939 char *class;
941 AST_APP_ARG(class);
942 );
943
944 parse = ast_strdupa(data);
945
947
948 class = S_OR(args.class, NULL);
949 if (ast_moh_start(chan, class, NULL))
950 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
951
952 return 0;
953}

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

956{
957 ast_moh_stop(chan);
958
959 return 0;
960}

References ast_moh_stop().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2304 of file res_musiconhold.c.

2305{
2306 int res = 0;
2307 struct mohclass *class = NULL;
2308
2309 /* XXX This check shouldn't be required if module ref counting was being used
2310 * properly ... */
2311 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2312 class = mohclass_unref(class, "unref of class from module unload callback");
2313 res = -1;
2314 }
2315
2316 if (res < 0) {
2317 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2318 return res;
2319 }
2320
2322
2329
2330 return res;
2331}
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1067
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:7780
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 2339 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2339 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2225 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ global_flags

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

global MOH_ flags

Definition at line 171 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_file_stream

struct ast_generator moh_file_stream
static

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

Referenced by local_ast_moh_start().

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 132 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ respawn_time

int respawn_time = 20
static

Definition at line 136 of file res_musiconhold.c.

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

◆ start_moh

const char start_moh[] = "StartMusicOnHold"
static

Definition at line 133 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 134 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().