Mon Oct 8 12:39:28 2012

Asterisk developer's documentation


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 <sys/signal.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/manager.h"
#include "asterisk/paths.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/time.h"
#include "asterisk/poll-compat.h"

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

#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_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_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define moh_register(a, b, c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
#define MOH_SINGLE   (1 << 1)
#define MOH_SORTALPHA   (1 << 4)
#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"

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 void ast_moh_destroy (void)
static int ast_moh_files_next (struct ast_channel *chan)
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 int init_app_class (struct mohclass *class)
static int init_files_class (struct mohclass *class)
static int load_module (void)
static int load_moh_classes (int reload)
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 int moh_add_file (struct mohclass *class, const char *filepath)
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 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 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_release (struct ast_channel *chan, void *data)
static void moh_rescan_files (void)
static int moh_scan_files (struct mohclass *class)
static int moh_sort_compare (const void *i1, const void *i2)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int play_moh_exec (struct ast_channel *chan, const char *data)
static int reload (void)
static int set_moh_exec (struct ast_channel *chan, const char *data)
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)
static int wait_moh_exec (struct ast_channel *chan, const char *data)

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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static 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 set_moh [] = "SetMusicOnHold"
static const char start_moh [] = "StartMusicOnHold"
static const char stop_moh [] = "StopMusicOnHold"
static const char wait_moh [] = "WaitMusicOnHold"


Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define DONT_UNREF   0

Definition at line 72 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

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

Definition at line 841 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define HANDLE_REF   1

Definition at line 71 of file res_musiconhold.c.

#define INITIAL_NUM_FILES   8

Definition at line 70 of file res_musiconhold.c.

Referenced by moh_add_file().

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

Definition at line 221 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 223 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CACHERTCLASSES   (1 << 5)

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

Definition at line 174 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

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

Definition at line 1298 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

#define MOH_CUSTOM   (1 << 2)

Definition at line 170 of file res_musiconhold.c.

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

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_NOTDELETED   (1 << 30)

Find only records that aren't deleted?

Definition at line 177 of file res_musiconhold.c.

Referenced by _moh_register(), and moh_class_cmp().

#define MOH_QUIET   (1 << 0)

Definition at line 168 of file res_musiconhold.c.

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

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 171 of file res_musiconhold.c.

Referenced by ast_moh_files_next(), init_files_class(), load_moh_classes(), local_ast_moh_start(), and moh_files_alloc().

#define moh_register ( a,
b,
 )     _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)

Note:
This function owns the reference it gets to moh if unref is true

Definition at line 1223 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

#define MOH_SINGLE   (1 << 1)

Definition at line 169 of file res_musiconhold.c.

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

#define MOH_SORTALPHA   (1 << 4)

Definition at line 172 of file res_musiconhold.c.

Referenced by load_moh_classes(), and local_ast_moh_start().

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

Definition at line 227 of file res_musiconhold.c.

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

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

Definition at line 230 of file res_musiconhold.c.

Referenced by _moh_register(), handle_cli_moh_show_classes(), handle_cli_moh_show_files(), local_ast_moh_cleanup(), local_ast_moh_start(), moh_alloc(), moh_files_alloc(), moh_files_release(), moh_handle_digit(), moh_release(), and unload_module().

#define MPG_123   "/usr/bin/mpg123"

Definition at line 222 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1984 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1984 of file res_musiconhold.c.

static struct mohclass* _get_mohbyname ( const char *  name,
int  warn,
int  flags,
const char *  file,
int  lineno,
const char *  funcname 
) [static]

Definition at line 843 of file res_musiconhold.c.

References __ao2_find(), __ao2_find_debug(), ast_copy_string(), ast_log(), mohclass::flags, LOG_DEBUG, moh, and mohclass::name.

Referenced by _moh_register().

00844 {
00845    struct mohclass *moh = NULL;
00846    struct mohclass tmp_class = {
00847       .flags = 0,
00848    };
00849 
00850    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00851 
00852 #ifdef REF_DEBUG
00853    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00854 #else
00855    moh = __ao2_find(mohclasses, &tmp_class, flags);
00856 #endif
00857 
00858    if (!moh && warn) {
00859       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00860    }
00861 
00862    return moh;
00863 }

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

Definition at line 1300 of file res_musiconhold.c.

References __ao2_alloc_debug(), __AST_DEBUG_MALLOC, ao2_alloc, AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().

01301 {
01302    struct mohclass *class;
01303 
01304    if ((class =
01305 #ifdef REF_DEBUG
01306          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01307 #elif defined(__AST_DEBUG_MALLOC)
01308          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01309 #else
01310          ao2_alloc(sizeof(*class), moh_class_destructor)
01311 #endif
01312       )) {
01313       class->format = AST_FORMAT_SLINEAR;
01314       class->srcfd = -1;
01315    }
01316 
01317    return class;
01318 }

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

Definition at line 1224 of file res_musiconhold.c.

References _get_mohbyname(), ao2_t_link, ast_log(), init_app_class(), init_files_class(), LOG_WARNING, moh, moh_diff(), MOH_NOTDELETED, and mohclass_unref.

01225 {
01226    struct mohclass *mohclass = NULL;
01227 
01228    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01229 
01230    if (mohclass && !moh_diff(mohclass, moh)) {
01231       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01232       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01233       if (unref) {
01234          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01235       }
01236       return -1;
01237    } else if (mohclass) {
01238       /* Found a class, but it's different from the one being registered */
01239       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01240    }
01241 
01242    time(&moh->start);
01243    moh->start -= respawn_time;
01244 
01245    if (!strcasecmp(moh->mode, "files")) {
01246       if (init_files_class(moh)) {
01247          if (unref) {
01248             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01249          }
01250          return -1;
01251       }
01252    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01253          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01254          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01255       if (init_app_class(moh)) {
01256          if (unref) {
01257             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01258          }
01259          return -1;
01260       }
01261    } else {
01262       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01263       if (unref) {
01264          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01265       }
01266       return -1;
01267    }
01268 
01269    ao2_t_link(mohclasses, moh, "Adding class to container");
01270 
01271    if (unref) {
01272       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01273    }
01274 
01275    return 0;
01276 }

static void ast_moh_destroy ( void   )  [static]

Definition at line 1776 of file res_musiconhold.c.

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

Referenced by load_module(), and unload_module().

