Tue Aug 24 2010 19:41:32

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