Fri Mar 8 17:29:59 2019

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

Generated on 8 Mar 2019 for Asterisk - the Open Source PBX by  doxygen 1.6.1