Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Enumerations | Functions | Variables
res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.h"
Include dependency graph for res_musiconhold.c:

Go to the source code of this file.

Data Structures

struct  moh_files_state
 
struct  mohclass
 
struct  mohdata
 

Macros

#define DONT_UNREF   0
 
#define get_mohbyname(a, b, c)   _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
 
#define HANDLE_REF   1
 
#define INITIAL_NUM_FILES   8
 
#define LOCAL_MPG_123   "/usr/local/bin/mpg123"
 
#define MAX_MP3S   256
 
#define MOH_ANNOUNCEMENT   (1 << 6)
 
#define MOH_CACHERTCLASSES   (1 << 5)
 
#define moh_class_malloc()   _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
 
#define MOH_CUSTOM   (1 << 2)
 
#define MOH_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)   _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
 
#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)
 
static struct mohclass_mohclass_unref (struct mohclass *class, const char *tag, 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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 1583 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 1410 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 1478 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 
)    _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)

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

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2298 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 1585 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.

1586 {
1587  struct mohclass *class;
1588 
1590  "Allocating new moh class", file, line, funcname);
1591  if (class) {
1592  class->format = ao2_bump(ast_format_slin);
1593  class->srcfd = -1;
1594  class->kill_delay = 100000;
1595 
1596  /* We create an empty one by default */
1597  class->files = moh_file_vector_alloc(0);
1598  if (!class->files) {
1599  ao2_ref(class, -1);
1600  return NULL;
1601  }
1602  }
1603 
1604  return class;
1605 }
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 1411 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.

1412 {
1413  struct mohclass *mohclass = NULL;
1414 
1415  mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1416 
1417  if (mohclass && !moh_diff(mohclass, moh)) {
1418  ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1419  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1420  if (unref) {
1421  moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1422  }
1423  return -1;
1424  } else if (mohclass) {
1425  /* Found a class, but it's different from the one being registered */
1426  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1427  }
1428 
1429  time(&moh->start);
1430  moh->start -= respawn_time;
1431 
1432  if (!strcasecmp(moh->mode, "files")) {
1433  if (init_files_class(moh)) {
1434  if (unref) {
1435  moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1436  }
1437  return -1;
1438  }
1439  } else if (!strcasecmp(moh->mode, "playlist")) {
1440  size_t file_count;
1441 
1442  ao2_lock(moh);
1443  file_count = AST_VECTOR_SIZE(moh->files);
1444  ao2_unlock(moh);
1445 
1446  if (!file_count) {
1447  if (unref) {
1448  moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1449  }
1450  return -1;
1451  }
1452  } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1453  !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1454  !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1455  if (init_app_class(moh)) {
1456  if (unref) {
1457  moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1458  }
1459  return -1;
1460  }
1461  } else {
1462  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1463  if (unref) {
1464  moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1465  }
1466  return -1;
1467  }
1468 
1469  ao2_t_link(mohclasses, moh, "Adding class to container");
1470 
1471  if (unref) {
1472  moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1473  }
1474 
1475  return 0;
1476 }
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 1479 of file res_musiconhold.c.

References ao2_t_unlink.

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

◆ _mohclass_unref()

static struct mohclass* _mohclass_unref ( struct mohclass class,
const char *  tag,
const char *  file,
int  line,
const char *  funcname 
)
static

Definition at line 221 of file res_musiconhold.c.

References __ao2_ref(), ao2_callback, ao2_match_by_addr(), ao2_ref, ast_log, moh_files_state::class, LOG_WARNING, mohclass::name, NULL, and OBJ_POINTER.

222 {
224 
225  if (dup) {
226  if (__ao2_ref(dup, -1, tag, file, line, funcname) == 2) {
227  ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
228  class, class->name, file, line, funcname);
229  } else {
230  ao2_ref(class, -1);
231  }
232  } else {
233  __ao2_ref(class, -1, tag, file, line, funcname);
234  }
235  return NULL;
236 }
static struct ao2_container * mohclasses
char name[MAX_MUSICCLASS]
#define OBJ_POINTER
Definition: astobj2.h:1154
#define LOG_WARNING
Definition: logger.h:274
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
#define NULL
Definition: resample.c:96
int __ao2_ref(void *o, int delta, const char *tag, const char *file, int line, const char *func)
Definition: astobj2.c:498
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 2298 of file res_musiconhold.c.

