Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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)   ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })
 
#define MPG_123   "/usr/bin/mpg123"
 

Enumerations

enum  kill_methods { KILL_METHOD_PROCESS_GROUP = 0 , KILL_METHOD_PROCESS }
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static struct mohclass_get_mohbyname (const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
 
static struct mohclass_moh_class_malloc (const char *file, int line, const char *funcname)
 
static int _moh_register (struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
 
static int _moh_unregister (struct mohclass *moh, const char *file, int line, const char *funcname)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static void ast_moh_destroy (void)
 
static int ast_moh_files_next (struct ast_channel *chan)
 
static char * complete_mohclass_realtime (const char *line, const char *word, int pos, int state)
 Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match a prefix in the given position. As many functions of this kind, each invokation has O(state) time complexity so be careful in using it. More...
 
static struct mohclassget_mohbydigit (char digit)
 
static char * handle_cli_moh_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_cli_moh_show_classes (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_cli_moh_show_files (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_cli_moh_unregister_class (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int init_app_class (struct mohclass *class)
 
static int init_files_class (struct mohclass *class)
 
static int killer (pid_t pid, int signum, enum kill_methods kill_method)
 
static void killpid (int pid, size_t delay, enum kill_methods kill_method)
 
static int load_module (void)
 Load the module. More...
 
static int load_moh_classes (int reload)
 
static struct ast_variableload_realtime_musiconhold (const char *name)
 
static void local_ast_moh_cleanup (struct ast_channel *chan)
 
static int local_ast_moh_start (struct ast_channel *chan, const char *mclass, const char *interpclass)
 
static void local_ast_moh_stop (struct ast_channel *chan)
 
static void * moh_alloc (struct ast_channel *chan, void *params)
 
static int moh_class_cmp (void *obj, void *arg, int flags)
 
static void moh_class_destructor (void *obj)
 
static int moh_class_hash (const void *obj, const int flags)
 
static int moh_class_inuse (void *obj, void *arg, int flags)
 
static int moh_class_mark (void *obj, void *arg, int flags)
 
static int moh_classes_delete_marked (void *obj, void *arg, int flags)
 
static int moh_diff (struct mohclass *old, struct mohclass *new)
 
static int moh_digit_match (void *obj, void *arg, int flags)
 
static struct ast_vector_stringmoh_file_vector_alloc (int initial_capacity)
 
static void moh_file_vector_destructor (void *obj)
 
static int moh_filename_strcasecmp (const void *a, const void *b)
 
static void * moh_files_alloc (struct ast_channel *chan, void *params)
 
static int moh_files_generator (struct ast_channel *chan, void *data, int len, int samples)
 
static struct ast_framemoh_files_readframe (struct ast_channel *chan)
 
static void moh_files_release (struct ast_channel *chan, void *data)
 
static void moh_files_write_format_change (struct ast_channel *chan, void *data)
 
static int moh_generate (struct ast_channel *chan, void *data, int len, int samples)
 
static void moh_handle_digit (struct ast_channel *chan, char digit)
 
static void moh_parse_options (struct ast_variable *var, struct mohclass *mohclass)
 
static void moh_post_start (struct ast_channel *chan, const char *moh_class_name)
 
static void moh_post_stop (struct ast_channel *chan)
 
static void moh_release (struct ast_channel *chan, void *data)
 
static void moh_rescan_files (void)
 
static int moh_scan_files (struct mohclass *class)
 
static struct mohdatamohalloc (struct mohclass *cl)
 
static void * monmp3thread (void *data)
 
static int on_moh_file (const char *directory, const char *filename, void *obj)
 
static int play_moh_exec (struct ast_channel *chan, const char *data)
 
static int reload (void)
 
static int spawn_mp3 (struct mohclass *class)
 
static int start_moh_exec (struct ast_channel *chan, const char *data)
 
static int stop_moh_exec (struct ast_channel *chan, const char *data)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_cli_entry cli_moh []
 
static struct ast_flags global_flags [1] = {{0}}
 
static struct ast_generator moh_file_stream
 
static struct ao2_containermohclasses
 
static struct ast_generator mohgen
 
static const char play_moh [] = "MusicOnHold"
 
static int respawn_time = 20
 
static const char start_moh [] = "StartMusicOnHold"
 
static const char stop_moh [] = "StopMusicOnHold"
 

Detailed Description

Routines implementing music on hold.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m

Definition in file res_musiconhold.c.

Macro Definition Documentation

◆ DONT_UNREF

#define DONT_UNREF   0

Definition at line 78 of file res_musiconhold.c.

◆ get_mohbyname

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

Definition at line 934 of file res_musiconhold.c.

◆ HANDLE_REF

#define HANDLE_REF   1

Definition at line 77 of file res_musiconhold.c.

◆ INITIAL_NUM_FILES

#define INITIAL_NUM_FILES   8

Definition at line 76 of file res_musiconhold.c.

◆ LOCAL_MPG_123

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

Definition at line 208 of file res_musiconhold.c.

◆ MAX_MP3S

#define MAX_MP3S   256

Definition at line 210 of file res_musiconhold.c.

◆ MOH_ANNOUNCEMENT

#define MOH_ANNOUNCEMENT   (1 << 6)

Do we play announcement files between songs on this channel?

Definition at line 153 of file res_musiconhold.c.

◆ MOH_CACHERTCLASSES

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 152 of file res_musiconhold.c.

◆ moh_class_malloc

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

Definition at line 1583 of file res_musiconhold.c.

◆ MOH_CUSTOM

#define MOH_CUSTOM   (1 << 2)

Definition at line 146 of file res_musiconhold.c.

◆ MOH_MS_INTERVAL

#define MOH_MS_INTERVAL   100

◆ MOH_NOTDELETED

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 157 of file res_musiconhold.c.

◆ MOH_PREFERCHANNELCLASS

#define MOH_PREFERCHANNELCLASS   (1 << 7)

Should queue moh override channel moh

Definition at line 154 of file res_musiconhold.c.

◆ MOH_QUIET

#define MOH_QUIET   (1 << 0)

Definition at line 144 of file res_musiconhold.c.

◆ MOH_RANDOMIZE

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 147 of file res_musiconhold.c.

◆ MOH_RANDSTART

#define MOH_RANDSTART   (MOH_RANDOMIZE | MOH_SORTALPHA)

Sorted but start at random position

Definition at line 149 of file res_musiconhold.c.

◆ MOH_REALTIME

#define MOH_REALTIME   (1 << 31)

Find only records that are realtime

Definition at line 158 of file res_musiconhold.c.

◆ moh_register

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

Definition at line 1410 of file res_musiconhold.c.

◆ MOH_SINGLE

#define MOH_SINGLE   (1 << 1)

Definition at line 145 of file res_musiconhold.c.

◆ MOH_SORTALPHA

#define MOH_SORTALPHA   (1 << 4)

Definition at line 148 of file res_musiconhold.c.

◆ MOH_SORTMODE

#define MOH_SORTMODE   (3 << 3)

Definition at line 150 of file res_musiconhold.c.

◆ moh_unregister

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

Definition at line 1478 of file res_musiconhold.c.

◆ mohclass_ref

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

Definition at line 215 of file res_musiconhold.c.

◆ mohclass_unref

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

Definition at line 218 of file res_musiconhold.c.

◆ MPG_123

#define MPG_123   "/usr/bin/mpg123"

Definition at line 209 of file res_musiconhold.c.

Enumeration Type Documentation

◆ kill_methods

Enumerator
KILL_METHOD_PROCESS_GROUP 
KILL_METHOD_PROCESS 

Definition at line 162 of file res_musiconhold.c.

162  {
165 };
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS

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.

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 }
#define ast_log
Definition: astobj2.c:42
void * __ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
static const char name[]
Definition: format_mp3.c:68
#define LOG_WARNING
static struct ao2_container * mohclasses
#define NULL
Definition: resample.c:96
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
char name[MAX_MUSICCLASS]
unsigned int flags

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

Referenced by _moh_register().

◆ _moh_class_malloc()

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

Definition at line 1585 of file res_musiconhold.c.

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 }
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void * __ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result
Definition: astobj2.c:768
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)
static void moh_class_destructor(void *obj)

References __ao2_alloc(), AO2_ALLOC_OPT_LOCK_MUTEX, ao2_bump, ao2_ref, ast_format_slin, make_ari_stubs::file, moh_class_destructor(), moh_file_vector_alloc(), and NULL.

◆ _moh_register()

static int _moh_register ( struct mohclass moh,
int  reload,
int  unref,
const char *  file,
int  line,
const char *  funcname 
)
static

Definition at line 1411 of file res_musiconhold.c.

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 }
#define ao2_t_link(container, obj, tag)
Definition: astobj2.h:1534
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
static int init_app_class(struct mohclass *class)
static int init_files_class(struct mohclass *class)
static int moh_diff(struct mohclass *old, struct mohclass *new)
static struct mohclass * _get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
#define MOH_NOTDELETED
static int respawn_time
#define mohclass_unref(class, string)
struct ast_vector_string * files
char mode[80]
time_t start
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609

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

◆ _moh_unregister()

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

Definition at line 1479 of file res_musiconhold.c.

1480 {
1481  ao2_t_unlink(mohclasses, moh, "Removing class from container");
1482  return 0;
1483 }
#define ao2_t_unlink(container, obj, tag)
Definition: astobj2.h:1580

References ao2_t_unlink, and mohclasses.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

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

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 }
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
Definition: astobj2.h:1696
#define ast_verb(level,...)

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