01777 {
01778    ast_verb(2, "Destroying musiconhold processes\n");
01779    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01780 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 283 of file res_musiconhold.c.

References ast_closestream(), ast_fileexists(), ast_log(), ast_random(), ast_strlen_zero(), ast_test_flag, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, state, and ast_channel::stream.

Referenced by moh_files_readframe().

00284 {
00285    struct moh_files_state *state = chan->music_state;
00286    int tries;
00287 
00288    /* Discontinue a stream if it is running already */
00289    if (chan->stream) {
00290       ast_closestream(chan->stream);
00291       chan->stream = NULL;
00292    }
00293 
00294    if (!state->class->total_files) {
00295       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00296       return -1;
00297    }
00298 
00299    if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
00300       /* First time so lets play the file. */
00301       state->save_pos = -1;
00302    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
00303       /* If a specific file has been saved confirm it still exists and that it is still valid */
00304       state->pos = state->save_pos;
00305       state->save_pos = -1;
00306    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00307       /* Get a random file and ensure we can open it */
00308       for (tries = 0; tries < 20; tries++) {
00309          state->pos = ast_random() % state->class->total_files;
00310          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00311             break;
00312          }
00313       }
00314       state->save_pos = -1;
00315       state->samples = 0;
00316    } else {
00317       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00318       state->pos++;
00319       state->pos %= state->class->total_files;
00320       state->save_pos = -1;
00321       state->samples = 0;
00322    }
00323 
00324    for (tries = 0; tries < state->class->total_files; ++tries) {
00325       if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00326          break;
00327       }
00328 
00329       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00330       state->pos++;
00331       state->pos %= state->class->total_files;
00332    }
00333 
00334    if (tries == state->class->total_files) {
00335       return -1;
00336    }
00337 
00338    /* Record the pointer to the filename for position resuming later */
00339    ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
00340 
00341    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00342 
00343    if (state->samples) {
00344       size_t loc;
00345       /* seek *SHOULD* be good since it's from a known location */
00346       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00347       /* if the seek failed then recover because if there is not a valid read,
00348        * moh_files_generate will return -1 and MOH will stop */
00349       loc = ast_tellstream(chan->stream);
00350       if (state->samples > loc && loc) {
00351          /* seek one sample from the end for one guaranteed valid read */
00352          ast_seekstream(chan->stream, 1, SEEK_END);
00353       }
00354    }
00355 
00356    return 0;
00357 }

static struct mohclass* get_mohbydigit ( char  digit  )  [static]

Note:
This function should be called with the mohclasses list locked

Definition at line 456 of file res_musiconhold.c.

References ao2_t_callback, and moh_digit_match().

Referenced by moh_handle_digit().

00457 {
00458    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00459 }

static char* handle_cli_moh_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1782 of file res_musiconhold.c.

References ast_cli_args::argc, ast_cli_entry::args, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, reload, and ast_cli_entry::usage.

01783 {
01784    switch (cmd) {
01785    case CLI_INIT:
01786       e->command = "moh reload";
01787       e->usage =
01788          "Usage: moh reload\n"
01789          "       Reloads the MusicOnHold module.\n"
01790          "       Alias for 'module reload res_musiconhold.so'\n";
01791       return NULL;
01792    case CLI_GENERATE:
01793       return NULL;
01794    }
01795 
01796    if (a->argc != e->args)
01797       return CLI_SHOWUSAGE;
01798 
01799    reload();
01800 
01801    return CLI_SUCCESS;
01802 }

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

Definition at line 1842 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), ast_getformatname(), ast_test_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, MOH_CUSTOM, mohclass_unref, S_OR, and ast_cli_entry::usage.

01843 {
01844    struct mohclass *class;
01845    struct ao2_iterator i;
01846 
01847    switch (cmd) {
01848    case CLI_INIT:
01849       e->command = "moh show classes";
01850       e->usage =
01851          "Usage: moh show classes\n"
01852          "       Lists all MusicOnHold classes.\n";
01853       return NULL;
01854    case CLI_GENERATE:
01855       return NULL;
01856    }
01857 
01858    if (a->argc != e->args)
01859       return CLI_SHOWUSAGE;
01860 
01861    i = ao2_iterator_init(mohclasses, 0);
01862    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01863       ast_cli(a->fd, "Class: %s\n", class->name);
01864       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01865       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01866       if (ast_test_flag(class, MOH_CUSTOM)) {
01867          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01868       }
01869       if (strcasecmp(class->mode, "files")) {
01870          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01871       }
01872    }
01873    ao2_iterator_destroy(&i);
01874 
01875    return CLI_SUCCESS;
01876 }

static char* handle_cli_moh_show_files ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1804 of file res_musiconhold.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_cli_args::argc, ast_cli_entry::args, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, mohclass_unref, and ast_cli_entry::usage.

01805 {
01806    struct mohclass *class;
01807    struct ao2_iterator i;
01808 
01809    switch (cmd) {
01810    case CLI_INIT:
01811       e->command = "moh show files";
01812       e->usage =
01813          "Usage: moh show files\n"
01814          "       Lists all loaded file-based MusicOnHold classes and their\n"
01815          "       files.\n";
01816       return NULL;
01817    case CLI_GENERATE:
01818       return NULL;
01819    }
01820 
01821    if (a->argc != e->args)
01822       return CLI_SHOWUSAGE;
01823 
01824    i = ao2_iterator_init(mohclasses, 0);
01825    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01826       int x;
01827 
01828       if (!class->total_files) {
01829          continue;
01830       }
01831 
01832       ast_cli(a->fd, "Class: %s\n", class->name);
01833       for (x = 0; x < class->total_files; x++) {
01834          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01835       }
01836    }
01837    ao2_iterator_destroy(&i);
01838 
01839    return CLI_SUCCESS;
01840 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 1184 of file res_musiconhold.c.

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

Referenced by _moh_register().

01185 {
01186    if (!strcasecmp(class->mode, "custom")) {
01187       ast_set_flag(class, MOH_CUSTOM);
01188    } else if (!strcasecmp(class->mode, "mp3nb")) {
01189       ast_set_flag(class, MOH_SINGLE);
01190    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01191       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01192    } else if (!strcasecmp(class->mode, "quietmp3")) {
01193       ast_set_flag(class, MOH_QUIET);
01194    }
01195 
01196    class->srcfd = -1;
01197 
01198    if (!(class->timer = ast_timer_open())) {
01199       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01200       return -1;
01201    }
01202    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01203       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01204       ast_timer_close(class->timer);
01205       class->timer = NULL;
01206    }
01207 
01208    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01209       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01210       if (class->timer) {
01211          ast_timer_close(class->timer);
01212          class->timer = NULL;
01213       }
01214       return -1;
01215    }
01216 
01217    return 0;
01218 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 1121 of file res_musiconhold.c.

References mohclass::args, ast_set_flag, ast_verbose, mohclass::dir, MOH_RANDOMIZE, moh_scan_files(), mohclass::name, option_verbose, and VERBOSE_PREFIX_3.

Referenced by _moh_register().

01122 {
01123    int res;
01124 
01125    res = moh_scan_files(class);
01126 
01127    if (res < 0) {
01128       return -1;
01129    }
01130 
01131    if (!res) {
01132       if (option_verbose > 2) {
01133          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01134                class->dir, class->name);
01135       }
01136       return -1;
01137    }
01138 
01139 #if 0
01140    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01141    if (strchr(class->args, 'r')) {
01142       ast_set_flag(class, MOH_RANDOMIZE);
01143    }
01144 #endif
01145 
01146    return 0;
01147 }

static int load_module ( void   )  [static]

Definition at line 1900 of file res_musiconhold.c.

References ao2_t_container_alloc, 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(), play_moh_exec(), set_moh_exec(), start_moh_exec(), stop_moh_exec(), and wait_moh_exec().

