Sun Oct 16 2011 08:41:45

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