Mon Jun 27 16:50:56 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: 305473 $")
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       class->srcfd = -1;
01281    }
01282 
01283    return class;
01284 }
01285 
01286 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01287 {
01288    struct mohclass *mohclass = NULL;
01289    struct moh_files_state *state = chan->music_state;
01290    struct ast_variable *var = NULL;
01291    int res;
01292    int realtime_possible = ast_check_realtime("musiconhold");
01293 
01294    /* The following is the order of preference for which class to use:
01295     * 1) The channels explicitly set musicclass, which should *only* be
01296     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01297     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01298     *    result of receiving a HOLD control frame, this should be the
01299     *    payload that came with the frame.
01300     * 3) The interpclass argument. This would be from the mohinterpret
01301     *    option from channel drivers. This is the same as the old musicclass
01302     *    option.
01303     * 4) The default class.
01304     */
01305    if (!ast_strlen_zero(chan->musicclass)) {
01306       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01307       if (!mohclass && realtime_possible) {
01308          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01309       }
01310    }
01311    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01312       mohclass = get_mohbyname(mclass, 1, 0);
01313       if (!mohclass && realtime_possible) {
01314          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01315       }
01316    }
01317    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01318       mohclass = get_mohbyname(interpclass, 1, 0);
01319       if (!mohclass && realtime_possible) {
01320          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01321       }
01322    }
01323 
01324    if (!mohclass && !var) {
01325       mohclass = get_mohbyname("default", 1, 0);
01326       if (!mohclass && realtime_possible) {
01327          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01328       }
01329    }
01330 
01331    /* If no moh class found in memory, then check RT. Note that the logic used
01332     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01333     */
01334    if (var) {
01335       struct ast_variable *tmp = NULL;
01336 
01337       if ((mohclass = moh_class_malloc())) {
01338          mohclass->realtime = 1;
01339          for (tmp = var; tmp; tmp = tmp->next) {
01340             if (!strcasecmp(tmp->name, "name"))
01341                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01342             else if (!strcasecmp(tmp->name, "mode"))
01343                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01344             else if (!strcasecmp(tmp->name, "directory"))
01345                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01346             else if (!strcasecmp(tmp->name, "application"))
01347                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01348             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01349                mohclass->digit = *tmp->value;
01350             else if (!strcasecmp(tmp->name, "random"))
01351                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01352             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01353                ast_set_flag(mohclass, MOH_RANDOMIZE);
01354             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01355                ast_set_flag(mohclass, MOH_SORTALPHA);
01356             else if (!strcasecmp(tmp->name, "format")) {
01357                mohclass->format = ast_getformatbyname(tmp->value);
01358                if (!mohclass->format) {
01359                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01360                   mohclass->format = AST_FORMAT_SLINEAR;
01361                }
01362             }
01363          }
01364          ast_variables_destroy(var);
01365          if (ast_strlen_zero(mohclass->dir)) {
01366             if (!strcasecmp(mohclass->mode, "custom")) {
01367                strcpy(mohclass->dir, "nodir");
01368             } else {
01369                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01370                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01371                return -1;
01372             }
01373          }
01374          if (ast_strlen_zero(mohclass->mode)) {
01375             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01376             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01377             return -1;
01378          }
01379          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01380             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01381             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01382             return -1;
01383          }
01384 
01385          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01386             /* CACHERTCLASSES enabled, let's add this class to default tree */
01387             if (state && state->class) {
01388                /* Class already exist for this channel */
01389                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01390                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01391                   /* we found RT class with the same name, seems like we should continue playing existing one */
01392                   /* XXX This code is impossible to reach */
01393                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01394                   mohclass = state->class;
01395                }
01396             }
01397             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01398              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01399              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01400              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01401              * invalid memory.
01402              */
01403             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01404                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01405                return -1;
01406             }
01407          } else {
01408             /* We don't register RT moh class, so let's init it manualy */
01409 
01410             time(&mohclass->start);
01411             mohclass->start -= respawn_time;
01412 
01413             if (!strcasecmp(mohclass->mode, "files")) {
01414                if (!moh_scan_files(mohclass)) {
01415                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01416                   return -1;
01417                }
01418                if (strchr(mohclass->args, 'r'))
01419                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01420             } 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")) {
01421 
01422                if (!strcasecmp(mohclass->mode, "custom"))
01423                   ast_set_flag(mohclass, MOH_CUSTOM);
01424                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01425                   ast_set_flag(mohclass, MOH_SINGLE);
01426                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01427                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01428                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01429                   ast_set_flag(mohclass, MOH_QUIET);
01430 
01431                mohclass->srcfd = -1;
01432                if (!(mohclass->timer = ast_timer_open())) {
01433                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01434                }
01435                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01436                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01437                   ast_timer_close(mohclass->timer);
01438                   mohclass->timer = NULL;
01439                }
01440 
01441                /* Let's check if this channel already had a moh class before */
01442                if (state && state->class) {
01443                   /* Class already exist for this channel */
01444                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01445                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01446                      /* we found RT class with the same name, seems like we should continue playing existing one */
01447                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01448                      mohclass = state->class;
01449                   }
01450                } else {
01451                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01452                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01453                      if (mohclass->timer) {
01454                         ast_timer_close(mohclass->timer);
01455                         mohclass->timer = NULL;
01456                      }
01457                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01458                      return -1;
01459                   }
01460                }
01461             } else {
01462                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01463                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01464                return -1;
01465             }
01466          }
01467       } else {
01468          ast_variables_destroy(var);
01469       }
01470    }
01471 
01472    if (!mohclass) {
01473       return -1;
01474    }
01475 
01476    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01477       "State: Start\r\n"
01478       "Channel: %s\r\n"
01479       "UniqueID: %s\r\n"
01480       "Class: %s\r\n",
01481       chan->name, chan->uniqueid,
01482       mohclass->name);
01483 
01484    ast_set_flag(chan, AST_FLAG_MOH);
01485 
01486    if (mohclass->total_files) {
01487       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01488    } else {
01489       res = ast_activate_generator(chan, &mohgen, mohclass);
01490    }
01491 
01492    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01493 
01494    return res;
01495 }
01496 
01497 static void local_ast_moh_stop(struct ast_channel *chan)
01498 {
01499    ast_clear_flag(chan, AST_FLAG_MOH);
01500    ast_deactivate_generator(chan);
01501 
01502    ast_channel_lock(chan);
01503    if (chan->music_state) {
01504       if (chan->stream) {
01505          ast_closestream(chan->stream);
01506          chan->stream = NULL;
01507       }
01508    }
01509 
01510    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01511       "State: Stop\r\n"
01512       "Channel: %s\r\n"
01513       "UniqueID: %s\r\n",
01514       chan->name, chan->uniqueid);
01515    ast_channel_unlock(chan);
01516 }
01517 
01518 static void moh_class_destructor(void *obj)
01519 {
01520    struct mohclass *class = obj;
01521    struct mohdata *member;
01522    pthread_t tid = 0;
01523 
01524    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01525 
01526    /* Kill the thread first, so it cannot restart the child process while the
01527     * class is being destroyed */
01528    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01529       tid = class->thread;
01530       class->thread = AST_PTHREADT_NULL;
01531       pthread_cancel(tid);
01532       /* We'll collect the exit status later, after we ensure all the readers
01533        * are dead. */
01534    }
01535 
01536    if (class->pid > 1) {
01537       char buff[8192];
01538       int bytes, tbytes = 0, stime = 0, pid = 0;
01539 
01540       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01541 
01542       stime = time(NULL) + 2;
01543       pid = class->pid;
01544       class->pid = 0;
01545 
01546       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01547        * to give the process a reason and time enough to kill off its
01548        * children. */
01549       do {
01550          if (killpg(pid, SIGHUP) < 0) {
01551             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01552          }
01553          usleep(100000);
01554          if (killpg(pid, SIGTERM) < 0) {
01555             if (errno == ESRCH) {
01556                break;
01557             }
01558             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01559          }
01560          usleep(100000);
01561          if (killpg(pid, SIGKILL) < 0) {
01562             if (errno == ESRCH) {
01563                break;
01564             }
01565             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01566          }
01567       } while (0);
01568 
01569       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01570             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01571          tbytes = tbytes + bytes;
01572       }
01573 
01574       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01575 
01576       close(class->srcfd);
01577    }
01578 
01579    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01580       free(member);
01581    }
01582 
01583    if (class->filearray) {
01584       int i;
01585       for (i = 0; i < class->total_files; i++) {
01586          free(class->filearray[i]);
01587       }
01588       free(class->filearray);
01589       class->filearray = NULL;
01590    }
01591 
01592    if (class->timer) {
01593       ast_timer_close(class->timer);
01594       class->timer = NULL;
01595    }
01596 
01597    /* Finally, collect the exit status of the monitor thread */
01598    if (tid > 0) {
01599       pthread_join(tid, NULL);
01600    }
01601 }
01602 
01603 static int moh_class_mark(void *obj, void *arg, int flags)
01604 {
01605    struct mohclass *class = obj;
01606 
01607    class->delete = 1;
01608 
01609    return 0;
01610 }
01611 
01612 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01613 {
01614    struct mohclass *class = obj;
01615 
01616    return class->delete ? CMP_MATCH : 0;
01617 }
01618 
01619 static int load_moh_classes(int reload)
01620 {
01621    struct ast_config *cfg;
01622    struct ast_variable *var;
01623    struct mohclass *class; 
01624    char *cat;
01625    int numclasses = 0;
01626    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01627 
01628    cfg = ast_config_load("musiconhold.conf", config_flags);
01629 
01630    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01631       if (ast_check_realtime("musiconhold") && reload) {
01632          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01633          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01634       }
01635       if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01636          moh_rescan_files();
01637       }
01638       return 0;
01639    }
01640 
01641    if (reload) {
01642       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01643    }
01644 
01645    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01646 
01647    cat = ast_category_browse(cfg, NULL);
01648    for (; cat; cat = ast_category_browse(cfg, cat)) {
01649       /* Setup common options from [general] section */
01650       if (!strcasecmp(cat, "general")) {
01651          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01652             if (!strcasecmp(var->name, "cachertclasses")) {
01653                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01654             } else {
01655                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01656             }
01657          }
01658       }
01659       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01660       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01661             !strcasecmp(cat, "general")) {
01662          continue;
01663       }
01664 
01665       if (!(class = moh_class_malloc())) {
01666          break;
01667       }
01668 
01669       ast_copy_string(class->name, cat, sizeof(class->name));  
01670       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01671          if (!strcasecmp(var->name, "mode"))
01672             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01673          else if (!strcasecmp(var->name, "directory"))
01674             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01675          else if (!strcasecmp(var->name, "application"))
01676             ast_copy_string(class->args, var->value, sizeof(class->args));
01677          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01678             class->digit = *var->value;
01679          else if (!strcasecmp(var->name, "random"))
01680             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01681          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01682             ast_set_flag(class, MOH_RANDOMIZE);
01683          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01684             ast_set_flag(class, MOH_SORTALPHA);
01685          else if (!strcasecmp(var->name, "format")) {
01686             class->format = ast_getformatbyname(var->value);
01687             if (!class->format) {
01688                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01689                class->format = AST_FORMAT_SLINEAR;
01690             }
01691          }
01692       }
01693 
01694       if (ast_strlen_zero(class->dir)) {
01695          if (!strcasecmp(class->mode, "custom")) {
01696             strcpy(class->dir, "nodir");
01697          } else {
01698             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01699             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01700             continue;
01701          }
01702       }
01703       if (ast_strlen_zero(class->mode)) {
01704          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01705          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01706          continue;
01707       }
01708       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01709          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01710          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01711          continue;
01712       }
01713 
01714       /* Don't leak a class when it's already registered */
01715       if (!moh_register(class, reload, HANDLE_REF)) {
01716          numclasses++;
01717       }
01718    }
01719 
01720    ast_config_destroy(cfg);
01721 
01722    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01723          moh_classes_delete_marked, NULL, "Purge marked classes");
01724 
01725    return numclasses;
01726 }
01727 
01728 static void ast_moh_destroy(void)
01729 {
01730    ast_verb(2, "Destroying musiconhold processes\n");
01731    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01732 }
01733 
01734 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01735 {
01736    switch (cmd) {
01737    case CLI_INIT:
01738       e->command = "moh reload";
01739       e->usage =
01740          "Usage: moh reload\n"
01741          "       Reloads the MusicOnHold module.\n"
01742          "       Alias for 'module reload res_musiconhold.so'\n";
01743       return NULL;
01744    case CLI_GENERATE:
01745       return NULL;
01746    }
01747 
01748    if (a->argc != e->args)
01749       return CLI_SHOWUSAGE;
01750 
01751    reload();
01752 
01753    return CLI_SUCCESS;
01754 }
01755 
01756 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01757 {
01758    struct mohclass *class;
01759    struct ao2_iterator i;
01760 
01761    switch (cmd) {
01762    case CLI_INIT:
01763       e->command = "moh show files";
01764       e->usage =
01765          "Usage: moh show files\n"
01766          "       Lists all loaded file-based MusicOnHold classes and their\n"
01767          "       files.\n";
01768       return NULL;
01769    case CLI_GENERATE:
01770       return NULL;
01771    }
01772 
01773    if (a->argc != e->args)
01774       return CLI_SHOWUSAGE;
01775 
01776    i = ao2_iterator_init(mohclasses, 0);
01777    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01778       int x;
01779 
01780       if (!class->total_files) {
01781          continue;
01782       }
01783 
01784       ast_cli(a->fd, "Class: %s\n", class->name);
01785       for (x = 0; x < class->total_files; x++) {
01786          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01787       }
01788    }
01789    ao2_iterator_destroy(&i);
01790 
01791    return CLI_SUCCESS;
01792 }
01793 
01794 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01795 {
01796    struct mohclass *class;
01797    struct ao2_iterator i;
01798 
01799    switch (cmd) {
01800    case CLI_INIT:
01801       e->command = "moh show classes";
01802       e->usage =
01803          "Usage: moh show classes\n"
01804          "       Lists all MusicOnHold classes.\n";
01805       return NULL;
01806    case CLI_GENERATE:
01807       return NULL;
01808    }
01809 
01810    if (a->argc != e->args)
01811       return CLI_SHOWUSAGE;
01812 
01813    i = ao2_iterator_init(mohclasses, 0);
01814    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01815       ast_cli(a->fd, "Class: %s\n", class->name);
01816       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01817       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01818       if (ast_test_flag(class, MOH_CUSTOM)) {
01819          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01820       }
01821       if (strcasecmp(class->mode, "files")) {
01822          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01823       }
01824    }
01825    ao2_iterator_destroy(&i);
01826 
01827    return CLI_SUCCESS;
01828 }
01829 
01830 static struct ast_cli_entry cli_moh[] = {
01831    AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
01832    AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01833    AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
01834 };
01835 
01836 static int moh_class_hash(const void *obj, const int flags)
01837 {
01838    const struct mohclass *class = obj;
01839 
01840    return ast_str_case_hash(class->name);
01841 }
01842 
01843 static int moh_class_cmp(void *obj, void *arg, int flags)
01844 {
01845    struct mohclass *class = obj, *class2 = arg;
01846 
01847    return strcasecmp(class->name, class2->name) ? 0 :
01848       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01849       CMP_MATCH | CMP_STOP;
01850 }
01851 
01852 static int load_module(void)
01853 {
01854    int res;
01855 
01856    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01857       return AST_MODULE_LOAD_DECLINE;
01858    }
01859 
01860    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01861       ast_log(LOG_WARNING, "No music on hold classes configured, "
01862             "disabling music on hold.\n");
01863    } else {
01864       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01865             local_ast_moh_cleanup);
01866    }
01867 
01868    res = ast_register_application_xml(play_moh, play_moh_exec);
01869    ast_register_atexit(ast_moh_destroy);
01870    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01871    if (!res)
01872       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01873    if (!res)
01874       res = ast_register_application_xml(set_moh, set_moh_exec);
01875    if (!res)
01876       res = ast_register_application_xml(start_moh, start_moh_exec);
01877    if (!res)
01878       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01879 
01880    return AST_MODULE_LOAD_SUCCESS;
01881 }
01882 
01883 static int reload(void)
01884 {
01885    if (load_moh_classes(1)) {
01886       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01887             local_ast_moh_cleanup);
01888    }
01889 
01890    return AST_MODULE_LOAD_SUCCESS;
01891 }
01892 
01893 static int moh_class_inuse(void *obj, void *arg, int flags)
01894 {
01895    struct mohclass *class = obj;
01896 
01897    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01898 }
01899 
01900 static int unload_module(void)
01901 {
01902    int res = 0;
01903    struct mohclass *class = NULL;
01904 
01905    /* XXX This check shouldn't be required if module ref counting was being used
01906     * properly ... */
01907    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01908       class = mohclass_unref(class, "unref of class from module unload callback");
01909       res = -1;
01910    }
01911 
01912    if (res < 0) {
01913       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01914       return res;
01915    }
01916 
01917    ast_uninstall_music_functions();
01918 
01919    ast_moh_destroy();
01920    res = ast_unregister_application(play_moh);
01921    res |= ast_unregister_application(wait_moh);
01922    res |= ast_unregister_application(set_moh);
01923    res |= ast_unregister_application(start_moh);
01924    res |= ast_unregister_application(stop_moh);
01925    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01926    ast_unregister_atexit(ast_moh_destroy);
01927 
01928    return res;
01929 }
01930 
01931 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
01932    .load = load_module,
01933    .unload = unload_module,
01934    .reload = reload,
01935    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
01936 );

Generated on Mon Jun 27 16:50:56 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7