◆ ast_moh_destroy()

static void ast_moh_destroy ( void  )
static

Definition at line 2066 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().

2067 {
2068  ast_verb(2, "Destroying musiconhold processes\n");
2069  if (mohclasses) {
2070  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
2071  ao2_ref(mohclasses, -1);
2072  mohclasses = NULL;
2073  }
2074 }
static struct ao2_container * mohclasses
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
#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
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:452
#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:2064
#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
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 1510 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().

1511 {
1512  int which=0;
1513  struct mohclass *cur;
1514  char *c = NULL;
1515  int wordlen = strlen(word);
1516  struct ao2_iterator i;
1517 
1518  if (pos != 3) {
1519  return NULL;
1520  }
1521 
1522  i = ao2_iterator_init(mohclasses, 0);
1523  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1524  if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1525  c = ast_strdup(cur->name);
1526  mohclass_unref(cur, "drop ref in iterator loop break");
1527  break;
1528  }
1529  mohclass_unref(cur, "drop ref in iterator loop");
1530  }
1532 
1533  return c;
1534 }
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 2076 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.

2077 {
2078  switch (cmd) {
2079  case CLI_INIT:
2080  e->command = "moh reload";
2081  e->usage =
2082  "Usage: moh reload\n"
2083  " Reloads the MusicOnHold module.\n"
2084  " Alias for 'module reload res_musiconhold.so'\n";
2085  return NULL;
2086  case CLI_GENERATE:
2087  return NULL;
2088  }
2089 
2090  if (a->argc != e->args)
2091  return CLI_SHOWUSAGE;
2092 
2093  /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2094  ast_module_reload("res_musiconhold");
2095 
2096  return CLI_SUCCESS;
2097 }
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1562
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 2142 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.

2143 {
2144  struct mohclass *class;
2145  struct ao2_iterator i;
2146 
2147  switch (cmd) {
2148  case CLI_INIT:
2149  e->command = "moh show classes";
2150  e->usage =
2151  "Usage: moh show classes\n"
2152  " Lists all MusicOnHold classes.\n";
2153  return NULL;
2154  case CLI_GENERATE:
2155  return NULL;
2156  }
2157 
2158  if (a->argc != e->args)
2159  return CLI_SHOWUSAGE;
2160 
2161  i = ao2_iterator_init(mohclasses, 0);
2162  for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2163  ast_cli(a->fd, "Class: %s\n", class->name);
2164  ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2165  ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2166  if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2167  ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2168  }
2169  if (ast_test_flag(class, MOH_CUSTOM)) {
2170  ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2171  ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2172  ast_cli(a->fd, "\tKill Method: %s\n",
2173  class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2174  }
2175  if (strcasecmp(class->mode, "files")) {
2176  ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2177  }
2178  }
2180 
2181  return CLI_SUCCESS;
2182 }
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 2099 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.

2100 {
2101  struct mohclass *class;
2102  struct ao2_iterator i;
2103 
2104  switch (cmd) {
2105  case CLI_INIT:
2106  e->command = "moh show files";
2107  e->usage =
2108  "Usage: moh show files\n"
2109  " Lists all loaded file-based MusicOnHold classes and their\n"
2110  " files.\n";
2111  return NULL;
2112  case CLI_GENERATE:
2113  return NULL;
2114  }
2115 
2116  if (a->argc != e->args)
2117  return CLI_SHOWUSAGE;
2118 
2119  i = ao2_iterator_init(mohclasses, 0);
2120  for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2121  struct ast_vector_string *files;
2122 
2123  ao2_lock(class);
2124  files = ao2_bump(class->files);
2125  ao2_unlock(class);
2126 
2127  if (AST_VECTOR_SIZE(files)) {
2128  int x;
2129  ast_cli(a->fd, "Class: %s\n", class->name);
2130  for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2131  ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2132  }
2133  }
2134 
2135  ao2_ref(files, -1);
2136  }
2138 
2139  return CLI_SUCCESS;
2140 }
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 1536 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.

