Sun Aug 6 15:02:45 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:46 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2