Tue Sep 30 01:19:39 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: 115418 $")
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 = ast_random() % 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          state->sample_queue -= f->samples;
00308          res = ast_write(chan, f);
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    int res = 0;
00873 
00874    AST_LIST_LOCK(&mohclasses);
00875    if ((mohclass = get_mohbyname(moh->name, 0))) {
00876       if (!mohclass->delete) {
00877          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00878          free(moh);
00879          AST_LIST_UNLOCK(&mohclasses);
00880          return -1;
00881       }
00882    }
00883    AST_LIST_UNLOCK(&mohclasses);
00884 
00885    time(&moh->start);
00886    moh->start -= respawn_time;
00887    
00888    if (!strcasecmp(moh->mode, "files")) {
00889       res = moh_scan_files(moh);
00890       if (res <= 0) {
00891          if (res == 0) {
00892              if (option_verbose > 2)
00893                               ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
00894          }
00895          ast_moh_free_class(&moh);
00896          return -1;
00897       }
00898       if (strchr(moh->args, 'r'))
00899          ast_set_flag(moh, MOH_RANDOMIZE);
00900    } 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")) {
00901 
00902       if (!strcasecmp(moh->mode, "custom"))
00903          ast_set_flag(moh, MOH_CUSTOM);
00904       else if (!strcasecmp(moh->mode, "mp3nb"))
00905          ast_set_flag(moh, MOH_SINGLE);
00906       else if (!strcasecmp(moh->mode, "quietmp3nb"))
00907          ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00908       else if (!strcasecmp(moh->mode, "quietmp3"))
00909          ast_set_flag(moh, MOH_QUIET);
00910       
00911       moh->srcfd = -1;
00912 #ifdef HAVE_ZAPTEL
00913       /* Open /dev/zap/pseudo for timing...  Is
00914          there a better, yet reliable way to do this? */
00915       moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00916       if (moh->pseudofd < 0) {
00917          ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00918       } else {
00919          x = 320;
00920          ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00921       }
00922 #else
00923       moh->pseudofd = -1;
00924 #endif
00925       if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
00926          ast_log(LOG_WARNING, "Unable to create moh...\n");
00927          if (moh->pseudofd > -1)
00928             close(moh->pseudofd);
00929          ast_moh_free_class(&moh);
00930          return -1;
00931       }
00932    } else {
00933       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00934       ast_moh_free_class(&moh);
00935       return -1;
00936    }
00937 
00938    AST_LIST_LOCK(&mohclasses);
00939    AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
00940    AST_LIST_UNLOCK(&mohclasses);
00941    
00942    return 0;
00943 }
00944 
00945 static void local_ast_moh_cleanup(struct ast_channel *chan)
00946 {
00947    if (chan->music_state) {
00948       free(chan->music_state);
00949       chan->music_state = NULL;
00950    }
00951 }
00952 
00953 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
00954 {
00955    struct mohclass *mohclass = NULL;
00956 
00957    /* The following is the order of preference for which class to use:
00958     * 1) The channels explicitly set musicclass, which should *only* be
00959     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
00960     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
00961     *    result of receiving a HOLD control frame, this should be the
00962     *    payload that came with the frame.
00963     * 3) The interpclass argument. This would be from the mohinterpret
00964     *    option from channel drivers. This is the same as the old musicclass
00965     *    option.
00966     * 4) The default class.
00967     */
00968    AST_LIST_LOCK(&mohclasses);
00969    if (!ast_strlen_zero(chan->musicclass))
00970       mohclass = get_mohbyname(chan->musicclass, 1);
00971    if (!mohclass && !ast_strlen_zero(mclass))
00972       mohclass = get_mohbyname(mclass, 1);
00973    if (!mohclass && !ast_strlen_zero(interpclass))
00974       mohclass = get_mohbyname(interpclass, 1);
00975    if (!mohclass) 
00976       mohclass = get_mohbyname("default", 1);
00977    if (mohclass)
00978       ast_atomic_fetchadd_int(&mohclass->inuse, +1);
00979    AST_LIST_UNLOCK(&mohclasses);
00980 
00981    if (!mohclass)
00982       return -1;
00983 
00984    ast_set_flag(chan, AST_FLAG_MOH);
00985    if (mohclass->total_files) {
00986       return ast_activate_generator(chan, &moh_file_stream, mohclass);
00987    } else
00988       return ast_activate_generator(chan, &mohgen, mohclass);
00989 }
00990 
00991 static void local_ast_moh_stop(struct ast_channel *chan)
00992 {
00993    ast_clear_flag(chan, AST_FLAG_MOH);
00994    ast_deactivate_generator(chan);
00995 
00996    if (chan->music_state) {
00997       if (chan->stream) {
00998          ast_closestream(chan->stream);
00999          chan->stream = NULL;
01000       }
01001    }
01002 }
01003 
01004 static struct mohclass *moh_class_malloc(void)
01005 {
01006    struct mohclass *class;
01007 
01008    if ((class = ast_calloc(1, sizeof(*class))))
01009       class->format = AST_FORMAT_SLINEAR;
01010 
01011    return class;
01012 }
01013 
01014 static int load_moh_classes(int reload)
01015 {
01016    struct ast_config *cfg;
01017    struct ast_variable *var;
01018    struct mohclass *class; 
01019    char *data;
01020    char *args;
01021    char *cat;
01022    int numclasses = 0;
01023    static int dep_warning = 0;
01024 
01025    cfg = ast_config_load("musiconhold.conf");
01026 
01027    if (!cfg)
01028       return 0;
01029 
01030    if (reload) {
01031       AST_LIST_LOCK(&mohclasses);
01032       AST_LIST_TRAVERSE(&mohclasses, class, list)
01033          class->delete = 1;
01034       AST_LIST_UNLOCK(&mohclasses);
01035    }
01036 
01037    cat = ast_category_browse(cfg, NULL);
01038    for (; cat; cat = ast_category_browse(cfg, cat)) {
01039       if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {       
01040          if (!(class = moh_class_malloc())) {
01041             break;
01042          }           
01043          ast_copy_string(class->name, cat, sizeof(class->name));  
01044          var = ast_variable_browse(cfg, cat);
01045          while (var) {
01046             if (!strcasecmp(var->name, "mode"))
01047                ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01048             else if (!strcasecmp(var->name, "directory"))
01049                ast_copy_string(class->dir, var->value, sizeof(class->dir));
01050             else if (!strcasecmp(var->name, "application"))
01051                ast_copy_string(class->args, var->value, sizeof(class->args));
01052             else if (!strcasecmp(var->name, "random"))
01053                ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01054             else if (!strcasecmp(var->name, "format")) {
01055                class->format = ast_getformatbyname(var->value);
01056                if (!class->format) {
01057                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01058                   class->format = AST_FORMAT_SLINEAR;
01059                }
01060             }
01061             var = var->next;
01062          }
01063 
01064          if (ast_strlen_zero(class->dir)) {
01065             if (!strcasecmp(class->mode, "custom")) {
01066                strcpy(class->dir, "nodir");
01067             } else {
01068                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01069                free(class);
01070                continue;
01071             }
01072          }
01073          if (ast_strlen_zero(class->mode)) {
01074             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01075             free(class);
01076             continue;
01077          }
01078          if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01079             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01080             free(class);
01081             continue;
01082          }
01083 
01084          /* Don't leak a class when it's already registered */
01085          moh_register(class, reload);
01086 
01087          numclasses++;
01088       }
01089    }
01090    
01091 
01092    /* Deprecated Old-School Configuration */
01093    var = ast_variable_browse(cfg, "classes");
01094    while (var) {
01095       if (!dep_warning) {
01096          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");
01097          dep_warning = 1;
01098       }
01099       data = strchr(var->value, ':');
01100       if (data) {
01101          *data++ = '\0';
01102          args = strchr(data, ',');
01103          if (args)
01104             *args++ = '\0';
01105          if (!(get_mohbyname(var->name, 0))) {        
01106             if (!(class = moh_class_malloc())) {
01107                break;
01108             }
01109             
01110             ast_copy_string(class->name, var->name, sizeof(class->name));
01111             ast_copy_string(class->dir, data, sizeof(class->dir));
01112             ast_copy_string(class->mode, var->value, sizeof(class->mode));
01113             if (args)
01114                ast_copy_string(class->args, args, sizeof(class->args));
01115             
01116             moh_register(class, reload);
01117             numclasses++;
01118          }
01119       }
01120       var = var->next;
01121    }
01122    var = ast_variable_browse(cfg, "moh_files");
01123    while (var) {
01124       if (!dep_warning) {
01125          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");
01126          dep_warning = 1;
01127       }
01128       if (!(get_mohbyname(var->name, 0))) {
01129          args = strchr(var->value, ',');
01130          if (args)
01131             *args++ = '\0';         
01132          if (!(class = moh_class_malloc())) {
01133             break;
01134          }
01135          
01136          ast_copy_string(class->name, var->name, sizeof(class->name));
01137          ast_copy_string(class->dir, var->value, sizeof(class->dir));
01138          strcpy(class->mode, "files");
01139          if (args)   
01140             ast_copy_string(class->args, args, sizeof(class->args));
01141          
01142          moh_register(class, reload);
01143          numclasses++;
01144       }
01145       var = var->next;
01146    }
01147 
01148    ast_config_destroy(cfg);
01149 
01150    return numclasses;
01151 }
01152 
01153 static int ast_moh_destroy_one(struct mohclass *moh)
01154 {
01155    char buff[8192];
01156    int bytes, tbytes = 0, stime = 0, pid = 0;
01157 
01158    if (moh) {
01159       if (moh->pid > 1) {
01160          ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01161          stime = time(NULL) + 2;
01162          pid = moh->pid;
01163          moh->pid = 0;
01164          /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01165           * to give the process a reason and time enough to kill off its
01166           * children. */
01167          kill(pid, SIGHUP);
01168          usleep(100000);
01169          kill(pid, SIGTERM);
01170          usleep(100000);
01171          kill(pid, SIGKILL);
01172          while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
01173             tbytes = tbytes + bytes;
01174          ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01175          close(moh->srcfd);
01176       }
01177       ast_moh_free_class(&moh);
01178    }
01179 
01180    return 0;
01181 }
01182 
01183 static void ast_moh_destroy(void)
01184 {
01185    struct mohclass *moh;
01186 
01187    if (option_verbose > 1)
01188       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01189 
01190    AST_LIST_LOCK(&mohclasses);
01191    while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
01192       ast_moh_destroy_one(moh);
01193    }
01194    AST_LIST_UNLOCK(&mohclasses);
01195 }
01196 
01197 static int moh_cli(int fd, int argc, char *argv[]) 
01198 {
01199    reload();
01200    return 0;
01201 }
01202 
01203 static int cli_files_show(int fd, int argc, char *argv[])
01204 {
01205    int i;
01206    struct mohclass *class;
01207 
01208    AST_LIST_LOCK(&mohclasses);
01209    AST_LIST_TRAVERSE(&mohclasses, class, list) {
01210       if (!class->total_files)
01211          continue;
01212 
01213       ast_cli(fd, "Class: %s\n", class->name);
01214       for (i = 0; i < class->total_files; i++)
01215          ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01216    }
01217    AST_LIST_UNLOCK(&mohclasses);
01218 
01219    return 0;
01220 }
01221 
01222 static int moh_classes_show(int fd, int argc, char *argv[])
01223 {
01224    struct mohclass *class;
01225 
01226    AST_LIST_LOCK(&mohclasses);
01227    AST_LIST_TRAVERSE(&mohclasses, class, list) {
01228       ast_cli(fd, "Class: %s\n", class->name);
01229       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01230       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01231       ast_cli(fd, "\tUse Count: %d\n", class->inuse);
01232       if (ast_test_flag(class, MOH_CUSTOM))
01233          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01234       if (strcasecmp(class->mode, "files"))
01235          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01236    }
01237    AST_LIST_UNLOCK(&mohclasses);
01238 
01239    return 0;
01240 }
01241 
01242 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
01243    { "moh", "classes", "show"},
01244    moh_classes_show, NULL,
01245    NULL };
01246 
01247 static struct ast_cli_entry cli_moh_files_show_deprecated = {
01248    { "moh", "files", "show"},
01249    cli_files_show, NULL,
01250    NULL };
01251 
01252 static struct ast_cli_entry cli_moh[] = {
01253    { { "moh", "reload"},
01254    moh_cli, "Music On Hold",
01255    "Usage: moh reload\n    Rereads configuration\n" },
01256 
01257    { { "moh", "show", "classes"},
01258    moh_classes_show, "List MOH classes",
01259    "Usage: moh show classes\n    Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
01260 
01261    { { "moh", "show", "files"},
01262    cli_files_show, "List MOH file-based classes",
01263    "Usage: moh show files\n    Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
01264 };
01265 
01266 static int init_classes(int reload) 
01267 {
01268    struct mohclass *moh;
01269     
01270    if (!load_moh_classes(reload))      /* Load classes from config */
01271       return 0;         /* Return if nothing is found */
01272 
01273    AST_LIST_LOCK(&mohclasses);
01274    AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
01275       if (reload && moh->delete) {
01276          AST_LIST_REMOVE_CURRENT(&mohclasses, list);
01277          if (!moh->inuse)
01278             ast_moh_destroy_one(moh);
01279       }
01280    }
01281    AST_LIST_TRAVERSE_SAFE_END
01282    AST_LIST_UNLOCK(&mohclasses);
01283 
01284    return 1;
01285 }
01286 
01287 static int load_module(void)
01288 {
01289    int res;
01290 
01291    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01292    ast_register_atexit(ast_moh_destroy);
01293    ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01294    if (!res)
01295       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01296    if (!res)
01297       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01298    if (!res)
01299       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01300    if (!res)
01301       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01302 
01303    if (!init_classes(0)) {    /* No music classes configured, so skip it */
01304       ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
01305    } else {
01306       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01307    }
01308 
01309    return 0;
01310 }
01311 
01312 static int reload(void)
01313 {
01314    if (init_classes(1))
01315       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01316 
01317    return 0;
01318 }
01319 
01320 static int unload_module(void)
01321 {
01322    int res = 0;
01323    struct mohclass *class = NULL;
01324 
01325    AST_LIST_LOCK(&mohclasses);
01326    AST_LIST_TRAVERSE(&mohclasses, class, list) {
01327       if (class->inuse > 0) {
01328          res = -1;
01329          break;
01330       }
01331    }
01332    AST_LIST_UNLOCK(&mohclasses);
01333    if (res < 0) {
01334       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01335       return res;
01336    }
01337 
01338    ast_uninstall_music_functions();
01339    ast_moh_destroy();
01340    res = ast_unregister_application(app0);
01341    res |= ast_unregister_application(app1);
01342    res |= ast_unregister_application(app2);
01343    res |= ast_unregister_application(app3);
01344    res |= ast_unregister_application(app4);
01345    ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01346    return res;
01347 }
01348 
01349 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
01350       .load = load_module,
01351       .unload = unload_module,
01352       .reload = reload,
01353           );

Generated on Tue Sep 30 01:19:39 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.6