Asterisk - The Open Source Telephony Project  GIT-master-060ce10
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"

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_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_string * moh_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.

Referenced by local_ast_moh_start().

◆ get_mohbyname

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

Definition at line 934 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

◆ HANDLE_REF

#define HANDLE_REF   1

Definition at line 77 of file res_musiconhold.c.

Referenced by load_moh_classes().

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

Referenced by spawn_mp3().

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 210 of file res_musiconhold.c.

Referenced by spawn_mp3().

◆ MOH_ANNOUNCEMENT

#define MOH_ANNOUNCEMENT   (1 << 6)

Do we play announcement files between songs on this channel?

Definition at line 153 of file res_musiconhold.c.

Referenced by ast_moh_files_next(), handle_cli_moh_show_classes(), and moh_parse_options().

◆ MOH_CACHERTCLASSES

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 152 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_class_malloc

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

Definition at line 1581 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

◆ MOH_MS_INTERVAL

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

◆ MOH_NOTDELETED

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 157 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 154 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 144 of file res_musiconhold.c.

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

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 149 of file res_musiconhold.c.

Referenced by moh_parse_options().

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

Definition at line 158 of file res_musiconhold.c.

Referenced by load_moh_classes(), and moh_class_mark().

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

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 145 of file res_musiconhold.c.

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

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 148 of file res_musiconhold.c.

Referenced by moh_parse_options(), and moh_scan_files().

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 150 of file res_musiconhold.c.

Referenced by ast_moh_files_next().

◆ moh_unregister

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

Definition at line 1476 of file res_musiconhold.c.

Referenced by handle_cli_moh_unregister_class().

◆ mohclass_ref

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

Definition at line 215 of file res_musiconhold.c.

Referenced by local_ast_moh_start(), moh_alloc(), moh_files_alloc(), and mohalloc().

◆ mohclass_unref

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

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 209 of file res_musiconhold.c.

Referenced by spawn_mp3().

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 162 of file res_musiconhold.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2296 of file res_musiconhold.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

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

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

Referenced by _moh_register().

937 {
938  struct mohclass *moh = NULL;
939  struct mohclass tmp_class = {
940  .flags = 0,
941  };
942 
943  ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
944 
945  moh = __ao2_find(mohclasses, &tmp_class, flags,
946  "get_mohbyname", file, lineno, funcname);
947 
948  if (!moh && warn) {
949  ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
950  }
951 
952  return moh;
953 }
static struct ao2_container * mohclasses
char name[MAX_MUSICCLASS]
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)
#define LOG_WARNING
Definition: logger.h:274
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
unsigned int flags
static const char name[]
Definition: cdr_mysql.c:74
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401

◆ _moh_class_malloc()

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

Definition at line 1583 of file res_musiconhold.c.

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

1584 {
1585  struct mohclass *class;
1586 
1588  "Allocating new moh class", file, line, funcname);
1589  if (class) {
1590  class->format = ao2_bump(ast_format_slin);
1591  class->srcfd = -1;
1592  class->kill_delay = 100000;
1593 
1594  /* We create an empty one by default */
1595  class->files = moh_file_vector_alloc(0);
1596  if (!class->files) {
1597  ao2_ref(class, -1);
1598  return NULL;
1599  }
1600  }
1601 
1602  return class;
1603 }
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:765
#define NULL
Definition: resample.c:96
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static void moh_class_destructor(void *obj)
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)

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

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

1410 {
1411  struct mohclass *mohclass = NULL;
1412 
1413  mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1414 
1415  if (mohclass && !moh_diff(mohclass, moh)) {
1416  ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1417  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1418  if (unref) {
1419  moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1420  }
1421  return -1;
1422  } else if (mohclass) {
1423  /* Found a class, but it's different from the one being registered */
1424  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1425  }
1426 
1427  time(&moh->start);
1428  moh->start -= respawn_time;
1429 
1430  if (!strcasecmp(moh->mode, "files")) {
1431  if (init_files_class(moh)) {
1432  if (unref) {
1433  moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1434  }
1435  return -1;
1436  }
1437  } else if (!strcasecmp(moh->mode, "playlist")) {
1438  size_t file_count;
1439 
1440  ao2_lock(moh);
1441  file_count = AST_VECTOR_SIZE(moh->files);
1442  ao2_unlock(moh);
1443 
1444  if (!file_count) {
1445  if (unref) {
1446  moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1447  }
1448  return -1;
1449  }
1450  } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1451  !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1452  !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1453  if (init_app_class(moh)) {
1454  if (unref) {
1455  moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1456  }
1457  return -1;
1458  }
1459  } else {
1460  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1461  if (unref) {
1462  moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1463  }
1464  return -1;
1465  }
1466 
1467  ao2_t_link(mohclasses, moh, "Adding class to container");
1468 
1469  if (unref) {
1470  moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1471  }
1472 
1473  return 0;
1474 }
static struct ao2_container * mohclasses
char name[MAX_MUSICCLASS]
#define MOH_NOTDELETED
static int init_files_class(struct mohclass *class)
#define LOG_WARNING
Definition: logger.h:274
static int init_app_class(struct mohclass *class)
#define ao2_t_link(container, obj, tag)
Add an object to a container.
Definition: astobj2.h:1547
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
static struct mohclass * _get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
#define ao2_lock(a)
Definition: astobj2.h:718
struct ast_vector_string * files
static int moh_diff(struct mohclass *old, struct mohclass *new)
time_t start
static int respawn_time
#define mohclass_unref(class, string)
char mode[80]
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611

◆ _moh_unregister()

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

Definition at line 1477 of file res_musiconhold.c.

References ao2_t_unlink.

1478 {
1479  ao2_t_unlink(mohclasses, moh, "Removing class from container");
1480  return 0;
1481 }
static struct ao2_container * mohclasses
#define ao2_t_unlink(container, obj, tag)
Remove an object from a container.
Definition: astobj2.h:1596

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 2296 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2064 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

2065 {
2066  ast_verb(2, "Destroying musiconhold processes\n");
2067  if (mohclasses) {
2068  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
2069  ao2_ref(mohclasses, -1);
2070  mohclasses = NULL;
2071  }
2072 }
static struct ao2_container * mohclasses
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:455
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1714

◆ ast_moh_files_next()

static int ast_moh_files_next ( struct ast_channel chan)
static

Definition at line 313 of file res_musiconhold.c.

References moh_files_state::announcement, mohclass::announcement, 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, moh_files_state::class, errno, mohclass::files, LOG_WARNING, MOH_ANNOUNCEMENT, MOH_RANDOMIZE, MOH_SORTMODE, mohclass::name, NULL, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, and moh_files_state::save_pos_filename.

Referenced by moh_files_readframe().

314 {
316  struct ast_vector_string *files;
317  int tries;
318  size_t file_count;
319 
320  /* Discontinue a stream if it is running already */
321  if (ast_channel_stream(chan)) {
324  }
325 
326  if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
327  state->announcement = 1;
328  if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
329  ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
330  return 0;
331  }
332  } else {
333  state->announcement = 0;
334  }
335 
336  ao2_lock(state->class);
337  files = ao2_bump(state->class->files);
338  ao2_unlock(state->class);
339 
340  file_count = AST_VECTOR_SIZE(files);
341  if (!file_count) {
342  ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
343  ao2_ref(files, -1);
344  return -1;
345  }
346 
347  if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
348  /* First time so lets play the file. */
349  state->save_pos = -1;
350  } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
351  /* If a specific file has been saved confirm it still exists and that it is still valid */
352  state->pos = state->save_pos;
353  state->save_pos = -1;
354  } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
355  /* Get a random file and ensure we can open it */
356  for (tries = 0; tries < 20; tries++) {
357  state->pos = ast_random() % file_count;
358  if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
359  break;
360  }
361  }
362  state->save_pos = -1;
363  state->samples = 0;
364  } else {
365  /* This is easy, just increment our position and make sure we don't exceed the total file count */
366  state->pos++;
367  state->pos %= file_count;
368  state->save_pos = -1;
369  state->samples = 0;
370  }
371 
372  for (tries = 0; tries < file_count; ++tries) {
373  if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
374  break;
375  }
376 
377  ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
378  state->pos++;
379  state->pos %= file_count;
380  }
381 
382  if (tries == file_count) {
383  ao2_ref(files, -1);
384  return -1;
385  }
386 
387  /* Record the pointer to the filename for position resuming later */
388  ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
389 
390  ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
391 
392  if (state->samples) {
393  size_t loc;
394  /* seek *SHOULD* be good since it's from a known location */
395  ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
396  /* if the seek failed then recover because if there is not a valid read,
397  * moh_files_generate will return -1 and MOH will stop */
398  loc = ast_tellstream(ast_channel_stream(chan));
399  if (state->samples > loc && loc) {
400  /* seek one sample from the end for one guaranteed valid read */
401  ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
402  }
403  }
404 
405  ao2_ref(files, -1);
406  return 0;
407 }
char name[MAX_MUSICCLASS]
char announcement[256]
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
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:760
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1048
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
char save_pos_filename[PATH_MAX]
#define MOH_SORTMODE
#define ao2_ref(o, delta)
Definition: astobj2.h:464
long int ast_random(void)
Definition: main/utils.c:1935
#define ao2_lock(a)
Definition: astobj2.h:718
struct ast_vector_string * files
#define MOH_ANNOUNCEMENT
int errno
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1038
#define ast_strlen_zero(a)
Definition: muted.c:73
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const char * ast_channel_name(const struct ast_channel *chan)
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
void * ast_channel_music_state(const struct ast_channel *chan)
const char * ast_channel_language(const struct ast_channel *chan)
#define MOH_RANDOMIZE
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611

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

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

Referenced by handle_cli_moh_unregister_class().

1509 {
1510  int which=0;
1511  struct mohclass *cur;
1512  char *c = NULL;
1513  int wordlen = strlen(word);
1514  struct ao2_iterator i;
1515 
1516  if (pos != 3) {
1517  return NULL;
1518  }
1519 
1520  i = ao2_iterator_init(mohclasses, 0);
1521  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1522  if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1523  c = ast_strdup(cur->name);
1524  mohclass_unref(cur, "drop ref in iterator loop break");
1525  break;
1526  }
1527  mohclass_unref(cur, "drop ref in iterator loop");
1528  }
1530 
1531  return c;
1532 }
static struct ao2_container * mohclasses
char name[MAX_MUSICCLASS]
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static struct test_val c
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
unsigned int realtime
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1931
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define mohclass_unref(class, string)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
short word