01901 {
01902    int res;
01903 
01904    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01905       return AST_MODULE_LOAD_DECLINE;
01906    }
01907 
01908    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01909       ast_log(LOG_WARNING, "No music on hold classes configured, "
01910             "disabling music on hold.\n");
01911    } else {
01912       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01913             local_ast_moh_cleanup);
01914    }
01915 
01916    res = ast_register_application_xml(play_moh, play_moh_exec);
01917    ast_register_atexit(ast_moh_destroy);
01918    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01919    if (!res)
01920       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01921    if (!res)
01922       res = ast_register_application_xml(set_moh, set_moh_exec);
01923    if (!res)
01924       res = ast_register_application_xml(start_moh, start_moh_exec);
01925    if (!res)
01926       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01927 
01928    return AST_MODULE_LOAD_SUCCESS;
01929 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1666 of file res_musiconhold.c.

References ao2_t_callback, ast_category_browse(), ast_check_realtime(), ast_clear_flag, ast_config_load, ast_copy_string(), AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_log(), ast_set2_flag, ast_set_flag, ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, global_flags, LOG_WARNING, MOH_CACHERTCLASSES, moh_class_malloc, moh_class_mark(), moh_classes_delete_marked(), MOH_RANDOMIZE, moh_rescan_files(), MOH_SORTALPHA, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, and var.

Referenced by load_module().

01667 {
01668    struct ast_config *cfg;
01669    struct ast_variable *var;
01670    struct mohclass *class; 
01671    char *cat;
01672    int numclasses = 0;
01673    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01674 
01675    cfg = ast_config_load("musiconhold.conf", config_flags);
01676 
01677    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01678       if (ast_check_realtime("musiconhold") && reload) {
01679          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01680          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01681       }
01682       return 0;
01683    }
01684    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01685       moh_rescan_files();
01686       return 0;
01687    }
01688 
01689    if (reload) {
01690       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01691    }
01692 
01693    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01694 
01695    cat = ast_category_browse(cfg, NULL);
01696    for (; cat; cat = ast_category_browse(cfg, cat)) {
01697       /* Setup common options from [general] section */
01698       if (!strcasecmp(cat, "general")) {
01699          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01700             if (!strcasecmp(var->name, "cachertclasses")) {
01701                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01702             } else {
01703                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01704             }
01705          }
01706       }
01707       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01708       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01709             !strcasecmp(cat, "general")) {
01710          continue;
01711       }
01712 
01713       if (!(class = moh_class_malloc())) {
01714          break;
01715       }
01716 
01717       ast_copy_string(class->name, cat, sizeof(class->name));  
01718       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01719          if (!strcasecmp(var->name, "mode"))
01720             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01721          else if (!strcasecmp(var->name, "directory"))
01722             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01723          else if (!strcasecmp(var->name, "application"))
01724             ast_copy_string(class->args, var->value, sizeof(class->args));
01725          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01726             class->digit = *var->value;
01727          else if (!strcasecmp(var->name, "random"))
01728             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01729          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01730             ast_set_flag(class, MOH_RANDOMIZE);
01731          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01732             ast_set_flag(class, MOH_SORTALPHA);
01733          else if (!strcasecmp(var->name, "format")) {
01734             class->format = ast_getformatbyname(var->value);
01735             if (!class->format) {
01736                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01737                class->format = AST_FORMAT_SLINEAR;
01738             }
01739          }
01740       }
01741 
01742       if (ast_strlen_zero(class->dir)) {
01743          if (!strcasecmp(class->mode, "custom")) {
01744             strcpy(class->dir, "nodir");
01745          } else {
01746             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01747             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01748             continue;
01749          }
01750       }
01751       if (ast_strlen_zero(class->mode)) {
01752          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01753          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01754          continue;
01755       }
01756       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01757          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01758          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01759          continue;
01760       }
01761 
01762       /* Don't leak a class when it's already registered */
01763       if (!moh_register(class, reload, HANDLE_REF)) {
01764          numclasses++;
01765       }
01766    }
01767 
01768    ast_config_destroy(cfg);
01769 
01770    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01771          moh_classes_delete_marked, NULL, "Purge marked classes");
01772 
01773    return numclasses;
01774 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 1278 of file res_musiconhold.c.

References ast_free, ast_log(), ast_module_unref(), LOG_WARNING, mohclass_unref, ast_channel::music_state, and state.

Referenced by load_module().

01279 {
01280    struct moh_files_state *state = chan->music_state;
01281 
01282    if (state) {
01283       if (state->class) {
01284          /* This should never happen.  We likely just leaked some resource. */
01285          state->class =
01286             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01287          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01288       }
01289       ast_free(chan->music_state);
01290       chan->music_state = NULL;
01291       /* Only held a module reference if we had a music state */
01292       ast_module_unref(ast_module_info->self);
01293    }
01294 }

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

Definition at line 1320 of file res_musiconhold.c.

References mohclass::args, ast_check_realtime(), ast_copy_string(), AST_FORMAT_SLINEAR, ast_getformatbyname(), ast_load_realtime(), ast_log(), ast_pthread_create_background, ast_set2_flag, ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_timer_close(), ast_timer_open(), ast_timer_set_rate(), ast_true(), ast_variables_destroy(), mohclass::digit, mohclass::dir, DONT_UNREF, errno, mohclass::format, get_mohbyname, global_flags, LOG_NOTICE, LOG_WARNING, mohclass::mode, MOH_CACHERTCLASSES, moh_class_malloc, MOH_CUSTOM, MOH_QUIET, MOH_RANDOMIZE, moh_register, moh_scan_files(), MOH_SINGLE, MOH_SORTALPHA, mohclass_unref, monmp3thread(), ast_channel::music_state, ast_channel::musicclass, mohclass::name, ast_variable::name, ast_variable::next, mohclass::realtime, SENTINEL, mohclass::srcfd, mohclass::start, state, mohclass::thread, mohclass::timer, ast_variable::value, and var.

Referenced by load_module().