Referenced by load_module().

◆ ast_moh_files_next()

static int ast_moh_files_next ( struct ast_channel chan)
static

Definition at line 313 of file res_musiconhold.c.

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 }
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
void * ast_channel_music_state(const struct ast_channel *chan)
const char * ast_channel_name(const struct ast_channel *chan)
const char * ast_channel_language(const struct ast_channel *chan)
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1083
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1073
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1109
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1127
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition: file.c:795
#define ast_debug(level,...)
Log a DEBUG message.
int errno
#define MOH_SORTMODE
#define MOH_RANDOMIZE
#define MOH_ANNOUNCEMENT
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
String vector definitions.
Definition: vector.h:55
char name[TZ_STRLEN_MAX+1]
Definition: localtime.c:160
#define ast_test_flag(p, flag)
Definition: utils.h:63
long int ast_random(void)
Definition: main/utils.c:2210
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680

References ao2_bump, ao2_lock, ao2_ref, ao2_unlock, ast_channel_language(), ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_copy_string(), ast_debug, ast_fileexists(), ast_log, ast_openstream_full(), ast_random(), ast_seekstream(), ast_strlen_zero(), ast_tellstream(), ast_test_flag, AST_VECTOR_GET, AST_VECTOR_SIZE, errno, LOG_WARNING, MOH_ANNOUNCEMENT, MOH_RANDOMIZE, MOH_SORTMODE, state::name, and NULL.