◆ get_mohbydigit()

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

Definition at line 557 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

558 {
559  return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
560 }
static struct ao2_container * mohclasses
char digit
static int moh_digit_match(void *obj, void *arg, int flags)
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1714

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

References ast_cli_args::argc, 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.

2075 {
2076  switch (cmd) {
2077  case CLI_INIT:
2078  e->command = "moh reload";
2079  e->usage =
2080  "Usage: moh reload\n"
2081  " Reloads the MusicOnHold module.\n"
2082  " Alias for 'module reload res_musiconhold.so'\n";
2083  return NULL;
2084  case CLI_GENERATE:
2085  return NULL;
2086  }
2087 
2088  if (a->argc != e->args)
2089  return CLI_SHOWUSAGE;
2090 
2091  /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2092  ast_module_reload("res_musiconhold");
2093 
2094  return CLI_SUCCESS;
2095 }
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1560
const int argc
Definition: cli.h:160
Definition: cli.h:152
#define NULL
Definition: resample.c:96
int args
This gets set in ast_cli_register()
Definition: cli.h:185
#define CLI_SHOWUSAGE
Definition: cli.h:45
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44

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

References mohclass::announcement, ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, mohclass::args, ast_cli_entry::args, ast_cli(), ast_format_get_name(), ast_test_flag, moh_files_state::class, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, mohclass::dir, ast_cli_args::fd, mohclass::format, mohclass::kill_delay, mohclass::kill_method, KILL_METHOD_PROCESS, mohclass::mode, MOH_ANNOUNCEMENT, MOH_CUSTOM, mohclass_unref, mohclass::name, NULL, S_OR, and ast_cli_entry::usage.

2141 {
2142  struct mohclass *class;
2143  struct ao2_iterator i;
2144 
2145  switch (cmd) {
2146  case CLI_INIT:
2147  e->command = "moh show classes";
2148  e->usage =
2149  "Usage: moh show classes\n"
2150  " Lists all MusicOnHold classes.\n";
2151  return NULL;
2152  case CLI_GENERATE:
2153  return NULL;
2154  }
2155 
2156  if (a->argc != e->args)
2157  return CLI_SHOWUSAGE;
2158 
2159  i = ao2_iterator_init(mohclasses, 0);
2160  for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2161  ast_cli(a->fd, "Class: %s\n", class->name);
2162  ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2163  ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2164  if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2165  ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2166  }
2167  if (ast_test_flag(class, MOH_CUSTOM)) {
2168  ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2169  ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2170  ast_cli(a->fd, "\tKill Method: %s\n",
2171  class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2172  }
2173  if (strcasecmp(class->mode, "files")) {
2174  ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2175  }
2176  }
2178 
2179  return CLI_SUCCESS;
2180 }
static struct ao2_container * mohclasses
#define ast_test_flag(p, flag)
Definition: utils.h:63
const int argc
Definition: cli.h:160
Definition: cli.h:152
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
int args
This gets set in ast_cli_register()
Definition: cli.h:185
const int fd
Definition: cli.h:159
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1931
#define MOH_ANNOUNCEMENT
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define MOH_CUSTOM
#define CLI_SUCCESS
Definition: cli.h:44
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
#define mohclass_unref(class, string)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.

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

References ao2_bump, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), AST_VECTOR_GET, AST_VECTOR_SIZE, moh_files_state::class, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, mohclass::files, mohclass_unref, mohclass::name, NULL, and ast_cli_entry::usage.

2098 {
2099  struct mohclass *class;
2100  struct ao2_iterator i;
2101 
2102  switch (cmd) {
2103  case CLI_INIT:
2104  e->command = "moh show files";
2105  e->usage =
2106  "Usage: moh show files\n"
2107  " Lists all loaded file-based MusicOnHold classes and their\n"
2108  " files.\n";
2109  return NULL;
2110  case CLI_GENERATE:
2111  return NULL;
2112  }
2113 
2114  if (a->argc != e->args)
2115  return CLI_SHOWUSAGE;
2116 
2117  i = ao2_iterator_init(mohclasses, 0);
2118  for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2119  struct ast_vector_string *files;
2120 
2121  ao2_lock(class);
2122  files = ao2_bump(class->files);
2123  ao2_unlock(class);
2124 
2125  if (AST_VECTOR_SIZE(files)) {
2126  int x;
2127  ast_cli(a->fd, "Class: %s\n", class->name);
2128  for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2129  ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2130  }
2131  }
2132 
2133  ao2_ref(files, -1);
2134  }
2136 
2137  return CLI_SUCCESS;
2138 }
static struct ao2_container * mohclasses
const int argc
Definition: cli.h:160
Definition: cli.h:152
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
int args
This gets set in ast_cli_register()
Definition: cli.h:185
#define ao2_bump(obj)
Definition: astobj2.h:491
const int fd
Definition: cli.h:159
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1931
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define mohclass_unref(class, string)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_mohclass_realtime(), ast_cli_args::fd, len(), ast_cli_args::line, moh_class_destructor(), moh_unregister, mohclass_unref, ast_cli_args::n, mohclass::name, NULL, ast_cli_args::pos, mohclass::realtime, ast_cli_entry::usage, and ast_cli_args::word.

1535 {
1536  struct mohclass *cur;
1537  int len;
1538  int found = 0;
1539  struct ao2_iterator i;
1540 
1541  switch (cmd) {
1542  case CLI_INIT:
1543  e->command = "moh unregister class";
1544  e->usage =
1545  "Usage: moh unregister class <class>\n"
1546  " Unregisters a realtime moh class.\n";
1547  return NULL;
1548  case CLI_GENERATE:
1549  return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1550  }
1551 
1552  if (a->argc != 4)
1553  return CLI_SHOWUSAGE;
1554 
1555  len = strlen(a->argv[3]);
1556 
1557  i = ao2_iterator_init(mohclasses, 0);
1558  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1559  if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1560  found = 1;
1561  break;
1562  }
1563  mohclass_unref(cur, "drop ref in iterator loop");
1564  }
1566 
1567  if (found) {
1568  moh_unregister(cur);
1569  mohclass_unref(cur, "drop ref after unregister");
1570  } else {
1571  ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1572  }
1573 
1574  return CLI_SUCCESS;
1575 }
static struct ao2_container * mohclasses
char name[MAX_MUSICCLASS]
const int argc
Definition: cli.h:160
Definition: cli.h:152
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const char * line
Definition: cli.h:162
unsigned int realtime
const int fd
Definition: cli.h:159
const int n
Definition: cli.h:165
const char *const * argv
Definition: cli.h:161
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1931
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define moh_unregister(a)
char * command
Definition: cli.h:186
const char * word
Definition: cli.h:163
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
const int pos
Definition: cli.h:164
#define mohclass_unref(class, string)
static char * complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
Support routing for &#39;moh unregister class&#39; CLI This is in charge of generating all strings that match...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.

◆ init_app_class()

static int init_app_class ( struct mohclass class)
static

Definition at line 1369 of file res_musiconhold.c.

References ast_log, ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), moh_files_state::class, errno, LOG_WARNING, mohclass::mode, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), NULL, mohclass::thread, and mohclass::timer.

Referenced by _moh_register().

1370 {
1371  if (!strcasecmp(class->mode, "custom")) {
1372  ast_set_flag(class, MOH_CUSTOM);
1373  } else if (!strcasecmp(class->mode, "mp3nb")) {
1374  ast_set_flag(class, MOH_SINGLE);
1375  } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1376  ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1377  } else if (!strcasecmp(class->mode, "quietmp3")) {
1378  ast_set_flag(class, MOH_QUIET);
1379  }
1380 
1381  class->srcfd = -1;
1382 
1383  if (!(class->timer = ast_timer_open())) {
1384  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1385  return -1;
1386  }
1387  if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1388  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1389  ast_timer_close(class->timer);
1390  class->timer = NULL;
1391  }
1392 
1393  if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1394  ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1395  if (class->timer) {
1396  ast_timer_close(class->timer);
1397  class->timer = NULL;
1398  }
1399  return -1;
1400  }
1401 
1402  return 0;
1403 }
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:274
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
#define NULL
Definition: resample.c:96
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:507
#define ast_log
Definition: astobj2.c:42
#define MOH_QUIET
int errno
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 * timer
#define MOH_CUSTOM
pthread_t thread
char mode[80]
static void * monmp3thread(void *data)
#define MOH_SINGLE

◆ init_files_class()

static int init_files_class ( struct mohclass class)
static

Definition at line 1315 of file res_musiconhold.c.

References ast_verb, moh_files_state::class, mohclass::dir, moh_scan_files(), and mohclass::name.

Referenced by _moh_register().

1316 {
1317  int res;
1318 
1319  res = moh_scan_files(class);
1320 
1321  if (res < 0) {
1322  return -1;
1323  }
1324 
1325  if (!res) {
1326  ast_verb(3, "Files not found in %s for moh class:%s\n",
1327  class->dir, class->name);
1328  return -1;
1329  }
1330 
1331  return 0;
1332 }
char name[MAX_MUSICCLASS]
static int moh_scan_files(struct mohclass *class)
#define ast_verb(level,...)
Definition: logger.h:455
char dir[256]

◆ killer()

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

Definition at line 725 of file res_musiconhold.c.

References KILL_METHOD_PROCESS, and KILL_METHOD_PROCESS_GROUP.

Referenced by killpid().

726 {
727  switch (kill_method) {
729  return killpg(pid, signum);
730  case KILL_METHOD_PROCESS:
731  return kill(pid, signum);
732  }
733 
734  return -1;
735 }

◆ killpid()

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

Definition at line 737 of file res_musiconhold.c.

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

Referenced by moh_class_destructor(), and monmp3thread().

