Mon Apr 28 2014 10:05:46

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