Fri May 26 01:45:36 2006

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

Generated on Fri May 26 01:45:36 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6