738 {
739  if (killer(pid, SIGHUP, kill_method) < 0) {
740  if (errno == ESRCH) {
741  return;
742  }
743  ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
744  } else {
745  ast_debug(1, "Sent HUP to pid %d%s\n", pid,
746  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
747  }
748  usleep(delay);
749  if (killer(pid, SIGTERM, kill_method) < 0) {
750  if (errno == ESRCH) {
751  return;
752  }
753  ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
754  } else {
755  ast_debug(1, "Sent TERM to pid %d%s\n", pid,
756  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
757  }
758  usleep(delay);
759  if (killer(pid, SIGKILL, kill_method) < 0) {
760  if (errno == ESRCH) {
761  return;
762  }
763  ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
764  } else {
765  ast_debug(1, "Sent KILL to pid %d%s\n", pid,
766  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
767  }
768 }
#define LOG_WARNING
Definition: logger.h:274
static int killer(pid_t pid, int signum, enum kill_methods kill_method)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
int errno

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

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(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), NULL, play_moh, play_moh_exec(), start_moh, start_moh_exec(), stop_moh, and stop_moh_exec().

Referenced by unload_module().

2216 {
2217  int res;
2218 
2220  moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2221  if (!mohclasses) {
2222  return AST_MODULE_LOAD_DECLINE;
2223  }
2224 
2225  if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2226  ast_log(LOG_WARNING, "No music on hold classes configured, "
2227  "disabling music on hold.\n");
2228  } else {
2231  }
2232 
2236  if (!res)
2238  if (!res)
2240 
2241  return AST_MODULE_LOAD_SUCCESS;
2242 }
static int stop_moh_exec(struct ast_channel *chan, const char *data)
static struct ao2_container * mohclasses
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static const char stop_moh[]
static int start_moh_exec(struct ast_channel *chan, const char *data)
static int load_moh_classes(int reload)
#define LOG_WARNING
Definition: logger.h:274
static struct ast_cli_entry cli_moh[]
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3363
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define NULL
Definition: resample.c:96
static void ast_moh_destroy(void)
static void local_ast_moh_cleanup(struct ast_channel *chan)
static void local_ast_moh_stop(struct ast_channel *chan)
#define ast_log
Definition: astobj2.c:42
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
static const char start_moh[]
static int play_moh_exec(struct ast_channel *chan, const char *data)
static int moh_class_cmp(void *obj, void *arg, int flags)
static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
#define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1308
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:7713
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int moh_class_hash(const void *obj, const int flags)
static const char play_moh[]
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626

◆ load_moh_classes()

static int load_moh_classes ( int  reload)
static

Definition at line 1970 of file res_musiconhold.c.

References ao2_t_callback, mohclass::args, 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(), moh_files_state::class, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, mohclass::dir, HANDLE_REF, LOG_WARNING, mohclass::mode, 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, ast_variable::name, mohclass::name, ast_variable::next, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, ast_variable::value, and var.

Referenced by load_module(), and reload().

1971 {
1972  struct ast_config *cfg;
1973  struct ast_variable *var;
1974  struct mohclass *class;
1975  char *cat;
1976  int numclasses = 0;
1977  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1978 
1979  cfg = ast_config_load("musiconhold.conf", config_flags);
1980 
1981  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1982  if (ast_check_realtime("musiconhold") && reload) {
1983  ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
1985  }
1986  moh_rescan_files();
1987  return 0;
1988  }
1989 
1991  if (ast_check_realtime("musiconhold") && reload) {
1992  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1994  }
1995  return 0;
1996  }
1997 
1998  if (reload) {
1999  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2000  }
2001 
2004 
2005  cat = ast_category_browse(cfg, NULL);
2006  for (; cat; cat = ast_category_browse(cfg, cat)) {
2007  /* Setup common options from [general] section */
2008  if (!strcasecmp(cat, "general")) {
2009  for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2010  if (!strcasecmp(var->name, "cachertclasses")) {
2012  } else if (!strcasecmp(var->name, "preferchannelclass")) {
2014  } else {
2015  ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2016  }
2017  }
2018  continue;
2019  }
2020 
2021  if (!(class = moh_class_malloc())) {
2022  break;
2023  }
2024 
2025  moh_parse_options(ast_variable_browse(cfg, cat), class);
2026  /* For compatibility with the past, we overwrite any name=name
2027  * with the context [name]. */
2028  ast_copy_string(class->name, cat, sizeof(class->name));
2029 
2030  if (ast_strlen_zero(class->dir)) {
2031  if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2032  strcpy(class->dir, "nodir");
2033  } else {
2034  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2035  class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2036  continue;
2037  }
2038  }
2039  if (ast_strlen_zero(class->mode)) {
2040  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2041  class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2042  continue;
2043  }
2044  if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2045  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2046  class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2047  continue;
2048  }
2049 
2050  /* Don't leak a class when it's already registered */
2051  if (!moh_register(class, reload, HANDLE_REF)) {
2052  numclasses++;
2053  }
2054  }
2055 
2056  ast_config_destroy(cfg);
2057 
2059  moh_classes_delete_marked, NULL, "Purge marked classes");
2060 
2061  return numclasses;
2062 }
static struct ao2_container * mohclasses
struct ast_variable * next
#define HANDLE_REF
static int moh_classes_delete_marked(void *obj, void *arg, int flags)
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
#define moh_register(moh, reload, unref)
static int reload(void)
#define LOG_WARNING
Definition: logger.h:274
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3363
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
#define NULL
Definition: resample.c:96
static void moh_rescan_files(void)
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
#define MOH_CACHERTCLASSES
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define MOH_PREFERCHANNELCLASS
#define CONFIG_STATUS_FILEUNCHANGED
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1822
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1714
#define ast_strlen_zero(a)
Definition: muted.c:73
#define AST_FLAGS_ALL
Definition: utils.h:196
#define MOH_REALTIME
static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
Structure used to handle boolean flags.
Definition: utils.h:199
#define ast_clear_flag(p, flag)
Definition: utils.h:77
static struct ast_flags global_flags[1]
#define CONFIG_STATUS_FILEMISSING
static int moh_class_mark(void *obj, void *arg, int flags)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define mohclass_unref(class, string)
static const char app[]
Definition: app_mysql.c:62
#define moh_class_malloc()

◆ load_realtime_musiconhold()

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

Definition at line 1605 of file res_musiconhold.c.

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

Referenced by local_ast_moh_start().

1606 {
1607  struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1608 
1609  if (var) {
1610  const char *mode = ast_variable_find_in_list(var, "mode");
1611  if (ast_strings_equal(mode, "playlist")) {
1612  struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1613  char *category = NULL;
1614  size_t entry_count = 0;
1615 
1616  /* entries is NULL if there are no results */
1617  if (entries) {
1618  while ((category = ast_category_browse(entries, category))) {
1619  const char *entry = ast_variable_retrieve(entries, category, "entry");
1620 
1621  if (entry) {
1622  struct ast_variable *dup = ast_variable_new("entry", entry, "");
1623  if (dup) {
1624  entry_count++;
1625  ast_variable_list_append(&var, dup);
1626  }
1627  }
1628  }
1629  ast_config_destroy(entries);
1630  }
1631 
1632  if (entry_count == 0) {
1633  /* Behave as though this class doesn't exist */
1634  ast_variables_destroy(var);
1635  var = NULL;
1636  }
1637  }
1638  }
1639 
1640  if (!var) {
1642  "Music on Hold class '%s' not found in memory/database. "
1643  "Verify your configuration.\n",
1644  name);
1645  }
1646  return var;
1647 }
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3339
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
#define LOG_WARNING
Definition: logger.h:274
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
#define SENTINEL
Definition: compiler.h:87
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
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:830
#define ast_variable_new(name, value, filename)
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3452
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:239
static const char name[]
Definition: cdr_mysql.c:74
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
Definition: search.h:40
#define ast_variable_list_append(head, new_var)

◆ local_ast_moh_cleanup()

static void local_ast_moh_cleanup ( struct ast_channel chan)
static

Definition at line 1483 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

1484 {
1486 
1487  if (state) {
1489  if (state->class) {
1490  /* This should never happen. We likely just leaked some resource. */
1491  state->class =
1492  mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1493  ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1494  }
1495  ao2_cleanup(state->origwfmt);
1496  ao2_cleanup(state->mohwfmt);
1497  ast_free(state);
1498  /* Only held a module reference if we had a music state */
1500  }
1501 }
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
struct ast_format * origwfmt
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define NULL
Definition: resample.c:96
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
#define ast_log
Definition: astobj2.c:42
struct ast_module * self
Definition: module.h:342
#define ast_free(a)
Definition: astmm.h:182
struct ast_format * mohwfmt
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)

◆ local_ast_moh_start()

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

Definition at line 1649 of file res_musiconhold.c.

References 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_unlock, ast_check_realtime(), AST_FLAG_MOH, ast_log, ast_pthread_create_background, ast_set_flag, ast_strlen_zero, ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_variables_destroy(), AST_VECTOR_SIZE, moh_files_state::class, mohclass::dir, DONT_UNREF, errno, mohclass::files, get_mohbyname, load_realtime_musiconhold(), LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, mohclass_ref, mohclass_unref, monmp3thread(), mohclass::name, NULL, mohclass::realtime, respawn_time, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, and var.

Referenced by load_module(), and reload().