01321 {
01322    struct mohclass *mohclass = NULL;
01323    struct moh_files_state *state = chan->music_state;
01324    struct ast_variable *var = NULL;
01325    int res;
01326    int realtime_possible = ast_check_realtime("musiconhold");
01327 
01328    /* The following is the order of preference for which class to use:
01329     * 1) The channels explicitly set musicclass, which should *only* be
01330     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01331     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01332     *    result of receiving a HOLD control frame, this should be the
01333     *    payload that came with the frame.
01334     * 3) The interpclass argument. This would be from the mohinterpret
01335     *    option from channel drivers. This is the same as the old musicclass
01336     *    option.
01337     * 4) The default class.
01338     */
01339    if (!ast_strlen_zero(chan->musicclass)) {
01340       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01341       if (!mohclass && realtime_possible) {
01342          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01343       }
01344    }
01345    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01346       mohclass = get_mohbyname(mclass, 1, 0);
01347       if (!mohclass && realtime_possible) {
01348          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01349       }
01350    }
01351    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01352       mohclass = get_mohbyname(interpclass, 1, 0);
01353       if (!mohclass && realtime_possible) {
01354          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01355       }
01356    }
01357 
01358    if (!mohclass && !var) {
01359       mohclass = get_mohbyname("default", 1, 0);
01360       if (!mohclass && realtime_possible) {
01361          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01362       }
01363    }
01364 
01365    /* If no moh class found in memory, then check RT. Note that the logic used
01366     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01367     */
01368    if (var) {
01369       struct ast_variable *tmp = NULL;
01370 
01371       if ((mohclass = moh_class_malloc())) {
01372          mohclass->realtime = 1;
01373          for (tmp = var; tmp; tmp = tmp->next) {
01374             if (!strcasecmp(tmp->name, "name"))
01375                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01376             else if (!strcasecmp(tmp->name, "mode"))
01377                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01378             else if (!strcasecmp(tmp->name, "directory"))
01379                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01380             else if (!strcasecmp(tmp->name, "application"))
01381                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01382             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01383                mohclass->digit = *tmp->value;
01384             else if (!strcasecmp(tmp->name, "random"))
01385                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01386             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01387                ast_set_flag(mohclass, MOH_RANDOMIZE);
01388             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01389                ast_set_flag(mohclass, MOH_SORTALPHA);
01390             else if (!strcasecmp(tmp->name, "format")) {
01391                mohclass->format = ast_getformatbyname(tmp->value);
01392                if (!mohclass->format) {
01393                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01394                   mohclass->format = AST_FORMAT_SLINEAR;
01395                }
01396             }
01397          }
01398          ast_variables_destroy(var);
01399          if (ast_strlen_zero(mohclass->dir)) {
01400             if (!strcasecmp(mohclass->mode, "custom")) {
01401                strcpy(mohclass->dir, "nodir");
01402             } else {
01403                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01404                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01405                return -1;
01406             }
01407          }
01408          if (ast_strlen_zero(mohclass->mode)) {
01409             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01410             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01411             return -1;
01412          }
01413          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01414             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01415             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01416             return -1;
01417          }
01418 
01419          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01420             /* CACHERTCLASSES enabled, let's add this class to default tree */
01421             if (state && state->class) {
01422                /* Class already exist for this channel */
01423                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01424                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01425                   /* we found RT class with the same name, seems like we should continue playing existing one */
01426                   /* XXX This code is impossible to reach */
01427                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01428                   mohclass = state->class;
01429                }
01430             }
01431             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01432              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01433              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01434              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01435              * invalid memory.
01436              */
01437             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01438                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01439                return -1;
01440             }
01441          } else {
01442             /* We don't register RT moh class, so let's init it manualy */
01443 
01444             time(&mohclass->start);
01445             mohclass->start -= respawn_time;
01446 
01447             if (!strcasecmp(mohclass->mode, "files")) {
01448                if (!moh_scan_files(mohclass)) {
01449                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01450                   return -1;
01451                }
01452                if (strchr(mohclass->args, 'r'))
01453                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01454             } 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")) {
01455 
01456                if (!strcasecmp(mohclass->mode, "custom"))
01457                   ast_set_flag(mohclass, MOH_CUSTOM);
01458                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01459                   ast_set_flag(mohclass, MOH_SINGLE);
01460                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01461                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01462                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01463                   ast_set_flag(mohclass, MOH_QUIET);
01464 
01465                mohclass->srcfd = -1;
01466                if (!(mohclass->timer = ast_timer_open())) {
01467                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01468                }
01469                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01470                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01471                   ast_timer_close(mohclass->timer);
01472                   mohclass->timer = NULL;
01473                }
01474 
01475                /* Let's check if this channel already had a moh class before */
01476                if (state && state->class) {
01477                   /* Class already exist for this channel */
01478                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01479                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01480                      /* we found RT class with the same name, seems like we should continue playing existing one */
01481                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01482                      mohclass = state->class;
01483                   }
01484                } else {
01485                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01486                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01487                      if (mohclass->timer) {
01488                         ast_timer_close(mohclass->timer);
01489                         mohclass->timer = NULL;
01490                      }
01491                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01492                      return -1;
01493                   }
01494                }
01495             } else {
01496                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01497                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01498                return -1;
01499             }
01500          }
01501       } else {
01502          ast_variables_destroy(var);
01503          var = NULL;
01504       }
01505    }
01506 
01507    if (!mohclass) {
01508       return -1;
01509    }
01510 
01511    /* If we are using a cached realtime class with files, re-scan the files */
01512    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01513       if (!moh_scan_files(mohclass)) {
01514          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01515          return -1;
01516       }
01517    }
01518 
01519    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01520       "State: Start\r\n"
01521       "Channel: %s\r\n"
01522       "UniqueID: %s\r\n"
01523       "Class: %s\r\n",
01524       chan->name, chan->uniqueid,
01525       mohclass->name);
01526 
01527    ast_set_flag(chan, AST_FLAG_MOH);
01528 
01529    if (mohclass->total_files) {
01530       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01531    } else {
01532       res = ast_activate_generator(chan, &mohgen, mohclass);
01533    }
01534 
01535    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01536 
01537    return res;
01538 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1540 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_manager_event, EVENT_FLAG_CALL, ast_channel::music_state, ast_channel::name, ast_channel::stream, and ast_channel::uniqueid.

Referenced by load_module().

01541 {
01542    ast_clear_flag(chan, AST_FLAG_MOH);
01543    ast_deactivate_generator(chan);
01544 
01545    ast_channel_lock(chan);
01546    if (chan->music_state) {
01547       if (chan->stream) {
01548          ast_closestream(chan->stream);
01549          chan->stream = NULL;
01550       }
01551    }
01552 
01553    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01554       "State: Stop\r\n"
01555       "Channel: %s\r\n"
01556       "UniqueID: %s\r\n",
01557       chan->name, chan->uniqueid);
01558    ast_channel_unlock(chan);
01559 }

static int moh_add_file ( struct mohclass class,
const char *  filepath 
) [static]

Definition at line 1004 of file res_musiconhold.c.

References mohclass::allowed_files, ast_calloc, ast_realloc, ast_strdup, mohclass::filearray, INITIAL_NUM_FILES, and mohclass::total_files.

Referenced by moh_scan_files().

01005 {
01006    if (!class->allowed_files) {
01007       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
01008          return -1;
01009       class->allowed_files = INITIAL_NUM_FILES;
01010    } else if (class->total_files == class->allowed_files) {
01011       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
01012          class->allowed_files = 0;
01013          class->total_files = 0;
01014          return -1;
01015       }
01016       class->allowed_files *= 2;
01017    }
01018 
01019    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
01020       return -1;
01021 
01022    class->total_files++;
01023 
01024    return 0;
01025 }

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

Definition at line 933 of file res_musiconhold.c.

References ast_calloc, ast_codec2str(), ast_log(), ast_module_ref(), ast_set_write_format(), ast_verb, moh_files_state::class, mohclass::format, LOG_WARNING, moh_release(), mohalloc(), mohclass_ref, mohclass_unref, ast_channel::music_state, mohclass::name, ast_channel::name, mohdata::origwfmt, state, and ast_channel::writeformat.

00934 {
00935    struct mohdata *res;
00936    struct mohclass *class = params;
00937    struct moh_files_state *state;
00938 
00939    /* Initiating music_state for current channel. Channel should know name of moh class */
00940    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00941       chan->music_state = state;
00942       ast_module_ref(ast_module_info->self);
00943    } else {
00944       state = chan->music_state;
00945       if (!state) {
00946          return NULL;
00947       }
00948       if (state->class) {
00949          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00950          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00951       }
00952       memset(state, 0, sizeof(*state));
00953    }
00954 
00955    if ((res = mohalloc(class))) {
00956       res->origwfmt = chan->writeformat;
00957       if (ast_set_write_format(chan, class->format)) {
00958          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00959          moh_release(NULL, res);
00960          res = NULL;
00961       } else {
00962          state->class = mohclass_ref(class, "Placing reference into state container");
00963       }
00964       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00965    }
00966    return res;
00967 }

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

