Wed Oct 28 15:47:55 2009

Asterisk developer's documentation


res_musiconhold.c

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

Generated on Wed Oct 28 15:47:55 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6