1650 {
1651  struct mohclass *mohclass = NULL;
1653  struct ast_variable *var = NULL;
1654  int res = 0;
1655  int i;
1656  int realtime_possible = ast_check_realtime("musiconhold");
1657  int warn_if_not_in_memory = !realtime_possible;
1658  const char *classes[] = {NULL, NULL, interpclass, "default"};
1659 
1661  classes[0] = ast_channel_musicclass(chan);
1662  classes[1] = mclass;
1663  } else {
1664  classes[0] = mclass;
1665  classes[1] = ast_channel_musicclass(chan);
1666  }
1667 
1668  /* The following is the order of preference for which class to use:
1669  * 1) The channels explicitly set musicclass, which should *only* be
1670  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1671  * Unless preferchannelclass in musiconhold.conf is false
1672  * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1673  * result of receiving a HOLD control frame, this should be the
1674  * payload that came with the frame.
1675  * 3) The channels explicitly set musicclass, which should *only* be
1676  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1677  * 4) The interpclass argument. This would be from the mohinterpret
1678  * option from channel drivers. This is the same as the old musicclass
1679  * option.
1680  * 5) The default class.
1681  */
1682 
1683  for (i = 0; i < ARRAY_LEN(classes); ++i) {
1684  if (!ast_strlen_zero(classes[i])) {
1685  mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1686  if (!mohclass && realtime_possible) {
1687  var = load_realtime_musiconhold(classes[i]);
1688  }
1689  if (mohclass || var) {
1690  break;
1691  }
1692  }
1693  }
1694 
1695  /* If no moh class found in memory, then check RT. Note that the logic used
1696  * above guarantees that if var is non-NULL, then mohclass must be NULL.
1697  */
1698  if (var) {
1699  if ((mohclass = moh_class_malloc())) {
1700  mohclass->realtime = 1;
1701 
1702  moh_parse_options(var, mohclass);
1703  ast_variables_destroy(var);
1704 
1705  if (ast_strlen_zero(mohclass->dir)) {
1706  if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1707  strcpy(mohclass->dir, "nodir");
1708  } else {
1709  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1710  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1711  return -1;
1712  }
1713  }
1714  if (ast_strlen_zero(mohclass->mode)) {
1715  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1716  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1717  return -1;
1718  }
1719  if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1720  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1721  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1722  return -1;
1723  }
1724 
1726  /* CACHERTCLASSES enabled, let's add this class to default tree */
1727  if (state && state->class) {
1728  /* Class already exist for this channel */
1729  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1730  }
1731  /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1732  * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1733  * be that the destructor would be called when the generator on the channel is deactivated. The container then
1734  * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1735  * invalid memory.
1736  */
1737  if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1738  mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1739  return -1;
1740  }
1741  } else {
1742  /* We don't register RT moh class, so let's init it manualy */
1743 
1744  time(&mohclass->start);
1745  mohclass->start -= respawn_time;
1746 
1747  if (!strcasecmp(mohclass->mode, "files")) {
1748  /*
1749  * XXX moh_scan_files returns -1 if it is unable to open the
1750  * configured directory or there is a memory allocation
1751  * failure. Otherwise it returns the number of files for this music
1752  * class. This check is only checking if the number of files is zero
1753  * and it ignores the -1 case. To avoid a behavior change we keep this
1754  * as-is, but we should address what the 'correct' behavior should be.
1755  */
1756  if (!moh_scan_files(mohclass)) {
1757  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1758  return -1;
1759  }
1760  if (strchr(mohclass->args, 'r')) {
1761  static int deprecation_warning = 0;
1762  if (!deprecation_warning) {
1763  ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1764  deprecation_warning = 1;
1765  }
1766  ast_set_flag(mohclass, MOH_RANDOMIZE);
1767  }
1768  } else if (!strcasecmp(mohclass->mode, "playlist")) {
1769  size_t file_count;
1770 
1771  ao2_lock(mohclass);
1772  file_count = AST_VECTOR_SIZE(mohclass->files);
1773  ao2_unlock(mohclass);
1774 
1775  if (!file_count) {
1776  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1777  return -1;
1778  }
1779  } 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")) {
1780 
1781  if (!strcasecmp(mohclass->mode, "custom"))
1782  ast_set_flag(mohclass, MOH_CUSTOM);
1783  else if (!strcasecmp(mohclass->mode, "mp3nb"))
1784  ast_set_flag(mohclass, MOH_SINGLE);
1785  else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1786  ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1787  else if (!strcasecmp(mohclass->mode, "quietmp3"))
1788  ast_set_flag(mohclass, MOH_QUIET);
1789 
1790  mohclass->srcfd = -1;
1791  if (!(mohclass->timer = ast_timer_open())) {
1792  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1793  }
1794  if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1795  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1796  ast_timer_close(mohclass->timer);
1797  mohclass->timer = NULL;
1798  }
1799 
1800  /* Let's check if this channel already had a moh class before */
1801  if (state && state->class) {
1802  /* Class already exist for this channel */
1803  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1804  if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1805  /* we found RT class with the same name, seems like we should continue playing existing one */
1806  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1807  mohclass = mohclass_ref(state->class, "using existing class from state");
1808  }
1809  } else {
1810  if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1811  ast_log(LOG_WARNING, "Unable to create moh...\n");
1812  if (mohclass->timer) {
1813  ast_timer_close(mohclass->timer);
1814  mohclass->timer = NULL;
1815  }
1816  mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1817  return -1;
1818  }
1819  }
1820  } else {
1821  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1822  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1823  return -1;
1824  }
1825  }
1826  } else {
1827  ast_variables_destroy(var);
1828  var = NULL;
1829  }
1830  }
1831 
1832  if (!mohclass) {
1833  return -1;
1834  }
1835 
1836  /* If we are using a cached realtime class with files, re-scan the files */
1837  if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1838  /*
1839  * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1840  * or there is a memory allocation failure. Otherwise it returns the number of
1841  * files for this music class. This check is only checking if the number of files
1842  * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1843  * as-is, but we should address what the 'correct' behavior should be.
1844  */
1845  if (!moh_scan_files(mohclass)) {
1846  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1847  return -1;
1848  }
1849  }
1850 
1851  if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1852  size_t file_count;
1853 
1854  ao2_lock(mohclass);
1855  file_count = AST_VECTOR_SIZE(mohclass->files);
1856  ao2_unlock(mohclass);
1857 
1858  if (file_count) {
1859  res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1860  } else {
1861  res = ast_activate_generator(chan, &mohgen, mohclass);
1862  }
1863  }
1864  if (!res) {
1865  ast_channel_lock(chan);
1866  ast_channel_latest_musicclass_set(chan, mohclass->name);
1868  ast_channel_unlock(chan);
1869  }
1870 
1871  mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1872 
1873  return res;
1874 }
#define ast_channel_lock(chan)
Definition: channel.h:2890
char name[MAX_MUSICCLASS]
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
#define ast_test_flag(p, flag)
Definition: utils.h:63
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2946
#define moh_register(moh, reload, unref)
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3363
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
static int moh_scan_files(struct mohclass *class)
#define mohclass_ref(class, string)
char dir[256]
unsigned int realtime
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:507
#define ast_log
Definition: astobj2.c:42
#define MOH_CACHERTCLASSES
#define ao2_lock(a)
Definition: astobj2.h:718
#define MOH_QUIET
#define MOH_PREFERCHANNELCLASS
struct ast_vector_string * files
int errno
static struct ast_generator moh_file_stream
#define LOG_NOTICE
Definition: logger.h:263
#define ast_strlen_zero(a)
Definition: muted.c:73
#define ast_channel_unlock(chan)
Definition: channel.h:2891
time_t start
static int respawn_time
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 * timer
static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
static struct ast_flags global_flags[1]
#define MOH_CUSTOM
pthread_t thread
char args[256]
static struct ast_variable * load_realtime_musiconhold(const char *name)
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
char mode[80]
static struct ast_generator mohgen
static void * monmp3thread(void *data)
#define MOH_SINGLE
#define moh_class_malloc()
#define MOH_RANDOMIZE
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
#define DONT_UNREF
#define get_mohbyname(a, b, c)
const char * ast_channel_musicclass(const struct ast_channel *chan)

◆ local_ast_moh_stop()

static void local_ast_moh_stop ( struct ast_channel chan)
static

Definition at line 1876 of file res_musiconhold.c.

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

Referenced by load_module(), and reload().

1877 {
1879 
1880  ast_channel_lock(chan);
1882  if (ast_channel_music_state(chan)) {
1883  if (ast_channel_stream(chan)) {
1886  }
1887  }
1888  ast_channel_unlock(chan);
1889 }
#define ast_channel_lock(chan)
Definition: channel.h:2890
#define NULL
Definition: resample.c:96
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
#define ast_channel_unlock(chan)
Definition: channel.h:2891
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
#define ast_clear_flag(p, flag)
Definition: utils.h:77
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2888
void * ast_channel_music_state(const struct ast_channel *chan)
struct ast_flags * ast_channel_flags(struct ast_channel *chan)

◆ moh_alloc()

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

Definition at line 1018 of file res_musiconhold.c.

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, moh_files_state::mohwfmt, mohclass::name, NULL, moh_files_state::origwfmt, mohdata::origwfmt, ast_module_info::self, and state.

1019 {
1020  struct mohdata *res;
1021  struct mohclass *class = params;
1022  struct moh_files_state *state;
1023 
1024  /* Initiating music_state for current channel. Channel should know name of moh class */
1025  state = ast_channel_music_state(chan);
1026  if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1027  ast_channel_music_state_set(chan, state);
1029  } else {
1030  if (!state) {
1031  return NULL;
1032  }
1033  if (state->class) {
1034  mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1035  ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1036  }
1037  ao2_cleanup(state->origwfmt);
1038  ao2_cleanup(state->mohwfmt);
1039  memset(state, 0, sizeof(*state));
1040  }
1041 
1042  if ((res = mohalloc(class))) {
1044  if (ast_set_write_format(chan, class->format)) {
1045  ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1047  moh_release(NULL, res);
1048  res = NULL;
1049  } else {
1050  state->class = mohclass_ref(class, "Placing reference into state container");
1051  moh_post_start(chan, class->name);
1052  }
1053  }
1054  return res;
1055 }
static void moh_release(struct ast_channel *chan, void *data)
enum sip_cc_notify_state state
Definition: chan_sip.c:957
char name[MAX_MUSICCLASS]
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
struct ast_format * origwfmt
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define NULL
Definition: resample.c:96
static struct mohdata * mohalloc(struct mohclass *cl)
#define mohclass_ref(class, string)
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ast_log
Definition: astobj2.c:42
struct ast_module * self
Definition: module.h:342
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5789
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_format * mohwfmt
struct ast_format * format
struct ast_format * origwfmt
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443

◆ moh_class_cmp()

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

Definition at line 2196 of file res_musiconhold.c.

References moh_files_state::class, CMP_MATCH, CMP_STOP, mohclass::delete, MOH_NOTDELETED, and mohclass::name.

Referenced by load_module().

2197 {
2198  struct mohclass *class = obj, *class2 = arg;
2199 
2200  return strcasecmp(class->name, class2->name) ? 0 :
2201  (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2202  CMP_MATCH | CMP_STOP;
2203 }
#define MOH_NOTDELETED

◆ moh_class_destructor()

static void moh_class_destructor ( void *  obj)
static

Definition at line 1891 of file res_musiconhold.c.

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, moh_files_state::class, mohclass::files, mohclass::format, mohclass::kill_delay, mohclass::kill_method, killpid(), mohdata::list, mohclass::members, mohclass::name, NULL, mohclass::pid, mohclass::srcfd, mohclass::thread, and mohclass::timer.

Referenced by _moh_class_malloc(), and handle_cli_moh_unregister_class().

1892 {
1893  struct mohclass *class = obj;
1894  struct mohdata *member;
1895  pthread_t tid = 0;
1896 
1897  ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1898 
1899  ao2_lock(class);
1900  while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1901  ast_free(member);
1902  }
1903  ao2_cleanup(class->files);
1904  ao2_unlock(class);
1905 
1906  /* Kill the thread first, so it cannot restart the child process while the
1907  * class is being destroyed */
1908  if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1909  tid = class->thread;
1910  class->thread = AST_PTHREADT_NULL;
1911  pthread_cancel(tid);
1912  /* We'll collect the exit status later, after we ensure all the readers
1913  * are dead. */
1914  }
1915 
1916  if (class->pid > 1) {
1917  char buff[8192];
1918  int bytes, tbytes = 0, stime = 0;
1919 
1920  ast_debug(1, "killing %d!\n", class->pid);
1921 
1922  stime = time(NULL) + 2;
1923  killpid(class->pid, class->kill_delay, class->kill_method);
1924 
1925  while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1926  (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1927  tbytes = tbytes + bytes;
1928  }
1929 
1930  ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1931  class->pid, tbytes);
1932 
1933  class->pid = 0;
1934  close(class->srcfd);
1935  class->srcfd = -1;
1936  }
1937 
1938  if (class->timer) {
1939  ast_timer_close(class->timer);
1940  class->timer = NULL;
1941  }
1942 
1943  ao2_cleanup(class->format);
1944 
1945  /* Finally, collect the exit status of the monitor thread */
1946  if (tid > 0) {
1947  pthread_join(tid, NULL);
1948  }
1949 
1950 }
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define AST_PTHREADT_NULL
Definition: lock.h:66
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
#define ao2_lock(a)
Definition: astobj2.h:718
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
#define ast_free(a)
Definition: astmm.h:182
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
static unsigned char * buff
Definition: chan_unistim.c:259
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1390
struct mohdata::@458 list