Definition at line 1891 of file res_musiconhold.c.

References CMP_MATCH, CMP_STOP, and MOH_NOTDELETED.

Referenced by load_module().

01892 {
01893    struct mohclass *class = obj, *class2 = arg;
01894 
01895    return strcasecmp(class->name, class2->name) ? 0 :
01896       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01897       CMP_MATCH | CMP_STOP;
01898 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1561 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_debug, AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_timer_close(), ast_wait_for_input(), buff, errno, free, mohdata::list, LOG_DEBUG, and LOG_WARNING.

Referenced by _moh_class_malloc().

01562 {
01563    struct mohclass *class = obj;
01564    struct mohdata *member;
01565    pthread_t tid = 0;
01566 
01567    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01568 
01569    ao2_lock(class);
01570    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01571       free(member);
01572    }
01573    ao2_unlock(class);
01574 
01575    /* Kill the thread first, so it cannot restart the child process while the
01576     * class is being destroyed */
01577    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01578       tid = class->thread;
01579       class->thread = AST_PTHREADT_NULL;
01580       pthread_cancel(tid);
01581       /* We'll collect the exit status later, after we ensure all the readers
01582        * are dead. */
01583    }
01584 
01585    if (class->pid > 1) {
01586       char buff[8192];
01587       int bytes, tbytes = 0, stime = 0, pid = 0;
01588 
01589       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01590 
01591       stime = time(NULL) + 2;
01592       pid = class->pid;
01593       class->pid = 0;
01594 
01595       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01596        * to give the process a reason and time enough to kill off its
01597        * children. */
01598       do {
01599          if (killpg(pid, SIGHUP) < 0) {
01600             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01601          }
01602          usleep(100000);
01603          if (killpg(pid, SIGTERM) < 0) {
01604             if (errno == ESRCH) {
01605                break;
01606             }
01607             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01608          }
01609          usleep(100000);
01610          if (killpg(pid, SIGKILL) < 0) {
01611             if (errno == ESRCH) {
01612                break;
01613             }
01614             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01615          }
01616       } while (0);
01617 
01618       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01619             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01620          tbytes = tbytes + bytes;
01621       }
01622 
01623       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01624 
01625       close(class->srcfd);
01626       class->srcfd = -1;
01627    }
01628 
01629    if (class->filearray) {
01630       int i;
01631       for (i = 0; i < class->total_files; i++) {
01632          free(class->filearray[i]);
01633       }
01634       free(class->filearray);
01635       class->filearray = NULL;
01636    }
01637 
01638    if (class->timer) {
01639       ast_timer_close(class->timer);
01640       class->timer = NULL;
01641    }
01642 
01643    /* Finally, collect the exit status of the monitor thread */
01644    if (tid > 0) {
01645       pthread_join(tid, NULL);
01646    }
01647 
01648 }

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

Definition at line 1884 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01885 {
01886    const struct mohclass *class = obj;
01887 
01888    return ast_str_case_hash(class->name);
01889 }

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

Definition at line 1941 of file res_musiconhold.c.

References AST_LIST_EMPTY, CMP_MATCH, and CMP_STOP.

Referenced by unload_module().

01942 {
01943    struct mohclass *class = obj;
01944 
01945    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01946 }

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

Definition at line 1650 of file res_musiconhold.c.

Referenced by load_moh_classes().

01651 {
01652    struct mohclass *class = obj;
01653 
01654    class->delete = 1;
01655 
01656    return 0;
01657 }

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

Definition at line 1659 of file res_musiconhold.c.

References CMP_MATCH.

Referenced by load_moh_classes().

01660 {
01661    struct mohclass *class = obj;
01662 
01663    return class->delete ? CMP_MATCH : 0;
01664 }

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

Definition at line 1165 of file res_musiconhold.c.

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

Referenced by _moh_register().

01166 {
01167    if (!old || !new) {
01168       return -1;
01169    }
01170 
01171    if (strcmp(old->dir, new->dir)) {
01172       return -1;
01173    } else if (strcmp(old->mode, new->mode)) {
01174       return -1;
01175    } else if (strcmp(old->args, new->args)) {
01176       return -1;
01177    } else if (old->flags != new->flags) {
01178       return -1;
01179    }
01180 
01181    return 0;
01182 }

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

Definition at line 447 of file res_musiconhold.c.

References CMP_MATCH, and CMP_STOP.

Referenced by get_mohbydigit().

00448 {
00449    char *digit = arg;
00450    struct mohclass *class = obj;
00451 
00452    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00453 }

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

Definition at line 405 of file res_musiconhold.c.

References ast_calloc, ast_copy_string(), ast_log(), ast_module_ref(), ast_random(), ast_test_flag, ast_verb, LOG_WARNING, MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::music_state, ast_channel::name, state, and ast_channel::writeformat.

00406 {
00407    struct moh_files_state *state;
00408    struct mohclass *class = params;
00409 
00410    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00411       chan->music_state = state;
00412       ast_module_ref(ast_module_info->self);
00413    } else {
00414       state = chan->music_state;
00415       if (!state) {
00416          return NULL;
00417       }
00418       if (state->class) {
00419          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00420          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00421       }
00422    }
00423 
00424    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00425     * malloc may allocate a different class to the same memory block.  This
00426     * might only happen when two reloads are generated in a short period of
00427     * time, but it's still important to protect against.
00428     * PROG: Compare the quick operation first, to save CPU. */
00429    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00430       memset(state, 0, sizeof(*state));
00431       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00432          state->pos = ast_random() % class->total_files;
00433       }
00434    }
00435 
00436    state->class = mohclass_ref(class, "Reffing music class for channel");
00437    state->origwfmt = chan->writeformat;
00438    /* For comparison on restart of MOH (see above) */
00439    ast_copy_string(state->name, class->name, sizeof(state->name));
00440    state->save_total = class->total_files;
00441 
00442    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00443    
00444    return chan->music_state;
00445 }

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

Definition at line 371 of file res_musiconhold.c.

References ast_channel_lock, ast_channel_unlock, ast_frfree, ast_log(), ast_write(), errno, f, LOG_WARNING, moh_files_readframe(), ast_channel::music_state, and state.

00372 {
00373    struct moh_files_state *state = chan->music_state;
00374    struct ast_frame *f = NULL;
00375    int res = 0;
00376 
00377    state->sample_queue += samples;
00378 
00379    while (state->sample_queue > 0) {
00380       ast_channel_lock(chan);
00381       if ((f = moh_files_readframe(chan))) {
00382          /* We need to be sure that we unlock
00383           * the channel prior to calling
00384           * ast_write. Otherwise, the recursive locking
00385           * that occurs can cause deadlocks when using
00386           * indirect channels, like local channels
00387           */
00388          ast_channel_unlock(chan);
00389          state->samples += f->samples;
00390          state->sample_queue -= f->samples;
00391          res = ast_write(chan, f);
00392          ast_frfree(f);
00393          if (res < 0) {
00394             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00395             return -1;
00396          }
00397       } else {
00398          ast_channel_unlock(chan);
00399          return -1;  
00400       }
00401    }
00402    return res;
00403 }

