Wed Apr 6 11:29:47 2011

Asterisk developer's documentation


res_musiconhold.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2010, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Routines implementing music on hold
00022  *
00023  * \arg See also \ref Config_moh
00024  * 
00025  * \author Mark Spencer <markster@digium.com>
00026  */
00027 
00028 /*** MODULEINFO
00029    <conflict>win32</conflict>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 292376 $")
00035 
00036 #include <ctype.h>
00037 #include <signal.h>
00038 #include <sys/time.h>
00039 #include <sys/signal.h>
00040 #include <netinet/in.h>
00041 #include <sys/stat.h>
00042 #include <dirent.h>
00043 
00044 #ifdef SOLARIS
00045 #include <thread.h>
00046 #endif
00047 
00048 #include "asterisk/lock.h"
00049 #include "asterisk/file.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/pbx.h"
00052 #include "asterisk/app.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/translate.h"
00055 #include "asterisk/say.h"
00056 #include "asterisk/musiconhold.h"
00057 #include "asterisk/config.h"
00058 #include "asterisk/utils.h"
00059 #include "asterisk/cli.h"
00060 #include "asterisk/stringfields.h"
00061 #include "asterisk/linkedlists.h"
00062 #include "asterisk/manager.h"
00063 #include "asterisk/paths.h"
00064 #include "asterisk/astobj2.h"
00065 #include "asterisk/timing.h"
00066 #include "asterisk/time.h"
00067 #include "asterisk/poll-compat.h"
00068 
00069 #define INITIAL_NUM_FILES   8
00070 #define HANDLE_REF   1
00071 #define DONT_UNREF   0
00072 
00073 /*** DOCUMENTATION
00074    <application name="MusicOnHold" language="en_US">
00075       <synopsis>
00076          Play Music On Hold indefinitely.
00077       </synopsis>
00078       <syntax>
00079          <parameter name="class" required="true" />
00080          <parameter name="duration" />
00081       </syntax>
00082       <description>
00083          <para>Plays hold music specified by class. If omitted, the default music
00084          source for the channel will be used. Change the default class with
00085          Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
00086          specified number of seconds. If duration is ommited, music plays indefinitely.
00087          Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
00088       </description>
00089    </application>
00090    <application name="WaitMusicOnHold" language="en_US">
00091       <synopsis>
00092          Wait, playing Music On Hold.
00093       </synopsis>
00094       <syntax>
00095          <parameter name="delay" required="true" />
00096       </syntax>
00097       <description>
00098          <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
00099          <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
00100          or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
00101          with no sound.</para>
00102          <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
00103       </description>
00104    </application>
00105    <application name="SetMusicOnHold" language="en_US">
00106       <synopsis>
00107          Set default Music On Hold class.
00108       </synopsis>
00109       <syntax>
00110          <parameter name="class" required="yes" />
00111       </syntax>
00112       <description>
00113          <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
00114          <para>Sets the default class for music on hold for a given channel.
00115          When music on hold is activated, this class will be used to select which
00116          music is played.</para>
00117          <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
00118       </description>
00119    </application>
00120    <application name="StartMusicOnHold" language="en_US">
00121       <synopsis>
00122          Play Music On Hold.
00123       </synopsis>
00124       <syntax>
00125          <parameter name="class" required="true" />
00126       </syntax>
00127       <description>
00128          <para>Starts playing music on hold, uses default music class for channel.
00129          Starts playing music specified by class. If omitted, the default music
00130          source for the channel will be used. Always returns <literal>0</literal>.</para>
00131       </description>
00132    </application>
00133    <application name="StopMusicOnHold" language="en_US">
00134       <synopsis>
00135          Stop playing Music On Hold.
00136       </synopsis>
00137       <syntax />
00138       <description>
00139          <para>Stops playing music on hold.</para>
00140       </description>
00141    </application>
00142  ***/
00143 
00144 static const char play_moh[] = "MusicOnHold";
00145 static const char wait_moh[] = "WaitMusicOnHold";
00146 static const char set_moh[] = "SetMusicOnHold";
00147 static const char start_moh[] = "StartMusicOnHold";
00148 static const char stop_moh[] = "StopMusicOnHold";
00149 
00150 static int respawn_time = 20;
00151 
00152 struct moh_files_state {
00153    struct mohclass *class;
00154    char name[MAX_MUSICCLASS];
00155    format_t origwfmt;
00156    int samples;
00157    int sample_queue;
00158    int pos;
00159    int save_pos;
00160    int save_total;
00161    char *save_pos_filename;
00162 };
00163 
00164 #define MOH_QUIET    (1 << 0)
00165 #define MOH_SINGLE      (1 << 1)
00166 #define MOH_CUSTOM      (1 << 2)
00167 #define MOH_RANDOMIZE      (1 << 3)
00168 #define MOH_SORTALPHA      (1 << 4)
00169 
00170 #define MOH_CACHERTCLASSES      (1 << 5)        /*!< Should we use a separate instance of MOH for each user or not */
00171 
00172 /* Custom astobj2 flag */
00173 #define MOH_NOTDELETED          (1 << 30)       /*!< Find only records that aren't deleted? */
00174 
00175 static struct ast_flags global_flags[1] = {{0}};        /*!< global MOH_ flags */
00176 
00177 struct mohclass {
00178    char name[MAX_MUSICCLASS];
00179    char dir[256];
00180    char args[256];
00181    char mode[80];
00182    char digit;
00183    /*! A dynamically sized array to hold the list of filenames in "files" mode */
00184    char **filearray;
00185    /*! The current size of the filearray */
00186    int allowed_files;
00187    /*! The current number of files loaded into the filearray */
00188    int total_files;
00189    unsigned int flags;
00190    /*! The format from the MOH source, not applicable to "files" mode */
00191    format_t format;
00192    /*! The pid of the external application delivering MOH */
00193    int pid;
00194    time_t start;
00195    pthread_t thread;
00196    /*! Source of audio */
00197    int srcfd;
00198    /*! Generic timer */
00199    struct ast_timer *timer;
00200    /*! Created on the fly, from RT engine */
00201    unsigned int realtime:1;
00202    unsigned int delete:1;
00203    AST_LIST_HEAD_NOLOCK(, mohdata) members;
00204    AST_LIST_ENTRY(mohclass) list;
00205 };
00206 
00207 struct mohdata {
00208    int pipe[2];
00209    format_t origwfmt;
00210    struct mohclass *parent;
00211    struct ast_frame f;
00212    AST_LIST_ENTRY(mohdata) list;
00213 };
00214 
00215 static struct ao2_container *mohclasses;
00216 
00217 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00218 #define MPG_123 "/usr/bin/mpg123"
00219 #define MAX_MP3S 256
00220 
00221 static int reload(void);
00222 
00223 #define mohclass_ref(class,string)   (ao2_t_ref((class), +1, (string)), class)
00224 
00225 #ifndef REF_DEBUG
00226 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
00227 #else
00228 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
00229 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
00230 {
00231    struct mohclass *dup;
00232    if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
00233       if (_ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
00234          FILE *ref = fopen("/tmp/refs", "a");
00235          if (ref) {
00236             fprintf(ref, "%p =1   %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
00237             fclose(ref);
00238          }
00239          ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
00240             class, class->name, file, line, funcname);
00241       } else {
00242          ao2_ref(class, -1);
00243       }
00244    } else {
00245       ao2_t_ref(class, -1, (char *) tag);
00246    }
00247    return NULL;
00248 }
00249 #endif
00250 
00251 static void moh_files_release(struct ast_channel *chan, void *data)
00252 {
00253    struct moh_files_state *state;
00254 
00255    if (!chan || !chan->music_state) {
00256       return;
00257    }
00258 
00259    state = chan->music_state;
00260 
00261    if (chan->stream) {
00262       ast_closestream(chan->stream);
00263       chan->stream = NULL;
00264    }
00265    
00266    if (option_verbose > 2) {
00267       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00268    }
00269 
00270    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00271       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
00272    }
00273 
00274    state->save_pos = state->pos;
00275 
00276    state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00277 }
00278 
00279 static int ast_moh_files_next(struct ast_channel *chan) 
00280 {
00281    struct moh_files_state *state = chan->music_state;
00282    int tries;
00283 
00284    /* Discontinue a stream if it is running already */
00285    if (chan->stream) {
00286       ast_closestream(chan->stream);
00287       chan->stream = NULL;
00288    }
00289 
00290    if (!state->class->total_files) {
00291       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00292       return -1;
00293    }
00294 
00295    if (state->pos == 0 && state->save_pos_filename == NULL) {
00296       /* First time so lets play the file. */
00297       state->save_pos = -1;
00298    } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00299       /* If a specific file has been saved confirm it still exists and that it is still valid */
00300       state->pos = state->save_pos;
00301       state->save_pos = -1;
00302    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00303       /* Get a random file and ensure we can open it */
00304       for (tries = 0; tries < 20; tries++) {
00305          state->pos = ast_random() % state->class->total_files;
00306          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00307             break;
00308          }
00309       }
00310       state->save_pos = -1;
00311       state->samples = 0;
00312    } else {
00313       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00314       state->pos++;
00315       state->pos %= state->class->total_files;
00316       state->save_pos = -1;
00317       state->samples = 0;
00318    }
00319 
00320    for (tries = 0; tries < state->class->total_files; ++tries) {
00321       if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00322          break;
00323       }
00324 
00325       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00326       state->pos++;
00327       state->pos %= state->class->total_files;
00328    }
00329 
00330    if (tries == state->class->total_files) {
00331       return -1;
00332    }
00333 
00334    /* Record the pointer to the filename for position resuming later */
00335    state->save_pos_filename = state->class->filearray[state->pos];
00336 
00337    ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00338 
00339    if (state->samples) {
00340       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00341    }
00342 
00343    return 0;
00344 }
00345 
00346 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
00347 {
00348    struct ast_frame *f = NULL;
00349    
00350    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00351       if (!ast_moh_files_next(chan))
00352          f = ast_readframe(chan->stream);
00353    }
00354 
00355    return f;
00356 }
00357 
00358 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00359 {
00360    struct moh_files_state *state = chan->music_state;
00361    struct ast_frame *f = NULL;
00362    int res = 0;
00363 
00364    state->sample_queue += samples;
00365 
00366    while (state->sample_queue > 0) {
00367       ast_channel_lock(chan);
00368       if ((f = moh_files_readframe(chan))) {
00369          /* We need to be sure that we unlock
00370           * the channel prior to calling
00371           * ast_write. Otherwise, the recursive locking
00372           * that occurs can cause deadlocks when using
00373           * indirect channels, like local channels
00374           */
00375          ast_channel_unlock(chan);
00376          state->samples += f->samples;
00377          state->sample_queue -= f->samples;
00378          res = ast_write(chan, f);
00379          ast_frfree(f);
00380          if (res < 0) {
00381             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00382             return -1;
00383          }
00384       } else {
00385          ast_channel_unlock(chan);
00386          return -1;  
00387       }
00388    }
00389    return res;
00390 }
00391 
00392 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00393 {
00394    struct moh_files_state *state;
00395    struct mohclass *class = params;
00396 
00397    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00398       chan->music_state = state;
00399       ast_module_ref(ast_module_info->self);
00400    } else {
00401       state = chan->music_state;
00402    }
00403 
00404    if (!state) {
00405       return NULL;
00406    }
00407 
00408    /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
00409     * malloc may allocate a different class to the same memory block.  This
00410     * might only happen when two reloads are generated in a short period of
00411     * time, but it's still important to protect against.
00412     * PROG: Compare the quick operation first, to save CPU. */
00413    if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00414       memset(state, 0, sizeof(*state));
00415       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00416          state->pos = ast_random() % class->total_files;
00417       }
00418    }
00419 
00420    state->class = mohclass_ref(class, "Reffing music class for channel");
00421    state->origwfmt = chan->writeformat;
00422    /* For comparison on restart of MOH (see above) */
00423    ast_copy_string(state->name, class->name, sizeof(state->name));
00424    state->save_total = class->total_files;
00425 
00426    ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00427    
00428    return chan->music_state;
00429 }
00430 
00431 static int moh_digit_match(void *obj, void *arg, int flags)
00432 {
00433    char *digit = arg;
00434    struct mohclass *class = obj;
00435 
00436    return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00437 }
00438 
00439 /*! \note This function should be called with the mohclasses list locked */
00440 static struct mohclass *get_mohbydigit(char digit)
00441 {
00442    return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00443 }
00444 
00445 static void moh_handle_digit(struct ast_channel *chan, char digit)
00446 {
00447    struct mohclass *class;
00448    const char *classname = NULL;
00449 
00450    if ((class = get_mohbydigit(digit))) {
00451       classname = ast_strdupa(class->name);
00452       class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00453       ast_string_field_set(chan,musicclass,classname);
00454       ast_moh_stop(chan);
00455       ast_moh_start(chan, classname, NULL);
00456    }
00457 }
00458 
00459 static struct ast_generator moh_file_stream = 
00460 {
00461    .alloc    = moh_files_alloc,
00462    .release  = moh_files_release,
00463    .generate = moh_files_generator,
00464    .digit    = moh_handle_digit,
00465 };
00466 
00467 static int spawn_mp3(struct mohclass *class)
00468 {
00469    int fds[2];
00470    int files = 0;
00471    char fns[MAX_MP3S][80];
00472    char *argv[MAX_MP3S + 50];
00473    char xargs[256];
00474    char *argptr;
00475    int argc = 0;
00476    DIR *dir = NULL;
00477    struct dirent *de;
00478 
00479    
00480    if (!strcasecmp(class->dir, "nodir")) {
00481       files = 1;
00482    } else {
00483       dir = opendir(class->dir);
00484       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00485          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00486          return -1;
00487       }
00488    }
00489 
00490    if (!ast_test_flag(class, MOH_CUSTOM)) {
00491       argv[argc++] = "mpg123";
00492       argv[argc++] = "-q";
00493       argv[argc++] = "-s";
00494       argv[argc++] = "--mono";
00495       argv[argc++] = "-r";
00496       argv[argc++] = "8000";
00497       
00498       if (!ast_test_flag(class, MOH_SINGLE)) {
00499          argv[argc++] = "-b";
00500          argv[argc++] = "2048";
00501       }
00502       
00503       argv[argc++] = "-f";
00504       
00505       if (ast_test_flag(class, MOH_QUIET))
00506          argv[argc++] = "4096";
00507       else
00508          argv[argc++] = "8192";
00509       
00510       /* Look for extra arguments and add them to the list */
00511       ast_copy_string(xargs, class->args, sizeof(xargs));
00512       argptr = xargs;
00513       while (!ast_strlen_zero(argptr)) {
00514          argv[argc++] = argptr;
00515          strsep(&argptr, ",");
00516       }
00517    } else  {
00518       /* Format arguments for argv vector */
00519       ast_copy_string(xargs, class->args, sizeof(xargs));
00520       argptr = xargs;
00521       while (!ast_strlen_zero(argptr)) {
00522          argv[argc++] = argptr;
00523          strsep(&argptr, " ");
00524       }
00525    }
00526 
00527    if (!strncasecmp(class->dir, "http://", 7)) {
00528       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00529       argv[argc++] = fns[files];
00530       files++;
00531    } else if (dir) {
00532       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00533          if ((strlen(de->d_name) > 3) && 
00534              ((ast_test_flag(class, MOH_CUSTOM) && 
00535                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00536                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00537               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00538             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00539             argv[argc++] = fns[files];
00540             files++;
00541          }
00542       }
00543    }
00544    argv[argc] = NULL;
00545    if (dir) {
00546       closedir(dir);
00547    }
00548    if (pipe(fds)) {  
00549       ast_log(LOG_WARNING, "Pipe failed\n");
00550       return -1;
00551    }
00552    if (!files) {
00553       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00554       close(fds[0]);
00555       close(fds[1]);
00556       return -1;
00557    }
00558    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00559       sleep(respawn_time - (time(NULL) - class->start));
00560    }
00561 
00562    time(&class->start);
00563    class->pid = ast_safe_fork(0);
00564    if (class->pid < 0) {
00565       close(fds[0]);
00566       close(fds[1]);
00567       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00568       return -1;
00569    }
00570    if (!class->pid) {
00571       if (ast_opt_high_priority)
00572          ast_set_priority(0);
00573 
00574       close(fds[0]);
00575       /* Stdout goes to pipe */
00576       dup2(fds[1], STDOUT_FILENO);
00577 
00578       /* Close everything else */
00579       ast_close_fds_above_n(STDERR_FILENO);
00580 
00581       /* Child */
00582       if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00583          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00584          _exit(1);
00585       }
00586       setpgid(0, getpid());
00587       if (ast_test_flag(class, MOH_CUSTOM)) {
00588          execv(argv[0], argv);
00589       } else {
00590          /* Default install is /usr/local/bin */
00591          execv(LOCAL_MPG_123, argv);
00592          /* Many places have it in /usr/bin */
00593          execv(MPG_123, argv);
00594          /* Check PATH as a last-ditch effort */
00595          execvp("mpg123", argv);
00596       }
00597       /* Can't use logger, since log FDs are closed */
00598       fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00599       close(fds[1]);
00600       _exit(1);
00601    } else {
00602       /* Parent */
00603       close(fds[1]);
00604    }
00605    return fds[0];
00606 }
00607 
00608 static void *monmp3thread(void *data)
00609 {
00610 #define  MOH_MS_INTERVAL      100
00611 
00612    struct mohclass *class = data;
00613    struct mohdata *moh;
00614    short sbuf[8192];
00615    int res = 0, res2;
00616    int len;
00617    struct timeval deadline, tv_tmp;
00618 
00619    deadline.tv_sec = 0;
00620    deadline.tv_usec = 0;
00621    for(;/* ever */;) {
00622       pthread_testcancel();
00623       /* Spawn mp3 player if it's not there */
00624       if (class->srcfd < 0) {
00625          if ((class->srcfd = spawn_mp3(class)) < 0) {
00626             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00627             /* Try again later */
00628             sleep(500);
00629             pthread_testcancel();
00630          }
00631       }
00632       if (class->timer) {
00633          struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN, };
00634          struct timeval tv;
00635 
00636 #ifdef SOLARIS
00637          thr_yield();
00638 #endif
00639          /* Pause some amount of time */
00640          tv = ast_tvnow();
00641          if (ast_poll(&pfd, 1, -1) > 0) {
00642             ast_timer_ack(class->timer, 1);
00643             res = 320;
00644          } else {
00645             ast_log(LOG_ERROR, "poll() failed: %s\n", strerror(errno));
00646             res = 0;
00647          }
00648          pthread_testcancel();
00649       } else {
00650          long delta;
00651          /* Reliable sleep */
00652          tv_tmp = ast_tvnow();
00653          if (ast_tvzero(deadline))
00654             deadline = tv_tmp;
00655          delta = ast_tvdiff_ms(tv_tmp, deadline);
00656          if (delta < MOH_MS_INTERVAL) {   /* too early */
00657             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00658             usleep(1000 * (MOH_MS_INTERVAL - delta));
00659             pthread_testcancel();
00660          } else {
00661             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00662             deadline = tv_tmp;
00663          }
00664          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00665       }
00666       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00667          continue;
00668       /* Read mp3 audio */
00669       len = ast_codec_get_len(class->format, res);
00670 
00671       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00672          if (!res2) {
00673             close(class->srcfd);
00674             class->srcfd = -1;
00675             pthread_testcancel();
00676             if (class->pid > 1) {
00677                do {
00678                   if (killpg(class->pid, SIGHUP) < 0) {
00679                      if (errno == ESRCH) {
00680                         break;
00681                      }
00682                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00683                   }
00684                   usleep(100000);
00685                   if (killpg(class->pid, SIGTERM) < 0) {
00686                      if (errno == ESRCH) {
00687                         break;
00688                      }
00689                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00690                   }
00691                   usleep(100000);
00692                   if (killpg(class->pid, SIGKILL) < 0) {
00693                      if (errno == ESRCH) {
00694                         break;
00695                      }
00696                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00697                   }
00698                } while (0);
00699                class->pid = 0;
00700             }
00701          } else {
00702             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00703          }
00704          continue;
00705       }
00706 
00707       pthread_testcancel();
00708 
00709       ao2_lock(class);
00710       AST_LIST_TRAVERSE(&class->members, moh, list) {
00711          /* Write data */
00712          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00713             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00714          }
00715       }
00716       ao2_unlock(class);
00717    }
00718    return NULL;
00719 }
00720 
00721 static int play_moh_exec(struct ast_channel *chan, const char *data)
00722 {
00723    char *parse;
00724    char *class;
00725    int timeout = -1;
00726    int res;
00727    AST_DECLARE_APP_ARGS(args,
00728       AST_APP_ARG(class);
00729       AST_APP_ARG(duration);
00730    );
00731 
00732    parse = ast_strdupa(data);
00733 
00734    AST_STANDARD_APP_ARGS(args, parse);
00735 
00736    if (!ast_strlen_zero(args.duration)) {
00737       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00738          timeout *= 1000;
00739       } else {
00740          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00741       }
00742    }
00743 
00744    class = S_OR(args.class, NULL);
00745    if (ast_moh_start(chan, class, NULL)) {
00746       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00747       return 0;
00748    }
00749 
00750    if (timeout > 0)
00751       res = ast_safe_sleep(chan, timeout);
00752    else {
00753       while (!(res = ast_safe_sleep(chan, 10000)));
00754    }
00755 
00756    ast_moh_stop(chan);
00757 
00758    return res;
00759 }
00760 
00761 static int wait_moh_exec(struct ast_channel *chan, const char *data)
00762 {
00763    static int deprecation_warning = 0;
00764    int res;
00765 
00766    if (!deprecation_warning) {
00767       deprecation_warning = 1;
00768       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00769    }
00770 
00771    if (!data || !atoi(data)) {
00772       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00773       return -1;
00774    }
00775    if (ast_moh_start(chan, NULL, NULL)) {
00776       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00777       return 0;
00778    }
00779    res = ast_safe_sleep(chan, atoi(data) * 1000);
00780    ast_moh_stop(chan);
00781    return res;
00782 }
00783 
00784 static int set_moh_exec(struct ast_channel *chan, const char *data)
00785 {
00786    static int deprecation_warning = 0;
00787 
00788    if (!deprecation_warning) {
00789       deprecation_warning = 1;
00790       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00791    }
00792 
00793    if (ast_strlen_zero(data)) {
00794       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00795       return -1;
00796    }
00797    ast_string_field_set(chan, musicclass, data);
00798    return 0;
00799 }
00800 
00801 static int start_moh_exec(struct ast_channel *chan, const char *data)
00802 {
00803    char *parse;
00804    char *class;
00805    AST_DECLARE_APP_ARGS(args,
00806       AST_APP_ARG(class);
00807    );
00808 
00809    parse = ast_strdupa(data);
00810 
00811    AST_STANDARD_APP_ARGS(args, parse);
00812 
00813    class = S_OR(args.class, NULL);
00814    if (ast_moh_start(chan, class, NULL)) 
00815       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00816 
00817    return 0;
00818 }
00819 
00820 static int stop_moh_exec(struct ast_channel *chan, const char *data)
00821 {
00822    ast_moh_stop(chan);
00823 
00824    return 0;
00825 }
00826 
00827 #define get_mohbyname(a,b,c)  _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00828 
00829 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00830 {
00831    struct mohclass *moh = NULL;
00832    struct mohclass tmp_class = {
00833       .flags = 0,
00834    };
00835 
00836    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00837 
00838 #ifdef REF_DEBUG
00839    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00840 #else
00841    moh = __ao2_find(mohclasses, &tmp_class, flags);
00842 #endif
00843 
00844    if (!moh && warn) {
00845       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00846    }
00847 
00848    return moh;
00849 }
00850 
00851 static struct mohdata *mohalloc(struct mohclass *cl)
00852 {
00853    struct mohdata *moh;
00854    long flags; 
00855    
00856    if (!(moh = ast_calloc(1, sizeof(*moh))))
00857       return NULL;
00858    
00859    if (pipe(moh->pipe)) {
00860       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00861       ast_free(moh);
00862       return NULL;
00863    }
00864 
00865    /* Make entirely non-blocking */
00866    flags = fcntl(moh->pipe[0], F_GETFL);
00867    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00868    flags = fcntl(moh->pipe[1], F_GETFL);
00869    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00870 
00871    moh->f.frametype = AST_FRAME_VOICE;
00872    moh->f.subclass.codec = cl->format;
00873    moh->f.offset = AST_FRIENDLY_OFFSET;
00874 
00875    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00876 
00877    ao2_lock(cl);
00878    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00879    ao2_unlock(cl);
00880    
00881    return moh;
00882 }
00883 
00884 static void moh_release(struct ast_channel *chan, void *data)
00885 {
00886    struct mohdata *moh = data;
00887    struct mohclass *class = moh->parent;
00888    format_t oldwfmt;
00889 
00890    ao2_lock(class);
00891    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00892    ao2_unlock(class);
00893    
00894    close(moh->pipe[0]);
00895    close(moh->pipe[1]);
00896 
00897    oldwfmt = moh->origwfmt;
00898 
00899    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00900 
00901    ast_free(moh);
00902 
00903    if (chan) {
00904       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00905          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00906                chan->name, ast_getformatname(oldwfmt));
00907       }
00908 
00909       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00910    }
00911 }
00912 
00913 static void *moh_alloc(struct ast_channel *chan, void *params)
00914 {
00915    struct mohdata *res;
00916    struct mohclass *class = params;
00917    struct moh_files_state *state;
00918 
00919    /* Initiating music_state for current channel. Channel should know name of moh class */
00920    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00921       chan->music_state = state;
00922       state->class = mohclass_ref(class, "Copying reference into state container");
00923       ast_module_ref(ast_module_info->self);
00924    } else
00925       state = chan->music_state;
00926    if (state && state->class != class) {
00927       memset(state, 0, sizeof(*state));
00928       state->class = class;
00929    }
00930 
00931    if ((res = mohalloc(class))) {
00932       res->origwfmt = chan->writeformat;
00933       if (ast_set_write_format(chan, class->format)) {
00934          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00935          moh_release(NULL, res);
00936          res = NULL;
00937       }
00938       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00939    }
00940    return res;
00941 }
00942 
00943 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00944 {
00945    struct mohdata *moh = data;
00946    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00947    int res;
00948 
00949    len = ast_codec_get_len(moh->parent->format, samples);
00950 
00951    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00952       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00953       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00954    }
00955    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00956    if (res <= 0)
00957       return 0;
00958 
00959    moh->f.datalen = res;
00960    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00961    moh->f.samples = ast_codec_get_samples(&moh->f);
00962 
00963    if (ast_write(chan, &moh->f) < 0) {
00964       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00965       return -1;
00966    }
00967 
00968    return 0;
00969 }
00970 
00971 static struct ast_generator mohgen = {
00972    .alloc    = moh_alloc,
00973    .release  = moh_release,
00974    .generate = moh_generate,
00975    .digit    = moh_handle_digit,
00976 };
00977 
00978 static int moh_add_file(struct mohclass *class, const char *filepath)
00979 {
00980    if (!class->allowed_files) {
00981       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00982          return -1;
00983       class->allowed_files = INITIAL_NUM_FILES;
00984    } else if (class->total_files == class->allowed_files) {
00985       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00986          class->allowed_files = 0;
00987          class->total_files = 0;
00988          return -1;
00989       }
00990       class->allowed_files *= 2;
00991    }
00992 
00993    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00994       return -1;
00995 
00996    class->total_files++;
00997 
00998    return 0;
00999 }
01000 
01001 static int moh_sort_compare(const void *i1, const void *i2)
01002 {
01003    char *s1, *s2;
01004 
01005    s1 = ((char **)i1)[0];
01006    s2 = ((char **)i2)[0];
01007 
01008    return strcasecmp(s1, s2);
01009 }
01010 
01011 static int moh_scan_files(struct mohclass *class) {
01012 
01013    DIR *files_DIR;
01014    struct dirent *files_dirent;
01015    char dir_path[PATH_MAX];
01016    char path[PATH_MAX];
01017    char filepath[PATH_MAX];
01018    char *ext;
01019    struct stat statbuf;
01020    int dirnamelen;
01021    int i;
01022 
01023    if (class->dir[0] != '/') {
01024       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01025       strncat(dir_path, "/", sizeof(dir_path) - 1);
01026       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01027    } else {
01028       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01029    }
01030    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01031    files_DIR = opendir(dir_path);
01032    if (!files_DIR) {
01033       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01034       return -1;
01035    }
01036 
01037    for (i = 0; i < class->total_files; i++)
01038       ast_free(class->filearray[i]);
01039 
01040    class->total_files = 0;
01041    dirnamelen = strlen(dir_path) + 2;
01042    if (!getcwd(path, sizeof(path))) {
01043       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01044       return -1;
01045    }
01046    if (chdir(dir_path) < 0) {
01047       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01048       return -1;
01049    }
01050    while ((files_dirent = readdir(files_DIR))) {
01051       /* The file name must be at least long enough to have the file type extension */
01052       if ((strlen(files_dirent->d_name) < 4))
01053          continue;
01054 
01055       /* Skip files that starts with a dot */
01056       if (files_dirent->d_name[0] == '.')
01057          continue;
01058 
01059       /* Skip files without extensions... they are not audio */
01060       if (!strchr(files_dirent->d_name, '.'))
01061          continue;
01062 
01063       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01064 
01065       if (stat(filepath, &statbuf))
01066          continue;
01067 
01068       if (!S_ISREG(statbuf.st_mode))
01069          continue;
01070 
01071       if ((ext = strrchr(filepath, '.')))
01072          *ext = '\0';
01073 
01074       /* if the file is present in multiple formats, ensure we only put it into the list once */
01075       for (i = 0; i < class->total_files; i++)
01076          if (!strcmp(filepath, class->filearray[i]))
01077             break;
01078 
01079       if (i == class->total_files) {
01080          if (moh_add_file(class, filepath))
01081             break;
01082       }
01083    }
01084 
01085    closedir(files_DIR);
01086    if (chdir(path) < 0) {
01087       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01088       return -1;
01089    }
01090    if (ast_test_flag(class, MOH_SORTALPHA))
01091       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01092    return class->total_files;
01093 }
01094 
01095 static int init_files_class(struct mohclass *class)
01096 {
01097    int res;
01098 
01099    res = moh_scan_files(class);
01100 
01101    if (res < 0) {
01102       return -1;
01103    }
01104 
01105    if (!res) {
01106       if (option_verbose > 2) {
01107          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01108                class->dir, class->name);
01109       }
01110       return -1;
01111    }
01112 
01113 #if 0
01114    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01115    if (strchr(class->args, 'r')) {
01116       ast_set_flag(class, MOH_RANDOMIZE);
01117    }
01118 #endif
01119 
01120    return 0;
01121 }
01122 
01123 static void moh_rescan_files(void) {
01124    struct ao2_iterator i;
01125    struct mohclass *c;
01126 
01127    i = ao2_iterator_init(mohclasses, 0);
01128 
01129    while ((c = ao2_iterator_next(&i))) {
01130       moh_scan_files(c);
01131       ao2_ref(c, -1);
01132    }
01133 
01134    ao2_iterator_destroy(&i);
01135 }
01136 
01137 static int moh_diff(struct mohclass *old, struct mohclass *new)
01138 {
01139    if (!old || !new) {
01140       return -1;
01141    }
01142 
01143    if (strcmp(old->dir, new->dir)) {
01144       return -1;
01145    } else if (strcmp(old->mode, new->mode)) {
01146       return -1;
01147    } else if (strcmp(old->args, new->args)) {
01148       return -1;
01149    } else if (old->flags != new->flags) {
01150       return -1;
01151    }
01152 
01153    return 0;
01154 }
01155 
01156 static int init_app_class(struct mohclass *class)
01157 {
01158    if (!strcasecmp(class->mode, "custom")) {
01159       ast_set_flag(class, MOH_CUSTOM);
01160    } else if (!strcasecmp(class->mode, "mp3nb")) {
01161       ast_set_flag(class, MOH_SINGLE);
01162    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01163       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01164    } else if (!strcasecmp(class->mode, "quietmp3")) {
01165       ast_set_flag(class, MOH_QUIET);
01166    }
01167 
01168    class->srcfd = -1;
01169 
01170    if (!(class->timer = ast_timer_open())) {
01171       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01172    }
01173    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01174       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01175       ast_timer_close(class->timer);
01176       class->timer = NULL;
01177    }
01178 
01179    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01180       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01181       if (class->timer) {
01182          ast_timer_close(class->timer);
01183          class->timer = NULL;
01184       }
01185       return -1;
01186    }
01187 
01188    return 0;
01189 }
01190 
01191 /*!
01192  * \note This function owns the reference it gets to moh if unref is true
01193  */
01194 #define moh_register(a,b,c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01195 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01196 {
01197    struct mohclass *mohclass = NULL;
01198 
01199    if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
01200       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01201       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01202       if (unref) {
01203          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01204       }
01205       return -1;
01206    } else if (mohclass) {
01207       /* Found a class, but it's different from the one being registered */
01208       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01209    }
01210 
01211    time(&moh->start);
01212    moh->start -= respawn_time;
01213 
01214    if (!strcasecmp(moh->mode, "files")) {
01215       if (init_files_class(moh)) {
01216          if (unref) {
01217             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01218          }
01219          return -1;
01220       }
01221    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01222          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01223          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01224       if (init_app_class(moh)) {
01225          if (unref) {
01226             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01227          }
01228          return -1;
01229       }
01230    } else {
01231       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01232       if (unref) {
01233          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01234       }
01235       return -1;
01236    }
01237 
01238    ao2_t_link(mohclasses, moh, "Adding class to container");
01239 
01240    if (unref) {
01241       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01242    }
01243 
01244    return 0;
01245 }
01246 
01247 static void local_ast_moh_cleanup(struct ast_channel *chan)
01248 {
01249    struct moh_files_state *state = chan->music_state;
01250 
01251    if (state) {
01252       if (state->class) {
01253          state->class = mohclass_unref(state->class, "Channel MOH state destruction");
01254       }
01255       ast_free(chan->music_state);
01256       chan->music_state = NULL;
01257       /* Only held a module reference if we had a music state */
01258       ast_module_unref(ast_module_info->self);
01259    }
01260 }
01261 
01262 static void moh_class_destructor(void *obj);
01263 
01264 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01265 
01266 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01267 {
01268    struct mohclass *class;
01269 
01270    if ((class =
01271 #ifdef REF_DEBUG
01272          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01273 #elif defined(__AST_DEBUG_MALLOC)
01274          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01275 #else
01276          ao2_alloc(sizeof(*class), moh_class_destructor)
01277 #endif
01278       )) {
01279       class->format = AST_FORMAT_SLINEAR;
01280    }
01281 
01282    return class;
01283 }
01284 
01285 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01286 {
01287    struct mohclass *mohclass = NULL;
01288    struct moh_files_state *state = chan->music_state;
01289    struct ast_variable *var = NULL;
01290    int res;
01291    int realtime_possible = ast_check_realtime("musiconhold");
01292 
01293    /* The following is the order of preference for which class to use:
01294     * 1) The channels explicitly set musicclass, which should *only* be
01295     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01296     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01297     *    result of receiving a HOLD control frame, this should be the
01298     *    payload that came with the frame.
01299     * 3) The interpclass argument. This would be from the mohinterpret
01300     *    option from channel drivers. This is the same as the old musicclass
01301     *    option.
01302     * 4) The default class.
01303     */
01304    if (!ast_strlen_zero(chan->musicclass)) {
01305       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01306       if (!mohclass && realtime_possible) {
01307          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01308       }
01309    }
01310    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01311       mohclass = get_mohbyname(mclass, 1, 0);
01312       if (!mohclass && realtime_possible) {
01313          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01314       }
01315    }
01316    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01317       mohclass = get_mohbyname(interpclass, 1, 0);
01318       if (!mohclass && realtime_possible) {
01319          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01320       }
01321    }
01322 
01323    if (!mohclass && !var) {
01324       mohclass = get_mohbyname("default", 1, 0);
01325       if (!mohclass && realtime_possible) {
01326          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01327       }
01328    }
01329 
01330    /* If no moh class found in memory, then check RT. Note that the logic used
01331     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01332     */
01333    if (var) {
01334       struct ast_variable *tmp = NULL;
01335 
01336       if ((mohclass = moh_class_malloc())) {
01337          mohclass->realtime = 1;
01338          for (tmp = var; tmp; tmp = tmp->next) {
01339             if (!strcasecmp(tmp->name, "name"))
01340                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01341             else if (!strcasecmp(tmp->name, "mode"))
01342                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01343             else if (!strcasecmp(tmp->name, "directory"))
01344                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01345             else if (!strcasecmp(tmp->name, "application"))
01346                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01347             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01348                mohclass->digit = *tmp->value;
01349             else if (!strcasecmp(tmp->name, "random"))
01350                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01351             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01352                ast_set_flag(mohclass, MOH_RANDOMIZE);
01353             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01354                ast_set_flag(mohclass, MOH_SORTALPHA);
01355             else if (!strcasecmp(tmp->name, "format")) {
01356                mohclass->format = ast_getformatbyname(tmp->value);
01357                if (!mohclass->format) {
01358                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01359                   mohclass->format = AST_FORMAT_SLINEAR;
01360                }
01361             }
01362          }
01363          ast_variables_destroy(var);
01364          if (ast_strlen_zero(mohclass->dir)) {
01365             if (!strcasecmp(mohclass->mode, "custom")) {
01366                strcpy(mohclass->dir, "nodir");
01367             } else {
01368                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01369                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01370                return -1;
01371             }
01372          }
01373          if (ast_strlen_zero(mohclass->mode)) {
01374             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01375             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01376             return -1;
01377          }
01378          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01379             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01380             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01381             return -1;
01382          }
01383 
01384          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01385             /* CACHERTCLASSES enabled, let's add this class to default tree */
01386             if (state && state->class) {
01387                /* Class already exist for this channel */
01388                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01389                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01390                   /* we found RT class with the same name, seems like we should continue playing existing one */
01391                   /* XXX This code is impossible to reach */
01392                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01393                   mohclass = state->class;
01394                }
01395             }
01396             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01397              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01398              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01399              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01400              * invalid memory.
01401              */
01402             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01403                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01404                return -1;
01405             }
01406          } else {
01407             /* We don't register RT moh class, so let's init it manualy */
01408 
01409             time(&mohclass->start);
01410             mohclass->start -= respawn_time;
01411 
01412             if (!strcasecmp(mohclass->mode, "files")) {
01413                if (!moh_scan_files(mohclass)) {
01414                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01415                   return -1;
01416                }
01417                if (strchr(mohclass->args, 'r'))
01418                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01419             } 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")) {
01420 
01421                if (!strcasecmp(mohclass->mode, "custom"))
01422                   ast_set_flag(mohclass, MOH_CUSTOM);
01423                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01424                   ast_set_flag(mohclass, MOH_SINGLE);
01425                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01426                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01427                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01428                   ast_set_flag(mohclass, MOH_QUIET);
01429 
01430                mohclass->srcfd = -1;
01431                if (!(mohclass->timer = ast_timer_open())) {
01432                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01433                }
01434                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01435                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01436                   ast_timer_close(mohclass->timer);
01437                   mohclass->timer = NULL;
01438                }
01439 
01440                /* Let's check if this channel already had a moh class before */
01441                if (state && state->class) {
01442                   /* Class already exist for this channel */
01443                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01444                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01445                      /* we found RT class with the same name, seems like we should continue playing existing one */
01446                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01447                      mohclass = state->class;
01448                   }
01449                } else {
01450                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01451                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01452                      if (mohclass->timer) {
01453                         ast_timer_close(mohclass->timer);
01454                         mohclass->timer = NULL;
01455                      }
01456                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01457                      return -1;
01458                   }
01459                }
01460             } else {
01461                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01462                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01463                return -1;
01464             }
01465          }
01466       } else {
01467          ast_variables_destroy(var);
01468       }
01469    }
01470 
01471    if (!mohclass) {
01472       return -1;
01473    }
01474 
01475    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01476       "State: Start\r\n"
01477       "Channel: %s\r\n"
01478       "UniqueID: %s\r\n"
01479       "Class: %s\r\n",
01480       chan->name, chan->uniqueid,
01481       mohclass->name);
01482 
01483    ast_set_flag(chan, AST_FLAG_MOH);
01484 
01485    if (mohclass->total_files) {
01486       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01487    } else {
01488       res = ast_activate_generator(chan, &mohgen, mohclass);
01489    }
01490 
01491    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01492 
01493    return res;
01494 }
01495 
01496 static void local_ast_moh_stop(struct ast_channel *chan)
01497 {
01498    ast_clear_flag(chan, AST_FLAG_MOH);
01499    ast_deactivate_generator(chan);
01500 
01501    ast_channel_lock(chan);
01502    if (chan->music_state) {
01503       if (chan->stream) {
01504          ast_closestream(chan->stream);
01505          chan->stream = NULL;
01506       }
01507    }
01508 
01509    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01510       "State: Stop\r\n"
01511       "Channel: %s\r\n"
01512       "UniqueID: %s\r\n",
01513       chan->name, chan->uniqueid);
01514    ast_channel_unlock(chan);
01515 }
01516 
01517 static void moh_class_destructor(void *obj)
01518 {
01519    struct mohclass *class = obj;
01520    struct mohdata *member;
01521    pthread_t tid = 0;
01522 
01523    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01524 
01525    /* Kill the thread first, so it cannot restart the child process while the
01526     * class is being destroyed */
01527    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01528       tid = class->thread;
01529       class->thread = AST_PTHREADT_NULL;
01530       pthread_cancel(tid);
01531       /* We'll collect the exit status later, after we ensure all the readers
01532        * are dead. */
01533    }
01534 
01535    if (class->pid > 1) {
01536       char buff[8192];
01537       int bytes, tbytes = 0, stime = 0, pid = 0;
01538 
01539       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01540 
01541       stime = time(NULL) + 2;
01542       pid = class->pid;
01543       class->pid = 0;
01544 
01545       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01546        * to give the process a reason and time enough to kill off its
01547        * children. */
01548       do {
01549          if (killpg(pid, SIGHUP) < 0) {
01550             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01551          }
01552          usleep(100000);
01553          if (killpg(pid, SIGTERM) < 0) {
01554             if (errno == ESRCH) {
01555                break;
01556             }
01557             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01558          }
01559          usleep(100000);
01560          if (killpg(pid, SIGKILL) < 0) {
01561             if (errno == ESRCH) {
01562                break;
01563             }
01564             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01565          }
01566       } while (0);
01567 
01568       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01569             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01570          tbytes = tbytes + bytes;
01571       }
01572 
01573       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01574 
01575       close(class->srcfd);
01576    }
01577 
01578    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01579       free(member);
01580    }
01581 
01582    if (class->filearray) {
01583       int i;
01584       for (i = 0; i < class->total_files; i++) {
01585          free(class->filearray[i]);
01586       }
01587       free(class->filearray);
01588       class->filearray = NULL;
01589    }
01590 
01591    /* Finally, collect the exit status of the monitor thread */
01592    if (tid > 0) {
01593       pthread_join(tid, NULL);
01594    }
01595 }
01596 
01597 static int moh_class_mark(void *obj, void *arg, int flags)
01598 {
01599    struct mohclass *class = obj;
01600 
01601    class->delete = 1;
01602 
01603    return 0;
01604 }
01605 
01606 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01607 {
01608    struct mohclass *class = obj;
01609 
01610    return class->delete ? CMP_MATCH : 0;
01611 }
01612 
01613 static int load_moh_classes(int reload)
01614 {
01615    struct ast_config *cfg;
01616    struct ast_variable *var;
01617    struct mohclass *class; 
01618    char *cat;
01619    int numclasses = 0;
01620    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01621 
01622    cfg = ast_config_load("musiconhold.conf", config_flags);
01623 
01624    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01625       if (ast_check_realtime("musiconhold") && reload) {
01626          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01627          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01628       }
01629       if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01630          moh_rescan_files();
01631       }
01632       return 0;
01633    }
01634 
01635    if (reload) {
01636       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01637    }
01638 
01639    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01640 
01641    cat = ast_category_browse(cfg, NULL);
01642    for (; cat; cat = ast_category_browse(cfg, cat)) {
01643       /* Setup common options from [general] section */
01644       if (!strcasecmp(cat, "general")) {
01645          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01646             if (!strcasecmp(var->name, "cachertclasses")) {
01647                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01648             } else {
01649                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01650             }
01651          }
01652       }
01653       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01654       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01655             !strcasecmp(cat, "general")) {
01656          continue;
01657       }
01658 
01659       if (!(class = moh_class_malloc())) {
01660          break;
01661       }
01662 
01663       ast_copy_string(class->name, cat, sizeof(class->name));  
01664       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01665          if (!strcasecmp(var->name, "mode"))
01666             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01667          else if (!strcasecmp(var->name, "directory"))
01668             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01669          else if (!strcasecmp(var->name, "application"))
01670             ast_copy_string(class->args, var->value, sizeof(class->args));
01671          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01672             class->digit = *var->value;
01673          else if (!strcasecmp(var->name, "random"))
01674             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01675          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01676             ast_set_flag(class, MOH_RANDOMIZE);
01677          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01678             ast_set_flag(class, MOH_SORTALPHA);
01679          else if (!strcasecmp(var->name, "format")) {
01680             class->format = ast_getformatbyname(var->value);
01681             if (!class->format) {
01682                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01683                class->format = AST_FORMAT_SLINEAR;
01684             }
01685          }
01686       }
01687 
01688       if (ast_strlen_zero(class->dir)) {
01689          if (!strcasecmp(class->mode, "custom")) {
01690             strcpy(class->dir, "nodir");
01691          } else {
01692             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01693             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01694             continue;
01695          }
01696       }
01697       if (ast_strlen_zero(class->mode)) {
01698          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01699          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01700          continue;
01701       }
01702       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01703          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01704          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01705          continue;
01706       }
01707 
01708       /* Don't leak a class when it's already registered */
01709       if (!moh_register(class, reload, HANDLE_REF)) {
01710          numclasses++;
01711       }
01712    }
01713 
01714    ast_config_destroy(cfg);
01715 
01716    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01717          moh_classes_delete_marked, NULL, "Purge marked classes");
01718 
01719    return numclasses;
01720 }
01721 
01722 static void ast_moh_destroy(void)
01723 {
01724    ast_verb(2, "Destroying musiconhold processes\n");
01725    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01726 }
01727 
01728 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01729 {
01730    switch (cmd) {
01731    case CLI_INIT:
01732       e->command = "moh reload";
01733       e->usage =
01734          "Usage: moh reload\n"
01735          "       Reloads the MusicOnHold module.\n"
01736          "       Alias for 'module reload res_musiconhold.so'\n";
01737       return NULL;
01738    case CLI_GENERATE:
01739       return NULL;
01740    }
01741 
01742    if (a->argc != e->args)
01743       return CLI_SHOWUSAGE;
01744 
01745    reload();
01746 
01747    return CLI_SUCCESS;
01748 }
01749 
01750 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01751 {
01752    struct mohclass *class;
01753    struct ao2_iterator i;
01754 
01755    switch (cmd) {
01756    case CLI_INIT:
01757       e->command = "moh show files";
01758       e->usage =
01759          "Usage: moh show files\n"
01760          "       Lists all loaded file-based MusicOnHold classes and their\n"
01761          "       files.\n";
01762       return NULL;
01763    case CLI_GENERATE:
01764       return NULL;
01765    }
01766 
01767    if (a->argc != e->args)
01768       return CLI_SHOWUSAGE;
01769 
01770    i = ao2_iterator_init(mohclasses, 0);
01771    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01772       int x;
01773 
01774       if (!class->total_files) {
01775          continue;
01776       }
01777 
01778       ast_cli(a->fd, "Class: %s\n", class->name);
01779       for (x = 0; x < class->total_files; x++) {
01780          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01781       }
01782    }
01783    ao2_iterator_destroy(&i);
01784 
01785    return CLI_SUCCESS;
01786 }
01787 
01788 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01789 {
01790    struct mohclass *class;
01791    struct ao2_iterator i;
01792 
01793    switch (cmd) {
01794    case CLI_INIT:
01795       e->command = "moh show classes";
01796       e->usage =
01797          "Usage: moh show classes\n"
01798          "       Lists all MusicOnHold classes.\n";
01799       return NULL;
01800    case CLI_GENERATE:
01801       return NULL;
01802    }
01803 
01804    if (a->argc != e->args)
01805       return CLI_SHOWUSAGE;
01806 
01807    i = ao2_iterator_init(mohclasses, 0);
01808    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01809       ast_cli(a->fd, "Class: %s\n", class->name);
01810       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01811       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01812       if (ast_test_flag(class, MOH_CUSTOM)) {
01813          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01814       }
01815       if (strcasecmp(class->mode, "files")) {
01816          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01817       }
01818    }
01819    ao2_iterator_destroy(&i);
01820 
01821    return CLI_SUCCESS;
01822 }
01823 
01824 static struct ast_cli_entry cli_moh[] = {
01825    AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
01826    AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01827    AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
01828 };
01829 
01830 static int moh_class_hash(const void *obj, const int flags)
01831 {
01832    const struct mohclass *class = obj;
01833 
01834    return ast_str_case_hash(class->name);
01835 }
01836 
01837 static int moh_class_cmp(void *obj, void *arg, int flags)
01838 {
01839    struct mohclass *class = obj, *class2 = arg;
01840 
01841    return strcasecmp(class->name, class2->name) ? 0 :
01842       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01843       CMP_MATCH | CMP_STOP;
01844 }
01845 
01846 static int load_module(void)
01847 {
01848    int res;
01849 
01850    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01851       return AST_MODULE_LOAD_DECLINE;
01852    }
01853 
01854    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01855       ast_log(LOG_WARNING, "No music on hold classes configured, "
01856             "disabling music on hold.\n");
01857    } else {
01858       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01859             local_ast_moh_cleanup);
01860    }
01861 
01862    res = ast_register_application_xml(play_moh, play_moh_exec);
01863    ast_register_atexit(ast_moh_destroy);
01864    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01865    if (!res)
01866       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01867    if (!res)
01868       res = ast_register_application_xml(set_moh, set_moh_exec);
01869    if (!res)
01870       res = ast_register_application_xml(start_moh, start_moh_exec);
01871    if (!res)
01872       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01873 
01874    return AST_MODULE_LOAD_SUCCESS;
01875 }
01876 
01877 static int reload(void)
01878 {
01879    if (load_moh_classes(1)) {
01880       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01881             local_ast_moh_cleanup);
01882    }
01883 
01884    return AST_MODULE_LOAD_SUCCESS;
01885 }
01886 
01887 static int moh_class_inuse(void *obj, void *arg, int flags)
01888 {
01889    struct mohclass *class = obj;
01890 
01891    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01892 }
01893 
01894 static int unload_module(void)
01895 {
01896    int res = 0;
01897    struct mohclass *class = NULL;
01898 
01899    /* XXX This check shouldn't be required if module ref counting was being used
01900     * properly ... */
01901    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01902       class = mohclass_unref(class, "unref of class from module unload callback");
01903       res = -1;
01904    }
01905 
01906    if (res < 0) {
01907       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01908       return res;
01909    }
01910 
01911    ast_uninstall_music_functions();
01912 
01913    ast_moh_destroy();
01914    res = ast_unregister_application(play_moh);
01915    res |= ast_unregister_application(wait_moh);
01916    res |= ast_unregister_application(set_moh);
01917    res |= ast_unregister_application(start_moh);
01918    res |= ast_unregister_application(stop_moh);
01919    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01920    ast_unregister_atexit(ast_moh_destroy);
01921 
01922    return res;
01923 }
01924 
01925 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
01926    .load = load_module,
01927    .unload = unload_module,
01928    .reload = reload,
01929    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
01930 );

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7