◆ moh_class_hash()

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

Definition at line 2189 of file res_musiconhold.c.

References ast_str_case_hash(), moh_files_state::class, and mohclass::name.

Referenced by load_module().

2190 {
2191  const struct mohclass *class = obj;
2192 
2193  return ast_str_case_hash(class->name);
2194 }
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:1250

◆ moh_class_inuse()

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

Definition at line 2254 of file res_musiconhold.c.

References AST_LIST_EMPTY, moh_files_state::class, CMP_MATCH, CMP_STOP, and mohclass::members.

Referenced by unload_module().

2255 {
2256  struct mohclass *class = obj;
2257 
2258  return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2259 }
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449

◆ moh_class_mark()

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

Definition at line 1952 of file res_musiconhold.c.

References moh_files_state::class, mohclass::delete, MOH_REALTIME, and mohclass::realtime.

Referenced by load_moh_classes().

1953 {
1954  struct mohclass *class = obj;
1955 
1956  if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1957  class->delete = 1;
1958  }
1959 
1960  return 0;
1961 }
unsigned int delete
#define MOH_REALTIME

◆ moh_classes_delete_marked()

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

Definition at line 1963 of file res_musiconhold.c.

References CMP_MATCH, and mohclass::delete.

Referenced by load_moh_classes().

1964 {
1965  struct mohclass *class = obj;
1966 
1967  return class->delete ? CMP_MATCH : 0;
1968 }
unsigned int delete

◆ moh_diff()

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

Definition at line 1350 of file res_musiconhold.c.

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

Referenced by _moh_register().

1351 {
1352  if (!old || !new) {
1353  return -1;
1354  }
1355 
1356  if (strcmp(old->dir, new->dir)) {
1357  return -1;
1358  } else if (strcmp(old->mode, new->mode)) {
1359  return -1;
1360  } else if (strcmp(old->args, new->args)) {
1361  return -1;
1362  } else if (old->flags != new->flags) {
1363  return -1;
1364  }
1365 
1366  return 0;
1367 }
char dir[256]
unsigned int flags
char args[256]
char mode[80]

◆ moh_digit_match()

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

Definition at line 548 of file res_musiconhold.c.

References moh_files_state::class, CMP_MATCH, CMP_STOP, digit, and mohclass::digit.

Referenced by get_mohbydigit().

549 {
550  char *digit = arg;
551  struct mohclass *class = obj;
552 
553  return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
554 }
char digit

◆ moh_file_vector_alloc()

static struct ast_vector_string* moh_file_vector_alloc ( int  initial_capacity)
static

Definition at line 1099 of file res_musiconhold.c.

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

1100 {
1101  struct ast_vector_string *files = ao2_alloc_options(
1102  sizeof(struct ast_vector_string),
1105  if (files) {
1106  AST_VECTOR_INIT(files, initial_capacity);
1107  }
1108  return files;
1109 }
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
static void moh_file_vector_destructor(void *obj)

◆ moh_file_vector_destructor()

static void moh_file_vector_destructor ( void *  obj)
static

Definition at line 1092 of file res_musiconhold.c.

References ast_free, AST_VECTOR_FREE, and AST_VECTOR_RESET.

Referenced by moh_file_vector_alloc().

1093 {
1094  struct ast_vector_string *files = obj;
1095  AST_VECTOR_RESET(files, ast_free);
1096  AST_VECTOR_FREE(files);
1097 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define ast_free(a)
Definition: astmm.h:182
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:627

◆ moh_filename_strcasecmp()

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

Definition at line 1271 of file res_musiconhold.c.

Referenced by moh_scan_files().

1272 {
1273  const char **s1 = (const char **) a;
1274  const char **s2 = (const char **) b;
1275  return strcasecmp(*s1, *s2);
1276 }
static struct test_val b
static struct test_val a

◆ moh_files_alloc()

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

Definition at line 500 of file res_musiconhold.c.

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, moh_files_state::class, mohclass::files, LOG_WARNING, moh_post_start(), MOH_RANDOMIZE, mohclass_ref, mohclass_unref, moh_files_state::mohwfmt, moh_files_state::name, mohclass::name, NULL, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_total, ast_module_info::self, and state.

501 {
502  struct moh_files_state *state;
503  struct mohclass *class = params;
504  size_t file_count;
505 
506  state = ast_channel_music_state(chan);
507  if (!state && (state = ast_calloc(1, sizeof(*state)))) {
508  ast_channel_music_state_set(chan, state);
510  } else {
511  if (!state) {
512  return NULL;
513  }
514  if (state->class) {
515  mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
516  ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
517  }
518  }
519 
520  ao2_lock(class);
521  file_count = AST_VECTOR_SIZE(class->files);
522  ao2_unlock(class);
523 
524  /* Resume MOH from where we left off last time or start from scratch? */
525  if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
526  /* Start MOH from scratch. */
527  ao2_cleanup(state->origwfmt);
528  ao2_cleanup(state->mohwfmt);
529  memset(state, 0, sizeof(*state));
530  if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
531  state->pos = ast_random() % file_count;
532  }
533  }
534 
535  state->class = mohclass_ref(class, "Reffing music class for channel");
536  /* it's possible state is not a new allocation, don't leak old refs */
539  /* For comparison on restart of MOH (see above) */
540  ast_copy_string(state->name, class->name, sizeof(state->name));
541  state->save_total = file_count;
542 
543  moh_post_start(chan, class->name);
544 
545  return state;
546 }
enum sip_cc_notify_state state
Definition: chan_sip.c:957
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
struct ast_format * origwfmt
#define ao2_unlock(a)
Definition: astobj2.h:730
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define NULL
Definition: resample.c:96
#define mohclass_ref(class, string)
#define ast_log
Definition: astobj2.c:42
struct ast_module * self
Definition: module.h:342
long int ast_random(void)
Definition: main/utils.c:1935
#define ao2_lock(a)
Definition: astobj2.h:718
const ast_string_field name
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_format * mohwfmt
#define ao2_replace(dst, src)
Definition: astobj2.h:517
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
char name[MAX_MUSICCLASS]
#define MOH_RANDOMIZE
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443

◆ moh_files_generator()

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

Definition at line 457 of file res_musiconhold.c.

References moh_files_state::announcement, 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(), moh_files_state::mohwfmt, NULL, moh_files_state::sample_queue, moh_files_state::samples, ast_frame::samples, and ast_frame::subclass.

458 {
460  struct ast_frame *f = NULL;
461  int res = 0;
462 
463  state->sample_queue += samples;
464 
465  while (state->sample_queue > 0) {
466  ast_channel_lock(chan);
467  f = moh_files_readframe(chan);
468 
469  /* We need to be sure that we unlock
470  * the channel prior to calling
471  * ast_write. Otherwise, the recursive locking
472  * that occurs can cause deadlocks when using
473  * indirect channels, like local channels
474  */
475  ast_channel_unlock(chan);
476  if (!f) {
477  return -1;
478  }
479 
480  /* Only track our offset within the current file if we are not in the
481  * the middle of an announcement */
482  if (!state->announcement) {
483  state->samples += f->samples;
484  }
485 
486  state->sample_queue -= f->samples;
488  ao2_replace(state->mohwfmt, f->subclass.format);
489  }
490  res = ast_write(chan, f);
491  ast_frfree(f);
492  if (res < 0) {
493  ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
494  return -1;
495  }
496  }
497  return res;
498 }
#define ast_channel_lock(chan)
Definition: channel.h:2890
static struct ast_frame * moh_files_readframe(struct ast_channel *chan)
#define LOG_WARNING
Definition: logger.h:274
#define NULL
Definition: resample.c:96
struct ast_frame_subclass subclass
#define ast_log
Definition: astobj2.c:42
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
int errno
#define ast_channel_unlock(chan)
Definition: channel.h:2891
struct ast_format * mohwfmt
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:5088
#define ao2_replace(dst, src)
Definition: astobj2.h:517
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_music_state(const struct ast_channel *chan)
#define ast_frfree(fr)
Data structure associated with a single frame of data.
struct ast_format * format

◆ moh_files_readframe()

static struct ast_frame* moh_files_readframe ( struct ast_channel chan)
static

Definition at line 409 of file res_musiconhold.c.

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

Referenced by moh_files_generator().

410 {
411  struct ast_frame *f;
412 
414  if (!f) {
415  /* Either there was no file stream setup or we reached EOF. */
416  if (!ast_moh_files_next(chan)) {
417  /*
418  * Either we resetup the previously saved file stream position
419  * or we started a new file stream.
420  */
422  if (!f) {
423  /*
424  * We can get here if we were very unlucky because the
425  * resetup file stream was saved at EOF when MOH was
426  * previously stopped.
427  */
428  if (!ast_moh_files_next(chan)) {
430  }
431  }
432  }
433  }
434 
435  return f;
436 }
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:899
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
Data structure associated with a single frame of data.
static int ast_moh_files_next(struct ast_channel *chan)

◆ moh_files_release()

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

Definition at line 281 of file res_musiconhold.c.

References moh_files_state::announcement, 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(), moh_files_state::class, LOG_WARNING, moh_post_stop(), mohclass_unref, moh_files_state::mohwfmt, NULL, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_pos, and state.

