Sat Feb 11 06:33:22 2012

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

Generated on Sat Feb 11 06:33:22 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6