Referenced by moh_files_readframe().

◆ complete_mohclass_realtime()

static char* complete_mohclass_realtime ( const char *  line,
const char *  word,
int  pos,
int  state 
)
static

Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match a prefix in the given position. As many functions of this kind, each invokation has O(state) time complexity so be careful in using it.

Definition at line 1510 of file res_musiconhold.c.

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 }
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1909
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
short word
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
unsigned int realtime
static struct test_val c

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

Referenced by handle_cli_moh_unregister_class().

◆ get_mohbydigit()

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

Definition at line 557 of file res_musiconhold.c.

558 {
559  return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
560 }
char digit
static int moh_digit_match(void *obj, void *arg, int flags)

References ao2_t_callback, digit, moh_digit_match(), and mohclasses.

◆ 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.

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 }
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1562
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
static struct test_val a

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

◆ handle_cli_moh_show_classes()

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

Definition at line 2142 of file res_musiconhold.c.

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 }
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define MOH_CUSTOM
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
const char * name

◆ 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.

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 }

References a, ao2_bump, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_cli_entry::args, ast_cli(), AST_VECTOR_GET, AST_VECTOR_SIZE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, mohclass_unref, mohclasses, test_val::name, NULL, and ast_cli_entry::usage.

◆ handle_cli_moh_unregister_class()

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