282 {
283  struct moh_files_state *state;
284 
285  if (!chan || !ast_channel_music_state(chan)) {
286  return;
287  }
288 
289  state = ast_channel_music_state(chan);
290 
291  if (ast_channel_stream(chan)) {
294  }
295 
296  moh_post_stop(chan);
297 
298  ao2_ref(state->mohwfmt, -1);
299  state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
300  if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
301  ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
302  ast_format_get_name(state->origwfmt));
303  }
304  ao2_cleanup(state->origwfmt);
305  state->origwfmt = NULL;
306 
307  state->save_pos = state->pos;
308  state->announcement = 0;
309 
310  state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
311 }
enum sip_cc_notify_state state
Definition: chan_sip.c:957
static void moh_post_stop(struct ast_channel *chan)
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
struct ast_format * origwfmt
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define NULL
Definition: resample.c:96
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5789
struct ast_format * mohwfmt
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)

◆ moh_files_write_format_change()

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

Definition at line 438 of file res_musiconhold.c.

References ao2_bump, ao2_replace, ast_channel_music_state(), ast_channel_writeformat(), ast_set_write_format(), moh_files_state::mohwfmt, NULL, moh_files_state::origwfmt, and tmp().

439 {
441 
442  /* In order to prevent a recursive call to this function as a result
443  * of setting the moh write format back on the channel. Clear
444  * the moh write format before setting the write format on the channel.*/
445  if (state->origwfmt) {
446  struct ast_format *tmp;
447 
448  tmp = ao2_bump(ast_channel_writeformat(chan));
449  ao2_replace(state->origwfmt, NULL);
450  if (state->mohwfmt) {
451  ast_set_write_format(chan, state->mohwfmt);
452  }
453  state->origwfmt = tmp;
454  }
455 }
static int tmp()
Definition: bt_open.c:389
struct ast_format * origwfmt
Definition of a media format.
Definition: format.c:43
#define NULL
Definition: resample.c:96
#define ao2_bump(obj)
Definition: astobj2.h:491
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5789
struct ast_format * mohwfmt
#define ao2_replace(dst, src)
Definition: astobj2.h:517
void * ast_channel_music_state(const struct ast_channel *chan)
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)

◆ moh_generate()

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

Definition at line 1057 of file res_musiconhold.c.

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, LOG_WARNING, mohdata::parent, mohdata::pipe, ast_frame::ptr, and ast_frame::samples.

1058 {
1059  struct mohdata *moh = data;
1060  short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1061  int res;
1062 
1063  len = ast_format_determine_length(moh->parent->format, samples);
1064 
1065  if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1066  ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1067  len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1068  }
1069  res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1070  if (res <= 0)
1071  return 0;
1072 
1073  moh->f.datalen = res;
1074  moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1075  moh->f.samples = ast_codec_samples_count(&moh->f);
1076 
1077  if (ast_write(chan, &moh->f) < 0) {
1078  ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1079  return -1;
1080  }
1081 
1082  return 0;
1083 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_WARNING
Definition: logger.h:274
union ast_frame::@257 data
#define ast_log
Definition: astobj2.c:42
#define AST_FRIENDLY_OFFSET
Offset into a frame&#39;s data buffer.
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:378
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
int pipe[2]
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
struct ast_format * format
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:5088
struct ast_frame f
const char * ast_channel_name(const struct ast_channel *chan)
struct mohclass * parent

◆ moh_handle_digit()

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

Definition at line 562 of file res_musiconhold.c.

References ast_moh_start(), ast_moh_stop(), ast_strdupa, moh_files_state::class, get_mohbydigit(), mohclass_unref, mohclass::name, and NULL.

563 {
564  struct mohclass *class;
565  const char *classname = NULL;
566 
567  if ((class = get_mohbydigit(digit))) {
568  classname = ast_strdupa(class->name);
569  class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
570  ast_channel_musicclass_set(chan, classname);
571  ast_moh_stop(chan);
572  ast_moh_start(chan, classname, NULL);
573  }
574 }
char digit
#define NULL
Definition: resample.c:96
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7739
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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:7729
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
#define mohclass_unref(class, string)
static struct mohclass * get_mohbydigit(char digit)

◆ moh_parse_options()

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

Definition at line 1111 of file res_musiconhold.c.

References mohclass::announcement, ao2_bump, ao2_cleanup, ao2_ref, ao2_replace, mohclass::args, ast_begins_with(), 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_RANDOMIZE, MOH_RANDSTART, MOH_SORTALPHA, ast_variable::name, mohclass::name, ast_variable::next, NULL, ast_variable::value, and var.

Referenced by load_moh_classes(), and local_ast_moh_start().

1112 {
1113  struct ast_vector_string *playlist_entries = NULL;
1114 
1115  for (; var; var = var->next) {
1116  if (!strcasecmp(var->name, "name")) {
1117  ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1118  } else if (!strcasecmp(var->name, "mode")) {
1119  ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1120  } else if (!strcasecmp(var->name, "entry")) {
1121  if (ast_begins_with(var->value, "/") || ast_begins_with(var->value, "http://") || ast_begins_with(var->value, "https://")) {
1122  char *dup;
1123 
1124  if (!playlist_entries) {
1125  playlist_entries = moh_file_vector_alloc(16);
1126  if (!playlist_entries) {
1127  continue;
1128  }
1129  }
1130 
1131  dup = ast_strdup(var->value);
1132  if (!dup) {
1133  continue;
1134  }
1135 
1136  if (ast_begins_with(dup, "/")) {
1137  char *last_pos_dot = strrchr(dup, '.');
1138  char *last_pos_slash = strrchr(dup, '/');
1139  if (last_pos_dot && last_pos_dot > last_pos_slash) {
1140  ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1141  dup);
1142  }
1143  }
1144 
1145  AST_VECTOR_APPEND(playlist_entries, dup);
1146  } else {
1147  ast_log(LOG_ERROR, "Playlist entries must be an HTTP(S) URL or absolute path, '%s' provided.\n", var->value);
1148  }
1149  } else if (!strcasecmp(var->name, "directory")) {
1150  ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1151  } else if (!strcasecmp(var->name, "application")) {
1152  ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1153  } else if (!strcasecmp(var->name, "announcement")) {
1154  ast_copy_string(mohclass->announcement, var->value, sizeof(mohclass->announcement));
1155  ast_set_flag(mohclass, MOH_ANNOUNCEMENT);
1156  } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1157  mohclass->digit = *var->value;
1158  } else if (!strcasecmp(var->name, "random")) {
1159  static int deprecation_warning = 0;
1160  if (!deprecation_warning) {
1161  ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1162  deprecation_warning = 1;
1163  }
1164  ast_set2_flag(mohclass, ast_true(var->value), MOH_RANDOMIZE);
1165  } else if (!strcasecmp(var->name, "sort")) {
1166  if (!strcasecmp(var->value, "random")) {
1167  ast_set_flag(mohclass, MOH_RANDOMIZE);
1168  } else if (!strcasecmp(var->value, "alpha")) {
1169  ast_set_flag(mohclass, MOH_SORTALPHA);
1170  } else if (!strcasecmp(var->value, "randstart")) {
1171  ast_set_flag(mohclass, MOH_RANDSTART);
1172  }
1173  } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1174  ao2_cleanup(mohclass->format);
1175  mohclass->format = ast_format_cache_get(var->value);
1176  if (!mohclass->format) {
1177  ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1178  mohclass->format = ao2_bump(ast_format_slin);
1179  }
1180  } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1181  if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1182  mohclass->kill_delay *= 1000;
1183  } else {
1184  ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1185  mohclass->kill_delay = 100000;
1186  }
1187  } else if (!strcasecmp(var->name, "kill_method")) {
1188  if (!strcasecmp(var->value, "process")) {
1189  mohclass->kill_method = KILL_METHOD_PROCESS;
1190  } else if (!strcasecmp(var->value, "process_group")) {
1192  } else {
1193  ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1195  }
1196  }
1197  }
1198 
1199  if (playlist_entries) {
1200  /* If we aren't in playlist mode, drop any list we may have already built */
1201  if (strcasecmp(mohclass->mode, "playlist")) {
1202  ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1203  mohclass->mode);
1204  ao2_ref(playlist_entries, -1);
1205  return;
1206  }
1207 
1208  AST_VECTOR_COMPACT(playlist_entries);
1209 
1210  /* We don't need to lock here because we are the thread that
1211  * created this mohclass and we haven't published it yet */
1212  ao2_replace(mohclass->files, playlist_entries);
1213  }
1214 }
struct ast_variable * next
char name[MAX_MUSICCLASS]
char announcement[256]
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:274
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define var
Definition: ast_expr2f.c:614
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:640
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
#define MOH_RANDSTART
#define ast_format_cache_get(name)
Definition: format_cache.h:281
char dir[256]
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define MOH_SORTALPHA
struct ast_vector_string * files
#define LOG_ERROR
Definition: logger.h:285
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1822
#define MOH_ANNOUNCEMENT
#define LOG_NOTICE
Definition: logger.h:263
#define ast_strlen_zero(a)
Definition: muted.c:73
struct ast_format * format
enum kill_methods kill_method
#define ao2_replace(dst, src)
Definition: astobj2.h:517
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
char args[256]
size_t kill_delay
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Definition: strings.h:94
char mode[80]
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
#define MOH_RANDOMIZE
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)

◆ moh_post_start()

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

Definition at line 239 of file res_musiconhold.c.

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

240 {
241  struct stasis_message *message;
242  struct ast_json *json_object;
243 
244  ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
245  moh_class_name, ast_channel_name(chan));
246 
247  json_object = ast_json_pack("{s: s}", "class", moh_class_name);
248  if (!json_object) {
249  return;
250  }
251 
253  ast_channel_moh_start_type(), json_object);
254  if (message) {
255  /* A channel snapshot must have been in the cache. */
256  ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
257 
258  stasis_publish(ast_channel_topic(chan), message);
259  }
260  ao2_cleanup(message);
261  ast_json_unref(json_object);
262 }
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
#define ast_assert(a)
Definition: utils.h:650
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:455
Blob of data associated with a channel.
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
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&#39;s subscribers.
Definition: stasis.c:1507
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
Abstract JSON element (object, array, string, int, ...).

◆ moh_post_stop()

static void moh_post_stop ( struct ast_channel chan)
static

Definition at line 264 of file res_musiconhold.c.

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

265 {
266  struct stasis_message *message;
267 
268  ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
269 
272  if (message) {
273  /* A channel snapshot must have been in the cache. */
274  ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
275 
276  stasis_publish(ast_channel_topic(chan), message);
277  }
278  ao2_cleanup(message);
279 }
#define ast_assert(a)
Definition: utils.h:650
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:455
Blob of data associated with a channel.
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
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&#39;s subscribers.
Definition: stasis.c:1507
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.