1537 {
1538  struct mohclass *cur;
1539  int len;
1540  int found = 0;
1541  struct ao2_iterator i;
1542 
1543  switch (cmd) {
1544  case CLI_INIT:
1545  e->command = "moh unregister class";
1546  e->usage =
1547  "Usage: moh unregister class <class>\n"
1548  " Unregisters a realtime moh class.\n";
1549  return NULL;
1550  case CLI_GENERATE:
1551  return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1552  }
1553 
1554  if (a->argc != 4)
1555  return CLI_SHOWUSAGE;
1556 
1557  len = strlen(a->argv[3]);
1558 
1559  i = ao2_iterator_init(mohclasses, 0);
1560  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1561  if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1562  found = 1;
1563  break;
1564  }
1565  mohclass_unref(cur, "drop ref in iterator loop");
1566  }
1568 
1569  if (found) {
1570  moh_unregister(cur);
1571  mohclass_unref(cur, "drop ref after unregister");
1572  } else {
1573  ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1574  }
1575 
1576  return CLI_SUCCESS;
1577 }
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 1371 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().

1372 {
1373  if (!strcasecmp(class->mode, "custom")) {
1374  ast_set_flag(class, MOH_CUSTOM);
1375  } else if (!strcasecmp(class->mode, "mp3nb")) {
1376  ast_set_flag(class, MOH_SINGLE);
1377  } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1378  ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1379  } else if (!strcasecmp(class->mode, "quietmp3")) {
1380  ast_set_flag(class, MOH_QUIET);
1381  }
1382 
1383  class->srcfd = -1;
1384 
1385  if (!(class->timer = ast_timer_open())) {
1386  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1387  return -1;
1388  }
1389  if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1390  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1391  ast_timer_close(class->timer);
1392  class->timer = NULL;
1393  }
1394 
1395  if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1396  ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1397  if (class->timer) {
1398  ast_timer_close(class->timer);
1399  class->timer = NULL;
1400  }
1401  return -1;
1402  }
1403 
1404  return 0;
1405 }
#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:567
#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 1317 of file res_musiconhold.c.

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

Referenced by _moh_register().

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

2218 {
2219  int res;
2220 
2222  moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2223  if (!mohclasses) {
2224  return AST_MODULE_LOAD_DECLINE;
2225  }
2226 
2227  if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2228  ast_log(LOG_WARNING, "No music on hold classes configured, "
2229  "disabling music on hold.\n");
2230  } else {
2233  }
2234 
2238  if (!res)
2240  if (!res)
2242 
2243  return AST_MODULE_LOAD_SUCCESS;
2244 }
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:7850
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 1972 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().

