Sat Aug 6 00:39:31 2011

Asterisk developer's documentation


res_musiconhold.c

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

Generated on Sat Aug 6 00:39:32 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7