◆ moh_release()

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

Definition at line 981 of file res_musiconhold.c.

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(), moh_files_state::class, LOG_WARNING, mohclass::members, moh_post_stop(), mohclass_unref, mohdata::origwfmt, mohdata::parent, mohdata::pipe, and state.

Referenced by moh_alloc().

982 {
983  struct mohdata *moh = data;
984  struct mohclass *class = moh->parent;
985  struct ast_format *oldwfmt;
986 
987  ao2_lock(class);
988  AST_LIST_REMOVE(&moh->parent->members, moh, list);
989  ao2_unlock(class);
990 
991  close(moh->pipe[0]);
992  close(moh->pipe[1]);
993 
994  oldwfmt = moh->origwfmt;
995 
996  moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
997 
998  ast_free(moh);
999 
1000  if (chan) {
1001  struct moh_files_state *state;
1002 
1003  state = ast_channel_music_state(chan);
1004  if (state && state->class) {
1005  state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1006  }
1007  if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1008  ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1009  ast_channel_name(chan), ast_format_get_name(oldwfmt));
1010  }
1011 
1012  moh_post_stop(chan);
1013  }
1014 
1015  ao2_cleanup(oldwfmt);
1016 }
enum sip_cc_notify_state state
Definition: chan_sip.c:957
static void moh_post_stop(struct ast_channel *chan)
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
struct mohclass::@456 members
Definition of a media format.
Definition: format.c:43
#define ao2_unlock(a)
Definition: astobj2.h:730
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:855
#define ast_log
Definition: astobj2.c:42
#define ao2_lock(a)
Definition: astobj2.h:718
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5789
static struct ast_generator generator
Definition: app_fax.c:359
int pipe[2]
#define ast_free(a)
Definition: astmm.h:182
struct ast_format * origwfmt
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_music_state(const struct ast_channel *chan)
#define mohclass_unref(class, string)
struct mohclass * parent

◆ moh_rescan_files()

static void moh_rescan_files ( void  )
static

Definition at line 1334 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

1334  {
1335  struct ao2_iterator i;
1336  struct mohclass *c;
1337 
1338  i = ao2_iterator_init(mohclasses, 0);
1339 
1340  while ((c = ao2_iterator_next(&i))) {
1341  if (!strcasecmp(c->mode, "files")) {
1342  moh_scan_files(c);
1343  }
1344  ao2_ref(c, -1);
1345  }
1346 
1348 }
static struct ao2_container * mohclasses
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static struct test_val c
static int moh_scan_files(struct mohclass *class)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
char mode[80]
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.

◆ moh_scan_files()

static int moh_scan_files ( struct mohclass class)
static

Definition at line 1278 of file res_musiconhold.c.

References ao2_lock, ao2_ref, ao2_replace, 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_files_state::class, mohclass::dir, mohclass::files, moh_file_vector_alloc(), moh_filename_strcasecmp(), MOH_SORTALPHA, mohclass::name, on_moh_file(), and PATH_MAX.

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

1278  {
1279 
1280  char dir_path[PATH_MAX - sizeof(class->dir)];
1281  struct ast_vector_string *files;
1282 
1283  if (class->dir[0] != '/') {
1284  snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1285  } else {
1286  ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1287  }
1288 
1289  ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1290 
1291  /* 16 seems like a reasonable default */
1292  files = moh_file_vector_alloc(16);
1293  if (!files) {
1294  return -1;
1295  }
1296 
1297  if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1298  ao2_ref(files, -1);
1299  return -1;
1300  }
1301 
1302  if (ast_test_flag(class, MOH_SORTALPHA)) {
1304  }
1305 
1306  AST_VECTOR_COMPACT(files);
1307 
1308  ao2_lock(class);
1309  ao2_replace(class->files, files);
1310  ao2_unlock(class);
1311 
1312  return AST_VECTOR_SIZE(files);
1313 }
char name[MAX_MUSICCLASS]
#define ast_test_flag(p, flag)
Definition: utils.h:63
static int on_moh_file(const char *directory, const char *filename, void *obj)
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:640
#define ao2_unlock(a)
Definition: astobj2.h:730
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:396
char dir[256]
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
static int moh_filename_strcasecmp(const void *a, const void *b)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
#define MOH_SORTALPHA
struct ast_vector_string * files
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
#define ao2_replace(dst, src)
Definition: astobj2.h:517
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#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:171
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)

◆ mohalloc()

static struct mohdata* mohalloc ( struct mohclass cl)
static

Definition at line 955 of file res_musiconhold.c.

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

956 {
957  struct mohdata *moh;
958 
959  if (!(moh = ast_calloc(1, sizeof(*moh))))
960  return NULL;
961 
962  if (ast_pipe_nonblock(moh->pipe)) {
963  ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
964  ast_free(moh);
965  return NULL;
966  }
967 
968  moh->f.frametype = AST_FRAME_VOICE;
969  moh->f.subclass.format = cl->format;
971 
972  moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
973 
974  ao2_lock(cl);
975  AST_LIST_INSERT_HEAD(&cl->members, moh, list);
976  ao2_unlock(cl);
977 
978  return moh;
979 }
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition: utils.h:1000
#define LOG_WARNING
Definition: logger.h:274
struct mohclass::@456 members
#define ao2_unlock(a)
Definition: astobj2.h:730
#define NULL
Definition: resample.c:96
#define mohclass_ref(class, string)
struct ast_frame_subclass subclass
#define ast_log
Definition: astobj2.c:42
#define AST_FRIENDLY_OFFSET
Offset into a frame&#39;s data buffer.
#define ao2_lock(a)
Definition: astobj2.h:718
int pipe[2]
int errno
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_format * format
struct ast_frame f
enum ast_frame_type frametype
struct ast_format * format
struct mohclass * parent
struct mohdata::@458 list

◆ monmp3thread()

static void* monmp3thread ( void *  data)
static

Definition at line 770 of file res_musiconhold.c.

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(), moh_files_state::class, mohclass::dir, errno, mohclass::format, mohclass::kill_delay, mohclass::kill_method, killpid(), len(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, mohclass::members, MOH_MS_INTERVAL, NULL, mohclass::pid, mohdata::pipe, spawn_mp3(), mohclass::srcfd, and mohclass::timer.

Referenced by init_app_class(), and local_ast_moh_start().

771 {
772 #define MOH_MS_INTERVAL 100
773 
774  struct mohclass *class = data;
775  struct mohdata *moh;
776  short sbuf[8192];
777  int res = 0, res2;
778  int len;
779  struct timeval deadline, tv_tmp;
780 
781  deadline.tv_sec = 0;
782  deadline.tv_usec = 0;
783  for(;/* ever */;) {
784  pthread_testcancel();
785  /* Spawn mp3 player if it's not there */
786  if (class->srcfd < 0) {
787  if ((class->srcfd = spawn_mp3(class)) < 0) {
788  ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
789  /* Try again later */
790  sleep(500);
791  continue;
792  }
793  }
794  if (class->timer) {
795  struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
796 
797 #ifdef SOLARIS
798  thr_yield();
799 #endif
800  /* Pause some amount of time */
801  if (ast_poll(&pfd, 1, -1) > 0) {
802  if (ast_timer_ack(class->timer, 1) < 0) {
803  ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
804  return NULL;
805  }
806  /* 25 samples per second => 40ms framerate => 320 samples */
807  res = 320; /* 320/40 = 8 samples/ms */
808  } else {
809  ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
810  res = 0;
811  }
812  pthread_testcancel();
813  } else {
814  long delta;
815  /* Reliable sleep */
816  tv_tmp = ast_tvnow();
817  if (ast_tvzero(deadline))
818  deadline = tv_tmp;
819  delta = ast_tvdiff_ms(tv_tmp, deadline);
820  if (delta < MOH_MS_INTERVAL) { /* too early */
821  deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
822  usleep(1000 * (MOH_MS_INTERVAL - delta));
823  pthread_testcancel();
824  } else {
825  ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
826  deadline = tv_tmp;
827  }
828  /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
829  res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
830  }
831  /* For non-8000Hz formats, we need to alter the resolution */
832  res = res * ast_format_get_sample_rate(class->format) / 8000;
833 
834  if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
835  continue;
836  /* Read mp3 audio */
837  len = ast_format_determine_length(class->format, res);
838 
839  if ((res2 = read(class->srcfd, sbuf, len)) != len) {
840  if (!res2) {
841  close(class->srcfd);
842  class->srcfd = -1;
843  pthread_testcancel();
844  if (class->pid > 1) {
845  killpid(class->pid, class->kill_delay, class->kill_method);
846  class->pid = 0;
847  }
848  } else {
849  ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
850  }
851  continue;
852  }
853 
854  pthread_testcancel();
855 
856  ao2_lock(class);
857  AST_LIST_TRAVERSE(&class->members, moh, list) {
858  /* Write data */
859  if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
860  ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
861  }
862  }
863  ao2_unlock(class);
864  }
865  return NULL;
866 }
#define LOG_WARNING
Definition: logger.h:274
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:108
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ao2_unlock(a)
Definition: astobj2.h:730
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
#define ao2_lock(a)
Definition: astobj2.h:718
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:238
#define MOH_MS_INTERVAL
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition: timing.c:171
#define LOG_ERROR
Definition: logger.h:285
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
int pipe[2]
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2283
#define LOG_NOTICE
Definition: logger.h:263
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition: timing.c:161
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
static int spawn_mp3(struct mohclass *class)

◆ on_moh_file()

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

Definition at line 1216 of file res_musiconhold.c.

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

Referenced by moh_scan_files().

1217 {
1218  struct ast_vector_string *files = obj;
1219  char *full_path;
1220  char *extension;
1221 
1222  /* Skip files that starts with a dot */
1223  if (*filename == '.') {
1224  ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1225  directory, filename);
1226  return 0;
1227  }
1228 
1229  /* We can't do anything with files that don't have an extension,
1230  * so check that first and punt if we can't find something */
1231  extension = strrchr(filename, '.');
1232  if (!extension) {
1233  ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1234  directory, filename);
1235  return 0;
1236  }
1237 
1238  /* The extension needs at least two characters (after the .) to be useful */
1239  if (strlen(extension) < 3) {
1240  ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1241  "character extension\n", directory, filename);
1242  return 0;
1243  }
1244 
1245  /* Build the full path (excluding the extension) */
1246  if (ast_asprintf(&full_path, "%s/%.*s",
1247  directory,
1248  (int) (extension - filename), filename) < 0) {
1249  /* If we don't have enough memory to build this path, there is no
1250  * point in continuing */
1251  return 1;
1252  }
1253 
1254  /* If the file is present in multiple formats, ensure we only put it
1255  * into the list once. Pretty sure this is O(n^2). */
1256  if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1257  ast_free(full_path);
1258  return 0;
1259  }
1260 
1261  if (AST_VECTOR_APPEND(files, full_path)) {
1262  /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1263  * we stop iterating */
1264  ast_free(full_path);
1265  return 1;
1266  }
1267 
1268  return 0;
1269 }
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
structure to hold extensions
#define ast_free(a)
Definition: astmm.h:182
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:733

