Sat Jul 12 2014 17:18:37

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