static struct ast_frame* moh_files_readframe ( struct ast_channel chan  )  [static]

Definition at line 359 of file res_musiconhold.c.

References ast_moh_files_next(), ast_readframe(), f, and ast_channel::stream.

Referenced by moh_files_generator().

00360 {
00361    struct ast_frame *f = NULL;
00362    
00363    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00364       if (!ast_moh_files_next(chan))
00365          f = ast_readframe(chan->stream);
00366    }
00367 
00368    return f;
00369 }

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

Definition at line 255 of file res_musiconhold.c.

References ast_closestream(), ast_getformatname(), ast_log(), ast_set_write_format(), ast_verbose, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, state, ast_channel::stream, and VERBOSE_PREFIX_3.

00256 {
00257    struct moh_files_state *state;
00258 
00259    if (!chan || !chan->music_state) {
00260       return;
00261    }
00262 
00263    state = chan->music_state;
00264 
00265    if (chan->stream) {
00266       ast_closestream(chan->stream);
00267       chan->stream = NULL;
00268    }
00269    
00270    if (option_verbose > 2) {
00271       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00272    }
00273 
00274    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00275       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
00276    }
00277 
00278    state->save_pos = state->pos;
00279 
00280    state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00281 }

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

Definition at line 969 of file res_musiconhold.c.

References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, LOG_WARNING, moh, and ast_channel::name.

00970 {
00971    struct mohdata *moh = data;
00972    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00973    int res;
00974 
00975    len = ast_codec_get_len(moh->parent->format, samples);
00976 
00977    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00978       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00979       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00980    }
00981    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00982    if (res <= 0)
00983       return 0;
00984 
00985    moh->f.datalen = res;
00986    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00987    moh->f.samples = ast_codec_get_samples(&moh->f);
00988 
00989    if (ast_write(chan, &moh->f) < 0) {
00990       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00991       return -1;
00992    }
00993 
00994    return 0;
00995 }

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

Definition at line 461 of file res_musiconhold.c.

References ast_moh_start(), ast_moh_stop(), ast_strdupa, ast_string_field_set, get_mohbydigit(), mohclass_unref, and musicclass.

00462 {
00463    struct mohclass *class;
00464    const char *classname = NULL;
00465 
00466    if ((class = get_mohbydigit(digit))) {
00467       classname = ast_strdupa(class->name);
00468       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00469       ast_string_field_set(chan,musicclass,classname);
00470       ast_moh_stop(chan);
00471       ast_moh_start(chan, classname, NULL);
00472    }
00473 }

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

Definition at line 898 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_free, ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verb, LOG_WARNING, moh, mohclass_unref, ast_channel::music_state, ast_channel::name, and state.

Referenced by moh_alloc().

00899 {
00900    struct mohdata *moh = data;
00901    struct mohclass *class = moh->parent;
00902    format_t oldwfmt;
00903 
00904    ao2_lock(class);
00905    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00906    ao2_unlock(class);
00907    
00908    close(moh->pipe[0]);
00909    close(moh->pipe[1]);
00910 
00911    oldwfmt = moh->origwfmt;
00912 
00913    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00914 
00915    ast_free(moh);
00916 
00917    if (chan) {
00918       struct moh_files_state *state;
00919 
00920       state = chan->music_state;
00921       if (state && state->class) {
00922          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00923       }
00924       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00925          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00926                chan->name, ast_getformatname(oldwfmt));
00927       }
00928 
00929       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00930    }
00931 }

static void moh_rescan_files ( void   )  [static]

Definition at line 1149 of file res_musiconhold.c.

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

Referenced by load_moh_classes().