◆ play_moh_exec()

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

Definition at line 868 of file res_musiconhold.c.

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, parse(), S_OR, and timeout.

Referenced by load_module().

869 {
870  char *parse;
871  char *class;
872  int timeout = -1;
873  int res;
875  AST_APP_ARG(class);
876  AST_APP_ARG(duration);
877  );
878 
879  parse = ast_strdupa(data);
880 
881  AST_STANDARD_APP_ARGS(args, parse);
882 
883  if (!ast_strlen_zero(args.duration)) {
884  if (sscanf(args.duration, "%30d", &timeout) == 1) {
885  timeout *= 1000;
886  } else {
887  ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
888  }
889  }
890 
891  class = S_OR(args.class, NULL);
892  if (ast_moh_start(chan, class, NULL)) {
893  ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
894  return 0;
895  }
896 
897  if (timeout > 0)
898  res = ast_safe_sleep(chan, timeout);
899  else {
900  while (!(res = ast_safe_sleep(chan, 10000)));
901  }
902 
903  ast_moh_stop(chan);
904 
905  return res;
906 }
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1565
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
static int timeout
Definition: cdr_mysql.c:86
const char * args
#define NULL
Definition: resample.c:96
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7739
#define ast_log
Definition: astobj2.c:42
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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:7729
#define ast_strlen_zero(a)
Definition: muted.c:73
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const char * ast_channel_name(const struct ast_channel *chan)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define AST_APP_ARG(name)
Define an application argument.

◆ reload()

static int reload ( void  )
static

Definition at line 2244 of file res_musiconhold.c.

References ast_install_music_functions(), AST_MODULE_LOAD_SUCCESS, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), and local_ast_moh_stop().

Referenced by unload_module().

2245 {
2246  if (load_moh_classes(1)) {
2249  }
2250 
2251  return AST_MODULE_LOAD_SUCCESS;
2252 }
static int load_moh_classes(int reload)
static void local_ast_moh_cleanup(struct ast_channel *chan)
static void local_ast_moh_stop(struct ast_channel *chan)
static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
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:7713

◆ spawn_mp3()

static int spawn_mp3 ( struct mohclass class)
static

Definition at line 584 of file res_musiconhold.c.

References mohclass::args, 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, moh_files_state::class, mohclass::dir, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, NULL, mohclass::pid, respawn_time, mohclass::start, and strsep().

Referenced by monmp3thread().

585 {
586  int fds[2];
587  int files = 0;
588  char fns[MAX_MP3S][80];
589  char *argv[MAX_MP3S + 50];
590  char xargs[256];
591  char *argptr;
592  int argc = 0;
593  DIR *dir = NULL;
594  struct dirent *de;
595 
596 
597  if (!strcasecmp(class->dir, "nodir")) {
598  files = 1;
599  } else {
600  dir = opendir(class->dir);
601  if (!dir && strncasecmp(class->dir, "http://", 7)) {
602  ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
603  return -1;
604  }
605  }
606 
607  if (!ast_test_flag(class, MOH_CUSTOM)) {
608  argv[argc++] = "mpg123";
609  argv[argc++] = "-q";
610  argv[argc++] = "-s";
611  argv[argc++] = "--mono";
612  argv[argc++] = "-r";
613  argv[argc++] = "8000";
614 
615  if (!ast_test_flag(class, MOH_SINGLE)) {
616  argv[argc++] = "-b";
617  argv[argc++] = "2048";
618  }
619 
620  argv[argc++] = "-f";
621 
622  if (ast_test_flag(class, MOH_QUIET))
623  argv[argc++] = "4096";
624  else
625  argv[argc++] = "8192";
626 
627  /* Look for extra arguments and add them to the list */
628  ast_copy_string(xargs, class->args, sizeof(xargs));
629  argptr = xargs;
630  while (!ast_strlen_zero(argptr)) {
631  argv[argc++] = argptr;
632  strsep(&argptr, ",");
633  }
634  } else {
635  /* Format arguments for argv vector */
636  ast_copy_string(xargs, class->args, sizeof(xargs));
637  argptr = xargs;
638  while (!ast_strlen_zero(argptr)) {
639  argv[argc++] = argptr;
640  strsep(&argptr, " ");
641  }
642  }
643 
644  if (!strncasecmp(class->dir, "http://", 7)) {
645  ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
646  argv[argc++] = fns[files];
647  files++;
648  } else if (dir) {
649  while ((de = readdir(dir)) && (files < MAX_MP3S)) {
650  if ((strlen(de->d_name) > 3) &&
651  ((ast_test_flag(class, MOH_CUSTOM) &&
652  (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
653  !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
654  !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
655  ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
656  argv[argc++] = fns[files];
657  files++;
658  }
659  }
660  }
661  argv[argc] = NULL;
662  if (dir) {
663  closedir(dir);
664  }
665  if (pipe(fds)) {
666  ast_log(LOG_WARNING, "Pipe failed\n");
667  return -1;
668  }
669  if (!files) {
670  ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
671  close(fds[0]);
672  close(fds[1]);
673  return -1;
674  }
675  if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
676  sleep(respawn_time - (time(NULL) - class->start));
677  }
678 
679  time(&class->start);
680  class->pid = ast_safe_fork(0);
681  if (class->pid < 0) {
682  close(fds[0]);
683  close(fds[1]);
684  ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
685  return -1;
686  }
687  if (!class->pid) {
689  ast_set_priority(0);
690 
691  close(fds[0]);
692  /* Stdout goes to pipe */
693  dup2(fds[1], STDOUT_FILENO);
694 
695  /* Close everything else */
696  ast_close_fds_above_n(STDERR_FILENO);
697 
698  /* Child */
699  if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
700  ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
701  _exit(1);
702  }
703  setpgid(0, getpid());
704  if (ast_test_flag(class, MOH_CUSTOM)) {
705  execv(argv[0], argv);
706  } else {
707  /* Default install is /usr/local/bin */
708  execv(LOCAL_MPG_123, argv);
709  /* Many places have it in /usr/bin */
710  execv(MPG_123, argv);
711  /* Check PATH as a last-ditch effort */
712  execvp("mpg123", argv);
713  }
714  /* Can't use logger, since log FDs are closed */
715  fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
716  close(fds[1]);
717  _exit(1);
718  } else {
719  /* Parent */
720  close(fds[1]);
721  }
722  return fds[0];
723 }
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define LOG_WARNING
Definition: logger.h:274
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:2965
#define MPG_123
#define NULL
Definition: resample.c:96
char dir[256]
#define ast_log
Definition: astobj2.c:42
#define MOH_QUIET
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:1799
#define MAX_MP3S
int errno
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:2970
#define ast_strlen_zero(a)
Definition: muted.c:73
time_t start
static int respawn_time
#define MOH_CUSTOM
char * strsep(char **str, const char *delims)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
char args[256]
#define MOH_SINGLE
#define ast_opt_high_priority
Definition: options.h:110
#define LOCAL_MPG_123

◆ start_moh_exec()

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

Definition at line 908 of file res_musiconhold.c.

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

Referenced by load_module().

909 {
910  char *parse;
911  char *class;
913  AST_APP_ARG(class);
914  );
915 
916  parse = ast_strdupa(data);
917 
918  AST_STANDARD_APP_ARGS(args, parse);
919 
920  class = S_OR(args.class, NULL);
921  if (ast_moh_start(chan, class, NULL))
922  ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
923 
924  return 0;
925 }
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
const char * args
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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:7729
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const char * ast_channel_name(const struct ast_channel *chan)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define AST_APP_ARG(name)
Define an application argument.

◆ stop_moh_exec()

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

Definition at line 927 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

928 {
929  ast_moh_stop(chan);
930 
931  return 0;
932 }
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7739

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2261 of file res_musiconhold.c.

References ao2_t_callback, ARRAY_LEN, ast_cli_unregister_multiple(), ast_log, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CHANNEL_DEPEND, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), ast_unregister_atexit(), ASTERISK_GPL_KEY, load_module(), LOG_WARNING, moh_class_inuse(), mohclass_unref, NULL, play_moh, reload(), start_moh, and stop_moh.

2262 {
2263  int res = 0;
2264  struct mohclass *class = NULL;
2265 
2266  /* XXX This check shouldn't be required if module ref counting was being used
2267  * properly ... */
2268  if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2269  class = mohclass_unref(class, "unref of class from module unload callback");
2270  res = -1;
2271  }
2272 
2273  if (res < 0) {
2274  ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2275  return res;
2276  }
2277 
2279 
2280  ast_moh_destroy();
2286 
2287  return res;
2288 }
static struct ao2_container * mohclasses
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static const char stop_moh[]
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define LOG_WARNING
Definition: logger.h:274
static struct ast_cli_entry cli_moh[]
#define NULL
Definition: resample.c:96
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
static void ast_moh_destroy(void)
void ast_uninstall_music_functions(void)
Definition: channel.c:7722
#define ast_log
Definition: astobj2.c:42
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1022
static int moh_class_inuse(void *obj, void *arg, int flags)
static const char start_moh[]
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1714
#define mohclass_unref(class, string)
static const char play_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 2296 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2296 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2182 of file res_musiconhold.c.

◆ global_flags

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

global MOH_ flags

Definition at line 160 of file res_musiconhold.c.

◆ moh_file_stream

struct ast_generator moh_file_stream
static

Definition at line 576 of file res_musiconhold.c.

◆ mohclasses

struct ao2_container* mohclasses
static

Definition at line 206 of file res_musiconhold.c.

◆ mohgen

struct ast_generator mohgen
static

Definition at line 1085 of file res_musiconhold.c.

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 123 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ respawn_time

int respawn_time = 20
static

Definition at line 127 of file res_musiconhold.c.

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

◆ start_moh

const char start_moh[] = "StartMusicOnHold"
static

Definition at line 124 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 125 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().