Definition at line 1536 of file res_musiconhold.c.

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 int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static char * complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match...
#define moh_unregister(a)

References a, ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_mohclass_realtime(), len(), moh_unregister, mohclass_unref, mohclasses, mohclass::name, NULL, mohclass::realtime, and ast_cli_entry::usage.

◆ init_app_class()

static int init_app_class ( struct mohclass class)
static

Definition at line 1371 of file res_musiconhold.c.

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 MOH_QUIET
#define MOH_SINGLE
static void * monmp3thread(void *data)
pthread_t thread
struct ast_timer * timer
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition: timing.c:166
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:587
#define ast_set_flag(p, flag)
Definition: utils.h:70

References ast_log, ast_pthread_create_background, ast_set_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), errno, LOG_WARNING, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), and NULL.

Referenced by _moh_register().

◆ init_files_class()

static int init_files_class ( struct mohclass class)
static

Definition at line 1317 of file res_musiconhold.c.

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 }
static int moh_scan_files(struct mohclass *class)
char dir[256]

References ast_verb, and moh_scan_files().

Referenced by _moh_register().

◆ killer()

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

Definition at line 725 of file res_musiconhold.c.

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 }

References KILL_METHOD_PROCESS, and KILL_METHOD_PROCESS_GROUP.

Referenced by killpid().

◆ killpid()

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

Definition at line 737 of file res_musiconhold.c.

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 }
static int killer(pid_t pid, int signum, enum kill_methods kill_method)

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

Referenced by moh_class_destructor(), and monmp3thread().

◆ load_module()

static int load_module ( void  )
static

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 2217 of file res_musiconhold.c.

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 }
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
#define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag)
Definition: astobj2.h:1306
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3429
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
void ast_install_music_functions(int(*start_ptr)(struct ast_channel *, const char *, const char *), void(*stop_ptr)(struct ast_channel *), void(*cleanup_ptr)(struct ast_channel *))
Definition: channel.c:7833
static struct ast_cli_entry cli_moh[]
static void ast_moh_destroy(void)
static int play_moh_exec(struct ast_channel *chan, const char *data)
static void local_ast_moh_stop(struct ast_channel *chan)
static int load_moh_classes(int reload)
static int stop_moh_exec(struct ast_channel *chan, const char *data)
static int start_moh_exec(struct ast_channel *chan, const char *data)
static int moh_class_cmp(void *obj, void *arg, int flags)
static const char play_moh[]
static int moh_class_hash(const void *obj, const int flags)
static const char start_moh[]
static const char stop_moh[]
static void local_ast_moh_cleanup(struct ast_channel *chan)
static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
#define ARRAY_LEN(a)
Definition: utils.h:661

References AO2_ALLOC_OPT_LOCK_MUTEX, ao2_t_container_alloc_hash, ARRAY_LEN, ast_check_realtime(), ast_cli_register_multiple, ast_install_music_functions(), ast_log, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application_xml, ast_register_atexit(), cli_moh, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh_class_cmp(), moh_class_hash(), mohclasses, NULL, play_moh, play_moh_exec(), start_moh, start_moh_exec(), stop_moh, and stop_moh_exec().

◆ load_moh_classes()

static int load_moh_classes ( int  reload)
static

Definition at line 1972 of file res_musiconhold.c.

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 const char app[]
Definition: app_adsiprog.c:56
#define var
Definition: ast_expr2f.c:614
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3327
#define CONFIG_STATUS_FILEMISSING
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define HANDLE_REF
static int moh_class_mark(void *obj, void *arg, int flags)
static void moh_rescan_files(void)
#define moh_register(moh, reload, unref)
#define moh_class_malloc()
#define MOH_PREFERCHANNELCLASS
#define MOH_CACHERTCLASSES
#define MOH_REALTIME
static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
static int reload(void)
static struct ast_flags global_flags[1]
static int moh_classes_delete_marked(void *obj, void *arg, int flags)
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
Structure used to handle boolean flags.
Definition: utils.h:199
Structure for variables, used for configurations and for channel variables.
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define AST_FLAGS_ALL
Definition: utils.h:196

