diff options
Diffstat (limited to 'trunk/res/res_musiconhold.c')
-rw-r--r-- | trunk/res/res_musiconhold.c | 1566 |
1 files changed, 0 insertions, 1566 deletions
diff --git a/trunk/res/res_musiconhold.c b/trunk/res/res_musiconhold.c deleted file mode 100644 index b4c5edfbb..000000000 --- a/trunk/res/res_musiconhold.c +++ /dev/null @@ -1,1566 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Routines implementing music on hold - * - * \arg See also \ref Config_moh - * - * \author Mark Spencer <markster@digium.com> - */ - -/*** MODULEINFO - <conflict>win32</conflict> - <use>zaptel</use> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#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 <sys/ioctl.h> -#ifdef SOLARIS -#include <thread.h> -#endif - -#include "asterisk/zapata.h" - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.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" - -#define INITIAL_NUM_FILES 8 - -static char *play_moh = "MusicOnHold"; -static char *wait_moh = "WaitMusicOnHold"; -static char *set_moh = "SetMusicOnHold"; -static char *start_moh = "StartMusicOnHold"; -static char *stop_moh = "StopMusicOnHold"; - -static char *play_moh_syn = "Play Music On Hold indefinitely"; -static char *wait_moh_syn = "Wait, playing Music On Hold"; -static char *set_moh_syn = "Set default Music On Hold class"; -static char *start_moh_syn = "Play Music On Hold"; -static char *stop_moh_syn = "Stop Playing Music On Hold"; - -static char *play_moh_desc = " MusicOnHold(class):\n" -"Plays hold music specified by class. If omitted, the default\n" -"music source for the channel will be used. Set the default \n" -"class with the SetMusicOnHold() application.\n" -"Returns -1 on hangup.\n" -"Never returns otherwise.\n"; - -static char *wait_moh_desc = " WaitMusicOnHold(delay):\n" -"Plays hold music specified number of seconds. Returns 0 when\n" -"done, or -1 on hangup. If no hold music is available, the delay will\n" -"still occur with no sound.\n"; - -static char *set_moh_desc = " SetMusicOnHold(class):\n" -"Sets the default class for music on hold for a given channel. When\n" -"music on hold is activated, this class will be used to select which\n" -"music is played.\n"; - -static char *start_moh_desc = " StartMusicOnHold(class):\n" -"Starts playing music on hold, uses default music class for channel.\n" -"Starts playing music specified by class. If omitted, the default\n" -"music source for the channel will be used. Always returns 0.\n"; - -static char *stop_moh_desc = " StopMusicOnHold(): " -"Stops playing music on hold.\n"; - -static int respawn_time = 20; - -struct moh_files_state { - struct mohclass *class; - int origwfmt; - int samples; - int sample_queue; - int pos; - int save_pos; -}; - -#define MOH_QUIET (1 << 0) -#define MOH_SINGLE (1 << 1) -#define MOH_CUSTOM (1 << 2) -#define MOH_RANDOMIZE (1 << 3) -#define MOH_SORTALPHA (1 << 4) - -#define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */ - -static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */ - -struct mohclass { - char name[MAX_MUSICCLASS]; - char dir[256]; - char args[256]; - char mode[80]; - char digit; - /*! A dynamically sized array to hold the list of filenames in "files" mode */ - char **filearray; - /*! The current size of the filearray */ - int allowed_files; - /*! The current number of files loaded into the filearray */ - int total_files; - unsigned int flags; - /*! The format from the MOH source, not applicable to "files" mode */ - int format; - /*! The pid of the external application delivering MOH */ - int pid; - time_t start; - pthread_t thread; - /*! Source of audio */ - int srcfd; - /*! FD for timing source */ - int pseudofd; - /*! Number of users */ - int inuse; - /*! Created on the fly, from RT engine */ - int realtime; - unsigned int delete:1; - AST_LIST_HEAD_NOLOCK(, mohdata) members; - AST_LIST_ENTRY(mohclass) list; -}; - -struct mohdata { - int pipe[2]; - int origwfmt; - struct mohclass *parent; - struct ast_frame f; - AST_LIST_ENTRY(mohdata) list; -}; - -AST_RWLIST_HEAD_STATIC(mohclasses, mohclass); - -#define LOCAL_MPG_123 "/usr/local/bin/mpg123" -#define MPG_123 "/usr/bin/mpg123" -#define MAX_MP3S 256 - -static int ast_moh_destroy_one(struct mohclass *moh); -static int reload(void); - -static void ast_moh_free_class(struct mohclass **mohclass) -{ - struct mohdata *member; - struct mohclass *class = *mohclass; - int i; - - while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) - ast_free(member); - - if (class->thread) { - pthread_cancel(class->thread); - class->thread = 0; - } - - if (class->filearray) { - for (i = 0; i < class->total_files; i++) - ast_free(class->filearray[i]); - ast_free(class->filearray); - } - - ast_free(class); - *mohclass = NULL; -} - - -static void moh_files_release(struct ast_channel *chan, void *data) -{ - struct moh_files_state *state = chan->music_state; - - if (chan && state) { - if (chan->stream) { - ast_closestream(chan->stream); - chan->stream = NULL; - } - ast_verb(3, "Stopped music on hold on %s\n", chan->name); - - if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) { - ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt); - } - state->save_pos = state->pos; - } - if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete) - ast_moh_destroy_one(state->class); -} - - -static int ast_moh_files_next(struct ast_channel *chan) -{ - struct moh_files_state *state = chan->music_state; - int tries; - - /* Discontinue a stream if it is running already */ - if (chan->stream) { - ast_closestream(chan->stream); - chan->stream = NULL; - } - - if (!state->class->total_files) { - ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name); - return -1; - } - - /* If a specific file has been saved, use it */ - if (state->save_pos >= 0) { - state->pos = state->save_pos; - state->save_pos = -1; - } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) { - /* Get a random file and ensure we can open it */ - for (tries = 0; tries < 20; tries++) { - state->pos = rand() % state->class->total_files; - if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) - break; - } - state->samples = 0; - } else { - /* This is easy, just increment our position and make sure we don't exceed the total file count */ - state->pos++; - state->pos %= state->class->total_files; - state->samples = 0; - } - - if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) { - ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno)); - state->pos++; - state->pos %= state->class->total_files; - return -1; - } - - ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); - - if (state->samples) - ast_seekstream(chan->stream, state->samples, SEEK_SET); - - return 0; -} - - -static struct ast_frame *moh_files_readframe(struct ast_channel *chan) -{ - struct ast_frame *f = NULL; - - if (!(chan->stream && (f = ast_readframe(chan->stream)))) { - if (!ast_moh_files_next(chan)) - f = ast_readframe(chan->stream); - } - - return f; -} - -static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples) -{ - struct moh_files_state *state = chan->music_state; - struct ast_frame *f = NULL; - int res = 0; - - state->sample_queue += samples; - - while (state->sample_queue > 0) { - if ((f = moh_files_readframe(chan))) { - state->samples += f->samples; - res = ast_write(chan, f); - state->sample_queue -= f->samples; - ast_frfree(f); - if (res < 0) { - ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); - return -1; - } - } else - return -1; - } - return res; -} - - -static void *moh_files_alloc(struct ast_channel *chan, void *params) -{ - struct moh_files_state *state; - struct mohclass *class = params; - - if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { - chan->music_state = state; - state->class = class; - state->save_pos = -1; - } else - state = chan->music_state; - - if (state) { - if (state->class != class) { - /* initialize */ - memset(state, 0, sizeof(*state)); - state->class = class; - if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files) - state->pos = ast_random() % class->total_files; - } - - state->origwfmt = chan->writeformat; - - ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name); - } - - return chan->music_state; -} - -/*! \note This function should be called with the mohclasses list locked */ -static struct mohclass *get_mohbydigit(char digit) -{ - struct mohclass *moh = NULL; - - AST_RWLIST_TRAVERSE(&mohclasses, moh, list) { - if (digit == moh->digit) - break; - } - - return moh; -} - -static void moh_handle_digit(struct ast_channel *chan, char digit) -{ - struct mohclass *moh; - const char *classname = NULL; - - AST_RWLIST_RDLOCK(&mohclasses); - if ((moh = get_mohbydigit(digit))) - classname = ast_strdupa(moh->name); - AST_RWLIST_UNLOCK(&mohclasses); - - if (!moh) - return; - - ast_moh_stop(chan); - ast_moh_start(chan, classname, NULL); -} - -static struct ast_generator moh_file_stream = -{ - alloc: moh_files_alloc, - release: moh_files_release, - generate: moh_files_generator, - digit: moh_handle_digit, -}; - -static int spawn_mp3(struct mohclass *class) -{ - int fds[2]; - int files = 0; - char fns[MAX_MP3S][80]; - char *argv[MAX_MP3S + 50]; - char xargs[256]; - char *argptr; - int argc = 0; - DIR *dir = NULL; - struct dirent *de; - sigset_t signal_set, old_set; - - - if (!strcasecmp(class->dir, "nodir")) { - files = 1; - } else { - dir = opendir(class->dir); - if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) { - ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir); - return -1; - } - } - - if (!ast_test_flag(class, MOH_CUSTOM)) { - argv[argc++] = "mpg123"; - argv[argc++] = "-q"; - argv[argc++] = "-s"; - argv[argc++] = "--mono"; - argv[argc++] = "-r"; - argv[argc++] = "8000"; - - if (!ast_test_flag(class, MOH_SINGLE)) { - argv[argc++] = "-b"; - argv[argc++] = "2048"; - } - - argv[argc++] = "-f"; - - if (ast_test_flag(class, MOH_QUIET)) - argv[argc++] = "4096"; - else - argv[argc++] = "8192"; - - /* Look for extra arguments and add them to the list */ - ast_copy_string(xargs, class->args, sizeof(xargs)); - argptr = xargs; - while (!ast_strlen_zero(argptr)) { - argv[argc++] = argptr; - strsep(&argptr, ","); - } - } else { - /* Format arguments for argv vector */ - ast_copy_string(xargs, class->args, sizeof(xargs)); - argptr = xargs; - while (!ast_strlen_zero(argptr)) { - argv[argc++] = argptr; - strsep(&argptr, " "); - } - } - - - if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) { - ast_copy_string(fns[files], class->dir, sizeof(fns[files])); - argv[argc++] = fns[files]; - files++; - } else if (dir) { - while ((de = readdir(dir)) && (files < MAX_MP3S)) { - if ((strlen(de->d_name) > 3) && - ((ast_test_flag(class, MOH_CUSTOM) && - (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || - !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) || - !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) { - ast_copy_string(fns[files], de->d_name, sizeof(fns[files])); - argv[argc++] = fns[files]; - files++; - } - } - } - argv[argc] = NULL; - if (dir) { - closedir(dir); - } - if (pipe(fds)) { - ast_log(LOG_WARNING, "Pipe failed\n"); - return -1; - } - if (!files) { - ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir); - close(fds[0]); - close(fds[1]); - return -1; - } - if (time(NULL) - class->start < respawn_time) { - sleep(respawn_time - (time(NULL) - class->start)); - } - - /* Block signals during the fork() */ - sigfillset(&signal_set); - pthread_sigmask(SIG_BLOCK, &signal_set, &old_set); - - time(&class->start); - class->pid = fork(); - if (class->pid < 0) { - close(fds[0]); - close(fds[1]); - ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); - return -1; - } - if (!class->pid) { - int x; - - if (ast_opt_high_priority) - ast_set_priority(0); - - /* Reset ignored signals back to default */ - signal(SIGPIPE, SIG_DFL); - pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL); - - close(fds[0]); - /* Stdout goes to pipe */ - dup2(fds[1], STDOUT_FILENO); - /* Close unused file descriptors */ - for (x=3;x<8192;x++) { - if (-1 != fcntl(x, F_GETFL)) { - close(x); - } - } - /* Child */ - chdir(class->dir); - if (ast_test_flag(class, MOH_CUSTOM)) { - execv(argv[0], argv); - } else { - /* Default install is /usr/local/bin */ - execv(LOCAL_MPG_123, argv); - /* Many places have it in /usr/bin */ - execv(MPG_123, argv); - /* Check PATH as a last-ditch effort */ - execvp("mpg123", argv); - } - ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno)); - close(fds[1]); - _exit(1); - } else { - /* Parent */ - pthread_sigmask(SIG_SETMASK, &old_set, NULL); - close(fds[1]); - } - return fds[0]; -} - -static void *monmp3thread(void *data) -{ -#define MOH_MS_INTERVAL 100 - - struct mohclass *class = data; - struct mohdata *moh; - char buf[8192]; - short sbuf[8192]; - int res, res2; - int len; - struct timeval tv, tv_tmp; - - tv.tv_sec = 0; - tv.tv_usec = 0; - for(;/* ever */;) { - pthread_testcancel(); - /* Spawn mp3 player if it's not there */ - if (class->srcfd < 0) { - if ((class->srcfd = spawn_mp3(class)) < 0) { - ast_log(LOG_WARNING, "Unable to spawn mp3player\n"); - /* Try again later */ - sleep(500); - pthread_testcancel(); - } - } - if (class->pseudofd > -1) { -#ifdef SOLARIS - thr_yield(); -#endif - /* Pause some amount of time */ - res = read(class->pseudofd, buf, sizeof(buf)); - pthread_testcancel(); - } else { - long delta; - /* Reliable sleep */ - tv_tmp = ast_tvnow(); - if (ast_tvzero(tv)) - tv = tv_tmp; - delta = ast_tvdiff_ms(tv_tmp, tv); - if (delta < MOH_MS_INTERVAL) { /* too early */ - tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */ - usleep(1000 * (MOH_MS_INTERVAL - delta)); - pthread_testcancel(); - } else { - ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n"); - tv = tv_tmp; - } - res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */ - } - if (AST_LIST_EMPTY(&class->members)) - continue; - /* Read mp3 audio */ - len = ast_codec_get_len(class->format, res); - - if ((res2 = read(class->srcfd, sbuf, len)) != len) { - if (!res2) { - close(class->srcfd); - class->srcfd = -1; - pthread_testcancel(); - if (class->pid > 1) { - kill(class->pid, SIGHUP); - usleep(100000); - kill(class->pid, SIGTERM); - usleep(100000); - kill(class->pid, SIGKILL); - class->pid = 0; - } - } else { - ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len); - } - continue; - } - pthread_testcancel(); - AST_RWLIST_RDLOCK(&mohclasses); - AST_RWLIST_TRAVERSE(&class->members, moh, list) { - /* Write data */ - if ((res = write(moh->pipe[1], sbuf, res2)) != res2) { - ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2); - } - } - AST_RWLIST_UNLOCK(&mohclasses); - } - return NULL; -} - -static int play_moh_exec(struct ast_channel *chan, void *data) -{ - if (ast_moh_start(chan, data, NULL)) { - ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name); - return 0; - } - while (!ast_safe_sleep(chan, 10000)); - ast_moh_stop(chan); - return -1; -} - -static int wait_moh_exec(struct ast_channel *chan, void *data) -{ - int res; - if (!data || !atoi(data)) { - ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n"); - return -1; - } - if (ast_moh_start(chan, NULL, NULL)) { - ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name); - return 0; - } - res = ast_safe_sleep(chan, atoi(data) * 1000); - ast_moh_stop(chan); - return res; -} - -static int set_moh_exec(struct ast_channel *chan, void *data) -{ - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n"); - return -1; - } - ast_string_field_set(chan, musicclass, data); - return 0; -} - -static int start_moh_exec(struct ast_channel *chan, void *data) -{ - char *class = NULL; - if (data && strlen(data)) - class = data; - if (ast_moh_start(chan, class, NULL)) - ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name); - - return 0; -} - -static int stop_moh_exec(struct ast_channel *chan, void *data) -{ - ast_moh_stop(chan); - - return 0; -} - -/*! \note This function should be called with the mohclasses list locked */ -static struct mohclass *get_mohbyname(const char *name, int warn) -{ - struct mohclass *moh = NULL; - - AST_RWLIST_TRAVERSE(&mohclasses, moh, list) { - if (!strcasecmp(name, moh->name)) - break; - } - - if (!moh && warn) - ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name); - - return moh; -} - -static struct mohdata *mohalloc(struct mohclass *cl) -{ - struct mohdata *moh; - long flags; - - if (!(moh = ast_calloc(1, sizeof(*moh)))) - return NULL; - - if (pipe(moh->pipe)) { - ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno)); - ast_free(moh); - return NULL; - } - - /* Make entirely non-blocking */ - flags = fcntl(moh->pipe[0], F_GETFL); - fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK); - flags = fcntl(moh->pipe[1], F_GETFL); - fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK); - - moh->f.frametype = AST_FRAME_VOICE; - moh->f.subclass = cl->format; - moh->f.offset = AST_FRIENDLY_OFFSET; - - moh->parent = cl; - - AST_RWLIST_WRLOCK(&mohclasses); - AST_LIST_INSERT_HEAD(&cl->members, moh, list); - AST_RWLIST_UNLOCK(&mohclasses); - - return moh; -} - -static void moh_release(struct ast_channel *chan, void *data) -{ - struct mohdata *moh = data; - int oldwfmt; - struct moh_files_state *state; - - AST_RWLIST_WRLOCK(&mohclasses); - AST_RWLIST_REMOVE(&moh->parent->members, moh, list); - AST_RWLIST_UNLOCK(&mohclasses); - - close(moh->pipe[0]); - close(moh->pipe[1]); - oldwfmt = moh->origwfmt; - state = chan->music_state; - if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse)) - ast_moh_destroy_one(moh->parent); - if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete) - ast_moh_destroy_one(state->class); - - ast_free(moh); - if (chan) { - if (oldwfmt && ast_set_write_format(chan, oldwfmt)) - ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt)); - ast_verb(3, "Stopped music on hold on %s\n", chan->name); - } -} - -static void *moh_alloc(struct ast_channel *chan, void *params) -{ - struct mohdata *res; - struct mohclass *class = params; - struct moh_files_state *state; - - /* Initiating music_state for current channel. Channel should know name of moh class */ - if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) { - chan->music_state = state; - state->class = class; - } else - state = chan->music_state; - if (state && state->class != class) { - memset(state, 0, sizeof(*state)); - state->class = class; - } - - if ((res = mohalloc(class))) { - res->origwfmt = chan->writeformat; - if (ast_set_write_format(chan, class->format)) { - ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format)); - moh_release(NULL, res); - res = NULL; - } - ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name); - } - return res; -} - -static int moh_generate(struct ast_channel *chan, void *data, int len, int samples) -{ - struct mohdata *moh = data; - short buf[1280 + AST_FRIENDLY_OFFSET / 2]; - int res; - - if (!moh->parent->pid) - return -1; - - len = ast_codec_get_len(moh->parent->format, samples); - - if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { - ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name); - len = sizeof(buf) - AST_FRIENDLY_OFFSET; - } - res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len); - if (res <= 0) - return 0; - - moh->f.datalen = res; - moh->f.data = buf + AST_FRIENDLY_OFFSET / 2; - moh->f.samples = ast_codec_get_samples(&moh->f); - - if (ast_write(chan, &moh->f) < 0) { - ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); - return -1; - } - - return 0; -} - -static struct ast_generator mohgen = -{ - alloc: moh_alloc, - release: moh_release, - generate: moh_generate, - digit: moh_handle_digit -}; - -static int moh_add_file(struct mohclass *class, const char *filepath) -{ - if (!class->allowed_files) { - if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray)))) - return -1; - class->allowed_files = INITIAL_NUM_FILES; - } else if (class->total_files == class->allowed_files) { - if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) { - class->allowed_files = 0; - class->total_files = 0; - return -1; - } - class->allowed_files *= 2; - } - - if (!(class->filearray[class->total_files] = ast_strdup(filepath))) - return -1; - - class->total_files++; - - return 0; -} - -static int moh_sort_compare(const void *i1, const void *i2) -{ - char *s1, *s2; - - s1 = ((char **)i1)[0]; - s2 = ((char **)i2)[0]; - - return strcasecmp(s1, s2); -} - -static int moh_scan_files(struct mohclass *class) { - - DIR *files_DIR; - struct dirent *files_dirent; - char path[PATH_MAX]; - char filepath[PATH_MAX]; - char *ext; - struct stat statbuf; - int dirnamelen; - int i; - - files_DIR = opendir(class->dir); - if (!files_DIR) { - ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir); - return -1; - } - - for (i = 0; i < class->total_files; i++) - ast_free(class->filearray[i]); - - class->total_files = 0; - dirnamelen = strlen(class->dir) + 2; - getcwd(path, sizeof(path)); - chdir(class->dir); - while ((files_dirent = readdir(files_DIR))) { - /* The file name must be at least long enough to have the file type extension */ - if ((strlen(files_dirent->d_name) < 4)) - continue; - - /* Skip files that starts with a dot */ - if (files_dirent->d_name[0] == '.') - continue; - - /* Skip files without extensions... they are not audio */ - if (!strchr(files_dirent->d_name, '.')) - continue; - - snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name); - - if (stat(filepath, &statbuf)) - continue; - - if (!S_ISREG(statbuf.st_mode)) - continue; - - if ((ext = strrchr(filepath, '.'))) - *ext = '\0'; - - /* if the file is present in multiple formats, ensure we only put it into the list once */ - for (i = 0; i < class->total_files; i++) - if (!strcmp(filepath, class->filearray[i])) - break; - - if (i == class->total_files) { - if (moh_add_file(class, filepath)) - break; - } - } - - closedir(files_DIR); - chdir(path); - if (ast_test_flag(class, MOH_SORTALPHA)) - qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare); - return class->total_files; -} - -static int moh_diff(struct mohclass *old, struct mohclass *new) -{ - if (!old || !new) - return -1; - - if (strcmp(old->dir, new->dir)) - return -1; - else if (strcmp(old->mode, new->mode)) - return -1; - else if (strcmp(old->args, new->args)) - return -1; - else if (old->flags != new->flags) - return -1; - - return 0; -} - -static int moh_register(struct mohclass *moh, int reload) -{ -#ifdef HAVE_ZAPTEL - int x; -#endif - struct mohclass *mohclass = NULL; - - AST_RWLIST_WRLOCK(&mohclasses); - if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) { - mohclass->delete = 0; - if (reload) { - ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name); - } else { - ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); - } - ast_free(moh); - AST_RWLIST_UNLOCK(&mohclasses); - return -1; - } - AST_RWLIST_UNLOCK(&mohclasses); - - time(&moh->start); - moh->start -= respawn_time; - - if (!strcasecmp(moh->mode, "files")) { - if (!moh_scan_files(moh)) { - ast_moh_free_class(&moh); - return -1; - } - if (strchr(moh->args, 'r')) - ast_set_flag(moh, MOH_RANDOMIZE); - } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { - - if (!strcasecmp(moh->mode, "custom")) - ast_set_flag(moh, MOH_CUSTOM); - else if (!strcasecmp(moh->mode, "mp3nb")) - ast_set_flag(moh, MOH_SINGLE); - else if (!strcasecmp(moh->mode, "quietmp3nb")) - ast_set_flag(moh, MOH_SINGLE | MOH_QUIET); - else if (!strcasecmp(moh->mode, "quietmp3")) - ast_set_flag(moh, MOH_QUIET); - - moh->srcfd = -1; -#ifdef HAVE_ZAPTEL - /* Open /dev/zap/pseudo for timing... Is - there a better, yet reliable way to do this? */ - moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY); - if (moh->pseudofd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); - } else { - x = 320; - ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x); - } -#else - moh->pseudofd = -1; -#endif - if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) { - ast_log(LOG_WARNING, "Unable to create moh...\n"); - if (moh->pseudofd > -1) - close(moh->pseudofd); - ast_moh_free_class(&moh); - return -1; - } - } else { - ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); - ast_moh_free_class(&moh); - return -1; - } - - AST_RWLIST_WRLOCK(&mohclasses); - AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list); - AST_RWLIST_UNLOCK(&mohclasses); - - return 0; -} - -static void local_ast_moh_cleanup(struct ast_channel *chan) -{ - struct moh_files_state *state = chan->music_state; - - if (state) { - if (state->class->realtime) { - if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) { - /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */ - if (!(state->class->inuse)) { - /* Remove this class from list */ - AST_RWLIST_WRLOCK(&mohclasses); - AST_RWLIST_REMOVE(&mohclasses, state->class, list); - AST_RWLIST_UNLOCK(&mohclasses); - - /* Free some memory */ - ast_moh_destroy_one(state->class); - } - } else { - ast_moh_destroy_one(state->class); - } - } - ast_free(chan->music_state); - chan->music_state = NULL; - } -} - -static struct mohclass *moh_class_malloc(void) -{ - struct mohclass *class; - - if ((class = ast_calloc(1, sizeof(*class)))) { - class->format = AST_FORMAT_SLINEAR; - class->realtime = 0; - } - - return class; -} - -static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass) -{ - struct mohclass *mohclass = NULL; - struct ast_variable *var = NULL; - struct ast_variable *tmp = NULL; - struct moh_files_state *state = chan->music_state; -#ifdef HAVE_ZAPTEL - int x; -#endif - - /* The following is the order of preference for which class to use: - * 1) The channels explicitly set musicclass, which should *only* be - * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan. - * 2) The mclass argument. If a channel is calling ast_moh_start() as the - * result of receiving a HOLD control frame, this should be the - * payload that came with the frame. - * 3) The interpclass argument. This would be from the mohinterpret - * option from channel drivers. This is the same as the old musicclass - * option. - * 4) The default class. - */ - - /* First, let's check in memory for static and cached RT classes */ - AST_RWLIST_RDLOCK(&mohclasses); - if (!ast_strlen_zero(chan->musicclass)) - mohclass = get_mohbyname(chan->musicclass, 1); - if (!mohclass && !ast_strlen_zero(mclass)) - mohclass = get_mohbyname(mclass, 1); - if (!mohclass && !ast_strlen_zero(interpclass)) - mohclass = get_mohbyname(interpclass, 1); - AST_RWLIST_UNLOCK(&mohclasses); - - /* If no moh class found in memory, then check RT */ - if (!mohclass && ast_check_realtime("musiconhold")) { - if (!ast_strlen_zero(chan->musicclass)) { - var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL); - } - if (!var && !ast_strlen_zero(mclass)) - var = ast_load_realtime("musiconhold", "name", mclass, NULL); - if (!var && !ast_strlen_zero(interpclass)) - var = ast_load_realtime("musiconhold", "name", interpclass, NULL); - if (!var) - var = ast_load_realtime("musiconhold", "name", "default", NULL); - if (var && (mohclass = moh_class_malloc())) { - mohclass->realtime = 1; - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "name")) - ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name)); - else if (!strcasecmp(tmp->name, "mode")) - ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); - else if (!strcasecmp(tmp->name, "directory")) - ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir)); - else if (!strcasecmp(tmp->name, "application")) - ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args)); - else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value))) - mohclass->digit = *tmp->value; - else if (!strcasecmp(tmp->name, "random")) - ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE); - else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random")) - ast_set_flag(mohclass, MOH_RANDOMIZE); - else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) - ast_set_flag(mohclass, MOH_SORTALPHA); - else if (!strcasecmp(tmp->name, "format")) { - mohclass->format = ast_getformatbyname(tmp->value); - if (!mohclass->format) { - ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value); - mohclass->format = AST_FORMAT_SLINEAR; - } - } - } - ast_variables_destroy(var); - if (ast_strlen_zero(mohclass->dir)) { - if (!strcasecmp(mohclass->mode, "custom")) { - strcpy(mohclass->dir, "nodir"); - } else { - ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name); - ast_free(mohclass); - return -1; - } - } - if (ast_strlen_zero(mohclass->mode)) { - ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name); - ast_free(mohclass); - return -1; - } - if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) { - ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name); - ast_free(mohclass); - return -1; - } - - if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) { - /* CACHERTCLASSES enabled, let's add this class to default tree */ - if (state && state->class) { - /* Class already exist for this channel */ - ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); - if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { - /* we found RT class with the same name, seems like we should continue playing existing one */ - ast_moh_free_class(&mohclass); - mohclass = state->class; - } - } - moh_register(mohclass, 0); - } else { - - /* We don't register RT moh class, so let's init it manualy */ - - time(&mohclass->start); - mohclass->start -= respawn_time; - - if (!strcasecmp(mohclass->mode, "files")) { - if (!moh_scan_files(mohclass)) { - ast_moh_free_class(&mohclass); - return -1; - } - if (strchr(mohclass->args, 'r')) - ast_set_flag(mohclass, MOH_RANDOMIZE); - } 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")) { - - if (!strcasecmp(mohclass->mode, "custom")) - ast_set_flag(mohclass, MOH_CUSTOM); - else if (!strcasecmp(mohclass->mode, "mp3nb")) - ast_set_flag(mohclass, MOH_SINGLE); - else if (!strcasecmp(mohclass->mode, "quietmp3nb")) - ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET); - else if (!strcasecmp(mohclass->mode, "quietmp3")) - ast_set_flag(mohclass, MOH_QUIET); - - mohclass->srcfd = -1; -#ifdef HAVE_ZAPTEL - /* Open /dev/zap/pseudo for timing... Is - there a better, yet reliable way to do this? */ - mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY); - if (mohclass->pseudofd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); - } else { - x = 320; - ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x); - } -#else - mohclass->pseudofd = -1; -#endif - /* Let's check if this channel already had a moh class before */ - if (state && state->class) { - /* Class already exist for this channel */ - ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name); - if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) { - /* we found RT class with the same name, seems like we should continue playing existing one */ - ast_moh_free_class(&mohclass); - mohclass = state->class; - - } - } else { - if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) { - ast_log(LOG_WARNING, "Unable to create moh...\n"); - if (mohclass->pseudofd > -1) - close(mohclass->pseudofd); - ast_moh_free_class(&mohclass); - return -1; - } - } - } else { - ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode); - ast_moh_free_class(&mohclass); - return -1; - } - - } - - } else if (var) - ast_variables_destroy(var); - } - - - - /* Requested MOH class not found, check for 'default' class in musiconhold.conf */ - if (!mohclass) { - AST_RWLIST_RDLOCK(&mohclasses); - mohclass = get_mohbyname("default", 1); - if (mohclass) - ast_atomic_fetchadd_int(&mohclass->inuse, +1); - AST_RWLIST_UNLOCK(&mohclasses); - } else { - AST_RWLIST_RDLOCK(&mohclasses); - ast_atomic_fetchadd_int(&mohclass->inuse, +1); - AST_RWLIST_UNLOCK(&mohclasses); - } - - if (!mohclass) - return -1; - - ast_set_flag(chan, AST_FLAG_MOH); - if (mohclass->total_files) { - return ast_activate_generator(chan, &moh_file_stream, mohclass); - } else - return ast_activate_generator(chan, &mohgen, mohclass); -} - -static void local_ast_moh_stop(struct ast_channel *chan) -{ - struct moh_files_state *state = chan->music_state; - ast_clear_flag(chan, AST_FLAG_MOH); - ast_deactivate_generator(chan); - - if (state) { - if (chan->stream) { - ast_closestream(chan->stream); - chan->stream = NULL; - } - } -} - -static int load_moh_classes(int reload) -{ - struct ast_config *cfg; - struct ast_variable *var; - struct mohclass *class; - char *cat; - int numclasses = 0; - struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; - - cfg = ast_config_load("musiconhold.conf", config_flags); - - if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED) - return 0; - - if (reload) { - AST_RWLIST_WRLOCK(&mohclasses); - AST_RWLIST_TRAVERSE(&mohclasses, class, list) { - class->delete = 1; - } - AST_RWLIST_UNLOCK(&mohclasses); - } - - ast_clear_flag(global_flags, AST_FLAGS_ALL); - - cat = ast_category_browse(cfg, NULL); - for (; cat; cat = ast_category_browse(cfg, cat)) { - /* Setup common options from [general] section */ - if (!strcasecmp(cat, "general")) { - var = ast_variable_browse(cfg, cat); - while (var) { - if (!strcasecmp(var->name, "cachertclasses")) { - ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES); - } else { - ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name); - } - var = var->next; - } - } - /* These names were deprecated in 1.4 and should not be used until after the next major release. */ - if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) { - if (!(class = moh_class_malloc())) - break; - - ast_copy_string(class->name, cat, sizeof(class->name)); - var = ast_variable_browse(cfg, cat); - while (var) { - if (!strcasecmp(var->name, "mode")) - ast_copy_string(class->mode, var->value, sizeof(class->mode)); - else if (!strcasecmp(var->name, "directory")) - ast_copy_string(class->dir, var->value, sizeof(class->dir)); - else if (!strcasecmp(var->name, "application")) - ast_copy_string(class->args, var->value, sizeof(class->args)); - else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) - class->digit = *var->value; - else if (!strcasecmp(var->name, "random")) - ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); - else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) - ast_set_flag(class, MOH_RANDOMIZE); - else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) - ast_set_flag(class, MOH_SORTALPHA); - else if (!strcasecmp(var->name, "format")) { - class->format = ast_getformatbyname(var->value); - if (!class->format) { - ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); - class->format = AST_FORMAT_SLINEAR; - } - } - var = var->next; - } - - if (ast_strlen_zero(class->dir)) { - if (!strcasecmp(class->mode, "custom")) { - strcpy(class->dir, "nodir"); - } else { - ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); - ast_free(class); - continue; - } - } - if (ast_strlen_zero(class->mode)) { - ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); - ast_free(class); - continue; - } - if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { - ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); - ast_free(class); - continue; - } - - /* Don't leak a class when it's already registered */ - moh_register(class, reload); - - numclasses++; - } - } - - ast_config_destroy(cfg); - - return numclasses; -} - -static int ast_moh_destroy_one(struct mohclass *moh) -{ - char buff[8192]; - int bytes, tbytes = 0, stime = 0, pid = 0; - - if (moh) { - if (moh->pid > 1) { - ast_debug(1, "killing %d!\n", moh->pid); - stime = time(NULL) + 2; - pid = moh->pid; - moh->pid = 0; - /* Back when this was just mpg123, SIGKILL was fine. Now we need - * to give the process a reason and time enough to kill off its - * children. */ - kill(pid, SIGHUP); - usleep(100000); - kill(pid, SIGTERM); - usleep(100000); - kill(pid, SIGKILL); - while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) - tbytes = tbytes + bytes; - ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); - close(moh->srcfd); - } - ast_moh_free_class(&moh); - } - - return 0; -} - -static void ast_moh_destroy(void) -{ - struct mohclass *moh; - - ast_verb(2, "Destroying musiconhold processes\n"); - - AST_RWLIST_WRLOCK(&mohclasses); - while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) { - ast_moh_destroy_one(moh); - } - AST_RWLIST_UNLOCK(&mohclasses); -} - -static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "moh reload"; - e->usage = - "Usage: moh reload\n" - " Reloads the MusicOnHold module.\n" - " Alias for 'module reload res_musiconhold.so'\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) - return CLI_SHOWUSAGE; - - reload(); - - return CLI_SUCCESS; -} - -static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int i; - struct mohclass *class; - - switch (cmd) { - case CLI_INIT: - e->command = "moh show files"; - e->usage = - "Usage: moh show files\n" - " Lists all loaded file-based MusicOnHold classes and their\n" - " files.\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) - return CLI_SHOWUSAGE; - - AST_RWLIST_RDLOCK(&mohclasses); - AST_RWLIST_TRAVERSE(&mohclasses, class, list) { - if (!class->total_files) - continue; - - ast_cli(a->fd, "Class: %s\n", class->name); - for (i = 0; i < class->total_files; i++) - ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]); - } - AST_RWLIST_UNLOCK(&mohclasses); - - return CLI_SUCCESS; -} - -static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - struct mohclass *class; - - switch (cmd) { - case CLI_INIT: - e->command = "moh show classes"; - e->usage = - "Usage: moh show classes\n" - " Lists all MusicOnHold classes.\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) - return CLI_SHOWUSAGE; - - AST_RWLIST_RDLOCK(&mohclasses); - AST_RWLIST_TRAVERSE(&mohclasses, class, list) { - ast_cli(a->fd, "Class: %s\n", class->name); - ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>")); - ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>")); - ast_cli(a->fd, "\tUse Count: %d\n", class->inuse); - if (class->digit) - ast_cli(a->fd, "\tDigit: %c\n", class->digit); - if (ast_test_flag(class, MOH_CUSTOM)) - ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>")); - if (strcasecmp(class->mode, "files")) - ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); - } - AST_RWLIST_UNLOCK(&mohclasses); - - return CLI_SUCCESS; -} - -static struct ast_cli_entry cli_moh[] = { - AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"), - AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"), - AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes") -}; - -static int init_classes(int reload) -{ - struct mohclass *moh; - - if (!load_moh_classes(reload)) /* Load classes from config */ - return 0; /* Return if nothing is found */ - - AST_RWLIST_WRLOCK(&mohclasses); - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) { - if (reload && moh->delete) { - AST_RWLIST_REMOVE_CURRENT(list); - if (!moh->inuse) - ast_moh_destroy_one(moh); - } else if (moh->total_files) { - if (moh_scan_files(moh) <= 0) { - ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name); - moh->delete = 1; - AST_LIST_REMOVE_CURRENT(list); - if (!moh->inuse) - ast_moh_destroy_one(moh); - } - } - } - AST_RWLIST_TRAVERSE_SAFE_END - AST_RWLIST_UNLOCK(&mohclasses); - - return 1; -} - -static int load_module(void) -{ - int res; - - res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc); - ast_register_atexit(ast_moh_destroy); - ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry)); - if (!res) - res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc); - if (!res) - res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc); - if (!res) - res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc); - if (!res) - res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc); - - if (!init_classes(0)) { /* No music classes configured, so skip it */ - ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n"); - } else { - ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup); - } - - return AST_MODULE_LOAD_SUCCESS; -} - -static int reload(void) -{ - if (init_classes(1)) - ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup); - - return 0; -} - -static int unload_module(void) -{ - return -1; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); |