Sat Mar 10 01:54:24 2012

Asterisk developer's documentation


res_musiconhold.c

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

Generated on Sat Mar 10 01:54:25 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7