Mon Mar 31 07:38:04 2008

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

Generated on Mon Mar 31 07:38:04 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1