References ao2_t_callback, app, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_destroy(), ast_config_load, ast_copy_string(), AST_FLAGS_ALL, ast_log, ast_set2_flag, ast_strlen_zero(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, global_flags, HANDLE_REF, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_REALTIME, moh_register, moh_rescan_files(), mohclass_unref, mohclasses, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, reload(), and var.

Referenced by load_module(), and reload().

◆ load_realtime_musiconhold()

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

Definition at line 1607 of file res_musiconhold.c.

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++;
1628  }
1629  }
1630  }
1631  ast_config_destroy(entries);
1632  }
1633 
1634  if (entry_count == 0) {
1635  /* Behave as though this class doesn't exist */
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 }
#define SENTINEL
Definition: compiler.h:87
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:904
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3405
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3521
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:768
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:239
Definition: search.h:40

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

Referenced by local_ast_moh_start().

◆ local_ast_moh_cleanup()

static void local_ast_moh_cleanup ( struct ast_channel chan)
static

Definition at line 1485 of file res_musiconhold.c.

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 ast_free(a)
Definition: astmm.h:180
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
struct ast_module * self
Definition: module.h:342

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

Referenced by load_module(), and reload().

◆ local_ast_moh_start()

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

Definition at line 1651 of file res_musiconhold.c.

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 
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 manually */
1745 
1746  time(&mohclass->start);
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  }
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);
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"))
1785  else if (!strcasecmp(mohclass->mode, "mp3nb"))
1787  else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1789  else if (!strcasecmp(mohclass->mode, "quietmp3"))
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  }
1797  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
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 {
1813  ast_log(LOG_WARNING, "Unable to create moh...\n");
1814  if (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 {
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);
1859 
1860  if (file_count) {
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 }
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2952
#define ast_channel_lock(chan)
Definition: channel.h:2922
@ AST_FLAG_MOH
Definition: channel.h:991
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
const char * ast_channel_musicclass(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2923
#define LOG_NOTICE
static struct ast_generator mohgen
#define get_mohbyname(a, b, c)
#define mohclass_ref(class, string)
static struct ast_variable * load_realtime_musiconhold(const char *name)
static struct ast_generator moh_file_stream
#define DONT_UNREF
char args[256]

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, mohclass::dir, DONT_UNREF, errno, mohclass::files, get_mohbyname, global_flags, load_realtime_musiconhold(), LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, moh_file_stream, moh_parse_options(), MOH_PREFERCHANNELCLASS, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, mohclass_ref, mohclass_unref, mohgen, monmp3thread(), mohclass::name, state::name, NULL, mohclass::realtime, respawn_time, mohclass::srcfd, mohclass::start, mohclass::thread, mohclass::timer, and var.

Referenced by load_module(), and reload().

◆ local_ast_moh_stop()

static void local_ast_moh_stop ( struct ast_channel chan)
static

Definition at line 1878 of file res_musiconhold.c.

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 }
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2894

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

Referenced by load_module(), and reload().

◆ moh_alloc()

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

Definition at line 1018 of file res_musiconhold.c.

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 */
1026  if (!state && (state = ast_calloc(1, sizeof(*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 }
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
enum sip_cc_notify_state state
Definition: chan_sip.c:966
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5880
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
static void moh_release(struct ast_channel *chan, void *data)
static struct mohdata * mohalloc(struct mohclass *cl)
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
struct mohclass * class
struct ast_format * format
struct ast_format * origwfmt

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

◆ moh_class_cmp()

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

Definition at line 2198 of file res_musiconhold.c.

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 }
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

◆ moh_class_destructor()

static void moh_class_destructor ( void *  obj)
static

Definition at line 1893 of file res_musiconhold.c.

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 }
static unsigned char * buff
Definition: chan_unistim.c:259
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_PTHREADT_NULL
Definition: lock.h:66
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
struct mohdata::@469 list
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1665

References ao2_cleanup, ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, killpid(), mohdata::list, and NULL.

Referenced by _moh_class_malloc().

◆ moh_class_hash()

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

Definition at line 2191 of file res_musiconhold.c.

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:1281

References ast_str_case_hash().

Referenced by load_module().

◆ moh_class_inuse()

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

Definition at line 2256 of file res_musiconhold.c.

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:450

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

◆ moh_class_mark()

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

Definition at line 1954 of file res_musiconhold.c.

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

References mohclass::delete, and MOH_REALTIME.

Referenced by load_moh_classes().

◆ moh_classes_delete_marked()

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

Definition at line 1965 of file res_musiconhold.c.

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

References CMP_MATCH, and mohclass::delete.

Referenced by load_moh_classes().

◆ moh_diff()

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

Definition at line 1352 of file res_musiconhold.c.

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 }

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

Referenced by _moh_register().

◆ moh_digit_match()

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

Definition at line 548 of file res_musiconhold.c.

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

References CMP_MATCH, CMP_STOP, and digit.

Referenced by get_mohbydigit().

◆ moh_file_vector_alloc()

static struct ast_vector_string* moh_file_vector_alloc ( int  initial_capacity)
static

Definition at line 1099 of file res_musiconhold.c.

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 }
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
static void moh_file_vector_destructor(void *obj)
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, AST_VECTOR_INIT, and moh_file_vector_destructor().

Referenced by _moh_class_malloc(), moh_parse_options(), and moh_scan_files().

◆ moh_file_vector_destructor()

static void moh_file_vector_destructor ( void *  obj)
static

Definition at line 1092 of file res_musiconhold.c.

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

References ast_free, AST_VECTOR_FREE, and AST_VECTOR_RESET.

Referenced by moh_file_vector_alloc().

◆ moh_filename_strcasecmp()

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

Definition at line 1272 of file res_musiconhold.c.

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

References a, and b.

Referenced by moh_scan_files().

◆ moh_files_alloc()

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

Definition at line 500 of file res_musiconhold.c.

501 {
502  struct moh_files_state *state;
503  struct mohclass *class = params;
504  size_t file_count;
505 
507  if (!state && (state = ast_calloc(1, sizeof(*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 */
537  ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
538  ao2_replace(state->mohwfmt, ast_channel_writeformat(chan));
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 }
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition: astobj2.h:501

References ao2_cleanup, ao2_lock, ao2_replace, ao2_unlock, ast_calloc, ast_channel_music_state(), ast_channel_music_state_set(), ast_channel_writeformat(), ast_copy_string(), ast_log, ast_module_ref, ast_random(), ast_test_flag, AST_VECTOR_SIZE, LOG_WARNING, moh_post_start(), MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::name, state::name, NULL, ast_module_info::self, and state.

◆ moh_files_generator()

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

Definition at line 457 of file res_musiconhold.c.

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 }
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:5179
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition: format.h:38
#define ast_frfree(fr)
static struct ast_frame * moh_files_readframe(struct ast_channel *chan)
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass

References ao2_replace, ast_channel_lock, ast_channel_music_state(), ast_channel_name(), ast_channel_unlock, ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_frfree, ast_log, ast_write(), errno, ast_frame_subclass::format, LOG_WARNING, moh_files_readframe(), NULL, ast_frame::samples, and ast_frame::subclass.

◆ moh_files_readframe()

static struct ast_frame* moh_files_readframe ( struct ast_channel chan)
static

Definition at line 409 of file res_musiconhold.c.

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:934
static int ast_moh_files_next(struct ast_channel *chan)

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

Referenced by moh_files_generator().

◆ moh_files_release()

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

Definition at line 281 of file res_musiconhold.c.

282 {
283  struct moh_files_state *state;
284 
285  if (!chan || !ast_channel_music_state(chan)) {
286  return;
287  }
288 
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 }
static void moh_post_stop(struct ast_channel *chan)

References ao2_cleanup, ao2_ref, ast_channel_music_state(), ast_channel_name(), ast_channel_stream(), ast_channel_stream_set(), ast_closestream(), ast_format_get_name(), ast_log, ast_set_write_format(), LOG_WARNING, moh_post_stop(), mohclass_unref, NULL, and state.

◆ moh_files_write_format_change()

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

Definition at line 438 of file res_musiconhold.c.

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 
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
Definition of a media format.
Definition: format.c:43

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

◆ moh_generate()

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

Definition at line 1057 of file res_musiconhold.c.

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 }
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:378
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples)
Get the length (in milliseconds) for the format with a given number of samples.
Definition: format.c:384
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
union ast_frame::@254 data
struct mohclass * parent
int pipe[2]
struct ast_frame f

References ast_channel_name(), ast_codec_samples_count(), ast_format_determine_length(), AST_FRIENDLY_OFFSET, ast_log, ast_write(), buf, ast_frame::data, ast_frame::datalen, errno, mohdata::f, mohclass::format, len(), LOG_WARNING, mohdata::parent, mohdata::pipe, ast_frame::ptr, and ast_frame::samples.

◆ moh_handle_digit()

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

Definition at line 562 of file res_musiconhold.c.

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 }
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7849
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7859
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.

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")) {
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  }
1165  } else if (!strcasecmp(var->name, "sort")) {
1166  if (!strcasecmp(var->value, "random")) {
1168  } else if (!strcasecmp(var->value, "alpha")) {
1170  } else if (!strcasecmp(var->value, "randstart")) {
1172  }
1173  } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1176  if (!mohclass->format) {
1177  ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
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")) {
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 }
#define ast_format_cache_get(name)
Retrieve a named format from the cache.
Definition: format_cache.h:278
#define LOG_ERROR
#define MOH_RANDSTART
#define MOH_SORTALPHA
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
char announcement[256]
enum kill_methods kill_method
size_t kill_delay
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:638