01149                                    {
01150    struct ao2_iterator i;
01151    struct mohclass *c;
01152 
01153    i = ao2_iterator_init(mohclasses, 0);
01154 
01155    while ((c = ao2_iterator_next(&i))) {
01156       if (!strcasecmp(c->mode, "files")) {
01157          moh_scan_files(c);
01158       }
01159       ao2_ref(c, -1);
01160    }
01161 
01162    ao2_iterator_destroy(&i);
01163 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 1037 of file res_musiconhold.c.

References ast_config_AST_DATA_DIR, ast_copy_string(), ast_debug, ast_free, ast_log(), mohclass::dir, errno, ext, mohclass::filearray, LOG_WARNING, moh_add_file(), mohclass::name, and mohclass::total_files.

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

01037                                                   {
01038 
01039    DIR *files_DIR;
01040    struct dirent *files_dirent;
01041    char dir_path[PATH_MAX];
01042    char path[PATH_MAX];
01043    char filepath[PATH_MAX];
01044    char *ext;
01045    struct stat statbuf;
01046    int i;
01047 
01048    if (class->dir[0] != '/') {
01049       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01050       strncat(dir_path, "/", sizeof(dir_path) - 1);
01051       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01052    } else {
01053       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01054    }
01055    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01056    files_DIR = opendir(dir_path);
01057    if (!files_DIR) {
01058       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01059       return -1;
01060    }
01061 
01062    for (i = 0; i < class->total_files; i++)
01063       ast_free(class->filearray[i]);
01064 
01065    class->total_files = 0;
01066    if (!getcwd(path, sizeof(path))) {
01067       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01068       closedir(files_DIR);
01069       return -1;
01070    }
01071    if (chdir(dir_path) < 0) {
01072       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01073       closedir(files_DIR);
01074       return -1;
01075    }
01076    while ((files_dirent = readdir(files_DIR))) {
01077       /* The file name must be at least long enough to have the file type extension */
01078       if ((strlen(files_dirent->d_name) < 4))
01079          continue;
01080 
01081       /* Skip files that starts with a dot */
01082       if (files_dirent->d_name[0] == '.')
01083          continue;
01084 
01085       /* Skip files without extensions... they are not audio */
01086       if (!strchr(files_dirent->d_name, '.'))
01087          continue;
01088 
01089       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01090 
01091       if (stat(filepath, &statbuf))
01092          continue;
01093 
01094       if (!S_ISREG(statbuf.st_mode))
01095          continue;
01096 
01097       if ((ext = strrchr(filepath, '.')))
01098          *ext = '\0';
01099 
01100       /* if the file is present in multiple formats, ensure we only put it into the list once */
01101       for (i = 0; i < class->total_files; i++)
01102          if (!strcmp(filepath, class->filearray[i]))
01103             break;
01104 
01105       if (i == class->total_files) {
01106          if (moh_add_file(class, filepath))
01107             break;
01108       }
01109    }
01110 
01111    closedir(files_DIR);
01112    if (chdir(path) < 0) {
01113       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01114       return -1;
01115    }
01116    if (ast_test_flag(class, MOH_SORTALPHA))
01117       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01118    return class->total_files;
01119 }

static int moh_sort_compare ( const void *  i1,
const void *  i2 
) [static]

Definition at line 1027 of file res_musiconhold.c.

01028 {
01029    char *s1, *s2;
01030 
01031    s1 = ((char **)i1)[0];
01032    s2 = ((char **)i2)[0];
01033 
01034    return strcasecmp(s1, s2);
01035 }

static struct mohdata* mohalloc ( struct mohclass cl  )  [static]

Definition at line 865 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_calloc, AST_FRAME_VOICE, ast_free, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohclass::format, mohdata::list, LOG_WARNING, mohclass::members, moh, mohclass_ref, and mohdata::pipe.

Referenced by moh_alloc().

00866 {
00867    struct mohdata *moh;
00868    long flags; 
00869    
00870    if (!(moh = ast_calloc(1, sizeof(*moh))))
00871       return NULL;
00872    
00873    if (pipe(moh->pipe)) {
00874       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00875       ast_free(moh);
00876       return NULL;
00877    }
00878 
00879    /* Make entirely non-blocking */
00880    flags = fcntl(moh->pipe[0], F_GETFL);
00881    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00882    flags = fcntl(moh->pipe[1], F_GETFL);
00883    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00884 
00885    moh->f.frametype = AST_FRAME_VOICE;
00886    moh->f.subclass.codec = cl->format;
00887    moh->f.offset = AST_FRIENDLY_OFFSET;
00888 
00889    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00890 
00891    ao2_lock(cl);
00892    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00893    ao2_unlock(cl);
00894    
00895    return moh;
00896 }

static void* monmp3thread ( void *  data  )  [static]

Definition at line 624 of file res_musiconhold.c.

References ao2_lock, ao2_unlock, ast_codec_get_len(), ast_debug, 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, len(), LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, and spawn_mp3().

Referenced by init_app_class(), and local_ast_moh_start().

00625 {
00626 #define  MOH_MS_INTERVAL      100
00627 
00628    struct mohclass *class = data;
00629    struct mohdata *moh;
00630    short sbuf[8192];
00631    int res = 0, res2;
00632    int len;
00633    struct timeval deadline, tv_tmp;
00634 
00635    deadline.tv_sec = 0;
00636    deadline.tv_usec = 0;
00637    for(;/* ever */;) {
00638       pthread_testcancel();
00639       /* Spawn mp3 player if it's not there */
00640       if (class->srcfd < 0) {
00641          if ((class->srcfd = spawn_mp3(class)) < 0) {
00642             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00643             /* Try again later */
00644             sleep(500);
00645             continue;
00646          }
00647       }
00648       if (class->timer) {
00649          struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
00650 
00651 #ifdef SOLARIS
00652          thr_yield();
00653 #endif
00654          /* Pause some amount of time */
00655          if (ast_poll(&pfd, 1, -1) > 0) {
00656             ast_timer_ack(class->timer, 1);
00657             res = 320;
00658          } else {
00659             ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00660             res = 0;
00661          }
00662          pthread_testcancel();
00663       } else {
00664          long delta;
00665          /* Reliable sleep */
00666          tv_tmp = ast_tvnow();
00667          if (ast_tvzero(deadline))
00668             deadline = tv_tmp;
00669          delta = ast_tvdiff_ms(tv_tmp, deadline);
00670          if (delta < MOH_MS_INTERVAL) {   /* too early */
00671             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00672             usleep(1000 * (MOH_MS_INTERVAL - delta));
00673             pthread_testcancel();
00674          } else {
00675             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00676             deadline = tv_tmp;
00677          }
00678          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00679       }
00680       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00681          continue;
00682       /* Read mp3 audio */
00683       len = ast_codec_get_len(class->format, res);
00684 
00685       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00686          if (!res2) {
00687             close(class->srcfd);
00688             class->srcfd = -1;
00689             pthread_testcancel();
00690             if (class->pid > 1) {
00691                do {
00692                   if (killpg(class->pid, SIGHUP) < 0) {
00693                      if (errno == ESRCH) {
00694                         break;
00695                      }
00696                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00697                   }
00698                   usleep(100000);
00699                   if (killpg(class->pid, SIGTERM) < 0) {
00700                      if (errno == ESRCH) {
00701                         break;
00702                      }
00703                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00704                   }
00705                   usleep(100000);
00706                   if (killpg(class->pid, SIGKILL) < 0) {
00707                      if (errno == ESRCH) {
00708                         break;
00709                      }
00710                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00711                   }
00712                } while (0);
00713                class->pid = 0;
00714             }
00715          } else {
00716             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00717          }
00718          continue;
00719       }
00720 
00721       pthread_testcancel();
00722 
00723       ao2_lock(class);
00724       AST_LIST_TRAVERSE(&class->members, moh, list) {
00725          /* Write data */
00726          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00727             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00728          }
00729       }
00730       ao2_unlock(class);
00731    }
00732    return NULL;
00733 }

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

Definition at line 735 of file res_musiconhold.c.

References args, AST_APP_ARG, 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, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

00736 {
00737    char *parse;
00738    char *class;
00739    int timeout = -1;
00740    int res;
00741    AST_DECLARE_APP_ARGS(args,
00742       AST_APP_ARG(class);
00743       AST_APP_ARG(duration);
00744    );
00745 
00746    parse = ast_strdupa(data);
00747 
00748    AST_STANDARD_APP_ARGS(args, parse);
00749 
00750    if (!ast_strlen_zero(args.duration)) {
00751       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00752          timeout *= 1000;
00753       } else {
00754          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00755       }
00756    }
00757 
00758    class = S_OR(args.class, NULL);
00759    if (ast_moh_start(chan, class, NULL)) {
00760       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00761       return 0;
00762    }
00763 
00764    if (timeout > 0)
00765       res = ast_safe_sleep(chan, timeout);
00766    else {
00767       while (!(res = ast_safe_sleep(chan, 10000)));
00768    }
00769 
00770    ast_moh_stop(chan);
00771 
00772    return res;
00773 }

static int reload ( void   )  [static]

static int set_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 798 of file res_musiconhold.c.

References ast_log(), ast_string_field_set, ast_strlen_zero(), LOG_WARNING, and musicclass.

Referenced by load_module().

