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

Generated on Thu Feb 5 16:25:51 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7