1973 {
1974  struct ast_config *cfg;
1975  struct ast_variable *var;
1976  struct mohclass *class;
1977  char *cat;
1978  int numclasses = 0;
1979  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1980 
1981  cfg = ast_config_load("musiconhold.conf", config_flags);
1982 
1983  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1984  if (ast_check_realtime("musiconhold") && reload) {
1985  ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
1987  }
1988  moh_rescan_files();
1989  return 0;
1990  }
1991 
1993  if (ast_check_realtime("musiconhold") && reload) {
1994  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1996  }
1997  return 0;
1998  }
1999 
2000  if (reload) {
2001  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2002  }
2003 
2006 
2007  cat = ast_category_browse(cfg, NULL);
2008  for (; cat; cat = ast_category_browse(cfg, cat)) {
2009  /* Setup common options from [general] section */
2010  if (!strcasecmp(cat, "general")) {
2011  for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2012  if (!strcasecmp(var->name, "cachertclasses")) {
2014  } else if (!strcasecmp(var->name, "preferchannelclass")) {
2016  } else {
2017  ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2018  }
2019  }
2020  continue;
2021  }
2022 
2023  if (!(class = moh_class_malloc())) {
2024  break;
2025  }
2026 
2027  moh_parse_options(ast_variable_browse(cfg, cat), class);
2028  /* For compatibility with the past, we overwrite any name=name
2029  * with the context [name]. */
2030  ast_copy_string(class->name, cat, sizeof(class->name));
2031 
2032  if (ast_strlen_zero(class->dir)) {
2033  if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2034  strcpy(class->dir, "nodir");
2035  } else {
2036  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2037  class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2038  continue;
2039  }
2040  }
2041  if (ast_strlen_zero(class->mode)) {
2042  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2043  class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2044  continue;
2045  }
2046  if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2047  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2048  class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2049  continue;
2050  }
2051 
2052  /* Don't leak a class when it's already registered */
2053  if (!moh_register(class, reload, HANDLE_REF)) {
2054  numclasses++;
2055  }
2056  }
2057 
2058  ast_config_destroy(cfg);
2059 
2061  moh_classes_delete_marked, NULL, "Purge marked classes");
2062 
2063  return numclasses;
2064 }
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
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:1951
#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_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 1607 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().

1608 {
1609  struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1610 
1611  if (var) {
1612  const char *mode = ast_variable_find_in_list(var, "mode");
1613  if (ast_strings_equal(mode, "playlist")) {
1614  struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1615  char *category = NULL;
1616  size_t entry_count = 0;
1617 
1618  /* entries is NULL if there are no results */
1619  if (entries) {
1620  while ((category = ast_category_browse(entries, category))) {
1621  const char *entry = ast_variable_retrieve(entries, category, "entry");
1622 
1623  if (entry) {
1624  struct ast_variable *dup = ast_variable_new("entry", entry, "");
1625  if (dup) {
1626  entry_count++;
1627  ast_variable_list_append(&var, dup);
1628  }
1629  }
1630  }
1631  ast_config_destroy(entries);
1632  }
1633 
1634  if (entry_count == 0) {
1635  /* Behave as though this class doesn't exist */
1636  ast_variables_destroy(var);
1637  var = NULL;
1638  }
1639  }
1640  }
1641 
1642  if (!var) {
1644  "Music on Hold class '%s' not found in memory/database. "
1645  "Verify your configuration.\n",
1646  name);
1647  }
1648  return var;
1649 }
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 1485 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().

1486 {
1488 
1489  if (state) {
1491  if (state->class) {
1492  /* This should never happen. We likely just leaked some resource. */
1493  state->class =
1494  mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1495  ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1496  }
1497  ao2_cleanup(state->origwfmt);
1498  ao2_cleanup(state->mohwfmt);
1499  ast_free(state);
1500  /* Only held a module reference if we had a music state */
1502  }
1503 }
#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 1651 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().

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

1879 {
1881 
1882  ast_channel_lock(chan);
1884  if (ast_channel_music_state(chan)) {
1885  if (ast_channel_stream(chan)) {
1888  }
1889  }
1890  ast_channel_unlock(chan);
1891 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
#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:2946
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:2902
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:959
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:5890
#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 2198 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().

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

◆ moh_class_destructor()

static void moh_class_destructor ( void *  obj)
static

Definition at line 1893 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().

1894 {
1895  struct mohclass *class = obj;
1896  struct mohdata *member;
1897  pthread_t tid = 0;
1898 
1899  ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1900 
1901  ao2_lock(class);
1902  while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1903  ast_free(member);
1904  }
1905  ao2_cleanup(class->files);
1906  ao2_unlock(class);
1907 
1908  /* Kill the thread first, so it cannot restart the child process while the
1909  * class is being destroyed */
1910  if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1911  tid = class->thread;
1912  class->thread = AST_PTHREADT_NULL;
1913  pthread_cancel(tid);
1914  /* We'll collect the exit status later, after we ensure all the readers
1915  * are dead. */
1916  }
1917 
1918  if (class->pid > 1) {
1919  char buff[8192];
1920  int bytes, tbytes = 0, stime = 0;
1921 
1922  ast_debug(1, "killing %d!\n", class->pid);
1923 
1924  stime = time(NULL) + 2;
1925  killpid(class->pid, class->kill_delay, class->kill_method);
1926 
1927  while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1928  (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1929  tbytes = tbytes + bytes;
1930  }
1931 
1932  ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1933  class->pid, tbytes);
1934 
1935  class->pid = 0;
1936  close(class->srcfd);
1937  class->srcfd = -1;
1938  }
1939 
1940  if (class->timer) {
1941  ast_timer_close(class->timer);
1942  class->timer = NULL;
1943  }
1944 
1945  ao2_cleanup(class->format);
1946 
1947  /* Finally, collect the exit status of the monitor thread */
1948  if (tid > 0) {
1949  pthread_join(tid, NULL);
1950  }
1951 
1952 }
struct mohdata::@467 list
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:452
#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:1519