References mohclass::announcement, 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, mohclass::name, NULL, and var.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_post_start()

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

Definition at line 239 of file res_musiconhold.c.

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 
259  }
261  ast_json_unref(json_object);
262 }
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.
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
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.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1513
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
Blob of data associated with a channel.
Abstract JSON element (object, array, string, int, ...).
#define ast_assert(a)
Definition: utils.h:734

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

Referenced by moh_alloc(), and moh_files_alloc().

◆ moh_post_stop()

static void moh_post_stop ( struct ast_channel chan)
static

Definition at line 264 of file res_musiconhold.c.

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 
277  }
279 }
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.

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

Referenced by moh_files_release(), and moh_release().

◆ moh_release()

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

Definition at line 981 of file res_musiconhold.c.

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 
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 }
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
struct mohclass::@467 members

References ao2_cleanup, ao2_lock, ao2_unlock, ast_channel_music_state(), ast_channel_name(), ast_format_get_name(), ast_free, AST_LIST_REMOVE, ast_log, ast_set_write_format(), LOG_WARNING, mohclass::members, moh_post_stop(), mohclass_unref, mohdata::origwfmt, mohdata::parent, mohdata::pipe, and state.

Referenced by moh_alloc().

◆ moh_rescan_files()

static void moh_rescan_files ( void  )
static

