Thu Apr 28 2011 17:15:25

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