◆ moh_class_hash()

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

Definition at line 2191 of file res_musiconhold.c.

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

Referenced by load_module().

2192 {
2193  const struct mohclass *class = obj;
2194 
2195  return ast_str_case_hash(class->name);
2196 }
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 2256 of file res_musiconhold.c.

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

Referenced by unload_module().

2257 {
2258  struct mohclass *class = obj;
2259 
2260  return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2261 }
#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 1954 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

1955 {
1956  struct mohclass *class = obj;
1957 
1958  if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1959  class->delete = 1;
1960  }
1961 
1962  return 0;
1963 }
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 1965 of file res_musiconhold.c.

References CMP_MATCH, and mohclass::delete.

Referenced by load_moh_classes().

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

◆ moh_diff()

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

Definition at line 1352 of file res_musiconhold.c.

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

Referenced by _moh_register().

1353 {
1354  if (!old || !new) {
1355  return -1;
1356  }
1357 
1358  if (strcmp(old->dir, new->dir)) {
1359  return -1;
1360  } else if (strcmp(old->mode, new->mode)) {
1361  return -1;
1362  } else if (strcmp(old->args, new->args)) {
1363  return -1;
1364  } else if (old->flags != new->flags) {
1365  return -1;
1366  }
1367 
1368  return 0;
1369 }
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 1272 of file res_musiconhold.c.

Referenced by moh_scan_files().

1273 {
1274  const char **s1 = (const char **) a;
1275  const char **s2 = (const char **) b;
1276  return strcasecmp(*s1, *s2);
1277 }
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:959
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:2064
#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:2945
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:2946
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:5189
#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:959
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:5890
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:5890
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
#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:5189
struct ast_frame f
const char * ast_channel_name(const struct ast_channel *chan)
union ast_frame::@263 data
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:7876
#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:7866
#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, 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, "/") || strstr(var->value, "://")) {
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 a URL or an 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_ref(mohclass->files, -1);
1213  mohclass->files = playlist_entries;
1214  }
1215 }
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:286
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:1951
#define MOH_ANNOUNCEMENT
#define LOG_NOTICE
Definition: logger.h:263
struct ast_format * format
enum kill_methods kill_method
#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:695
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
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:1511
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:695
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
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:1511
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:959
static void moh_post_stop(struct ast_channel *chan)
#define LOG_WARNING
Definition: logger.h:274
struct mohclass * class
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:5890
static struct ast_generator generator
Definition: app_fax.c:359
int pipe[2]
#define ast_free(a)
Definition: astmm.h:182
struct mohclass::@465 members
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 1336 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().

1336  {
1337  struct ao2_iterator i;
1338  struct mohclass *c;
1339 
1340  i = ao2_iterator_init(mohclasses, 0);
1341 
1342  while ((c = ao2_iterator_next(&i))) {
1343  if (!strcasecmp(c->mode, "files")) {
1344  moh_scan_files(c);
1345  }
1346  ao2_ref(c, -1);
1347  }
1348 
1350 }
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 1279 of file res_musiconhold.c.