Definition at line 1336 of file res_musiconhold.c.

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 }
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, c, moh_scan_files(), and mohclasses.

Referenced by load_moh_classes().

◆ moh_scan_files()

static int moh_scan_files ( struct mohclass class)
static

Definition at line 1279 of file res_musiconhold.c.

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 }
#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:201
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
static int moh_filename_strcasecmp(const void *a, const void *b)
static int on_moh_file(const char *directory, const char *filename, void *obj)
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:396

References ao2_lock, ao2_ref, ao2_unlock, ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_file_read_dir, ast_test_flag, AST_VECTOR_COMPACT, AST_VECTOR_SIZE, AST_VECTOR_SORT, moh_file_vector_alloc(), moh_filename_strcasecmp(), MOH_SORTALPHA, on_moh_file(), and PATH_MAX.

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

◆ mohalloc()

static struct mohdata* mohalloc ( struct mohclass cl)
static

Definition at line 955 of file res_musiconhold.c.

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 }
@ AST_FRAME_VOICE
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
enum ast_frame_type frametype
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition: utils.h:1085

References ao2_lock, ao2_unlock, ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log, ast_pipe_nonblock, errno, mohdata::f, ast_frame_subclass::format, mohclass::format, ast_frame::frametype, mohdata::list, LOG_WARNING, mohclass::members, mohclass_ref, NULL, ast_frame::offset, mohdata::parent, mohdata::pipe, and ast_frame::subclass.

