Thu Dec 17 13:33:37 2009

Asterisk developer's documentation


res_musiconhold.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Routines implementing music on hold
00022  *
00023  * \arg See also \ref Config_moh
00024  * 
00025  * \author Mark Spencer <markster@digium.com>
00026  */
00027 
00028 /*** MODULEINFO
00029    <conflict>win32</conflict>
00030    <use>dahdi</use>
00031    <depend>working_fork</depend>
00032  ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 222152 $")
00037 
00038 #include <stdlib.h>
00039 #include <errno.h>
00040 #include <unistd.h>
00041 #include <string.h>
00042 #include <signal.h>
00043 #include <stdlib.h>
00044 #include <stdio.h>
00045 
00046 #include <sys/time.h>
00047 #include <sys/signal.h>
00048 #include <netinet/in.h>
00049 #include <sys/stat.h>
00050 #include <dirent.h>
00051 #include <unistd.h>
00052 #include <sys/ioctl.h>
00053 #ifdef SOLARIS
00054 #include <thread.h>
00055 #endif
00056 
00057 #include "asterisk/dahdi_compat.h"
00058 
00059 #ifdef HAVE_CAP
00060 #include <sys/capability.h>
00061 #endif /* HAVE_CAP */
00062 
00063 #include "asterisk/lock.h"
00064 #include "asterisk/file.h"
00065 #include "asterisk/logger.h"
00066 #include "asterisk/channel.h"
00067 #include "asterisk/pbx.h"
00068 #include "asterisk/options.h"
00069 #include "asterisk/module.h"
00070 #include "asterisk/translate.h"
00071 #include "asterisk/say.h"
00072 #include "asterisk/musiconhold.h"
00073 #include "asterisk/config.h"
00074 #include "asterisk/utils.h"
00075 #include "asterisk/cli.h"
00076 #include "asterisk/stringfields.h"
00077 #include "asterisk/linkedlists.h"
00078 #include "asterisk/astobj2.h"
00079 
00080 
00081 #define INITIAL_NUM_FILES   8
00082 
00083 static char *app0 = "MusicOnHold";
00084 static char *app1 = "WaitMusicOnHold";
00085 static char *app2 = "SetMusicOnHold";
00086 static char *app3 = "StartMusicOnHold";
00087 static char *app4 = "StopMusicOnHold";
00088 
00089 static char *synopsis0 = "Play Music On Hold indefinitely";
00090 static char *synopsis1 = "Wait, playing Music On Hold";
00091 static char *synopsis2 = "Set default Music On Hold class";
00092 static char *synopsis3 = "Play Music On Hold";
00093 static char *synopsis4 = "Stop Playing Music On Hold";
00094 
00095 static char *descrip0 = "MusicOnHold(class): "
00096 "Plays hold music specified by class.  If omitted, the default\n"
00097 "music source for the channel will be used. Set the default \n"
00098 "class with the SetMusicOnHold() application.\n"
00099 "Returns -1 on hangup.\n"
00100 "Never returns otherwise.\n";
00101 
00102 static char *descrip1 = "WaitMusicOnHold(delay): "
00103 "Plays hold music specified number of seconds.  Returns 0 when\n"
00104 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
00105 "still occur with no sound.\n";
00106 
00107 static char *descrip2 = "SetMusicOnHold(class): "
00108 "Sets the default class for music on hold for a given channel.  When\n"
00109 "music on hold is activated, this class will be used to select which\n"
00110 "music is played.\n";
00111 
00112 static char *descrip3 = "StartMusicOnHold(class): "
00113 "Starts playing music on hold, uses default music class for channel.\n"
00114 "Starts playing music specified by class.  If omitted, the default\n"
00115 "music source for the channel will be used.  Always returns 0.\n";
00116 
00117 static char *descrip4 = "StopMusicOnHold: "
00118 "Stops playing music on hold.\n";
00119 
00120 static int respawn_time = 20;
00121 
00122 struct moh_files_state {
00123    struct mohclass *class;
00124    int origwfmt;
00125    int samples;
00126    int sample_queue;
00127    int pos;
00128    int save_pos;
00129    char *save_pos_filename;
00130 };
00131 
00132 #define MOH_QUIET    (1 << 0)
00133 #define MOH_SINGLE      (1 << 1)
00134 #define MOH_CUSTOM      (1 << 2)
00135 #define MOH_RANDOMIZE      (1 << 3)
00136 
00137 struct mohclass {
00138    char name[MAX_MUSICCLASS];
00139    char dir[256];
00140    char args[256];
00141    char mode[80];
00142    /*! A dynamically sized array to hold the list of filenames in "files" mode */
00143    char **filearray;
00144    /*! The current size of the filearray */
00145    int allowed_files;
00146    /*! The current number of files loaded into the filearray */
00147    int total_files;
00148    unsigned int flags;
00149    /*! The format from the MOH source, not applicable to "files" mode */
00150    int format;
00151    /*! The pid of the external application delivering MOH */
00152    int pid;
00153    time_t start;
00154    pthread_t thread;
00155    /*! Source of audio */
00156    int srcfd;
00157    /*! FD for timing source */
00158    int pseudofd;
00159    unsigned int delete:1;
00160    unsigned int deprecated:1;
00161    AST_LIST_HEAD_NOLOCK(, mohdata) members;
00162    AST_LIST_ENTRY(mohclass) list;
00163 };
00164 
00165 struct mohdata {
00166    int pipe[2];
00167    int origwfmt;
00168    struct mohclass *parent;
00169    struct ast_frame f;
00170    AST_LIST_ENTRY(mohdata) list;
00171 };
00172 
00173 static struct ao2_container *mohclasses;
00174 
00175 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00176 #define MPG_123 "/usr/bin/mpg123"
00177 #define MAX_MP3S 256
00178 
00179 static int reload(void);
00180 
00181 #define mohclass_ref(class)   (ao2_ref((class), +1), class)
00182 #define mohclass_unref(class) (ao2_ref((class), -1), (struct mohclass *) NULL)
00183 
00184 static void moh_files_release(struct ast_channel *chan, void *data)
00185 {
00186    struct moh_files_state *state;
00187 
00188    if (!chan || !chan->music_state) {
00189       return;
00190    }
00191 
00192    state = chan->music_state;
00193 
00194    if (chan->stream) {
00195       ast_closestream(chan->stream);
00196       chan->stream = NULL;
00197    }
00198    
00199    if (option_verbose > 2) {
00200       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00201    }
00202 
00203    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00204       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00205    }
00206 
00207    state->save_pos = state->pos;
00208 
00209    mohclass_unref(state->class);
00210 }
00211 
00212 
00213 static int ast_moh_files_next(struct ast_channel *chan) 
00214 {
00215    struct moh_files_state *state = chan->music_state;
00216    int tries;
00217 
00218    /* Discontinue a stream if it is running already */
00219    if (chan->stream) {
00220       ast_closestream(chan->stream);
00221       chan->stream = NULL;
00222    }
00223 
00224    if (!state->class->total_files) {
00225       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00226       return -1;
00227    }
00228 
00229    /* If a specific file has been saved confirm it still exists and that it is still valid */
00230    if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00231       state->pos = state->save_pos;
00232       state->save_pos = -1;
00233    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00234       /* Get a random file and ensure we can open it */
00235       for (tries = 0; tries < 20; tries++) {
00236          state->pos = ast_random() % state->class->total_files;
00237          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00238             break;
00239       }
00240       state->save_pos = -1;
00241       state->samples = 0;
00242    } else {
00243       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00244       state->pos++;
00245       state->pos %= state->class->total_files;
00246       state->save_pos = -1;
00247       state->samples = 0;
00248    }
00249 
00250    if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00251       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00252       state->pos++;
00253       state->pos %= state->class->total_files;
00254       return -1;
00255    }
00256 
00257    /* Record the pointer to the filename for position resuming later */
00258    state->save_pos_filename = state->class->filearray[state->pos];
00259 
00260    if (option_debug)
00261       ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00262 
00263    if (state->samples)
00264       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00265 
00266    return 0;
00267 }
00268 
00269 
00270 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
00271 {
00272    struct ast_frame *f = NULL;
00273    
00274    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00275       if (!ast_moh_files_next(chan))
00276          f = ast_readframe(chan->stream);
00277    }
00278 
00279    return f;
00280 }
00281 
00282 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00283 {
00284    struct moh_files_state *state = chan->music_state;
00285    struct ast_frame *f = NULL;
00286    int res = 0;
00287 
00288    state->sample_queue += samples;
00289 
00290    while (state->sample_queue > 0) {
00291       if ((f = moh_files_readframe(chan))) {
00292          state->samples += f->samples;
00293          state->sample_queue -= f->samples;
00294          res = ast_write(chan, f);
00295          ast_frfree(f);
00296          if (res < 0) {
00297             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00298             return -1;
00299          }
00300       } else
00301          return -1;  
00302    }
00303    return res;
00304 }
00305 
00306 
00307 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00308 {
00309    struct moh_files_state *state;
00310    struct mohclass *class = params;
00311 
00312    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00313       chan->music_state = state;
00314    } else {
00315       state = chan->music_state;
00316    }
00317 
00318    if (!state) {
00319       return NULL;
00320    }
00321 
00322    if (state->class != class) {
00323       memset(state, 0, sizeof(*state));
00324       if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00325          state->pos = ast_random() % class->total_files;
00326       }
00327    }
00328 
00329    state->class = mohclass_ref(class);
00330    state->origwfmt = chan->writeformat;
00331 
00332    if (option_verbose > 2) {
00333       ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", 
00334             class->name, chan->name);
00335    }
00336    
00337    return chan->music_state;
00338 }
00339 
00340 static struct ast_generator moh_file_stream = {
00341    .alloc    = moh_files_alloc,
00342    .release  = moh_files_release,
00343    .generate = moh_files_generator,
00344 };
00345 
00346 static int spawn_mp3(struct mohclass *class)
00347 {
00348    int fds[2];
00349    int files = 0;
00350    char fns[MAX_MP3S][80];
00351    char *argv[MAX_MP3S + 50];
00352    char xargs[256];
00353    char *argptr;
00354    int argc = 0;
00355    DIR *dir = NULL;
00356    struct dirent *de;
00357    sigset_t signal_set, old_set;
00358 
00359    
00360    if (!strcasecmp(class->dir, "nodir")) {
00361       files = 1;
00362    } else {
00363       dir = opendir(class->dir);
00364       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00365          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00366          return -1;
00367       }
00368    }
00369 
00370    if (!ast_test_flag(class, MOH_CUSTOM)) {
00371       argv[argc++] = "mpg123";
00372       argv[argc++] = "-q";
00373       argv[argc++] = "-s";
00374       argv[argc++] = "--mono";
00375       argv[argc++] = "-r";
00376       argv[argc++] = "8000";
00377       
00378       if (!ast_test_flag(class, MOH_SINGLE)) {
00379          argv[argc++] = "-b";
00380          argv[argc++] = "2048";
00381       }
00382       
00383       argv[argc++] = "-f";
00384       
00385       if (ast_test_flag(class, MOH_QUIET))
00386          argv[argc++] = "4096";
00387       else
00388          argv[argc++] = "8192";
00389       
00390       /* Look for extra arguments and add them to the list */
00391       ast_copy_string(xargs, class->args, sizeof(xargs));
00392       argptr = xargs;
00393       while (!ast_strlen_zero(argptr)) {
00394          argv[argc++] = argptr;
00395          strsep(&argptr, ",");
00396       }
00397    } else  {
00398       /* Format arguments for argv vector */
00399       ast_copy_string(xargs, class->args, sizeof(xargs));
00400       argptr = xargs;
00401       while (!ast_strlen_zero(argptr)) {
00402          argv[argc++] = argptr;
00403          strsep(&argptr, " ");
00404       }
00405    }
00406 
00407    if (!strncasecmp(class->dir, "http://", 7)) {
00408       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00409       argv[argc++] = fns[files];
00410       files++;
00411    } else if (dir) {
00412       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00413          if ((strlen(de->d_name) > 3) && 
00414              ((ast_test_flag(class, MOH_CUSTOM) && 
00415                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00416                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00417               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00418             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00419             argv[argc++] = fns[files];
00420             files++;
00421          }
00422       }
00423    }
00424    argv[argc] = NULL;
00425    if (dir) {
00426       closedir(dir);
00427    }
00428    if (pipe(fds)) {  
00429       ast_log(LOG_WARNING, "Pipe failed\n");
00430       return -1;
00431    }
00432    if (!files) {
00433       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00434       close(fds[0]);
00435       close(fds[1]);
00436       return -1;
00437    }
00438    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00439       sleep(respawn_time - (time(NULL) - class->start));
00440    }
00441 
00442    /* Block signals during the fork() */
00443    sigfillset(&signal_set);
00444    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00445 
00446    time(&class->start);
00447    class->pid = fork();
00448    if (class->pid < 0) {
00449       close(fds[0]);
00450       close(fds[1]);
00451       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00452       return -1;
00453    }
00454    if (!class->pid) {
00455       /* Child */
00456       int x;
00457 #ifdef HAVE_CAP
00458       cap_t cap;
00459 #endif
00460       if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00461          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00462          _exit(1);
00463       }
00464 
00465       if (ast_opt_high_priority)
00466          ast_set_priority(0);
00467 
00468       /* Reset ignored signals back to default */
00469       signal(SIGPIPE, SIG_DFL);
00470       pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
00471 
00472 #ifdef HAVE_CAP
00473       cap = cap_from_text("cap_net_admin-eip");
00474 
00475       if (cap_set_proc(cap)) {
00476          ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00477       }
00478       cap_free(cap);
00479 #endif
00480       close(fds[0]);
00481       /* Stdout goes to pipe */
00482       dup2(fds[1], STDOUT_FILENO);
00483       /* Close unused file descriptors */
00484       for (x=3;x<8192;x++) {
00485          if (-1 != fcntl(x, F_GETFL)) {
00486             close(x);
00487          }
00488       }
00489       setpgid(0, getpid());
00490 
00491       if (ast_test_flag(class, MOH_CUSTOM)) {
00492          execv(argv[0], argv);
00493       } else {
00494          /* Default install is /usr/local/bin */
00495          execv(LOCAL_MPG_123, argv);
00496          /* Many places have it in /usr/bin */
00497          execv(MPG_123, argv);
00498          /* Check PATH as a last-ditch effort */
00499          execvp("mpg123", argv);
00500       }
00501       ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00502       close(fds[1]);
00503       _exit(1);
00504    } else {
00505       /* Parent */
00506       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00507       close(fds[1]);
00508    }
00509    return fds[0];
00510 }
00511 
00512 static void *monmp3thread(void *data)
00513 {
00514 #define  MOH_MS_INTERVAL      100
00515 
00516    struct mohclass *class = data;
00517    struct mohdata *moh;
00518    char buf[8192];
00519    short sbuf[8192];
00520    int res, res2;
00521    int len;
00522    struct timeval tv, tv_tmp;
00523 
00524    tv.tv_sec = 0;
00525    tv.tv_usec = 0;
00526    for(;/* ever */;) {
00527       pthread_testcancel();
00528       /* Spawn mp3 player if it's not there */
00529       if (class->srcfd < 0) {
00530          if ((class->srcfd = spawn_mp3(class)) < 0) {
00531             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00532             /* Try again later */
00533             sleep(500);
00534             pthread_testcancel();
00535          }
00536       }
00537       if (class->pseudofd > -1) {
00538 #ifdef SOLARIS
00539          thr_yield();
00540 #endif
00541          /* Pause some amount of time */
00542          res = read(class->pseudofd, buf, sizeof(buf));
00543          pthread_testcancel();
00544       } else {
00545          long delta;
00546          /* Reliable sleep */
00547          tv_tmp = ast_tvnow();
00548          if (ast_tvzero(tv))
00549             tv = tv_tmp;
00550          delta = ast_tvdiff_ms(tv_tmp, tv);
00551          if (delta < MOH_MS_INTERVAL) {   /* too early */
00552             tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00553             usleep(1000 * (MOH_MS_INTERVAL - delta));
00554             pthread_testcancel();
00555          } else {
00556             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00557             tv = tv_tmp;
00558          }
00559          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00560       }
00561       if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00562          continue;
00563       /* Read mp3 audio */
00564       len = ast_codec_get_len(class->format, res);
00565 
00566       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00567          if (!res2) {
00568             close(class->srcfd);
00569             class->srcfd = -1;
00570             pthread_testcancel();
00571             if (class->pid > 1) {
00572                killpg(class->pid, SIGHUP);
00573                usleep(100000);
00574                killpg(class->pid, SIGTERM);
00575                usleep(100000);
00576                killpg(class->pid, SIGKILL);
00577                class->pid = 0;
00578             }
00579          } else
00580             ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
00581          continue;
00582       }
00583 
00584       pthread_testcancel();
00585 
00586       ao2_lock(class);
00587       AST_LIST_TRAVERSE(&class->members, moh, list) {
00588          /* Write data */
00589          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00590             if (option_debug)
00591                ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
00592          }
00593       }
00594       ao2_unlock(class);
00595    }
00596    return NULL;
00597 }
00598 
00599 static int moh0_exec(struct ast_channel *chan, void *data)
00600 {
00601    if (ast_moh_start(chan, data, NULL)) {
00602       ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00603       return 0;
00604    }
00605    while (!ast_safe_sleep(chan, 10000));
00606    ast_moh_stop(chan);
00607    return -1;
00608 }
00609 
00610 static int moh1_exec(struct ast_channel *chan, void *data)
00611 {
00612    int res;
00613    if (!data || !atoi(data)) {
00614       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00615       return -1;
00616    }
00617    if (ast_moh_start(chan, NULL, NULL)) {
00618       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00619       return 0;
00620    }
00621    res = ast_safe_sleep(chan, atoi(data) * 1000);
00622    ast_moh_stop(chan);
00623    return res;
00624 }
00625 
00626 static int moh2_exec(struct ast_channel *chan, void *data)
00627 {
00628    if (ast_strlen_zero(data)) {
00629       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00630       return -1;
00631    }
00632    ast_string_field_set(chan, musicclass, data);
00633    return 0;
00634 }
00635 
00636 static int moh3_exec(struct ast_channel *chan, void *data)
00637 {
00638    char *class = NULL;
00639    if (data && strlen(data))
00640       class = data;
00641    if (ast_moh_start(chan, class, NULL)) 
00642       ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00643 
00644    return 0;
00645 }
00646 
00647 static int moh4_exec(struct ast_channel *chan, void *data)
00648 {
00649    ast_moh_stop(chan);
00650 
00651    return 0;
00652 }
00653 
00654 static struct mohclass *get_mohbyname(const char *name, int warn)
00655 {
00656    struct mohclass *moh = NULL;
00657    struct mohclass tmp_class = {
00658       .flags = 0,
00659    };
00660 
00661    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00662 
00663    moh = ao2_find(mohclasses, &tmp_class, 0);
00664 
00665    if (!moh && warn) {
00666       ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
00667    }
00668 
00669    return moh;
00670 }
00671 
00672 static struct mohdata *mohalloc(struct mohclass *cl)
00673 {
00674    struct mohdata *moh;
00675    long flags; 
00676    
00677    if (!(moh = ast_calloc(1, sizeof(*moh))))
00678       return NULL;
00679    
00680    if (pipe(moh->pipe)) {
00681       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00682       free(moh);
00683       return NULL;
00684    }
00685 
00686    /* Make entirely non-blocking */
00687    flags = fcntl(moh->pipe[0], F_GETFL);
00688    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00689    flags = fcntl(moh->pipe[1], F_GETFL);
00690    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00691 
00692    moh->f.frametype = AST_FRAME_VOICE;
00693    moh->f.subclass = cl->format;
00694    moh->f.offset = AST_FRIENDLY_OFFSET;
00695 
00696    moh->parent = mohclass_ref(cl);
00697 
00698    ao2_lock(cl);
00699    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00700    ao2_unlock(cl);
00701    
00702    return moh;
00703 }
00704 
00705 static void moh_release(struct ast_channel *chan, void *data)
00706 {
00707    struct mohdata *moh = data;
00708    struct mohclass *class = moh->parent;
00709    int oldwfmt;
00710 
00711    ao2_lock(class);
00712    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00713    ao2_unlock(class);
00714    
00715    close(moh->pipe[0]);
00716    close(moh->pipe[1]);
00717 
00718    oldwfmt = moh->origwfmt;
00719 
00720    moh->parent = class = mohclass_unref(class);
00721 
00722    free(moh);
00723 
00724    if (chan) {
00725       if (oldwfmt && ast_set_write_format(chan, oldwfmt))  {
00726          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 
00727                chan->name, ast_getformatname(oldwfmt));
00728       }
00729       if (option_verbose > 2) {
00730          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00731       }
00732    }
00733 }
00734 
00735 static void *moh_alloc(struct ast_channel *chan, void *params)
00736 {
00737    struct mohdata *res;
00738    struct mohclass *class = params;
00739 
00740    if ((res = mohalloc(class))) {
00741       res->origwfmt = chan->writeformat;
00742       if (ast_set_write_format(chan, class->format)) {
00743          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00744          moh_release(NULL, res);
00745          res = NULL;
00746       }
00747       if (option_verbose > 2)
00748          ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00749    }
00750    return res;
00751 }
00752 
00753 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00754 {
00755    struct mohdata *moh = data;
00756    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00757    int res;
00758 
00759    len = ast_codec_get_len(moh->parent->format, samples);
00760 
00761    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00762       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00763       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00764    }
00765    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00766    if (res <= 0)
00767       return 0;
00768 
00769    moh->f.datalen = res;
00770    moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00771    moh->f.samples = ast_codec_get_samples(&moh->f);
00772 
00773    if (ast_write(chan, &moh->f) < 0) {
00774       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00775       return -1;
00776    }
00777 
00778    return 0;
00779 }
00780 
00781 static struct ast_generator mohgen = {
00782    .alloc    = moh_alloc,
00783    .release  = moh_release,
00784    .generate = moh_generate,
00785 };
00786 
00787 static int moh_add_file(struct mohclass *class, const char *filepath)
00788 {
00789    if (!class->allowed_files) {
00790       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00791          return -1;
00792       class->allowed_files = INITIAL_NUM_FILES;
00793    } else if (class->total_files == class->allowed_files) {
00794       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00795          class->allowed_files = 0;
00796          class->total_files = 0;
00797          return -1;
00798       }
00799       class->allowed_files *= 2;
00800    }
00801 
00802    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00803       return -1;
00804 
00805    class->total_files++;
00806 
00807    return 0;
00808 }
00809 
00810 static int moh_scan_files(struct mohclass *class) {
00811 
00812    DIR *files_DIR;
00813    struct dirent *files_dirent;
00814    char path[PATH_MAX];
00815    char filepath[PATH_MAX];
00816    char *ext;
00817    struct stat statbuf;
00818    int dirnamelen;
00819    int i;
00820    
00821    files_DIR = opendir(class->dir);
00822    if (!files_DIR) {
00823       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00824       return -1;
00825    }
00826 
00827    for (i = 0; i < class->total_files; i++)
00828       free(class->filearray[i]);
00829 
00830    class->total_files = 0;
00831    dirnamelen = strlen(class->dir) + 2;
00832    if (!getcwd(path, sizeof(path))) {
00833       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00834       return -1;
00835    }
00836    if (chdir(class->dir) < 0) {
00837       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00838       return -1;
00839    }
00840    while ((files_dirent = readdir(files_DIR))) {
00841       /* The file name must be at least long enough to have the file type extension */
00842       if ((strlen(files_dirent->d_name) < 4))
00843          continue;
00844 
00845       /* Skip files that starts with a dot */
00846       if (files_dirent->d_name[0] == '.')
00847          continue;
00848 
00849       /* Skip files without extensions... they are not audio */
00850       if (!strchr(files_dirent->d_name, '.'))
00851          continue;
00852 
00853       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00854 
00855       if (stat(filepath, &statbuf))
00856          continue;
00857 
00858       if (!S_ISREG(statbuf.st_mode))
00859          continue;
00860 
00861       if ((ext = strrchr(filepath, '.'))) {
00862          *ext = '\0';
00863          ext++;
00864       }
00865 
00866       /* if the file is present in multiple formats, ensure we only put it into the list once */
00867       for (i = 0; i < class->total_files; i++)
00868          if (!strcmp(filepath, class->filearray[i]))
00869             break;
00870 
00871       if (i == class->total_files) {
00872          if (moh_add_file(class, filepath))
00873             break;
00874       }
00875    }
00876 
00877    closedir(files_DIR);
00878    if (chdir(path) < 0) {
00879       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00880       return -1;
00881    }
00882    return class->total_files;
00883 }
00884 
00885 static int init_files_class(struct mohclass *class)
00886 {
00887    int res;
00888 
00889    res = moh_scan_files(class);
00890 
00891    if (res < 0) {
00892       return -1;
00893    }
00894 
00895    if (!res) {
00896       if (option_verbose > 2) {
00897          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00898                class->dir, class->name);
00899       }
00900       return -1;
00901    }
00902 
00903    if (strchr(class->args, 'r')) {
00904       ast_set_flag(class, MOH_RANDOMIZE);
00905    }
00906 
00907    return 0;
00908 }
00909 
00910 static int init_app_class(struct mohclass *class)
00911 {
00912 #ifdef HAVE_DAHDI
00913    int x;
00914 #endif
00915 
00916    if (!strcasecmp(class->mode, "custom")) {
00917       ast_set_flag(class, MOH_CUSTOM);
00918    } else if (!strcasecmp(class->mode, "mp3nb")) {
00919       ast_set_flag(class, MOH_SINGLE);
00920    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
00921       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
00922    } else if (!strcasecmp(class->mode, "quietmp3")) {
00923       ast_set_flag(class, MOH_QUIET);
00924    }
00925       
00926    class->srcfd = -1;
00927    class->pseudofd = -1;
00928 
00929 #ifdef HAVE_DAHDI
00930    /* Open /dev/zap/pseudo for timing...  Is
00931       there a better, yet reliable way to do this? */
00932    class->pseudofd = open(DAHDI_FILE_PSEUDO, O_RDONLY);
00933    if (class->pseudofd < 0) {
00934       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00935    } else {
00936       x = 320;
00937       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
00938    }
00939 #endif
00940 
00941    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
00942       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
00943       if (class->pseudofd > -1) {
00944          close(class->pseudofd);
00945          class->pseudofd = -1;
00946       }
00947       return -1;
00948    }
00949 
00950    return 0;
00951 }
00952 
00953 /*!
00954  * \note This function owns the reference it gets to moh
00955  */
00956 static int moh_register(struct mohclass *moh, int reload)
00957 {
00958    struct mohclass *mohclass = NULL;
00959 
00960    if ((mohclass = get_mohbyname(moh->name, 0))) {
00961       if (!mohclass->delete) {
00962          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00963          mohclass = mohclass_unref(mohclass);
00964          moh = mohclass_unref(moh);
00965          return -1;
00966       }
00967       mohclass = mohclass_unref(mohclass);
00968    }
00969 
00970    time(&moh->start);
00971    moh->start -= respawn_time;
00972    
00973    if (!strcasecmp(moh->mode, "files")) {
00974       if (init_files_class(moh)) {
00975          moh = mohclass_unref(moh);
00976          return -1;
00977       }
00978    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
00979          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
00980          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
00981       if (init_app_class(moh)) {
00982          moh = mohclass_unref(moh);
00983          return -1;
00984       }
00985    } else {
00986       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00987       moh = mohclass_unref(moh);
00988       return -1;
00989    }
00990 
00991    ao2_link(mohclasses, moh);
00992 
00993    moh = mohclass_unref(moh);
00994    
00995    return 0;
00996 }
00997 
00998 static void local_ast_moh_cleanup(struct ast_channel *chan)
00999 {
01000    if (chan->music_state) {
01001       free(chan->music_state);
01002       chan->music_state = NULL;
01003    }
01004 }
01005 
01006 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01007 {
01008    struct mohclass *mohclass = NULL;
01009    int res;
01010 
01011    /* The following is the order of preference for which class to use:
01012     * 1) The channels explicitly set musicclass, which should *only* be
01013     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01014     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01015     *    result of receiving a HOLD control frame, this should be the
01016     *    payload that came with the frame.
01017     * 3) The interpclass argument. This would be from the mohinterpret
01018     *    option from channel drivers. This is the same as the old musicclass
01019     *    option.
01020     * 4) The default class.
01021     */
01022    if (!ast_strlen_zero(chan->musicclass)) {
01023       mohclass = get_mohbyname(chan->musicclass, 1);
01024    }
01025    if (!mohclass && !ast_strlen_zero(mclass)) {
01026       mohclass = get_mohbyname(mclass, 1);
01027    }
01028    if (!mohclass && !ast_strlen_zero(interpclass)) {
01029       mohclass = get_mohbyname(interpclass, 1);
01030    }
01031    if (!mohclass) {
01032       mohclass = get_mohbyname("default", 1);
01033    }
01034 
01035    if (!mohclass) {
01036       return -1;
01037    }
01038 
01039    ast_set_flag(chan, AST_FLAG_MOH);
01040 
01041    if (mohclass->total_files) {
01042       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01043    } else {
01044       res = ast_activate_generator(chan, &mohgen, mohclass);
01045    }
01046 
01047    mohclass = mohclass_unref(mohclass);
01048 
01049    return res;
01050 }
01051 
01052 static void local_ast_moh_stop(struct ast_channel *chan)
01053 {
01054    ast_clear_flag(chan, AST_FLAG_MOH);
01055    ast_deactivate_generator(chan);
01056 
01057    if (chan->music_state) {
01058       if (chan->stream) {
01059          ast_closestream(chan->stream);
01060          chan->stream = NULL;
01061       }
01062    }
01063 }
01064 
01065 static void moh_class_destructor(void *obj)
01066 {
01067    struct mohclass *class = obj;
01068    struct mohdata *member;
01069 
01070    if (option_debug) {
01071       ast_log(LOG_DEBUG, "Destroying MOH class '%s'\n", class->name);
01072    }
01073 
01074    if (class->pid > 1) {
01075       char buff[8192];
01076       int bytes, tbytes = 0, stime = 0, pid = 0;
01077 
01078       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01079 
01080       stime = time(NULL) + 2;
01081       pid = class->pid;
01082       class->pid = 0;
01083 
01084       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01085        * to give the process a reason and time enough to kill off its
01086        * children. */
01087       killpg(pid, SIGHUP);
01088       usleep(100000);
01089       killpg(pid, SIGTERM);
01090       usleep(100000);
01091       killpg(pid, SIGKILL);
01092 
01093       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01094             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01095          tbytes = tbytes + bytes;
01096       }
01097 
01098       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01099 
01100       close(class->srcfd);
01101    }
01102 
01103    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01104       free(member);
01105    }
01106 
01107    if (class->thread) {
01108       pthread_cancel(class->thread);
01109       pthread_join(class->thread, NULL);
01110       class->thread = AST_PTHREADT_NULL;
01111    }
01112 
01113    if (class->filearray) {
01114       int i;
01115       for (i = 0; i < class->total_files; i++) {
01116          free(class->filearray[i]);
01117       }
01118       free(class->filearray);
01119       class->filearray = NULL;
01120    }
01121 }
01122 
01123 static struct mohclass *moh_class_malloc(void)
01124 {
01125    struct mohclass *class;
01126 
01127    if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01128       class->format = AST_FORMAT_SLINEAR;
01129    }
01130 
01131    return class;
01132 }
01133 
01134 static int moh_class_mark(void *obj, void *arg, int flags)
01135 {
01136    struct mohclass *class = obj;
01137 
01138    class->delete = 1;
01139 
01140    return 0;
01141 }
01142 
01143 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01144 {
01145    struct mohclass *class = obj;
01146 
01147    return class->delete ? CMP_MATCH : 0;
01148 }
01149 
01150 static int load_moh_classes(int reload)
01151 {
01152    struct ast_config *cfg;
01153    struct ast_variable *var;
01154    struct mohclass *class; 
01155    char *data;
01156    char *args;
01157    char *cat;
01158    int numclasses = 0;
01159    static int dep_warning = 0;
01160 
01161    cfg = ast_config_load("musiconhold.conf");
01162 
01163    if (!cfg) {
01164       return 0;
01165    }
01166 
01167    if (reload) {
01168       ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
01169    }
01170 
01171    cat = ast_category_browse(cfg, NULL);
01172    for (; cat; cat = ast_category_browse(cfg, cat)) {
01173       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files")) {
01174          continue;
01175       }
01176 
01177       if (!(class = moh_class_malloc())) {
01178          break;
01179       }
01180 
01181       ast_copy_string(class->name, cat, sizeof(class->name));  
01182 
01183       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01184          if (!strcasecmp(var->name, "mode")) {
01185             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01186          } else if (!strcasecmp(var->name, "directory")) {
01187             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01188          } else if (!strcasecmp(var->name, "application")) {
01189             ast_copy_string(class->args, var->value, sizeof(class->args));
01190          } else if (!strcasecmp(var->name, "random")) {
01191             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01192          } else if (!strcasecmp(var->name, "format")) {
01193             class->format = ast_getformatbyname(var->value);
01194             if (!class->format) {
01195                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01196                class->format = AST_FORMAT_SLINEAR;
01197             }
01198          }
01199       }
01200 
01201       if (ast_strlen_zero(class->dir)) {
01202          if (!strcasecmp(class->mode, "custom")) {
01203             ast_copy_string(class->dir, "nodir", sizeof(class->dir));
01204          } else {
01205             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01206             class = mohclass_unref(class);
01207             continue;
01208          }
01209       }
01210 
01211       if (ast_strlen_zero(class->mode)) {
01212          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01213          class = mohclass_unref(class);
01214          continue;
01215       }
01216 
01217       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01218          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01219          class = mohclass_unref(class);
01220          continue;
01221       }
01222 
01223       /* Don't leak a class when it's already registered */
01224       if (!moh_register(class, reload)) {
01225          numclasses++;
01226       }
01227    }
01228    
01229 
01230    /* Deprecated Old-School Configuration */
01231    for (var = ast_variable_browse(cfg, "classes"); var; var = var->next) {
01232       struct mohclass *tmp_class;
01233 
01234       if (!dep_warning) {
01235          ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
01236          dep_warning = 1;
01237       }
01238 
01239       if (!(data = strchr(var->value, ':'))) {
01240          continue;
01241       }
01242       *data++ = '\0';
01243 
01244       if ((args = strchr(data, ','))) {
01245          *args++ = '\0';
01246       }
01247 
01248       /* Only skip if this is a duplicate of an above item */
01249       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01250          tmp_class = mohclass_unref(tmp_class);
01251          continue;
01252       }
01253 
01254       if (!(class = moh_class_malloc())) {
01255          break;
01256       }
01257 
01258       class->deprecated = 1;
01259       ast_copy_string(class->name, var->name, sizeof(class->name));
01260       ast_copy_string(class->dir, data, sizeof(class->dir));
01261       ast_copy_string(class->mode, var->value, sizeof(class->mode));
01262       if (args) {
01263          ast_copy_string(class->args, args, sizeof(class->args));
01264       }
01265 
01266       moh_register(class, reload);
01267       class = NULL;
01268 
01269       numclasses++;
01270    }
01271 
01272    for (var = ast_variable_browse(cfg, "moh_files"); var; var = var->next) {
01273       struct mohclass *tmp_class;
01274 
01275       if (!dep_warning) {
01276          ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated!  Please refer to the sample configuration for information on the new syntax.\n");
01277          dep_warning = 1;
01278       }
01279 
01280       /* Only skip if this is a duplicate of an above item */
01281       if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01282          tmp_class = mohclass_unref(tmp_class);
01283          continue;
01284       }
01285 
01286       if ((args = strchr(var->value, ','))) {
01287          *args++ = '\0';
01288       }
01289 
01290       if (!(class = moh_class_malloc())) {
01291          break;
01292       }
01293 
01294       class->deprecated = 1;
01295       ast_copy_string(class->name, var->name, sizeof(class->name));
01296       ast_copy_string(class->dir, var->value, sizeof(class->dir));
01297       ast_copy_string(class->mode, "files", sizeof(class->mode));
01298       if (args) {
01299          ast_copy_string(class->args, args, sizeof(class->args));
01300       }
01301 
01302       moh_register(class, reload);
01303       class = NULL;
01304 
01305       numclasses++;
01306    }
01307 
01308    ast_config_destroy(cfg);
01309 
01310    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01311          moh_classes_delete_marked, NULL);
01312 
01313    return numclasses;
01314 }
01315 
01316 static void ast_moh_destroy(void)
01317 {
01318    if (option_verbose > 1) {
01319       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01320    }
01321 
01322    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01323 }
01324 
01325 static int moh_cli(int fd, int argc, char *argv[]) 
01326 {
01327    reload();
01328    return 0;
01329 }
01330 
01331 static int cli_files_show(int fd, int argc, char *argv[])
01332 {
01333    struct mohclass *class;
01334    struct ao2_iterator i;
01335 
01336    i = ao2_iterator_init(mohclasses, 0);
01337 
01338    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01339       int x;
01340 
01341       if (!class->total_files) {
01342          continue;
01343       }
01344 
01345       ast_cli(fd, "Class: %s\n", class->name);
01346 
01347       for (x = 0; x < class->total_files; x++) {
01348          ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01349       }
01350    }
01351 
01352    ao2_iterator_destroy(&i);
01353 
01354    return 0;
01355 }
01356 
01357 static int moh_classes_show(int fd, int argc, char *argv[])
01358 {
01359    struct mohclass *class;
01360    struct ao2_iterator i;
01361 
01362    i = ao2_iterator_init(mohclasses, 0);
01363 
01364    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01365       ast_cli(fd, "Class: %s\n", class->name);
01366       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01367       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01368       if (ast_test_flag(class, MOH_CUSTOM)) {
01369          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01370       }
01371       if (strcasecmp(class->mode, "files")) {
01372          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01373       }
01374    }
01375 
01376    ao2_iterator_destroy(&i);
01377 
01378    return 0;
01379 }
01380 
01381 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
01382    { "moh", "classes", "show"},
01383    moh_classes_show, NULL,
01384    NULL };
01385 
01386 static struct ast_cli_entry cli_moh_files_show_deprecated = {
01387    { "moh", "files", "show"},
01388    cli_files_show, NULL,
01389    NULL };
01390 
01391 static struct ast_cli_entry cli_moh[] = {
01392    { { "moh", "reload"},
01393    moh_cli, "Music On Hold",
01394    "Usage: moh reload\n    Rereads configuration\n" },
01395 
01396    { { "moh", "show", "classes"},
01397    moh_classes_show, "List MOH classes",
01398    "Usage: moh show classes\n    Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
01399 
01400    { { "moh", "show", "files"},
01401    cli_files_show, "List MOH file-based classes",
01402    "Usage: moh show files\n    Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
01403 };
01404 
01405 static int moh_class_hash(const void *obj, const int flags)
01406 {
01407    const struct mohclass *class = obj;
01408 
01409    return ast_str_case_hash(class->name);
01410 }
01411 
01412 static int moh_class_cmp(void *obj, void *arg, int flags)
01413 {
01414    struct mohclass *class = obj, *class2 = arg;
01415 
01416    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01417 }
01418 
01419 static int load_module(void)
01420 {
01421    int res;
01422 
01423    if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01424       return AST_MODULE_LOAD_DECLINE;
01425    }
01426 
01427    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01428       ast_log(LOG_WARNING, "No music on hold classes configured, "
01429             "disabling music on hold.\n");
01430    } else {
01431       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01432             local_ast_moh_cleanup);
01433    }
01434 
01435    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01436    ast_register_atexit(ast_moh_destroy);
01437    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01438    if (!res)
01439       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01440    if (!res)
01441       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01442    if (!res)
01443       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01444    if (!res)
01445       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01446 
01447    return AST_MODULE_LOAD_SUCCESS;
01448 }
01449 
01450 static int reload(void)
01451 {
01452    if (load_moh_classes(1)) {
01453       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01454             local_ast_moh_cleanup);
01455    }
01456 
01457    return 0;
01458 }
01459 
01460 static int moh_class_inuse(void *obj, void *arg, int flags)
01461 {
01462    struct mohclass *class = obj;
01463 
01464    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01465 }
01466 
01467 static int unload_module(void)
01468 {
01469    int res = 0;
01470    struct mohclass *class = NULL;
01471 
01472    /* XXX This check shouldn't be required if module ref counting was being used
01473     * properly ... */
01474    if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01475       class = mohclass_unref(class);
01476       res = -1;
01477    }
01478 
01479    if (res < 0) {
01480       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01481       return res;
01482    }
01483 
01484    ast_uninstall_music_functions();
01485 
01486    ast_moh_destroy();
01487 
01488    res = ast_unregister_application(app0);
01489    res |= ast_unregister_application(app1);
01490    res |= ast_unregister_application(app2);
01491    res |= ast_unregister_application(app3);
01492    res |= ast_unregister_application(app4);
01493 
01494    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01495 
01496    return res;
01497 }
01498 
01499 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
01500    .load = load_module,
01501    .unload = unload_module,
01502    .reload = reload,
01503 );

Generated on Thu Dec 17 13:33:37 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7