00799 {
00800    static int deprecation_warning = 0;
00801 
00802    if (!deprecation_warning) {
00803       deprecation_warning = 1;
00804       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00805    }
00806 
00807    if (ast_strlen_zero(data)) {
00808       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00809       return -1;
00810    }
00811    ast_string_field_set(chan, musicclass, data);
00812    return 0;
00813 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 483 of file res_musiconhold.c.

References mohclass::args, ast_close_fds_above_n(), ast_copy_string(), ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_strlen_zero(), ast_test_flag, mohclass::dir, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, mohclass::pid, mohclass::start, and strsep().

Referenced by monmp3thread().

00484 {
00485    int fds[2];
00486    int files = 0;
00487    char fns[MAX_MP3S][80];
00488    char *argv[MAX_MP3S + 50];
00489    char xargs[256];
00490    char *argptr;
00491    int argc = 0;
00492    DIR *dir = NULL;
00493    struct dirent *de;
00494 
00495    
00496    if (!strcasecmp(class->dir, "nodir")) {
00497       files = 1;
00498    } else {
00499       dir = opendir(class->dir);
00500       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00501          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00502          return -1;
00503       }
00504    }
00505 
00506    if (!ast_test_flag(class, MOH_CUSTOM)) {
00507       argv[argc++] = "mpg123";
00508       argv[argc++] = "-q";
00509       argv[argc++] = "-s";
00510       argv[argc++] = "--mono";
00511       argv[argc++] = "-r";
00512       argv[argc++] = "8000";
00513       
00514       if (!ast_test_flag(class, MOH_SINGLE)) {
00515          argv[argc++] = "-b";
00516          argv[argc++] = "2048";
00517       }
00518       
00519       argv[argc++] = "-f";
00520       
00521       if (ast_test_flag(class, MOH_QUIET))
00522          argv[argc++] = "4096";
00523       else
00524          argv[argc++] = "8192";
00525       
00526       /* Look for extra arguments and add them to the list */
00527       ast_copy_string(xargs, class->args, sizeof(xargs));
00528       argptr = xargs;
00529       while (!ast_strlen_zero(argptr)) {
00530          argv[argc++] = argptr;
00531          strsep(&argptr, ",");
00532       }
00533    } else  {
00534       /* Format arguments for argv vector */
00535       ast_copy_string(xargs, class->args, sizeof(xargs));
00536       argptr = xargs;
00537       while (!ast_strlen_zero(argptr)) {
00538          argv[argc++] = argptr;
00539          strsep(&argptr, " ");
00540       }
00541    }
00542 
00543    if (!strncasecmp(class->dir, "http://", 7)) {
00544       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00545       argv[argc++] = fns[files];
00546       files++;
00547    } else if (dir) {
00548       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00549          if ((strlen(de->d_name) > 3) && 
00550              ((ast_test_flag(class, MOH_CUSTOM) && 
00551                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00552                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00553               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00554             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00555             argv[argc++] = fns[files];
00556             files++;
00557          }
00558       }
00559    }
00560    argv[argc] = NULL;
00561    if (dir) {
00562       closedir(dir);
00563    }
00564    if (pipe(fds)) {  
00565       ast_log(LOG_WARNING, "Pipe failed\n");
00566       return -1;
00567    }
00568    if (!files) {
00569       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00570       close(fds[0]);
00571       close(fds[1]);
00572       return -1;
00573    }
00574    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00575       sleep(respawn_time - (time(NULL) - class->start));
00576    }
00577 
00578    time(&class->start);
00579    class->pid = ast_safe_fork(0);
00580    if (class->pid < 0) {
00581       close(fds[0]);
00582       close(fds[1]);
00583       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00584       return -1;
00585    }
00586    if (!class->pid) {
00587       if (ast_opt_high_priority)
00588          ast_set_priority(0);
00589 
00590       close(fds[0]);
00591       /* Stdout goes to pipe */
00592       dup2(fds[1], STDOUT_FILENO);
00593 
00594       /* Close everything else */
00595       ast_close_fds_above_n(STDERR_FILENO);
00596 
00597       /* Child */
00598       if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00599          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00600          _exit(1);
00601       }
00602       setpgid(0, getpid());
00603       if (ast_test_flag(class, MOH_CUSTOM)) {
00604          execv(argv[0], argv);
00605       } else {
00606          /* Default install is /usr/local/bin */
00607          execv(LOCAL_MPG_123, argv);
00608          /* Many places have it in /usr/bin */
00609          execv(MPG_123, argv);
00610          /* Check PATH as a last-ditch effort */
00611          execvp("mpg123", argv);
00612       }
00613       /* Can't use logger, since log FDs are closed */
00614       fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00615       close(fds[1]);
00616       _exit(1);
00617    } else {
00618       /* Parent */
00619       close(fds[1]);
00620    }
00621    return fds[0];
00622 }

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

Definition at line 815 of file res_musiconhold.c.

References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_moh_start(), AST_STANDARD_APP_ARGS, ast_strdupa, LOG_WARNING, ast_channel::name, parse(), and S_OR.

Referenced by load_module().

00816 {
00817    char *parse;
00818    char *class;
00819    AST_DECLARE_APP_ARGS(args,
00820       AST_APP_ARG(class);
00821    );
00822 
00823    parse = ast_strdupa(data);
00824 
00825    AST_STANDARD_APP_ARGS(args, parse);
00826 
00827    class = S_OR(args.class, NULL);
00828    if (ast_moh_start(chan, class, NULL)) 
00829       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00830 
00831    return 0;
00832 }

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

Definition at line 834 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00835 {
00836    ast_moh_stop(chan);
00837 
00838    return 0;
00839 }

static int unload_module ( void   )  [static]

Definition at line 1948 of file res_musiconhold.c.

References ao2_t_callback, ARRAY_LEN, ast_cli_unregister_multiple(), ast_log(), ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), ast_unregister_atexit(), cli_moh, LOG_WARNING, moh_class_inuse(), and mohclass_unref.

01949 {
01950    int res = 0;
01951    struct mohclass *class = NULL;
01952 
01953    /* XXX This check shouldn't be required if module ref counting was being used
01954     * properly ... */
01955    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01956       class = mohclass_unref(class, "unref of class from module unload callback");
01957       res = -1;
01958    }
01959 
01960    if (res < 0) {
01961       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01962       return res;
01963    }
01964 
01965    ast_uninstall_music_functions();
01966 
01967    ast_moh_destroy();
01968    res = ast_unregister_application(play_moh);
01969    res |= ast_unregister_application(wait_moh);
01970    res |= ast_unregister_application(set_moh);
01971    res |= ast_unregister_application(start_moh);
01972    res |= ast_unregister_application(stop_moh);
01973    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01974    ast_unregister_atexit(ast_moh_destroy);
01975 
01976    return res;
01977 }

static int wait_moh_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 775 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), LOG_WARNING, and ast_channel::name.

Referenced by load_module().

00776 {
00777    static int deprecation_warning = 0;
00778    int res;
00779 
00780    if (!deprecation_warning) {
00781       deprecation_warning = 1;
00782       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00783    }
00784 
00785    if (!data || !atoi(data)) {
00786       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00787       return -1;
00788    }
00789    if (ast_moh_start(chan, NULL, NULL)) {
00790       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00791       return 0;
00792    }
00793    res = ast_safe_sleep(chan, atoi(data) * 1000);
00794    ast_moh_stop(chan);
00795    return res;
00796 }


Variable Documentation

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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DEPEND, } [static]

Definition at line 1984 of file res_musiconhold.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1984 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Initial value:

 {
   { .handler =  handle_cli_moh_reload , .summary =  "Reload MusicOnHold" ,__VA_ARGS__ },
   { .handler =  handle_cli_moh_show_classes , .summary =  "List MusicOnHold classes" ,__VA_ARGS__ },
   { .handler =  handle_cli_moh_show_files , .summary =  "List MusicOnHold file-based classes" ,__VA_ARGS__ }
}

Definition at line 1878 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

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

global MOH_ flags

Definition at line 179 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Definition at line 475 of file res_musiconhold.c.

struct ao2_container* mohclasses [static]

Definition at line 219 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Definition at line 997 of file res_musiconhold.c.

const char play_moh[] = "MusicOnHold" [static]

Definition at line 147 of file res_musiconhold.c.

int respawn_time = 20 [static]

Definition at line 153 of file res_musiconhold.c.

const char set_moh[] = "SetMusicOnHold" [static]

Definition at line 149 of file res_musiconhold.c.

const char start_moh[] = "StartMusicOnHold" [static]

Definition at line 150 of file res_musiconhold.c.

const char stop_moh[] = "StopMusicOnHold" [static]

Definition at line 151 of file res_musiconhold.c.

const char wait_moh[] = "WaitMusicOnHold" [static]

Definition at line 148 of file res_musiconhold.c.


Generated on Mon Oct 8 12:39:28 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7