Referenced by moh_alloc().

◆ monmp3thread()

static void* monmp3thread ( void *  data)
static

Definition at line 770 of file res_musiconhold.c.

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 }
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
#define MOH_MS_INTERVAL
static int spawn_mp3(struct mohclass *class)
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:245
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:115
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:105
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:157
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition: timing.c:171
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition: timing.c:161

References ao2_lock, ao2_unlock, ast_debug, ast_format_determine_length(), ast_format_get_sample_rate(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log, ast_poll, ast_samp2tv(), ast_timer_ack(), ast_timer_fd(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), errno, killpid(), len(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, MOH_MS_INTERVAL, NULL, mohdata::pipe, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

◆ on_moh_file()

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

Definition at line 1217 of file res_musiconhold.c.

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_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
structure to hold extensions
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:731

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

Referenced by moh_scan_files().

◆ play_moh_exec()

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

Definition at line 868 of file res_musiconhold.c.

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 
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 }
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1844
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1568
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
const char * args

References args, AST_APP_ARG, ast_channel_name(), AST_DECLARE_APP_ARGS, ast_log, ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, NULL, parse(), and S_OR.

Referenced by load_module().

◆ reload()

static int reload ( void  )
static

◆ spawn_mp3()

static int spawn_mp3 ( struct mohclass class)
static

Definition at line 584 of file res_musiconhold.c.

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 }
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:1835
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:3267
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:3262
char * strsep(char **str, const char *delims)
#define ast_opt_high_priority
Definition: options.h:110
#define MPG_123
#define LOCAL_MPG_123
#define MAX_MP3S

References ast_close_fds_above_n(), ast_copy_string(), ast_log, ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_strlen_zero(), ast_test_flag, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, NULL, respawn_time, and strsep().

Referenced by monmp3thread().

◆ start_moh_exec()

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

Definition at line 908 of file res_musiconhold.c.

909 {
910  char *parse;
911  char *class;
913  AST_APP_ARG(class);
914  );
915 
916  parse = ast_strdupa(data);
917 
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 }

References args, AST_APP_ARG, ast_channel_name(), AST_DECLARE_APP_ARGS, ast_log, ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, NULL, parse(), and S_OR.

Referenced by load_module().

◆ stop_moh_exec()

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

Definition at line 927 of file res_musiconhold.c.

928 {
929  ast_moh_stop(chan);
930 
931  return 0;
932 }

References ast_moh_stop().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2263 of file res_musiconhold.c.

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 }
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1058
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
void ast_uninstall_music_functions(void)
Definition: channel.c:7842
static int moh_class_inuse(void *obj, void *arg, int flags)

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static

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

Referenced by load_module().

◆ global_flags

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

global MOH_ flags

Definition at line 127 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

◆ moh_file_stream

struct ast_generator moh_file_stream
static

Definition at line 562 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

◆ mohclasses

struct ao2_container* mohclasses
static

◆ mohgen

struct ast_generator mohgen
static

Definition at line 1057 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

◆ play_moh

const char play_moh[] = "MusicOnHold"
static

Definition at line 123 of file res_musiconhold.c.

Referenced by load_module().

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

◆ stop_moh

const char stop_moh[] = "StopMusicOnHold"
static

Definition at line 125 of file res_musiconhold.c.

Referenced by load_module().