Sat Apr 26 2014 22:01:40

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: 410044 $")
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 || !ast_channel_music_state(chan)) {
00264       return;
00265    }
00266 
00267    state = ast_channel_music_state(chan);
00268 
00269    if (ast_channel_stream(chan)) {
00270       ast_closestream(ast_channel_stream(chan));
00271       ast_channel_stream_set(chan, 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 = ast_channel_music_state(chan);
00290    int tries;
00291 
00292    /* Discontinue a stream if it is running already */
00293    if (ast_channel_stream(chan)) {
00294       ast_closestream(ast_channel_stream(chan));
00295       ast_channel_stream_set(chan, 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(ast_channel_stream(chan), 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(ast_channel_stream(chan));
00364       if (state->samples > loc && loc) {
00365          /* seek one sample from the end for one guaranteed valid read */
00366          ast_seekstream(ast_channel_stream(chan), 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 (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
00378       if (!ast_moh_files_next(chan))
00379          f = ast_readframe(ast_channel_stream(chan));
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 = ast_channel_music_state(chan);
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, ast_channel_writeformat(chan));
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 = ast_channel_music_state(chan);
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 (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
00447       ast_channel_music_state_set(chan, state);
00448       ast_module_ref(ast_module_info->self);
00449    } else {
00450       state = ast_channel_music_state(chan);
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, ast_channel_writeformat(chan));
00474    ast_format_copy(&state->mohwfmt, ast_channel_writeformat(chan));
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 ast_channel_music_state(chan);
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             continue;
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             if (ast_timer_ack(class->timer, 1) < 0) {
00696                ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
00697                return NULL;
00698             }
00699             res = 320;
00700          } else {
00701             ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00702             res = 0;
00703          }
00704          pthread_testcancel();
00705       } else {
00706          long delta;
00707          /* Reliable sleep */
00708          tv_tmp = ast_tvnow();
00709          if (ast_tvzero(deadline))
00710             deadline = tv_tmp;
00711          delta = ast_tvdiff_ms(tv_tmp, deadline);
00712          if (delta < MOH_MS_INTERVAL) {   /* too early */
00713             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00714             usleep(1000 * (MOH_MS_INTERVAL - delta));
00715             pthread_testcancel();
00716          } else {
00717             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00718             deadline = tv_tmp;
00719          }
00720          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00721       }
00722       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00723          continue;
00724       /* Read mp3 audio */
00725       len = ast_codec_get_len(&class->format, res);
00726 
00727       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00728          if (!res2) {
00729             close(class->srcfd);
00730             class->srcfd = -1;
00731             pthread_testcancel();
00732             if (class->pid > 1) {
00733                do {
00734                   if (killpg(class->pid, SIGHUP) < 0) {
00735                      if (errno == ESRCH) {
00736                         break;
00737                      }
00738                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00739                   }
00740                   usleep(100000);
00741                   if (killpg(class->pid, SIGTERM) < 0) {
00742                      if (errno == ESRCH) {
00743                         break;
00744                      }
00745                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00746                   }
00747                   usleep(100000);
00748                   if (killpg(class->pid, SIGKILL) < 0) {
00749                      if (errno == ESRCH) {
00750                         break;
00751                      }
00752                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00753                   }
00754                } while (0);
00755                class->pid = 0;
00756             }
00757          } else {
00758             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00759          }
00760          continue;
00761       }
00762 
00763       pthread_testcancel();
00764 
00765       ao2_lock(class);
00766       AST_LIST_TRAVERSE(&class->members, moh, list) {
00767          /* Write data */
00768          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00769             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00770          }
00771       }
00772       ao2_unlock(class);
00773    }
00774    return NULL;
00775 }
00776 
00777 static int play_moh_exec(struct ast_channel *chan, const char *data)
00778 {
00779    char *parse;
00780    char *class;
00781    int timeout = -1;
00782    int res;
00783    AST_DECLARE_APP_ARGS(args,
00784       AST_APP_ARG(class);
00785       AST_APP_ARG(duration);
00786    );
00787 
00788    parse = ast_strdupa(data);
00789 
00790    AST_STANDARD_APP_ARGS(args, parse);
00791 
00792    if (!ast_strlen_zero(args.duration)) {
00793       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00794          timeout *= 1000;
00795       } else {
00796          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00797       }
00798    }
00799 
00800    class = S_OR(args.class, NULL);
00801    if (ast_moh_start(chan, class, NULL)) {
00802       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00803       return 0;
00804    }
00805 
00806    if (timeout > 0)
00807       res = ast_safe_sleep(chan, timeout);
00808    else {
00809       while (!(res = ast_safe_sleep(chan, 10000)));
00810    }
00811 
00812    ast_moh_stop(chan);
00813 
00814    return res;
00815 }
00816 
00817 static int wait_moh_exec(struct ast_channel *chan, const char *data)
00818 {
00819    static int deprecation_warning = 0;
00820    int res;
00821 
00822    if (!deprecation_warning) {
00823       deprecation_warning = 1;
00824       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00825    }
00826 
00827    if (!data || !atoi(data)) {
00828       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00829       return -1;
00830    }
00831    if (ast_moh_start(chan, NULL, NULL)) {
00832       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), ast_channel_name(chan));
00833       return 0;
00834    }
00835    res = ast_safe_sleep(chan, atoi(data) * 1000);
00836    ast_moh_stop(chan);
00837    return res;
00838 }
00839 
00840 static int set_moh_exec(struct ast_channel *chan, const char *data)
00841 {
00842    static int deprecation_warning = 0;
00843 
00844    if (!deprecation_warning) {
00845       deprecation_warning = 1;
00846       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00847    }
00848 
00849    if (ast_strlen_zero(data)) {
00850       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00851       return -1;
00852    }
00853    ast_channel_musicclass_set(chan, data);
00854    return 0;
00855 }
00856 
00857 static int start_moh_exec(struct ast_channel *chan, const char *data)
00858 {
00859    char *parse;
00860    char *class;
00861    AST_DECLARE_APP_ARGS(args,
00862       AST_APP_ARG(class);
00863    );
00864 
00865    parse = ast_strdupa(data);
00866 
00867    AST_STANDARD_APP_ARGS(args, parse);
00868 
00869    class = S_OR(args.class, NULL);
00870    if (ast_moh_start(chan, class, NULL)) 
00871       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00872 
00873    return 0;
00874 }
00875 
00876 static int stop_moh_exec(struct ast_channel *chan, const char *data)
00877 {
00878    ast_moh_stop(chan);
00879 
00880    return 0;
00881 }
00882 
00883 #define get_mohbyname(a,b,c)  _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00884 
00885 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00886 {
00887    struct mohclass *moh = NULL;
00888    struct mohclass tmp_class = {
00889       .flags = 0,
00890    };
00891 
00892    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00893 
00894 #ifdef REF_DEBUG
00895    moh = __ao2_find_debug(mohclasses, &tmp_class, flags,
00896       "get_mohbyname", file, lineno, funcname);
00897 #else
00898    moh = __ao2_find(mohclasses, &tmp_class, flags);
00899 #endif
00900 
00901    if (!moh && warn) {
00902       ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
00903    }
00904 
00905    return moh;
00906 }
00907 
00908 static struct mohdata *mohalloc(struct mohclass *cl)
00909 {
00910    struct mohdata *moh;
00911    long flags;
00912 
00913    if (!(moh = ast_calloc(1, sizeof(*moh))))
00914       return NULL;
00915 
00916    if (pipe(moh->pipe)) {
00917       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00918       ast_free(moh);
00919       return NULL;
00920    }
00921 
00922    /* Make entirely non-blocking */
00923    flags = fcntl(moh->pipe[0], F_GETFL);
00924    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00925    flags = fcntl(moh->pipe[1], F_GETFL);
00926    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00927 
00928    moh->f.frametype = AST_FRAME_VOICE;
00929    ast_format_copy(&moh->f.subclass.format, &cl->format);
00930    moh->f.offset = AST_FRIENDLY_OFFSET;
00931 
00932    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00933 
00934    ao2_lock(cl);
00935    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00936    ao2_unlock(cl);
00937    
00938    return moh;
00939 }
00940 
00941 static void moh_release(struct ast_channel *chan, void *data)
00942 {
00943    struct mohdata *moh = data;
00944    struct mohclass *class = moh->parent;
00945    struct ast_format oldwfmt;
00946 
00947    ao2_lock(class);
00948    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00949    ao2_unlock(class);
00950    
00951    close(moh->pipe[0]);
00952    close(moh->pipe[1]);
00953 
00954    ast_format_copy(&oldwfmt, &moh->origwfmt);
00955 
00956    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00957 
00958    ast_free(moh);
00959 
00960    if (chan) {
00961       struct moh_files_state *state;
00962 
00963       state = ast_channel_music_state(chan);
00964       if (state && state->class) {
00965          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00966       }
00967       if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
00968          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00969                ast_channel_name(chan), ast_getformatname(&oldwfmt));
00970       }
00971 
00972       ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
00973    }
00974 }
00975 
00976 static void *moh_alloc(struct ast_channel *chan, void *params)
00977 {
00978    struct mohdata *res;
00979    struct mohclass *class = params;
00980    struct moh_files_state *state;
00981 
00982    /* Initiating music_state for current channel. Channel should know name of moh class */
00983    if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
00984       ast_channel_music_state_set(chan, state);
00985       ast_module_ref(ast_module_info->self);
00986    } else {
00987       state = ast_channel_music_state(chan);
00988       if (!state) {
00989          return NULL;
00990       }
00991       if (state->class) {
00992          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00993          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00994       }
00995       memset(state, 0, sizeof(*state));
00996    }
00997 
00998    if ((res = mohalloc(class))) {
00999       ast_format_copy(&res->origwfmt, ast_channel_writeformat(chan));
01000       if (ast_set_write_format(chan, &class->format)) {
01001          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan), ast_codec2str(&class->format));
01002          moh_release(NULL, res);
01003          res = NULL;
01004       } else {
01005          state->class = mohclass_ref(class, "Placing reference into state container");
01006       }
01007       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, ast_channel_name(chan));
01008    }
01009    return res;
01010 }
01011 
01012 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
01013 {
01014    struct mohdata *moh = data;
01015    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
01016    int res;
01017 
01018    len = ast_codec_get_len(&moh->parent->format, samples);
01019 
01020    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
01021       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
01022       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
01023    }
01024    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
01025    if (res <= 0)
01026       return 0;
01027 
01028    moh->f.datalen = res;
01029    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
01030    moh->f.samples = ast_codec_get_samples(&moh->f);
01031 
01032    if (ast_write(chan, &moh->f) < 0) {
01033       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
01034       return -1;
01035    }
01036 
01037    return 0;
01038 }
01039 
01040 static struct ast_generator mohgen = {
01041    .alloc    = moh_alloc,
01042    .release  = moh_release,
01043    .generate = moh_generate,
01044    .digit    = moh_handle_digit,
01045 };
01046 
01047 static int moh_add_file(struct mohclass *class, const char *filepath)
01048 {
01049    if (!class->allowed_files) {
01050       class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray));
01051       if (!class->filearray) {
01052          return -1;
01053       }
01054       class->allowed_files = INITIAL_NUM_FILES;
01055    } else if (class->total_files == class->allowed_files) {
01056       char **new_array;
01057 
01058       new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2);
01059       if (!new_array) {
01060          return -1;
01061       }
01062       class->filearray = new_array;
01063       class->allowed_files *= 2;
01064    }
01065 
01066    class->filearray[class->total_files] = ast_strdup(filepath);
01067    if (!class->filearray[class->total_files]) {
01068       return -1;
01069    }
01070 
01071    class->total_files++;
01072 
01073    return 0;
01074 }
01075 
01076 static int moh_sort_compare(const void *i1, const void *i2)
01077 {
01078    char *s1, *s2;
01079 
01080    s1 = ((char **)i1)[0];
01081    s2 = ((char **)i2)[0];
01082 
01083    return strcasecmp(s1, s2);
01084 }
01085 
01086 static int moh_scan_files(struct mohclass *class) {
01087 
01088    DIR *files_DIR;
01089    struct dirent *files_dirent;
01090    char dir_path[PATH_MAX];
01091    char path[PATH_MAX];
01092    char filepath[PATH_MAX];
01093    char *ext;
01094    struct stat statbuf;
01095    int i;
01096 
01097    if (class->dir[0] != '/') {
01098       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01099       strncat(dir_path, "/", sizeof(dir_path) - 1);
01100       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01101    } else {
01102       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01103    }
01104    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01105    files_DIR = opendir(dir_path);
01106    if (!files_DIR) {
01107       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01108       return -1;
01109    }
01110 
01111    for (i = 0; i < class->total_files; i++) {
01112       ast_free(class->filearray[i]);
01113    }
01114    class->total_files = 0;
01115 
01116    if (!getcwd(path, sizeof(path))) {
01117       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01118       closedir(files_DIR);
01119       return -1;
01120    }
01121    if (chdir(dir_path) < 0) {
01122       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01123       closedir(files_DIR);
01124       return -1;
01125    }
01126    while ((files_dirent = readdir(files_DIR))) {
01127       /* The file name must be at least long enough to have the file type extension */
01128       if ((strlen(files_dirent->d_name) < 4))
01129          continue;
01130 
01131       /* Skip files that starts with a dot */
01132       if (files_dirent->d_name[0] == '.')
01133          continue;
01134 
01135       /* Skip files without extensions... they are not audio */
01136       if (!strchr(files_dirent->d_name, '.'))
01137          continue;
01138 
01139       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01140 
01141       if (stat(filepath, &statbuf))
01142          continue;
01143 
01144       if (!S_ISREG(statbuf.st_mode))
01145          continue;
01146 
01147       if ((ext = strrchr(filepath, '.')))
01148          *ext = '\0';
01149 
01150       /* if the file is present in multiple formats, ensure we only put it into the list once */
01151       for (i = 0; i < class->total_files; i++)
01152          if (!strcmp(filepath, class->filearray[i]))
01153             break;
01154 
01155       if (i == class->total_files) {
01156          if (moh_add_file(class, filepath))
01157             break;
01158       }
01159    }
01160 
01161    closedir(files_DIR);
01162    if (chdir(path) < 0) {
01163       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01164       return -1;
01165    }
01166    if (ast_test_flag(class, MOH_SORTALPHA))
01167       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01168    return class->total_files;
01169 }
01170 
01171 static int init_files_class(struct mohclass *class)
01172 {
01173    int res;
01174 
01175    res = moh_scan_files(class);
01176 
01177    if (res < 0) {
01178       return -1;
01179    }
01180 
01181    if (!res) {
01182       ast_verb(3, "Files not found in %s for moh class:%s\n",
01183          class->dir, class->name);
01184       return -1;
01185    }
01186 
01187 #if 0
01188    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01189    if (strchr(class->args, 'r')) {
01190       ast_set_flag(class, MOH_RANDOMIZE);
01191    }
01192 #endif
01193 
01194    return 0;
01195 }
01196 
01197 static void moh_rescan_files(void) {
01198    struct ao2_iterator i;
01199    struct mohclass *c;
01200 
01201    i = ao2_iterator_init(mohclasses, 0);
01202 
01203    while ((c = ao2_iterator_next(&i))) {
01204       if (!strcasecmp(c->mode, "files")) {
01205          moh_scan_files(c);
01206       }
01207       ao2_ref(c, -1);
01208    }
01209 
01210    ao2_iterator_destroy(&i);
01211 }
01212 
01213 static int moh_diff(struct mohclass *old, struct mohclass *new)
01214 {
01215    if (!old || !new) {
01216       return -1;
01217    }
01218 
01219    if (strcmp(old->dir, new->dir)) {
01220       return -1;
01221    } else if (strcmp(old->mode, new->mode)) {
01222       return -1;
01223    } else if (strcmp(old->args, new->args)) {
01224       return -1;
01225    } else if (old->flags != new->flags) {
01226       return -1;
01227    }
01228 
01229    return 0;
01230 }
01231 
01232 static int init_app_class(struct mohclass *class)
01233 {
01234    if (!strcasecmp(class->mode, "custom")) {
01235       ast_set_flag(class, MOH_CUSTOM);
01236    } else if (!strcasecmp(class->mode, "mp3nb")) {
01237       ast_set_flag(class, MOH_SINGLE);
01238    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01239       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01240    } else if (!strcasecmp(class->mode, "quietmp3")) {
01241       ast_set_flag(class, MOH_QUIET);
01242    }
01243 
01244    class->srcfd = -1;
01245 
01246    if (!(class->timer = ast_timer_open())) {
01247       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01248       return -1;
01249    }
01250    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01251       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01252       ast_timer_close(class->timer);
01253       class->timer = NULL;
01254    }
01255 
01256    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01257       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01258       if (class->timer) {
01259          ast_timer_close(class->timer);
01260          class->timer = NULL;
01261       }
01262       return -1;
01263    }
01264 
01265    return 0;
01266 }
01267 
01268 /*!
01269  * \note This function owns the reference it gets to moh if unref is true
01270  */
01271 #define moh_register(a,b,c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01272 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01273 {
01274    struct mohclass *mohclass = NULL;
01275 
01276    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01277 
01278    if (mohclass && !moh_diff(mohclass, moh)) {
01279       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01280       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01281       if (unref) {
01282          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01283       }
01284       return -1;
01285    } else if (mohclass) {
01286       /* Found a class, but it's different from the one being registered */
01287       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01288    }
01289 
01290    time(&moh->start);
01291    moh->start -= respawn_time;
01292 
01293    if (!strcasecmp(moh->mode, "files")) {
01294       if (init_files_class(moh)) {
01295          if (unref) {
01296             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01297          }
01298          return -1;
01299       }
01300    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01301          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01302          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01303       if (init_app_class(moh)) {
01304          if (unref) {
01305             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01306          }
01307          return -1;
01308       }
01309    } else {
01310       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01311       if (unref) {
01312          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01313       }
01314       return -1;
01315    }
01316 
01317    ao2_t_link(mohclasses, moh, "Adding class to container");
01318 
01319    if (unref) {
01320       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01321    }
01322 
01323    return 0;
01324 }
01325 
01326 static void local_ast_moh_cleanup(struct ast_channel *chan)
01327 {
01328    struct moh_files_state *state = ast_channel_music_state(chan);
01329 
01330    if (state) {
01331       if (state->class) {
01332          /* This should never happen.  We likely just leaked some resource. */
01333          state->class =
01334             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01335          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01336       }
01337       ast_free(ast_channel_music_state(chan));
01338       ast_channel_music_state_set(chan, NULL);
01339       /* Only held a module reference if we had a music state */
01340       ast_module_unref(ast_module_info->self);
01341    }
01342 }
01343 
01344 static void moh_class_destructor(void *obj);
01345 
01346 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01347 
01348 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01349 {
01350    struct mohclass *class;
01351 
01352    if ((class =
01353 #ifdef REF_DEBUG
01354          __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01355             AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
01356 #elif defined(__AST_DEBUG_MALLOC)
01357          __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01358             AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
01359 #else
01360          ao2_alloc(sizeof(*class), moh_class_destructor)
01361 #endif
01362       )) {
01363       ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01364       class->srcfd = -1;
01365    }
01366 
01367    return class;
01368 }
01369 
01370 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01371 {
01372    struct mohclass *mohclass = NULL;
01373    struct moh_files_state *state = ast_channel_music_state(chan);
01374    struct ast_variable *var = NULL;
01375    int res;
01376    int realtime_possible = ast_check_realtime("musiconhold");
01377 
01378    /* The following is the order of preference for which class to use:
01379     * 1) The channels explicitly set musicclass, which should *only* be
01380     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01381     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01382     *    result of receiving a HOLD control frame, this should be the
01383     *    payload that came with the frame.
01384     * 3) The interpclass argument. This would be from the mohinterpret
01385     *    option from channel drivers. This is the same as the old musicclass
01386     *    option.
01387     * 4) The default class.
01388     */
01389    if (!ast_strlen_zero(ast_channel_musicclass(chan))) {
01390       mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
01391       if (!mohclass && realtime_possible) {
01392          var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), SENTINEL);
01393       }
01394    }
01395    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01396       mohclass = get_mohbyname(mclass, 1, 0);
01397       if (!mohclass && realtime_possible) {
01398          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01399       }
01400    }
01401    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01402       mohclass = get_mohbyname(interpclass, 1, 0);
01403       if (!mohclass && realtime_possible) {
01404          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01405       }
01406    }
01407 
01408    if (!mohclass && !var) {
01409       mohclass = get_mohbyname("default", 1, 0);
01410       if (!mohclass && realtime_possible) {
01411          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01412       }
01413    }
01414 
01415    /* If no moh class found in memory, then check RT. Note that the logic used
01416     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01417     */
01418    if (var) {
01419       struct ast_variable *tmp = NULL;
01420 
01421       if ((mohclass = moh_class_malloc())) {
01422          mohclass->realtime = 1;
01423          for (tmp = var; tmp; tmp = tmp->next) {
01424             if (!strcasecmp(tmp->name, "name"))
01425                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01426             else if (!strcasecmp(tmp->name, "mode"))
01427                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01428             else if (!strcasecmp(tmp->name, "directory"))
01429                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01430             else if (!strcasecmp(tmp->name, "application"))
01431                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01432             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01433                mohclass->digit = *tmp->value;
01434             else if (!strcasecmp(tmp->name, "random"))
01435                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01436             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01437                ast_set_flag(mohclass, MOH_RANDOMIZE);
01438             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01439                ast_set_flag(mohclass, MOH_SORTALPHA);
01440             else if (!strcasecmp(tmp->name, "format")) {
01441                ast_getformatbyname(tmp->value, &mohclass->format);
01442                if (!mohclass->format.id) {
01443                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01444                   ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
01445                }
01446             }
01447          }
01448          ast_variables_destroy(var);
01449          if (ast_strlen_zero(mohclass->dir)) {
01450             if (!strcasecmp(mohclass->mode, "custom")) {
01451                strcpy(mohclass->dir, "nodir");
01452             } else {
01453                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01454                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01455                return -1;
01456             }
01457          }
01458          if (ast_strlen_zero(mohclass->mode)) {
01459             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01460             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01461             return -1;
01462          }
01463          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01464             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01465             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01466             return -1;
01467          }
01468 
01469          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01470             /* CACHERTCLASSES enabled, let's add this class to default tree */
01471             if (state && state->class) {
01472                /* Class already exist for this channel */
01473                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01474             }
01475             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01476              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01477              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01478              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01479              * invalid memory.
01480              */
01481             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01482                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01483                return -1;
01484             }
01485          } else {
01486             /* We don't register RT moh class, so let's init it manualy */
01487 
01488             time(&mohclass->start);
01489             mohclass->start -= respawn_time;
01490 
01491             if (!strcasecmp(mohclass->mode, "files")) {
01492                if (!moh_scan_files(mohclass)) {
01493                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01494                   return -1;
01495                }
01496                if (strchr(mohclass->args, 'r'))
01497                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01498             } 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")) {
01499 
01500                if (!strcasecmp(mohclass->mode, "custom"))
01501                   ast_set_flag(mohclass, MOH_CUSTOM);
01502                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01503                   ast_set_flag(mohclass, MOH_SINGLE);
01504                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01505                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01506                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01507                   ast_set_flag(mohclass, MOH_QUIET);
01508 
01509                mohclass->srcfd = -1;
01510                if (!(mohclass->timer = ast_timer_open())) {
01511                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01512                }
01513                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01514                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01515                   ast_timer_close(mohclass->timer);
01516                   mohclass->timer = NULL;
01517                }
01518 
01519                /* Let's check if this channel already had a moh class before */
01520                if (state && state->class) {
01521                   /* Class already exist for this channel */
01522                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01523                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01524                      /* we found RT class with the same name, seems like we should continue playing existing one */
01525                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01526                      mohclass = mohclass_ref(state->class, "using existing class from state");
01527                   }
01528                } else {
01529                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01530                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01531                      if (mohclass->timer) {
01532                         ast_timer_close(mohclass->timer);
01533                         mohclass->timer = NULL;
01534                      }
01535                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01536                      return -1;
01537                   }
01538                }
01539             } else {
01540                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01541                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01542                return -1;
01543             }
01544          }
01545       } else {
01546          ast_variables_destroy(var);
01547          var = NULL;
01548       }
01549    }
01550 
01551    if (!mohclass) {
01552       return -1;
01553    }
01554 
01555    /* If we are using a cached realtime class with files, re-scan the files */
01556    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01557       if (!moh_scan_files(mohclass)) {
01558          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01559          return -1;
01560       }
01561    }
01562 
01563    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01564       "State: Start\r\n"
01565       "Channel: %s\r\n"
01566       "UniqueID: %s\r\n"
01567       "Class: %s\r\n",
01568       ast_channel_name(chan), ast_channel_uniqueid(chan),
01569       mohclass->name);
01570 
01571    ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
01572 
01573    if (mohclass->total_files) {
01574       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01575    } else {
01576       res = ast_activate_generator(chan, &mohgen, mohclass);
01577    }
01578 
01579    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01580 
01581    return res;
01582 }
01583 
01584 static void local_ast_moh_stop(struct ast_channel *chan)
01585 {
01586    ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
01587    ast_deactivate_generator(chan);
01588 
01589    ast_channel_lock(chan);
01590    if (ast_channel_music_state(chan)) {
01591       if (ast_channel_stream(chan)) {
01592          ast_closestream(ast_channel_stream(chan));
01593          ast_channel_stream_set(chan, NULL);
01594       }
01595    }
01596 
01597    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01598       "State: Stop\r\n"
01599       "Channel: %s\r\n"
01600       "UniqueID: %s\r\n",
01601       ast_channel_name(chan), ast_channel_uniqueid(chan));
01602    ast_channel_unlock(chan);
01603 }
01604 
01605 static void moh_class_destructor(void *obj)
01606 {
01607    struct mohclass *class = obj;
01608    struct mohdata *member;
01609    pthread_t tid = 0;
01610 
01611    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01612 
01613    ao2_lock(class);
01614    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01615       free(member);
01616    }
01617    ao2_unlock(class);
01618 
01619    /* Kill the thread first, so it cannot restart the child process while the
01620     * class is being destroyed */
01621    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01622       tid = class->thread;
01623       class->thread = AST_PTHREADT_NULL;
01624       pthread_cancel(tid);
01625       /* We'll collect the exit status later, after we ensure all the readers
01626        * are dead. */
01627    }
01628 
01629    if (class->pid > 1) {
01630       char buff[8192];
01631       int bytes, tbytes = 0, stime = 0, pid = 0;
01632 
01633       ast_debug(1, "killing %d!\n", class->pid);
01634 
01635       stime = time(NULL) + 2;
01636       pid = class->pid;
01637       class->pid = 0;
01638 
01639       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01640        * to give the process a reason and time enough to kill off its
01641        * children. */
01642       do {
01643          if (killpg(pid, SIGHUP) < 0) {
01644             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01645          }
01646          usleep(100000);
01647          if (killpg(pid, SIGTERM) < 0) {
01648             if (errno == ESRCH) {
01649                break;
01650             }
01651             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01652          }
01653          usleep(100000);
01654          if (killpg(pid, SIGKILL) < 0) {
01655             if (errno == ESRCH) {
01656                break;
01657             }
01658             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01659          }
01660       } while (0);
01661 
01662       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01663             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01664          tbytes = tbytes + bytes;
01665       }
01666 
01667       ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01668 
01669       close(class->srcfd);
01670       class->srcfd = -1;
01671    }
01672 
01673    if (class->filearray) {
01674       int i;
01675       for (i = 0; i < class->total_files; i++) {
01676          free(class->filearray[i]);
01677       }
01678       free(class->filearray);
01679       class->filearray = NULL;
01680    }
01681 
01682    if (class->timer) {
01683       ast_timer_close(class->timer);
01684       class->timer = NULL;
01685    }
01686 
01687    /* Finally, collect the exit status of the monitor thread */
01688    if (tid > 0) {
01689       pthread_join(tid, NULL);
01690    }
01691 
01692 }
01693 
01694 static int moh_class_mark(void *obj, void *arg, int flags)
01695 {
01696    struct mohclass *class = obj;
01697 
01698    class->delete = 1;
01699 
01700    return 0;
01701 }
01702 
01703 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01704 {
01705    struct mohclass *class = obj;
01706 
01707    return class->delete ? CMP_MATCH : 0;
01708 }
01709 
01710 static int load_moh_classes(int reload)
01711 {
01712    struct ast_config *cfg;
01713    struct ast_variable *var;
01714    struct mohclass *class; 
01715    char *cat;
01716    int numclasses = 0;
01717    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01718 
01719    cfg = ast_config_load("musiconhold.conf", config_flags);
01720 
01721    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01722       if (ast_check_realtime("musiconhold") && reload) {
01723          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01724          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01725       }
01726       return 0;
01727    }
01728    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01729       moh_rescan_files();
01730       return 0;
01731    }
01732 
01733    if (reload) {
01734       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01735    }
01736 
01737    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01738 
01739    cat = ast_category_browse(cfg, NULL);
01740    for (; cat; cat = ast_category_browse(cfg, cat)) {
01741       /* Setup common options from [general] section */
01742       if (!strcasecmp(cat, "general")) {
01743          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01744             if (!strcasecmp(var->name, "cachertclasses")) {
01745                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01746             } else {
01747                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01748             }
01749          }
01750       }
01751       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01752       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01753             !strcasecmp(cat, "general")) {
01754          continue;
01755       }
01756 
01757       if (!(class = moh_class_malloc())) {
01758          break;
01759       }
01760 
01761       ast_copy_string(class->name, cat, sizeof(class->name));
01762       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01763          if (!strcasecmp(var->name, "mode")) {
01764             ast_copy_string(class->mode, var->value, sizeof(class->mode));
01765          } else if (!strcasecmp(var->name, "directory")) {
01766             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01767          } else if (!strcasecmp(var->name, "application")) {
01768             ast_copy_string(class->args, var->value, sizeof(class->args));
01769          } else if (!strcasecmp(var->name, "announcement")) {
01770             ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
01771             ast_set_flag(class, MOH_ANNOUNCEMENT);
01772          } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
01773             class->digit = *var->value;
01774          } else if (!strcasecmp(var->name, "random")) {
01775             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01776          } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
01777             ast_set_flag(class, MOH_RANDOMIZE);
01778          } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
01779             ast_set_flag(class, MOH_SORTALPHA);
01780          } else if (!strcasecmp(var->name, "format")) {
01781             ast_getformatbyname(var->value, &class->format);
01782             if (!class->format.id) {
01783                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01784                ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01785             }
01786          }
01787       }
01788 
01789       if (ast_strlen_zero(class->dir)) {
01790          if (!strcasecmp(class->mode, "custom")) {
01791             strcpy(class->dir, "nodir");
01792          } else {
01793             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01794             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01795             continue;
01796          }
01797       }
01798       if (ast_strlen_zero(class->mode)) {
01799          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01800          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01801          continue;
01802       }
01803       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01804          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01805          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01806          continue;
01807       }
01808 
01809       /* Don't leak a class when it's already registered */
01810       if (!moh_register(class, reload, HANDLE_REF)) {
01811          numclasses++;
01812       }
01813    }
01814 
01815    ast_config_destroy(cfg);
01816 
01817    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01818          moh_classes_delete_marked, NULL, "Purge marked classes");
01819 
01820    return numclasses;
01821 }
01822 
01823 static void ast_moh_destroy(void)
01824 {
01825    ast_verb(2, "Destroying musiconhold processes\n");
01826    if (mohclasses) {
01827       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01828       ao2_ref(mohclasses, -1);
01829       mohclasses = NULL;
01830    }
01831 }
01832 
01833 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01834 {
01835    switch (cmd) {
01836    case CLI_INIT:
01837       e->command = "moh reload";
01838       e->usage =
01839          "Usage: moh reload\n"
01840          "       Reloads the MusicOnHold module.\n"
01841          "       Alias for 'module reload res_musiconhold.so'\n";
01842       return NULL;
01843    case CLI_GENERATE:
01844       return NULL;
01845    }
01846 
01847    if (a->argc != e->args)
01848       return CLI_SHOWUSAGE;
01849 
01850    reload();
01851 
01852    return CLI_SUCCESS;
01853 }
01854 
01855 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01856 {
01857    struct mohclass *class;
01858    struct ao2_iterator i;
01859 
01860    switch (cmd) {
01861    case CLI_INIT:
01862       e->command = "moh show files";
01863       e->usage =
01864          "Usage: moh show files\n"
01865          "       Lists all loaded file-based MusicOnHold classes and their\n"
01866          "       files.\n";
01867       return NULL;
01868    case CLI_GENERATE:
01869       return NULL;
01870    }
01871 
01872    if (a->argc != e->args)
01873       return CLI_SHOWUSAGE;
01874 
01875    i = ao2_iterator_init(mohclasses, 0);
01876    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01877       int x;
01878 
01879       if (!class->total_files) {
01880          continue;
01881       }
01882 
01883       ast_cli(a->fd, "Class: %s\n", class->name);
01884       for (x = 0; x < class->total_files; x++) {
01885          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01886       }
01887    }
01888    ao2_iterator_destroy(&i);
01889 
01890    return CLI_SUCCESS;
01891 }
01892 
01893 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01894 {
01895    struct mohclass *class;
01896    struct ao2_iterator i;
01897 
01898    switch (cmd) {
01899    case CLI_INIT:
01900       e->command = "moh show classes";
01901       e->usage =
01902          "Usage: moh show classes\n"
01903          "       Lists all MusicOnHold classes.\n";
01904       return NULL;
01905    case CLI_GENERATE:
01906       return NULL;
01907    }
01908 
01909    if (a->argc != e->args)
01910       return CLI_SHOWUSAGE;
01911 
01912    i = ao2_iterator_init(mohclasses, 0);
01913    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01914       ast_cli(a->fd, "Class: %s\n", class->name);
01915       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01916       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01917       if (ast_test_flag(class, MOH_CUSTOM)) {
01918          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01919       }
01920       if (strcasecmp(class->mode, "files")) {
01921          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(&class->format));
01922       }
01923    }
01924    ao2_iterator_destroy(&i);
01925 
01926    return CLI_SUCCESS;
01927 }
01928 
01929 static struct ast_cli_entry cli_moh[] = {
01930    AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
01931    AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01932    AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
01933 };
01934 
01935 static int moh_class_hash(const void *obj, const int flags)
01936 {
01937    const struct mohclass *class = obj;
01938 
01939    return ast_str_case_hash(class->name);
01940 }
01941 
01942 static int moh_class_cmp(void *obj, void *arg, int flags)
01943 {
01944    struct mohclass *class = obj, *class2 = arg;
01945 
01946    return strcasecmp(class->name, class2->name) ? 0 :
01947       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01948       CMP_MATCH | CMP_STOP;
01949 }
01950 
01951 static int load_module(void)
01952 {
01953    int res;
01954 
01955    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01956       return AST_MODULE_LOAD_DECLINE;
01957    }
01958 
01959    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01960       ast_log(LOG_WARNING, "No music on hold classes configured, "
01961             "disabling music on hold.\n");
01962    } else {
01963       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01964             local_ast_moh_cleanup);
01965    }
01966 
01967    res = ast_register_application_xml(play_moh, play_moh_exec);
01968    ast_register_atexit(ast_moh_destroy);
01969    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01970    if (!res)
01971       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01972    if (!res)
01973       res = ast_register_application_xml(set_moh, set_moh_exec);
01974    if (!res)
01975       res = ast_register_application_xml(start_moh, start_moh_exec);
01976    if (!res)
01977       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01978 
01979    return AST_MODULE_LOAD_SUCCESS;
01980 }
01981 
01982 static int reload(void)
01983 {
01984    if (load_moh_classes(1)) {
01985       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01986             local_ast_moh_cleanup);
01987    }
01988 
01989    return AST_MODULE_LOAD_SUCCESS;
01990 }
01991 
01992 static int moh_class_inuse(void *obj, void *arg, int flags)
01993 {
01994    struct mohclass *class = obj;
01995 
01996    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01997 }
01998 
01999 static int unload_module(void)
02000 {
02001    int res = 0;
02002    struct mohclass *class = NULL;
02003 
02004    /* XXX This check shouldn't be required if module ref counting was being used
02005     * properly ... */
02006    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
02007       class = mohclass_unref(class, "unref of class from module unload callback");
02008       res = -1;
02009    }
02010 
02011    if (res < 0) {
02012       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
02013       return res;
02014    }
02015 
02016    ast_uninstall_music_functions();
02017 
02018    ast_moh_destroy();
02019    res = ast_unregister_application(play_moh);
02020    res |= ast_unregister_application(wait_moh);
02021    res |= ast_unregister_application(set_moh);
02022    res |= ast_unregister_application(start_moh);
02023    res |= ast_unregister_application(stop_moh);
02024    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
02025    ast_unregister_atexit(ast_moh_destroy);
02026 
02027    return res;
02028 }
02029 
02030 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
02031    .load = load_module,
02032    .unload = unload_module,
02033    .reload = reload,
02034    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
02035 );