Mon Oct 8 12:39:04 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: 366880 $")
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             continue;
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       closedir(files_DIR);
01069       return -1;
01070    }
01071    if (chdir(dir_path) < 0) {
01072       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01073       closedir(files_DIR);
01074       return -1;
01075    }
01076    while ((files_dirent = readdir(files_DIR))) {
01077       /* The file name must be at least long enough to have the file type extension */
01078       if ((strlen(files_dirent->d_name) < 4))
01079          continue;
01080 
01081       /* Skip files that starts with a dot */
01082       if (files_dirent->d_name[0] == '.')
01083          continue;
01084 
01085       /* Skip files without extensions... they are not audio */
01086       if (!strchr(files_dirent->d_name, '.'))
01087          continue;
01088 
01089       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01090 
01091       if (stat(filepath, &statbuf))
01092          continue;
01093 
01094       if (!S_ISREG(statbuf.st_mode))
01095          continue;
01096 
01097       if ((ext = strrchr(filepath, '.')))
01098          *ext = '\0';
01099 
01100       /* if the file is present in multiple formats, ensure we only put it into the list once */
01101       for (i = 0; i < class->total_files; i++)
01102          if (!strcmp(filepath, class->filearray[i]))
01103             break;
01104 
01105       if (i == class->total_files) {
01106          if (moh_add_file(class, filepath))
01107             break;
01108       }
01109    }
01110 
01111    closedir(files_DIR);
01112    if (chdir(path) < 0) {
01113       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01114       return -1;
01115    }
01116    if (ast_test_flag(class, MOH_SORTALPHA))
01117       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01118    return class->total_files;
01119 }
01120 
01121 static int init_files_class(struct mohclass *class)
01122 {
01123    int res;
01124 
01125    res = moh_scan_files(class);
01126 
01127    if (res < 0) {
01128       return -1;
01129    }
01130 
01131    if (!res) {
01132       if (option_verbose > 2) {
01133          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01134                class->dir, class->name);
01135       }
01136       return -1;
01137    }
01138 
01139 #if 0
01140    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01141    if (strchr(class->args, 'r')) {
01142       ast_set_flag(class, MOH_RANDOMIZE);
01143    }
01144 #endif
01145 
01146    return 0;
01147 }
01148 
01149 static void moh_rescan_files(void) {
01150    struct ao2_iterator i;
01151    struct mohclass *c;
01152 
01153    i = ao2_iterator_init(mohclasses, 0);
01154 
01155    while ((c = ao2_iterator_next(&i))) {
01156       if (!strcasecmp(c->mode, "files")) {
01157          moh_scan_files(c);
01158       }
01159       ao2_ref(c, -1);
01160    }
01161 
01162    ao2_iterator_destroy(&i);
01163 }
01164 
01165 static int moh_diff(struct mohclass *old, struct mohclass *new)
01166 {
01167    if (!old || !new) {
01168       return -1;
01169    }
01170 
01171    if (strcmp(old->dir, new->dir)) {
01172       return -1;
01173    } else if (strcmp(old->mode, new->mode)) {
01174       return -1;
01175    } else if (strcmp(old->args, new->args)) {
01176       return -1;
01177    } else if (old->flags != new->flags) {
01178       return -1;
01179    }
01180 
01181    return 0;
01182 }
01183 
01184 static int init_app_class(struct mohclass *class)
01185 {
01186    if (!strcasecmp(class->mode, "custom")) {
01187       ast_set_flag(class, MOH_CUSTOM);
01188    } else if (!strcasecmp(class->mode, "mp3nb")) {
01189       ast_set_flag(class, MOH_SINGLE);
01190    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01191       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01192    } else if (!strcasecmp(class->mode, "quietmp3")) {
01193       ast_set_flag(class, MOH_QUIET);
01194    }
01195 
01196    class->srcfd = -1;
01197 
01198    if (!(class->timer = ast_timer_open())) {
01199       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01200       return -1;
01201    }
01202    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01203       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01204       ast_timer_close(class->timer);
01205       class->timer = NULL;
01206    }
01207 
01208    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01209       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01210       if (class->timer) {
01211          ast_timer_close(class->timer);
01212          class->timer = NULL;
01213       }
01214       return -1;
01215    }
01216 
01217    return 0;
01218 }
01219 
01220 /*!
01221  * \note This function owns the reference it gets to moh if unref is true
01222  */
01223 #define moh_register(a,b,c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01224 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01225 {
01226    struct mohclass *mohclass = NULL;
01227 
01228    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01229 
01230    if (mohclass && !moh_diff(mohclass, moh)) {
01231       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01232       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01233       if (unref) {
01234          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01235       }
01236       return -1;
01237    } else if (mohclass) {
01238       /* Found a class, but it's different from the one being registered */
01239       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01240    }
01241 
01242    time(&moh->start);
01243    moh->start -= respawn_time;
01244 
01245    if (!strcasecmp(moh->mode, "files")) {
01246       if (init_files_class(moh)) {
01247          if (unref) {
01248             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01249          }
01250          return -1;
01251       }
01252    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01253          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01254          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01255       if (init_app_class(moh)) {
01256          if (unref) {
01257             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01258          }
01259          return -1;
01260       }
01261    } else {
01262       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01263       if (unref) {
01264          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01265       }
01266       return -1;
01267    }
01268 
01269    ao2_t_link(mohclasses, moh, "Adding class to container");
01270 
01271    if (unref) {
01272       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01273    }
01274 
01275    return 0;
01276 }
01277 
01278 static void local_ast_moh_cleanup(struct ast_channel *chan)
01279 {
01280    struct moh_files_state *state = chan->music_state;
01281 
01282    if (state) {
01283       if (state->class) {
01284          /* This should never happen.  We likely just leaked some resource. */
01285          state->class =
01286             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01287          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01288       }
01289       ast_free(chan->music_state);
01290       chan->music_state = NULL;
01291       /* Only held a module reference if we had a music state */
01292       ast_module_unref(ast_module_info->self);
01293    }
01294 }
01295 
01296 static void moh_class_destructor(void *obj);
01297 
01298 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01299 
01300 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01301 {
01302    struct mohclass *class;
01303 
01304    if ((class =
01305 #ifdef REF_DEBUG
01306          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01307 #elif defined(__AST_DEBUG_MALLOC)
01308          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01309 #else
01310          ao2_alloc(sizeof(*class), moh_class_destructor)
01311 #endif
01312       )) {
01313       class->format = AST_FORMAT_SLINEAR;
01314       class->srcfd = -1;
01315    }
01316 
01317    return class;
01318 }
01319 
01320 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01321 {
01322    struct mohclass *mohclass = NULL;
01323    struct moh_files_state *state = chan->music_state;
01324    struct ast_variable *var = NULL;
01325    int res;
01326    int realtime_possible = ast_check_realtime("musiconhold");
01327 
01328    /* The following is the order of preference for which class to use:
01329     * 1) The channels explicitly set musicclass, which should *only* be
01330     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01331     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01332     *    result of receiving a HOLD control frame, this should be the
01333     *    payload that came with the frame.
01334     * 3) The interpclass argument. This would be from the mohinterpret
01335     *    option from channel drivers. This is the same as the old musicclass
01336     *    option.
01337     * 4) The default class.
01338     */
01339    if (!ast_strlen_zero(chan->musicclass)) {
01340       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01341       if (!mohclass && realtime_possible) {
01342          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01343       }
01344    }
01345    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01346       mohclass = get_mohbyname(mclass, 1, 0);
01347       if (!mohclass && realtime_possible) {
01348          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01349       }
01350    }
01351    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01352       mohclass = get_mohbyname(interpclass, 1, 0);
01353       if (!mohclass && realtime_possible) {
01354          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01355       }
01356    }
01357 
01358    if (!mohclass && !var) {
01359       mohclass = get_mohbyname("default", 1, 0);
01360       if (!mohclass && realtime_possible) {
01361          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01362       }
01363    }
01364 
01365    /* If no moh class found in memory, then check RT. Note that the logic used
01366     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01367     */
01368    if (var) {
01369       struct ast_variable *tmp = NULL;
01370 
01371       if ((mohclass = moh_class_malloc())) {
01372          mohclass->realtime = 1;
01373          for (tmp = var; tmp; tmp = tmp->next) {
01374             if (!strcasecmp(tmp->name, "name"))
01375                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01376             else if (!strcasecmp(tmp->name, "mode"))
01377                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01378             else if (!strcasecmp(tmp->name, "directory"))
01379                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01380             else if (!strcasecmp(tmp->name, "application"))
01381                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01382             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01383                mohclass->digit = *tmp->value;
01384             else if (!strcasecmp(tmp->name, "random"))
01385                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01386             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01387                ast_set_flag(mohclass, MOH_RANDOMIZE);
01388             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01389                ast_set_flag(mohclass, MOH_SORTALPHA);
01390             else if (!strcasecmp(tmp->name, "format")) {
01391                mohclass->format = ast_getformatbyname(tmp->value);
01392                if (!mohclass->format) {
01393                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01394                   mohclass->format = AST_FORMAT_SLINEAR;
01395                }
01396             }
01397          }
01398          ast_variables_destroy(var);
01399          if (ast_strlen_zero(mohclass->dir)) {
01400             if (!strcasecmp(mohclass->mode, "custom")) {
01401                strcpy(mohclass->dir, "nodir");
01402             } else {
01403                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01404                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01405                return -1;
01406             }
01407          }
01408          if (ast_strlen_zero(mohclass->mode)) {
01409             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01410             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01411             return -1;
01412          }
01413          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01414             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01415             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01416             return -1;
01417          }
01418 
01419          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01420             /* CACHERTCLASSES enabled, let's add this class to default tree */
01421             if (state && state->class) {
01422                /* Class already exist for this channel */
01423                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01424                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01425                   /* we found RT class with the same name, seems like we should continue playing existing one */
01426                   /* XXX This code is impossible to reach */
01427                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01428                   mohclass = state->class;
01429                }
01430             }
01431             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01432              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01433              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01434              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01435              * invalid memory.
01436              */
01437             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01438                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01439                return -1;
01440             }
01441          } else {
01442             /* We don't register RT moh class, so let's init it manualy */
01443 
01444             time(&mohclass->start);
01445             mohclass->start -= respawn_time;
01446 
01447             if (!strcasecmp(mohclass->mode, "files")) {
01448                if (!moh_scan_files(mohclass)) {
01449                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01450                   return -1;
01451                }
01452                if (strchr(mohclass->args, 'r'))
01453                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01454             } 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")) {
01455 
01456                if (!strcasecmp(mohclass->mode, "custom"))
01457                   ast_set_flag(mohclass, MOH_CUSTOM);
01458                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01459                   ast_set_flag(mohclass, MOH_SINGLE);
01460                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01461                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01462                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01463                   ast_set_flag(mohclass, MOH_QUIET);
01464 
01465                mohclass->srcfd = -1;
01466                if (!(mohclass->timer = ast_timer_open())) {
01467                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01468                }
01469                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01470                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01471                   ast_timer_close(mohclass->timer);
01472                   mohclass->timer = NULL;
01473                }
01474 
01475                /* Let's check if this channel already had a moh class before */
01476                if (state && state->class) {
01477                   /* Class already exist for this channel */
01478                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01479                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01480                      /* we found RT class with the same name, seems like we should continue playing existing one */
01481                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01482                      mohclass = state->class;
01483                   }
01484                } else {
01485                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01486                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01487                      if (mohclass->timer) {
01488                         ast_timer_close(mohclass->timer);
01489                         mohclass->timer = NULL;
01490                      }
01491                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01492                      return -1;
01493                   }
01494                }
01495             } else {
01496                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01497                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01498                return -1;
01499             }
01500          }
01501       } else {
01502          ast_variables_destroy(var);
01503          var = NULL;
01504       }
01505    }
01506 
01507    if (!mohclass) {
01508       return -1;
01509    }
01510 
01511    /* If we are using a cached realtime class with files, re-scan the files */
01512    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01513       if (!moh_scan_files(mohclass)) {
01514          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01515          return -1;
01516       }
01517    }
01518 
01519    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01520       "State: Start\r\n"
01521       "Channel: %s\r\n"
01522       "UniqueID: %s\r\n"
01523       "Class: %s\r\n",
01524       chan->name, chan->uniqueid,
01525       mohclass->name);
01526 
01527    ast_set_flag(chan, AST_FLAG_MOH);
01528 
01529    if (mohclass->total_files) {
01530       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01531    } else {
01532       res = ast_activate_generator(chan, &mohgen, mohclass);
01533    }
01534 
01535    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01536 
01537    return res;
01538 }
01539 
01540 static void local_ast_moh_stop(struct ast_channel *chan)
01541 {
01542    ast_clear_flag(chan, AST_FLAG_MOH);
01543    ast_deactivate_generator(chan);
01544 
01545    ast_channel_lock(chan);
01546    if (chan->music_state) {
01547       if (chan->stream) {
01548          ast_closestream(chan->stream);
01549          chan->stream = NULL;
01550       }
01551    }
01552 
01553    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01554       "State: Stop\r\n"
01555       "Channel: %s\r\n"
01556       "UniqueID: %s\r\n",
01557       chan->name, chan->uniqueid);
01558    ast_channel_unlock(chan);
01559 }
01560 
01561 static void moh_class_destructor(void *obj)
01562 {
01563    struct mohclass *class = obj;
01564    struct mohdata *member;
01565    pthread_t tid = 0;
01566 
01567    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01568 
01569    ao2_lock(class);
01570    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01571       free(member);
01572    }
01573    ao2_unlock(class);
01574 
01575    /* Kill the thread first, so it cannot restart the child process while the
01576     * class is being destroyed */
01577    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01578       tid = class->thread;
01579       class->thread = AST_PTHREADT_NULL;
01580       pthread_cancel(tid);
01581       /* We'll collect the exit status later, after we ensure all the readers
01582        * are dead. */
01583    }
01584 
01585    if (class->pid > 1) {
01586       char buff[8192];
01587       int bytes, tbytes = 0, stime = 0, pid = 0;
01588 
01589       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01590 
01591       stime = time(NULL) + 2;
01592       pid = class->pid;
01593       class->pid = 0;
01594 
01595       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01596        * to give the process a reason and time enough to kill off its
01597        * children. */
01598       do {
01599          if (killpg(pid, SIGHUP) < 0) {
01600             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01601          }
01602          usleep(100000);
01603          if (killpg(pid, SIGTERM) < 0) {
01604             if (errno == ESRCH) {
01605                break;
01606             }
01607             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01608          }
01609          usleep(100000);
01610          if (killpg(pid, SIGKILL) < 0) {
01611             if (errno == ESRCH) {
01612                break;
01613             }
01614             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01615          }
01616       } while (0);
01617 
01618       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01619             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01620          tbytes = tbytes + bytes;
01621       }
01622 
01623       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01624 
01625       close(class->srcfd);
01626       class->srcfd = -1;
01627    }
01628 
01629    if (class->filearray) {
01630       int i;
01631       for (i = 0; i < class->total_files; i++) {
01632          free(class->filearray[i]);
01633       }
01634       free(class->filearray);
01635       class->filearray = NULL;
01636    }
01637 
01638    if (class->timer) {
01639       ast_timer_close(class->timer);
01640       class->timer = NULL;
01641    }
01642 
01643    /* Finally, collect the exit status of the monitor thread */
01644    if (tid > 0) {
01645       pthread_join(tid, NULL);
01646    }
01647 
01648 }
01649 
01650 static int moh_class_mark(void *obj, void *arg, int flags)
01651 {
01652    struct mohclass *class = obj;
01653 
01654    class->delete = 1;
01655 
01656    return 0;
01657 }
01658 
01659 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01660 {
01661    struct mohclass *class = obj;
01662 
01663    return class->delete ? CMP_MATCH : 0;
01664 }
01665 
01666 static int load_moh_classes(int reload)
01667 {
01668    struct ast_config *cfg;
01669    struct ast_variable *var;
01670    struct mohclass *class; 
01671    char *cat;
01672    int numclasses = 0;
01673    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01674 
01675    cfg = ast_config_load("musiconhold.conf", config_flags);
01676 
01677    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01678       if (ast_check_realtime("musiconhold") && reload) {
01679          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01680          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01681       }
01682       return 0;
01683    }
01684    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01685       moh_rescan_files();
01686       return 0;
01687    }
01688 
01689    if (reload) {
01690       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01691    }
01692 
01693    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01694 
01695    cat = ast_category_browse(cfg, NULL);
01696    for (; cat; cat = ast_category_browse(cfg, cat)) {
01697       /* Setup common options from [general] section */
01698       if (!strcasecmp(cat, "general")) {
01699          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01700             if (!strcasecmp(var->name, "cachertclasses")) {
01701                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01702             } else {
01703                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01704             }
01705          }
01706       }
01707       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01708       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01709             !strcasecmp(cat, "general")) {
01710          continue;
01711       }
01712 
01713       if (!(class = moh_class_malloc())) {
01714          break;
01715       }
01716 
01717       ast_copy_string(class->name, cat, sizeof(class->name));  
01718       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01719          if (!strcasecmp(var->name, "mode"))
01720             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01721          else if (!strcasecmp(var->name, "directory"))
01722             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01723          else if (!strcasecmp(var->name, "application"))
01724             ast_copy_string(class->args, var->value, sizeof(class->args));
01725          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01726             class->digit = *var->value;
01727          else if (!strcasecmp(var->name, "random"))
01728             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01729          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01730             ast_set_flag(class, MOH_RANDOMIZE);
01731          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01732             ast_set_flag(class, MOH_SORTALPHA);
01733          else if (!strcasecmp(var->name, "format")) {
01734             class->format = ast_getformatbyname(var->value);
01735             if (!class->format) {
01736                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01737                class->format = AST_FORMAT_SLINEAR;
01738             }
01739          }
01740       }
01741 
01742       if (ast_strlen_zero(class->dir)) {
01743          if (!strcasecmp(class->mode, "custom")) {
01744             strcpy(class->dir, "nodir");
01745          } else {
01746             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01747             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01748             continue;
01749          }
01750       }
01751       if (ast_strlen_zero(class->mode)) {
01752          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01753          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01754          continue;
01755       }
01756       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01757          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01758          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01759          continue;
01760       }
01761 
01762       /* Don't leak a class when it's already registered */
01763       if (!moh_register(class, reload, HANDLE_REF)) {
01764          numclasses++;
01765       }
01766    }
01767 
01768    ast_config_destroy(cfg);
01769 
01770    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01771          moh_classes_delete_marked, NULL, "Purge marked classes");
01772 
01773    return numclasses;
01774 }
01775 
01776 static void ast_moh_destroy(void)
01777 {
01778    ast_verb(2, "Destroying musiconhold processes\n");
01779    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01780 }
01781 
01782 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01783 {
01784    switch (cmd) {
01785    case CLI_INIT:
01786       e->command = "moh reload";
01787       e->usage =
01788          "Usage: moh reload\n"
01789          "       Reloads the MusicOnHold module.\n"
01790          "       Alias for 'module reload res_musiconhold.so'\n";
01791       return NULL;
01792    case CLI_GENERATE:
01793       return NULL;
01794    }
01795 
01796    if (a->argc != e->args)
01797       return CLI_SHOWUSAGE;
01798 
01799    reload();
01800 
01801    return CLI_SUCCESS;
01802 }
01803 
01804 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01805 {
01806    struct mohclass *class;
01807    struct ao2_iterator i;
01808 
01809    switch (cmd) {
01810    case CLI_INIT:
01811       e->command = "moh show files";
01812       e->usage =
01813          "Usage: moh show files\n"
01814          "       Lists all loaded file-based MusicOnHold classes and their\n"
01815          "       files.\n";
01816       return NULL;
01817    case CLI_GENERATE:
01818       return NULL;
01819    }
01820 
01821    if (a->argc != e->args)
01822       return CLI_SHOWUSAGE;
01823 
01824    i = ao2_iterator_init(mohclasses, 0);
01825    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01826       int x;
01827 
01828       if (!class->total_files) {
01829          continue;
01830       }
01831 
01832       ast_cli(a->fd, "Class: %s\n", class->name);
01833       for (x = 0; x < class->total_files; x++) {
01834          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01835       }
01836    }
01837    ao2_iterator_destroy(&i);
01838 
01839    return CLI_SUCCESS;
01840 }
01841 
01842 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01843 {
01844    struct mohclass *class;
01845    struct ao2_iterator i;
01846 
01847    switch (cmd) {
01848    case CLI_INIT:
01849       e->command = "moh show classes";
01850       e->usage =
01851          "Usage: moh show classes\n"
01852          "       Lists all MusicOnHold classes.\n";
01853       return NULL;
01854    case CLI_GENERATE:
01855       return NULL;
01856    }
01857 
01858    if (a->argc != e->args)
01859       return CLI_SHOWUSAGE;
01860 
01861    i = ao2_iterator_init(mohclasses, 0);
01862    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01863       ast_cli(a->fd, "Class: %s\n", class->name);
01864       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01865       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01866       if (ast_test_flag(class, MOH_CUSTOM)) {
01867          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01868       }
01869       if (strcasecmp(class->mode, "files")) {
01870          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01871       }
01872    }
01873    ao2_iterator_destroy(&i);
01874 
01875    return CLI_SUCCESS;
01876 }
01877 
01878 static struct ast_cli_entry cli_moh[] = {
01879    AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
01880    AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01881    AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
01882 };
01883 
01884 static int moh_class_hash(const void *obj, const int flags)
01885 {
01886    const struct mohclass *class = obj;
01887 
01888    return ast_str_case_hash(class->name);
01889 }
01890 
01891 static int moh_class_cmp(void *obj, void *arg, int flags)
01892 {
01893    struct mohclass *class = obj, *class2 = arg;
01894 
01895    return strcasecmp(class->name, class2->name) ? 0 :
01896       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01897       CMP_MATCH | CMP_STOP;
01898 }
01899 
01900 static int load_module(void)
01901 {
01902    int res;
01903 
01904    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01905       return AST_MODULE_LOAD_DECLINE;
01906    }
01907 
01908    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01909       ast_log(LOG_WARNING, "No music on hold classes configured, "
01910             "disabling music on hold.\n");
01911    } else {
01912       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01913             local_ast_moh_cleanup);
01914    }
01915 
01916    res = ast_register_application_xml(play_moh, play_moh_exec);
01917    ast_register_atexit(ast_moh_destroy);
01918    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01919    if (!res)
01920       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01921    if (!res)
01922       res = ast_register_application_xml(set_moh, set_moh_exec);
01923    if (!res)
01924       res = ast_register_application_xml(start_moh, start_moh_exec);
01925    if (!res)
01926       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01927 
01928    return AST_MODULE_LOAD_SUCCESS;
01929 }
01930 
01931 static int reload(void)
01932 {
01933    if (load_moh_classes(1)) {
01934       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01935             local_ast_moh_cleanup);
01936    }
01937 
01938    return AST_MODULE_LOAD_SUCCESS;
01939 }
01940 
01941 static int moh_class_inuse(void *obj, void *arg, int flags)
01942 {
01943    struct mohclass *class = obj;
01944 
01945    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01946 }
01947 
01948 static int unload_module(void)
01949 {
01950    int res = 0;
01951    struct mohclass *class = NULL;
01952 
01953    /* XXX This check shouldn't be required if module ref counting was being used
01954     * properly ... */
01955    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01956       class = mohclass_unref(class, "unref of class from module unload callback");
01957       res = -1;
01958    }
01959 
01960    if (res < 0) {
01961       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01962       return res;
01963    }
01964 
01965    ast_uninstall_music_functions();
01966 
01967    ast_moh_destroy();
01968    res = ast_unregister_application(play_moh);
01969    res |= ast_unregister_application(wait_moh);
01970    res |= ast_unregister_application(set_moh);
01971    res |= ast_unregister_application(start_moh);
01972    res |= ast_unregister_application(stop_moh);
01973    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01974    ast_unregister_atexit(ast_moh_destroy);
01975 
01976    return res;
01977 }
01978 
01979 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
01980    .load = load_module,
01981    .unload = unload_module,
01982    .reload = reload,
01983    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
01984 );

Generated on Mon Oct 8 12:39:04 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7