Thu May 14 15:13:03 2009

Asterisk developer's documentation


res_musiconhold.c

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

Generated on Thu May 14 15:13:03 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7