Tue Aug 20 16:34:37 2013

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: 375893 $")
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             if (ast_timer_ack(class->timer, 1) < 0) {
00657                ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
00658                return NULL;
00659             }
00660             res = 320;
00661          } else {
00662             ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00663             res = 0;
00664          }
00665          pthread_testcancel();
00666       } else {
00667          long delta;
00668          /* Reliable sleep */
00669          tv_tmp = ast_tvnow();
00670          if (ast_tvzero(deadline))
00671             deadline = tv_tmp;
00672          delta = ast_tvdiff_ms(tv_tmp, deadline);
00673          if (delta < MOH_MS_INTERVAL) {   /* too early */
00674             deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00675             usleep(1000 * (MOH_MS_INTERVAL - delta));
00676             pthread_testcancel();
00677          } else {
00678             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00679             deadline = tv_tmp;
00680          }
00681          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00682       }
00683       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00684          continue;
00685       /* Read mp3 audio */
00686       len = ast_codec_get_len(class->format, res);
00687 
00688       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00689          if (!res2) {
00690             close(class->srcfd);
00691             class->srcfd = -1;
00692             pthread_testcancel();
00693             if (class->pid > 1) {
00694                do {
00695                   if (killpg(class->pid, SIGHUP) < 0) {
00696                      if (errno == ESRCH) {
00697                         break;
00698                      }
00699                      ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00700                   }
00701                   usleep(100000);
00702                   if (killpg(class->pid, SIGTERM) < 0) {
00703                      if (errno == ESRCH) {
00704                         break;
00705                      }
00706                      ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00707                   }
00708                   usleep(100000);
00709                   if (killpg(class->pid, SIGKILL) < 0) {
00710                      if (errno == ESRCH) {
00711                         break;
00712                      }
00713                      ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00714                   }
00715                } while (0);
00716                class->pid = 0;
00717             }
00718          } else {
00719             ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00720          }
00721          continue;
00722       }
00723 
00724       pthread_testcancel();
00725 
00726       ao2_lock(class);
00727       AST_LIST_TRAVERSE(&class->members, moh, list) {
00728          /* Write data */
00729          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00730             ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00731          }
00732       }
00733       ao2_unlock(class);
00734    }
00735    return NULL;
00736 }
00737 
00738 static int play_moh_exec(struct ast_channel *chan, const char *data)
00739 {
00740    char *parse;
00741    char *class;
00742    int timeout = -1;
00743    int res;
00744    AST_DECLARE_APP_ARGS(args,
00745       AST_APP_ARG(class);
00746       AST_APP_ARG(duration);
00747    );
00748 
00749    parse = ast_strdupa(data);
00750 
00751    AST_STANDARD_APP_ARGS(args, parse);
00752 
00753    if (!ast_strlen_zero(args.duration)) {
00754       if (sscanf(args.duration, "%30d", &timeout) == 1) {
00755          timeout *= 1000;
00756       } else {
00757          ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00758       }
00759    }
00760 
00761    class = S_OR(args.class, NULL);
00762    if (ast_moh_start(chan, class, NULL)) {
00763       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00764       return 0;
00765    }
00766 
00767    if (timeout > 0)
00768       res = ast_safe_sleep(chan, timeout);
00769    else {
00770       while (!(res = ast_safe_sleep(chan, 10000)));
00771    }
00772 
00773    ast_moh_stop(chan);
00774 
00775    return res;
00776 }
00777 
00778 static int wait_moh_exec(struct ast_channel *chan, const char *data)
00779 {
00780    static int deprecation_warning = 0;
00781    int res;
00782 
00783    if (!deprecation_warning) {
00784       deprecation_warning = 1;
00785       ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00786    }
00787 
00788    if (!data || !atoi(data)) {
00789       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00790       return -1;
00791    }
00792    if (ast_moh_start(chan, NULL, NULL)) {
00793       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00794       return 0;
00795    }
00796    res = ast_safe_sleep(chan, atoi(data) * 1000);
00797    ast_moh_stop(chan);
00798    return res;
00799 }
00800 
00801 static int set_moh_exec(struct ast_channel *chan, const char *data)
00802 {
00803    static int deprecation_warning = 0;
00804 
00805    if (!deprecation_warning) {
00806       deprecation_warning = 1;
00807       ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00808    }
00809 
00810    if (ast_strlen_zero(data)) {
00811       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00812       return -1;
00813    }
00814    ast_string_field_set(chan, musicclass, data);
00815    return 0;
00816 }
00817 
00818 static int start_moh_exec(struct ast_channel *chan, const char *data)
00819 {
00820    char *parse;
00821    char *class;
00822    AST_DECLARE_APP_ARGS(args,
00823       AST_APP_ARG(class);
00824    );
00825 
00826    parse = ast_strdupa(data);
00827 
00828    AST_STANDARD_APP_ARGS(args, parse);
00829 
00830    class = S_OR(args.class, NULL);
00831    if (ast_moh_start(chan, class, NULL)) 
00832       ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00833 
00834    return 0;
00835 }
00836 
00837 static int stop_moh_exec(struct ast_channel *chan, const char *data)
00838 {
00839    ast_moh_stop(chan);
00840 
00841    return 0;
00842 }
00843 
00844 #define get_mohbyname(a,b,c)  _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00845 
00846 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00847 {
00848    struct mohclass *moh = NULL;
00849    struct mohclass tmp_class = {
00850       .flags = 0,
00851    };
00852 
00853    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00854 
00855 #ifdef REF_DEBUG
00856    moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00857 #else
00858    moh = __ao2_find(mohclasses, &tmp_class, flags);
00859 #endif
00860 
00861    if (!moh && warn) {
00862       ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00863    }
00864 
00865    return moh;
00866 }
00867 
00868 static struct mohdata *mohalloc(struct mohclass *cl)
00869 {
00870    struct mohdata *moh;
00871    long flags; 
00872    
00873    if (!(moh = ast_calloc(1, sizeof(*moh))))
00874       return NULL;
00875    
00876    if (pipe(moh->pipe)) {
00877       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00878       ast_free(moh);
00879       return NULL;
00880    }
00881 
00882    /* Make entirely non-blocking */
00883    flags = fcntl(moh->pipe[0], F_GETFL);
00884    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00885    flags = fcntl(moh->pipe[1], F_GETFL);
00886    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00887 
00888    moh->f.frametype = AST_FRAME_VOICE;
00889    moh->f.subclass.codec = cl->format;
00890    moh->f.offset = AST_FRIENDLY_OFFSET;
00891 
00892    moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00893 
00894    ao2_lock(cl);
00895    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00896    ao2_unlock(cl);
00897    
00898    return moh;
00899 }
00900 
00901 static void moh_release(struct ast_channel *chan, void *data)
00902 {
00903    struct mohdata *moh = data;
00904    struct mohclass *class = moh->parent;
00905    format_t oldwfmt;
00906 
00907    ao2_lock(class);
00908    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00909    ao2_unlock(class);
00910    
00911    close(moh->pipe[0]);
00912    close(moh->pipe[1]);
00913 
00914    oldwfmt = moh->origwfmt;
00915 
00916    moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00917 
00918    ast_free(moh);
00919 
00920    if (chan) {
00921       struct moh_files_state *state;
00922 
00923       state = chan->music_state;
00924       if (state && state->class) {
00925          state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00926       }
00927       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00928          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00929                chan->name, ast_getformatname(oldwfmt));
00930       }
00931 
00932       ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00933    }
00934 }
00935 
00936 static void *moh_alloc(struct ast_channel *chan, void *params)
00937 {
00938    struct mohdata *res;
00939    struct mohclass *class = params;
00940    struct moh_files_state *state;
00941 
00942    /* Initiating music_state for current channel. Channel should know name of moh class */
00943    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00944       chan->music_state = state;
00945       ast_module_ref(ast_module_info->self);
00946    } else {
00947       state = chan->music_state;
00948       if (!state) {
00949          return NULL;
00950       }
00951       if (state->class) {
00952          mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00953          ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00954       }
00955       memset(state, 0, sizeof(*state));
00956    }
00957 
00958    if ((res = mohalloc(class))) {
00959       res->origwfmt = chan->writeformat;
00960       if (ast_set_write_format(chan, class->format)) {
00961          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00962          moh_release(NULL, res);
00963          res = NULL;
00964       } else {
00965          state->class = mohclass_ref(class, "Placing reference into state container");
00966       }
00967       ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00968    }
00969    return res;
00970 }
00971 
00972 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00973 {
00974    struct mohdata *moh = data;
00975    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00976    int res;
00977 
00978    len = ast_codec_get_len(moh->parent->format, samples);
00979 
00980    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00981       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00982       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00983    }
00984    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00985    if (res <= 0)
00986       return 0;
00987 
00988    moh->f.datalen = res;
00989    moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00990    moh->f.samples = ast_codec_get_samples(&moh->f);
00991 
00992    if (ast_write(chan, &moh->f) < 0) {
00993       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00994       return -1;
00995    }
00996 
00997    return 0;
00998 }
00999 
01000 static struct ast_generator mohgen = {
01001    .alloc    = moh_alloc,
01002    .release  = moh_release,
01003    .generate = moh_generate,
01004    .digit    = moh_handle_digit,
01005 };
01006 
01007 static int moh_add_file(struct mohclass *class, const char *filepath)
01008 {
01009    if (!class->allowed_files) {
01010       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
01011          return -1;
01012       class->allowed_files = INITIAL_NUM_FILES;
01013    } else if (class->total_files == class->allowed_files) {
01014       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
01015          class->allowed_files = 0;
01016          class->total_files = 0;
01017          return -1;
01018       }
01019       class->allowed_files *= 2;
01020    }
01021 
01022    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
01023       return -1;
01024 
01025    class->total_files++;
01026 
01027    return 0;
01028 }
01029 
01030 static int moh_sort_compare(const void *i1, const void *i2)
01031 {
01032    char *s1, *s2;
01033 
01034    s1 = ((char **)i1)[0];
01035    s2 = ((char **)i2)[0];
01036 
01037    return strcasecmp(s1, s2);
01038 }
01039 
01040 static int moh_scan_files(struct mohclass *class) {
01041 
01042    DIR *files_DIR;
01043    struct dirent *files_dirent;
01044    char dir_path[PATH_MAX];
01045    char path[PATH_MAX];
01046    char filepath[PATH_MAX];
01047    char *ext;
01048    struct stat statbuf;
01049    int i;
01050 
01051    if (class->dir[0] != '/') {
01052       ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01053       strncat(dir_path, "/", sizeof(dir_path) - 1);
01054       strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01055    } else {
01056       ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01057    }
01058    ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01059    files_DIR = opendir(dir_path);
01060    if (!files_DIR) {
01061       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01062       return -1;
01063    }
01064 
01065    for (i = 0; i < class->total_files; i++)
01066       ast_free(class->filearray[i]);
01067 
01068    class->total_files = 0;
01069    if (!getcwd(path, sizeof(path))) {
01070       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01071       closedir(files_DIR);
01072       return -1;
01073    }
01074    if (chdir(dir_path) < 0) {
01075       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01076       closedir(files_DIR);
01077       return -1;
01078    }
01079    while ((files_dirent = readdir(files_DIR))) {
01080       /* The file name must be at least long enough to have the file type extension */
01081       if ((strlen(files_dirent->d_name) < 4))
01082          continue;
01083 
01084       /* Skip files that starts with a dot */
01085       if (files_dirent->d_name[0] == '.')
01086          continue;
01087 
01088       /* Skip files without extensions... they are not audio */
01089       if (!strchr(files_dirent->d_name, '.'))
01090          continue;
01091 
01092       snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01093 
01094       if (stat(filepath, &statbuf))
01095          continue;
01096 
01097       if (!S_ISREG(statbuf.st_mode))
01098          continue;
01099 
01100       if ((ext = strrchr(filepath, '.')))
01101          *ext = '\0';
01102 
01103       /* if the file is present in multiple formats, ensure we only put it into the list once */
01104       for (i = 0; i < class->total_files; i++)
01105          if (!strcmp(filepath, class->filearray[i]))
01106             break;
01107 
01108       if (i == class->total_files) {
01109          if (moh_add_file(class, filepath))
01110             break;
01111       }
01112    }
01113 
01114    closedir(files_DIR);
01115    if (chdir(path) < 0) {
01116       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01117       return -1;
01118    }
01119    if (ast_test_flag(class, MOH_SORTALPHA))
01120       qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01121    return class->total_files;
01122 }
01123 
01124 static int init_files_class(struct mohclass *class)
01125 {
01126    int res;
01127 
01128    res = moh_scan_files(class);
01129 
01130    if (res < 0) {
01131       return -1;
01132    }
01133 
01134    if (!res) {
01135       if (option_verbose > 2) {
01136          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
01137                class->dir, class->name);
01138       }
01139       return -1;
01140    }
01141 
01142 #if 0
01143    /* XXX This isn't correct.  Args is an application for custom mode. XXX */
01144    if (strchr(class->args, 'r')) {
01145       ast_set_flag(class, MOH_RANDOMIZE);
01146    }
01147 #endif
01148 
01149    return 0;
01150 }
01151 
01152 static void moh_rescan_files(void) {
01153    struct ao2_iterator i;
01154    struct mohclass *c;
01155 
01156    i = ao2_iterator_init(mohclasses, 0);
01157 
01158    while ((c = ao2_iterator_next(&i))) {
01159       if (!strcasecmp(c->mode, "files")) {
01160          moh_scan_files(c);
01161       }
01162       ao2_ref(c, -1);
01163    }
01164 
01165    ao2_iterator_destroy(&i);
01166 }
01167 
01168 static int moh_diff(struct mohclass *old, struct mohclass *new)
01169 {
01170    if (!old || !new) {
01171       return -1;
01172    }
01173 
01174    if (strcmp(old->dir, new->dir)) {
01175       return -1;
01176    } else if (strcmp(old->mode, new->mode)) {
01177       return -1;
01178    } else if (strcmp(old->args, new->args)) {
01179       return -1;
01180    } else if (old->flags != new->flags) {
01181       return -1;
01182    }
01183 
01184    return 0;
01185 }
01186 
01187 static int init_app_class(struct mohclass *class)
01188 {
01189    if (!strcasecmp(class->mode, "custom")) {
01190       ast_set_flag(class, MOH_CUSTOM);
01191    } else if (!strcasecmp(class->mode, "mp3nb")) {
01192       ast_set_flag(class, MOH_SINGLE);
01193    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01194       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01195    } else if (!strcasecmp(class->mode, "quietmp3")) {
01196       ast_set_flag(class, MOH_QUIET);
01197    }
01198 
01199    class->srcfd = -1;
01200 
01201    if (!(class->timer = ast_timer_open())) {
01202       ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01203       return -1;
01204    }
01205    if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01206       ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01207       ast_timer_close(class->timer);
01208       class->timer = NULL;
01209    }
01210 
01211    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01212       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01213       if (class->timer) {
01214          ast_timer_close(class->timer);
01215          class->timer = NULL;
01216       }
01217       return -1;
01218    }
01219 
01220    return 0;
01221 }
01222 
01223 /*!
01224  * \note This function owns the reference it gets to moh if unref is true
01225  */
01226 #define moh_register(a,b,c)   _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01227 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01228 {
01229    struct mohclass *mohclass = NULL;
01230 
01231    mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01232 
01233    if (mohclass && !moh_diff(mohclass, moh)) {
01234       ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01235       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01236       if (unref) {
01237          moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01238       }
01239       return -1;
01240    } else if (mohclass) {
01241       /* Found a class, but it's different from the one being registered */
01242       mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01243    }
01244 
01245    time(&moh->start);
01246    moh->start -= respawn_time;
01247 
01248    if (!strcasecmp(moh->mode, "files")) {
01249       if (init_files_class(moh)) {
01250          if (unref) {
01251             moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01252          }
01253          return -1;
01254       }
01255    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
01256          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
01257          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01258       if (init_app_class(moh)) {
01259          if (unref) {
01260             moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01261          }
01262          return -1;
01263       }
01264    } else {
01265       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01266       if (unref) {
01267          moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01268       }
01269       return -1;
01270    }
01271 
01272    ao2_t_link(mohclasses, moh, "Adding class to container");
01273 
01274    if (unref) {
01275       moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01276    }
01277 
01278    return 0;
01279 }
01280 
01281 static void local_ast_moh_cleanup(struct ast_channel *chan)
01282 {
01283    struct moh_files_state *state = chan->music_state;
01284 
01285    if (state) {
01286       if (state->class) {
01287          /* This should never happen.  We likely just leaked some resource. */
01288          state->class =
01289             mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01290          ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01291       }
01292       ast_free(chan->music_state);
01293       chan->music_state = NULL;
01294       /* Only held a module reference if we had a music state */
01295       ast_module_unref(ast_module_info->self);
01296    }
01297 }
01298 
01299 static void moh_class_destructor(void *obj);
01300 
01301 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01302 
01303 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01304 {
01305    struct mohclass *class;
01306 
01307    if ((class =
01308 #ifdef REF_DEBUG
01309          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01310 #elif defined(__AST_DEBUG_MALLOC)
01311          __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01312 #else
01313          ao2_alloc(sizeof(*class), moh_class_destructor)
01314 #endif
01315       )) {
01316       class->format = AST_FORMAT_SLINEAR;
01317       class->srcfd = -1;
01318    }
01319 
01320    return class;
01321 }
01322 
01323 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01324 {
01325    struct mohclass *mohclass = NULL;
01326    struct moh_files_state *state = chan->music_state;
01327    struct ast_variable *var = NULL;
01328    int res;
01329    int realtime_possible = ast_check_realtime("musiconhold");
01330 
01331    /* The following is the order of preference for which class to use:
01332     * 1) The channels explicitly set musicclass, which should *only* be
01333     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01334     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01335     *    result of receiving a HOLD control frame, this should be the
01336     *    payload that came with the frame.
01337     * 3) The interpclass argument. This would be from the mohinterpret
01338     *    option from channel drivers. This is the same as the old musicclass
01339     *    option.
01340     * 4) The default class.
01341     */
01342    if (!ast_strlen_zero(chan->musicclass)) {
01343       mohclass = get_mohbyname(chan->musicclass, 1, 0);
01344       if (!mohclass && realtime_possible) {
01345          var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01346       }
01347    }
01348    if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01349       mohclass = get_mohbyname(mclass, 1, 0);
01350       if (!mohclass && realtime_possible) {
01351          var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01352       }
01353    }
01354    if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01355       mohclass = get_mohbyname(interpclass, 1, 0);
01356       if (!mohclass && realtime_possible) {
01357          var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01358       }
01359    }
01360 
01361    if (!mohclass && !var) {
01362       mohclass = get_mohbyname("default", 1, 0);
01363       if (!mohclass && realtime_possible) {
01364          var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01365       }
01366    }
01367 
01368    /* If no moh class found in memory, then check RT. Note that the logic used
01369     * above guarantees that if var is non-NULL, then mohclass must be NULL.
01370     */
01371    if (var) {
01372       struct ast_variable *tmp = NULL;
01373 
01374       if ((mohclass = moh_class_malloc())) {
01375          mohclass->realtime = 1;
01376          for (tmp = var; tmp; tmp = tmp->next) {
01377             if (!strcasecmp(tmp->name, "name"))
01378                ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01379             else if (!strcasecmp(tmp->name, "mode"))
01380                ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
01381             else if (!strcasecmp(tmp->name, "directory"))
01382                ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01383             else if (!strcasecmp(tmp->name, "application"))
01384                ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01385             else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01386                mohclass->digit = *tmp->value;
01387             else if (!strcasecmp(tmp->name, "random"))
01388                ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01389             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01390                ast_set_flag(mohclass, MOH_RANDOMIZE);
01391             else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
01392                ast_set_flag(mohclass, MOH_SORTALPHA);
01393             else if (!strcasecmp(tmp->name, "format")) {
01394                mohclass->format = ast_getformatbyname(tmp->value);
01395                if (!mohclass->format) {
01396                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01397                   mohclass->format = AST_FORMAT_SLINEAR;
01398                }
01399             }
01400          }
01401          ast_variables_destroy(var);
01402          if (ast_strlen_zero(mohclass->dir)) {
01403             if (!strcasecmp(mohclass->mode, "custom")) {
01404                strcpy(mohclass->dir, "nodir");
01405             } else {
01406                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01407                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01408                return -1;
01409             }
01410          }
01411          if (ast_strlen_zero(mohclass->mode)) {
01412             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01413             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01414             return -1;
01415          }
01416          if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01417             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01418             mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01419             return -1;
01420          }
01421 
01422          if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01423             /* CACHERTCLASSES enabled, let's add this class to default tree */
01424             if (state && state->class) {
01425                /* Class already exist for this channel */
01426                ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01427                if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01428                   /* we found RT class with the same name, seems like we should continue playing existing one */
01429                   /* XXX This code is impossible to reach */
01430                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01431                   mohclass = state->class;
01432                }
01433             }
01434             /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
01435              * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
01436              * be that the destructor would be called when the generator on the channel is deactivated. The container then
01437              * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
01438              * invalid memory.
01439              */
01440             if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01441                mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01442                return -1;
01443             }
01444          } else {
01445             /* We don't register RT moh class, so let's init it manualy */
01446 
01447             time(&mohclass->start);
01448             mohclass->start -= respawn_time;
01449 
01450             if (!strcasecmp(mohclass->mode, "files")) {
01451                if (!moh_scan_files(mohclass)) {
01452                   mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01453                   return -1;
01454                }
01455                if (strchr(mohclass->args, 'r'))
01456                   ast_set_flag(mohclass, MOH_RANDOMIZE);
01457             } 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")) {
01458 
01459                if (!strcasecmp(mohclass->mode, "custom"))
01460                   ast_set_flag(mohclass, MOH_CUSTOM);
01461                else if (!strcasecmp(mohclass->mode, "mp3nb"))
01462                   ast_set_flag(mohclass, MOH_SINGLE);
01463                else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01464                   ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01465                else if (!strcasecmp(mohclass->mode, "quietmp3"))
01466                   ast_set_flag(mohclass, MOH_QUIET);
01467 
01468                mohclass->srcfd = -1;
01469                if (!(mohclass->timer = ast_timer_open())) {
01470                   ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01471                }
01472                if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01473                   ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01474                   ast_timer_close(mohclass->timer);
01475                   mohclass->timer = NULL;
01476                }
01477 
01478                /* Let's check if this channel already had a moh class before */
01479                if (state && state->class) {
01480                   /* Class already exist for this channel */
01481                   ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01482                   if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01483                      /* we found RT class with the same name, seems like we should continue playing existing one */
01484                      mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01485                      mohclass = state->class;
01486                   }
01487                } else {
01488                   if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01489                      ast_log(LOG_WARNING, "Unable to create moh...\n");
01490                      if (mohclass->timer) {
01491                         ast_timer_close(mohclass->timer);
01492                         mohclass->timer = NULL;
01493                      }
01494                      mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01495                      return -1;
01496                   }
01497                }
01498             } else {
01499                ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01500                mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01501                return -1;
01502             }
01503          }
01504       } else {
01505          ast_variables_destroy(var);
01506          var = NULL;
01507       }
01508    }
01509 
01510    if (!mohclass) {
01511       return -1;
01512    }
01513 
01514    /* If we are using a cached realtime class with files, re-scan the files */
01515    if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01516       if (!moh_scan_files(mohclass)) {
01517          mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01518          return -1;
01519       }
01520    }
01521 
01522    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01523       "State: Start\r\n"
01524       "Channel: %s\r\n"
01525       "UniqueID: %s\r\n"
01526       "Class: %s\r\n",
01527       chan->name, chan->uniqueid,
01528       mohclass->name);
01529 
01530    ast_set_flag(chan, AST_FLAG_MOH);
01531 
01532    if (mohclass->total_files) {
01533       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01534    } else {
01535       res = ast_activate_generator(chan, &mohgen, mohclass);
01536    }
01537 
01538    mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01539 
01540    return res;
01541 }
01542 
01543 static void local_ast_moh_stop(struct ast_channel *chan)
01544 {
01545    ast_clear_flag(chan, AST_FLAG_MOH);
01546    ast_deactivate_generator(chan);
01547 
01548    ast_channel_lock(chan);
01549    if (chan->music_state) {
01550       if (chan->stream) {
01551          ast_closestream(chan->stream);
01552          chan->stream = NULL;
01553       }
01554    }
01555 
01556    ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01557       "State: Stop\r\n"
01558       "Channel: %s\r\n"
01559       "UniqueID: %s\r\n",
01560       chan->name, chan->uniqueid);
01561    ast_channel_unlock(chan);
01562 }
01563 
01564 static void moh_class_destructor(void *obj)
01565 {
01566    struct mohclass *class = obj;
01567    struct mohdata *member;
01568    pthread_t tid = 0;
01569 
01570    ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01571 
01572    ao2_lock(class);
01573    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01574       free(member);
01575    }
01576    ao2_unlock(class);
01577 
01578    /* Kill the thread first, so it cannot restart the child process while the
01579     * class is being destroyed */
01580    if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01581       tid = class->thread;
01582       class->thread = AST_PTHREADT_NULL;
01583       pthread_cancel(tid);
01584       /* We'll collect the exit status later, after we ensure all the readers
01585        * are dead. */
01586    }
01587 
01588    if (class->pid > 1) {
01589       char buff[8192];
01590       int bytes, tbytes = 0, stime = 0, pid = 0;
01591 
01592       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01593 
01594       stime = time(NULL) + 2;
01595       pid = class->pid;
01596       class->pid = 0;
01597 
01598       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01599        * to give the process a reason and time enough to kill off its
01600        * children. */
01601       do {
01602          if (killpg(pid, SIGHUP) < 0) {
01603             ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01604          }
01605          usleep(100000);
01606          if (killpg(pid, SIGTERM) < 0) {
01607             if (errno == ESRCH) {
01608                break;
01609             }
01610             ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01611          }
01612          usleep(100000);
01613          if (killpg(pid, SIGKILL) < 0) {
01614             if (errno == ESRCH) {
01615                break;
01616             }
01617             ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01618          }
01619       } while (0);
01620 
01621       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01622             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01623          tbytes = tbytes + bytes;
01624       }
01625 
01626       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01627 
01628       close(class->srcfd);
01629       class->srcfd = -1;
01630    }
01631 
01632    if (class->filearray) {
01633       int i;
01634       for (i = 0; i < class->total_files; i++) {
01635          free(class->filearray[i]);
01636       }
01637       free(class->filearray);
01638       class->filearray = NULL;
01639    }
01640 
01641    if (class->timer) {
01642       ast_timer_close(class->timer);
01643       class->timer = NULL;
01644    }
01645 
01646    /* Finally, collect the exit status of the monitor thread */
01647    if (tid > 0) {
01648       pthread_join(tid, NULL);
01649    }
01650 
01651 }
01652 
01653 static int moh_class_mark(void *obj, void *arg, int flags)
01654 {
01655    struct mohclass *class = obj;
01656 
01657    class->delete = 1;
01658 
01659    return 0;
01660 }
01661 
01662 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01663 {
01664    struct mohclass *class = obj;
01665 
01666    return class->delete ? CMP_MATCH : 0;
01667 }
01668 
01669 static int load_moh_classes(int reload)
01670 {
01671    struct ast_config *cfg;
01672    struct ast_variable *var;
01673    struct mohclass *class; 
01674    char *cat;
01675    int numclasses = 0;
01676    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01677 
01678    cfg = ast_config_load("musiconhold.conf", config_flags);
01679 
01680    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01681       if (ast_check_realtime("musiconhold") && reload) {
01682          ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01683          ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01684       }
01685       return 0;
01686    }
01687    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01688       moh_rescan_files();
01689       return 0;
01690    }
01691 
01692    if (reload) {
01693       ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01694    }
01695 
01696    ast_clear_flag(global_flags, AST_FLAGS_ALL);
01697 
01698    cat = ast_category_browse(cfg, NULL);
01699    for (; cat; cat = ast_category_browse(cfg, cat)) {
01700       /* Setup common options from [general] section */
01701       if (!strcasecmp(cat, "general")) {
01702          for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01703             if (!strcasecmp(var->name, "cachertclasses")) {
01704                ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01705             } else {
01706                ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01707             }
01708          }
01709       }
01710       /* These names were deprecated in 1.4 and should not be used until after the next major release. */
01711       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
01712             !strcasecmp(cat, "general")) {
01713          continue;
01714       }
01715 
01716       if (!(class = moh_class_malloc())) {
01717          break;
01718       }
01719 
01720       ast_copy_string(class->name, cat, sizeof(class->name));  
01721       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01722          if (!strcasecmp(var->name, "mode"))
01723             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01724          else if (!strcasecmp(var->name, "directory"))
01725             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01726          else if (!strcasecmp(var->name, "application"))
01727             ast_copy_string(class->args, var->value, sizeof(class->args));
01728          else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01729             class->digit = *var->value;
01730          else if (!strcasecmp(var->name, "random"))
01731             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01732          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01733             ast_set_flag(class, MOH_RANDOMIZE);
01734          else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
01735             ast_set_flag(class, MOH_SORTALPHA);
01736          else if (!strcasecmp(var->name, "format")) {
01737             class->format = ast_getformatbyname(var->value);
01738             if (!class->format) {
01739                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01740                class->format = AST_FORMAT_SLINEAR;
01741             }
01742          }
01743       }
01744 
01745       if (ast_strlen_zero(class->dir)) {
01746          if (!strcasecmp(class->mode, "custom")) {
01747             strcpy(class->dir, "nodir");
01748          } else {
01749             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01750             class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01751             continue;
01752          }
01753       }
01754       if (ast_strlen_zero(class->mode)) {
01755          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01756          class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01757          continue;
01758       }
01759       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01760          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01761          class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01762          continue;
01763       }
01764 
01765       /* Don't leak a class when it's already registered */
01766       if (!moh_register(class, reload, HANDLE_REF)) {
01767          numclasses++;
01768       }
01769    }
01770 
01771    ast_config_destroy(cfg);
01772 
01773    ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01774          moh_classes_delete_marked, NULL, "Purge marked classes");
01775 
01776    return numclasses;
01777 }
01778 
01779 static void ast_moh_destroy(void)
01780 {
01781    ast_verb(2, "Destroying musiconhold processes\n");
01782    if (mohclasses) {
01783       ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01784       ao2_ref(mohclasses, -1);
01785       mohclasses = NULL;
01786    }
01787 }
01788 
01789 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01790 {
01791    switch (cmd) {
01792    case CLI_INIT:
01793       e->command = "moh reload";
01794       e->usage =
01795          "Usage: moh reload\n"
01796          "       Reloads the MusicOnHold module.\n"
01797          "       Alias for 'module reload res_musiconhold.so'\n";
01798       return NULL;
01799    case CLI_GENERATE:
01800       return NULL;
01801    }
01802 
01803    if (a->argc != e->args)
01804       return CLI_SHOWUSAGE;
01805 
01806    reload();
01807 
01808    return CLI_SUCCESS;
01809 }
01810 
01811 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01812 {
01813    struct mohclass *class;
01814    struct ao2_iterator i;
01815 
01816    switch (cmd) {
01817    case CLI_INIT:
01818       e->command = "moh show files";
01819       e->usage =
01820          "Usage: moh show files\n"
01821          "       Lists all loaded file-based MusicOnHold classes and their\n"
01822          "       files.\n";
01823       return NULL;
01824    case CLI_GENERATE:
01825       return NULL;
01826    }
01827 
01828    if (a->argc != e->args)
01829       return CLI_SHOWUSAGE;
01830 
01831    i = ao2_iterator_init(mohclasses, 0);
01832    for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01833       int x;
01834 
01835       if (!class->total_files) {
01836          continue;
01837       }
01838 
01839       ast_cli(a->fd, "Class: %s\n", class->name);
01840       for (x = 0; x < class->total_files; x++) {
01841          ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01842       }
01843    }
01844    ao2_iterator_destroy(&i);
01845 
01846    return CLI_SUCCESS;
01847 }
01848 
01849 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01850 {
01851    struct mohclass *class;
01852    struct ao2_iterator i;
01853 
01854    switch (cmd) {
01855    case CLI_INIT:
01856       e->command = "moh show classes";
01857       e->usage =
01858          "Usage: moh show classes\n"
01859          "       Lists all MusicOnHold classes.\n";
01860       return NULL;
01861    case CLI_GENERATE:
01862       return NULL;
01863    }
01864 
01865    if (a->argc != e->args)
01866       return CLI_SHOWUSAGE;
01867 
01868    i = ao2_iterator_init(mohclasses, 0);
01869    for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01870       ast_cli(a->fd, "Class: %s\n", class->name);
01871       ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01872       ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01873       if (ast_test_flag(class, MOH_CUSTOM)) {
01874          ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01875       }
01876       if (strcasecmp(class->mode, "files")) {
01877          ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01878       }
01879    }
01880    ao2_iterator_destroy(&i);
01881 
01882    return CLI_SUCCESS;
01883 }
01884 
01885 static struct ast_cli_entry cli_moh[] = {
01886    AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
01887    AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01888    AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
01889 };
01890 
01891 static int moh_class_hash(const void *obj, const int flags)
01892 {
01893    const struct mohclass *class = obj;
01894 
01895    return ast_str_case_hash(class->name);
01896 }
01897 
01898 static int moh_class_cmp(void *obj, void *arg, int flags)
01899 {
01900    struct mohclass *class = obj, *class2 = arg;
01901 
01902    return strcasecmp(class->name, class2->name) ? 0 :
01903       (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01904       CMP_MATCH | CMP_STOP;
01905 }
01906 
01907 static int load_module(void)
01908 {
01909    int res;
01910 
01911    if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01912       return AST_MODULE_LOAD_DECLINE;
01913    }
01914 
01915    if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {   /* No music classes configured, so skip it */
01916       ast_log(LOG_WARNING, "No music on hold classes configured, "
01917             "disabling music on hold.\n");
01918    } else {
01919       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01920             local_ast_moh_cleanup);
01921    }
01922 
01923    res = ast_register_application_xml(play_moh, play_moh_exec);
01924    ast_register_atexit(ast_moh_destroy);
01925    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01926    if (!res)
01927       res = ast_register_application_xml(wait_moh, wait_moh_exec);
01928    if (!res)
01929       res = ast_register_application_xml(set_moh, set_moh_exec);
01930    if (!res)
01931       res = ast_register_application_xml(start_moh, start_moh_exec);
01932    if (!res)
01933       res = ast_register_application_xml(stop_moh, stop_moh_exec);
01934 
01935    return AST_MODULE_LOAD_SUCCESS;
01936 }
01937 
01938 static int reload(void)
01939 {
01940    if (load_moh_classes(1)) {
01941       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01942             local_ast_moh_cleanup);
01943    }
01944 
01945    return AST_MODULE_LOAD_SUCCESS;
01946 }
01947 
01948 static int moh_class_inuse(void *obj, void *arg, int flags)
01949 {
01950    struct mohclass *class = obj;
01951 
01952    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01953 }
01954 
01955 static int unload_module(void)
01956 {
01957    int res = 0;
01958    struct mohclass *class = NULL;
01959 
01960    /* XXX This check shouldn't be required if module ref counting was being used
01961     * properly ... */
01962    if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01963       class = mohclass_unref(class, "unref of class from module unload callback");
01964       res = -1;
01965    }
01966 
01967    if (res < 0) {
01968       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01969       return res;
01970    }
01971 
01972    ast_uninstall_music_functions();
01973 
01974    ast_moh_destroy();
01975    res = ast_unregister_application(play_moh);
01976    res |= ast_unregister_application(wait_moh);
01977    res |= ast_unregister_application(set_moh);
01978    res |= ast_unregister_application(start_moh);
01979    res |= ast_unregister_application(stop_moh);
01980    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01981    ast_unregister_atexit(ast_moh_destroy);
01982 
01983    return res;
01984 }
01985 
01986 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
01987    .load = load_module,
01988    .unload = unload_module,
01989    .reload = reload,
01990    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
01991 );

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1