References ao2_lock, ao2_ref, ao2_unlock, ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_file_read_dir, ast_test_flag, AST_VECTOR_COMPACT, AST_VECTOR_SIZE, AST_VECTOR_SORT, moh_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().

1279  {
1280 
1281  char dir_path[PATH_MAX - sizeof(class->dir)];
1282  struct ast_vector_string *files;
1283 
1284  if (class->dir[0] != '/') {
1285  snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1286  } else {
1287  ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1288  }
1289 
1290  ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1291 
1292  /* 16 seems like a reasonable default */
1293  files = moh_file_vector_alloc(16);
1294  if (!files) {
1295  return -1;
1296  }
1297 
1298  if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1299  ao2_ref(files, -1);
1300  return -1;
1301  }
1302 
1303  if (ast_test_flag(class, MOH_SORTALPHA)) {
1305  }
1306 
1307  AST_VECTOR_COMPACT(files);
1308 
1309  ao2_lock(class);
1310  ao2_ref(class->files, -1);
1311  class->files = files;
1312  ao2_unlock(class);
1313 
1314  return AST_VECTOR_SIZE(files);
1315 }
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:452
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
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:1060
struct mohdata::@467 list
#define LOG_WARNING
Definition: logger.h:274
#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 mohclass::@465 members
struct ast_frame f
enum ast_frame_type frametype
struct ast_format * format
struct mohclass * parent

◆ 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:452
#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 1217 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().

1218 {
1219  struct ast_vector_string *files = obj;
1220  char *full_path;
1221  char *extension;
1222 
1223  /* Skip files that starts with a dot */
1224  if (*filename == '.') {
1225  ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1226  directory, filename);
1227  return 0;
1228  }
1229 
1230  /* We can't do anything with files that don't have an extension,
1231  * so check that first and punt if we can't find something */
1232  extension = strrchr(filename, '.');
1233  if (!extension) {
1234  ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1235  directory, filename);
1236  return 0;
1237  }
1238 
1239  /* The extension needs at least two characters (after the .) to be useful */
1240  if (strlen(extension) < 3) {
1241  ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1242  "character extension\n", directory, filename);
1243  return 0;
1244  }
1245 
1246  /* Build the full path (excluding the extension) */
1247  if (ast_asprintf(&full_path, "%s/%.*s",
1248  directory,
1249  (int) (extension - filename), filename) < 0) {
1250  /* If we don't have enough memory to build this path, there is no
1251  * point in continuing */
1252  return 1;
1253  }
1254 
1255  /* If the file is present in multiple formats, ensure we only put it
1256  * into the list once. Pretty sure this is O(n^2). */
1257  if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1258  ast_free(full_path);
1259  return 0;
1260  }
1261 
1262  if (AST_VECTOR_APPEND(files, full_path)) {
1263  /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1264  * we stop iterating */
1265  ast_free(full_path);
1266  return 1;
1267  }
1268 
1269  return 0;
1270 }
#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:452
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:1574
#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:7876
#define ast_strlen_zero(foo)
Definition: strings.h:52
#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:7866
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 2246 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().

2247 {
2248  if (load_moh_classes(1)) {
2251  }
2252 
2253  return AST_MODULE_LOAD_SUCCESS;
2254 }
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:7850

◆ 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:3042
#define MPG_123
#define NULL
Definition: resample.c:96
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:3047
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:7866
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:7876

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2263 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.

2264 {
2265  int res = 0;
2266  struct mohclass *class = NULL;
2267 
2268  /* XXX This check shouldn't be required if module ref counting was being used
2269  * properly ... */
2270  if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2271  class = mohclass_unref(class, "unref of class from module unload callback");
2272  res = -1;
2273  }
2274 
2275  if (res < 0) {
2276  ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2277  return res;
2278  }
2279 
2281 
2282  ast_moh_destroy();
2288 
2289  return res;
2290 }
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:7859
#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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static

Definition at line 2298 of file res_musiconhold.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2298 of file res_musiconhold.c.

◆ cli_moh

struct ast_cli_entry cli_moh[]
static

Definition at line 2184 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().