00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
00045 #endif
00046 #include <unistd.h>
00047 #include <sys/ioctl.h>
00048
00049 #include "asterisk.h"
00050
00051 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
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;
00133 time_t start;
00134 pthread_t thread;
00135 struct mohdata *members;
00136
00137 int srcfd;
00138
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
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
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
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
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
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
00441 dup2(fds[1], STDOUT_FILENO);
00442
00443 for (x=3;x<8192;x++) {
00444 if (-1 != fcntl(x, F_GETFL)) {
00445 close(x);
00446 }
00447 }
00448
00449 chdir(class->dir);
00450 if (ast_test_flag(class, MOH_CUSTOM)) {
00451 execv(argv[0], argv);
00452 } else {
00453
00454 execv(LOCAL_MPG_123, argv);
00455
00456 execv(MPG_123, argv);
00457
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
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(;;) {
00485
00486 if (class->srcfd < 0) {
00487 if ((class->srcfd = spawn_mp3(class)) < 0) {
00488 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00489
00490 sleep(500);
00491 }
00492 }
00493 if (class->pseudofd > -1) {
00494
00495 res = read(class->pseudofd, buf, sizeof(buf));
00496 } else {
00497 long delta;
00498
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) {
00504 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));
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;
00511 }
00512 if (!class->members)
00513 continue;
00514
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
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
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
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
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
00783 closedir(files_DIR);
00784 chdir(path);
00785 return class->total_files;
00786 }
00787
00788 static int moh_register(struct mohclass *moh, int reload)
00789 {
00790 #ifdef ZAPATA_MOH
00791 int x;
00792 #endif
00793 ast_mutex_lock(&moh_lock);
00794 if (get_mohbyname(moh->name)) {
00795 if (reload) {
00796 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
00797 } else {
00798 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00799 }
00800 free(moh);
00801 ast_mutex_unlock(&moh_lock);
00802 return -1;
00803 }
00804 ast_mutex_unlock(&moh_lock);
00805
00806 time(&moh->start);
00807 moh->start -= respawn_time;
00808
00809 if (!strcasecmp(moh->mode, "files")) {
00810 if (!moh_scan_files(moh)) {
00811 ast_moh_free_class(&moh);
00812 return -1;
00813 }
00814 if (strchr(moh->args, 'r'))
00815 ast_set_flag(moh, MOH_RANDOMIZE);
00816 } 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")) {
00817
00818 if (!strcasecmp(moh->mode, "custom"))
00819 ast_set_flag(moh, MOH_CUSTOM);
00820 else if (!strcasecmp(moh->mode, "mp3nb"))
00821 ast_set_flag(moh, MOH_SINGLE);
00822 else if (!strcasecmp(moh->mode, "quietmp3nb"))
00823 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00824 else if (!strcasecmp(moh->mode, "quietmp3"))
00825 ast_set_flag(moh, MOH_QUIET);
00826
00827 moh->srcfd = -1;
00828 #ifdef ZAPATA_MOH
00829
00830
00831 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00832 if (moh->pseudofd < 0) {
00833 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00834 } else {
00835 x = 320;
00836 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00837 }
00838 #else
00839 moh->pseudofd = -1;
00840 #endif
00841 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
00842 ast_log(LOG_WARNING, "Unable to create moh...\n");
00843 if (moh->pseudofd > -1)
00844 close(moh->pseudofd);
00845 ast_moh_free_class(&moh);
00846 return -1;
00847 }
00848 } else {
00849 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00850 ast_moh_free_class(&moh);
00851 return -1;
00852 }
00853 ast_mutex_lock(&moh_lock);
00854 moh->next = mohclasses;
00855 mohclasses = moh;
00856 ast_mutex_unlock(&moh_lock);
00857 return 0;
00858 }
00859
00860 static void local_ast_moh_cleanup(struct ast_channel *chan)
00861 {
00862 if (chan->music_state) {
00863 free(chan->music_state);
00864 chan->music_state = NULL;
00865 }
00866 }
00867
00868 static int local_ast_moh_start(struct ast_channel *chan, char *class)
00869 {
00870 struct mohclass *mohclass;
00871
00872 if (ast_strlen_zero(class))
00873 class = chan->musicclass;
00874 if (ast_strlen_zero(class))
00875 class = "default";
00876 ast_mutex_lock(&moh_lock);
00877 mohclass = get_mohbyname(class);
00878 ast_mutex_unlock(&moh_lock);
00879
00880 if (!mohclass) {
00881 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
00882 return -1;
00883 }
00884
00885 ast_set_flag(chan, AST_FLAG_MOH);
00886 if (mohclass->total_files) {
00887 return ast_activate_generator(chan, &moh_file_stream, mohclass);
00888 } else
00889 return ast_activate_generator(chan, &mohgen, mohclass);
00890 }
00891
00892 static void local_ast_moh_stop(struct ast_channel *chan)
00893 {
00894 ast_clear_flag(chan, AST_FLAG_MOH);
00895 ast_deactivate_generator(chan);
00896
00897 if (chan->music_state) {
00898 if (chan->stream) {
00899 ast_closestream(chan->stream);
00900 chan->stream = NULL;
00901 }
00902 }
00903 }
00904
00905 static struct mohclass *moh_class_malloc(void)
00906 {
00907 struct mohclass *class;
00908
00909 class = malloc(sizeof(struct mohclass));
00910
00911 if (!class)
00912 return NULL;
00913
00914 memset(class, 0, sizeof(struct mohclass));
00915
00916 class->format = AST_FORMAT_SLINEAR;
00917
00918 return class;
00919 }
00920
00921 static int load_moh_classes(int reload)
00922 {
00923 struct ast_config *cfg;
00924 struct ast_variable *var;
00925 struct mohclass *class;
00926 char *data;
00927 char *args;
00928 char *cat;
00929 int numclasses = 0;
00930 static int dep_warning = 0;
00931
00932 cfg = ast_config_load("musiconhold.conf");
00933
00934 if (!cfg)
00935 return 0;
00936
00937 cat = ast_category_browse(cfg, NULL);
00938 for (; cat; cat = ast_category_browse(cfg, cat)) {
00939 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
00940 class = moh_class_malloc();
00941 if (!class) {
00942 ast_log(LOG_WARNING, "Out of memory!\n");
00943 break;
00944 }
00945 ast_copy_string(class->name, cat, sizeof(class->name));
00946 var = ast_variable_browse(cfg, cat);
00947 while (var) {
00948 if (!strcasecmp(var->name, "mode"))
00949 ast_copy_string(class->mode, var->value, sizeof(class->mode));
00950 else if (!strcasecmp(var->name, "directory"))
00951 ast_copy_string(class->dir, var->value, sizeof(class->dir));
00952 else if (!strcasecmp(var->name, "application"))
00953 ast_copy_string(class->args, var->value, sizeof(class->args));
00954 else if (!strcasecmp(var->name, "random"))
00955 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
00956 else if (!strcasecmp(var->name, "format")) {
00957 class->format = ast_getformatbyname(var->value);
00958 if (!class->format) {
00959 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
00960 class->format = AST_FORMAT_SLINEAR;
00961 }
00962 }
00963 var = var->next;
00964 }
00965
00966 if (ast_strlen_zero(class->dir)) {
00967 if (!strcasecmp(class->mode, "custom")) {
00968 strcpy(class->dir, "nodir");
00969 } else {
00970 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
00971 free(class);
00972 continue;
00973 }
00974 }
00975 if (ast_strlen_zero(class->mode)) {
00976 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
00977 free(class);
00978 continue;
00979 }
00980 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
00981 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
00982 free(class);
00983 continue;
00984 }
00985
00986
00987 moh_register(class, reload);
00988
00989 numclasses++;
00990 }
00991 }
00992
00993
00994
00995 var = ast_variable_browse(cfg, "classes");
00996 while (var) {
00997 if (!dep_warning) {
00998 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");
00999 dep_warning = 1;
01000 }
01001 data = strchr(var->value, ':');
01002 if (data) {
01003 *data++ = '\0';
01004 args = strchr(data, ',');
01005 if (args)
01006 *args++ = '\0';
01007 if (!(get_mohbyname(var->name))) {
01008 class = moh_class_malloc();
01009 if (!class) {
01010 ast_log(LOG_WARNING, "Out of memory!\n");
01011 return numclasses;
01012 }
01013
01014 ast_copy_string(class->name, var->name, sizeof(class->name));
01015 ast_copy_string(class->dir, data, sizeof(class->dir));
01016 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01017 if (args)
01018 ast_copy_string(class->args, args, sizeof(class->args));
01019
01020 moh_register(class, reload);
01021 numclasses++;
01022 }
01023 }
01024 var = var->next;
01025 }
01026 var = ast_variable_browse(cfg, "moh_files");
01027 while (var) {
01028 if (!dep_warning) {
01029 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");
01030 dep_warning = 1;
01031 }
01032 if (!(get_mohbyname(var->name))) {
01033 args = strchr(var->value, ',');
01034 if (args)
01035 *args++ = '\0';
01036 class = moh_class_malloc();
01037 if (!class) {
01038 ast_log(LOG_WARNING, "Out of memory!\n");
01039 return numclasses;
01040 }
01041
01042 ast_copy_string(class->name, var->name, sizeof(class->name));
01043 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01044 strcpy(class->mode, "files");
01045 if (args)
01046 ast_copy_string(class->args, args, sizeof(class->args));
01047
01048 moh_register(class, reload);
01049 numclasses++;
01050 }
01051 var = var->next;
01052 }
01053
01054 ast_config_destroy(cfg);
01055
01056 return numclasses;
01057 }
01058
01059 static void ast_moh_destroy(void)
01060 {
01061 struct mohclass *moh, *tmp;
01062 char buff[8192];
01063 int bytes, tbytes=0, stime = 0, pid = 0;
01064
01065 if (option_verbose > 1)
01066 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01067 ast_mutex_lock(&moh_lock);
01068 moh = mohclasses;
01069
01070 while (moh) {
01071 if (moh->pid) {
01072 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01073 stime = time(NULL) + 2;
01074 pid = moh->pid;
01075 moh->pid = 0;
01076 kill(pid, SIGKILL);
01077 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
01078 tbytes = tbytes + bytes;
01079 }
01080 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01081 close(moh->srcfd);
01082 }
01083 tmp = moh;
01084 moh = moh->next;
01085 ast_moh_free_class(&tmp);
01086 }
01087 mohclasses = NULL;
01088 ast_mutex_unlock(&moh_lock);
01089 }
01090
01091 static void moh_on_off(int on)
01092 {
01093 struct ast_channel *chan = NULL;
01094
01095 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
01096 if (ast_test_flag(chan, AST_FLAG_MOH)) {
01097 if (on)
01098 local_ast_moh_start(chan, NULL);
01099 else
01100 ast_deactivate_generator(chan);
01101 }
01102 ast_mutex_unlock(&chan->lock);
01103 }
01104 }
01105
01106 static int moh_cli(int fd, int argc, char *argv[])
01107 {
01108 int x;
01109
01110 moh_on_off(0);
01111 ast_moh_destroy();
01112 x = load_moh_classes(1);
01113 moh_on_off(1);
01114 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
01115 return 0;
01116 }
01117
01118 static int cli_files_show(int fd, int argc, char *argv[])
01119 {
01120 int i;
01121 struct mohclass *class;
01122
01123 ast_mutex_lock(&moh_lock);
01124 for (class = mohclasses; class; class = class->next) {
01125 if (!class->total_files)
01126 continue;
01127
01128 ast_cli(fd, "Class: %s\n", class->name);
01129 for (i = 0; i < class->total_files; i++)
01130 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01131 }
01132 ast_mutex_unlock(&moh_lock);
01133
01134 return 0;
01135 }
01136
01137 static int moh_classes_show(int fd, int argc, char *argv[])
01138 {
01139 struct mohclass *class;
01140
01141 ast_mutex_lock(&moh_lock);
01142 for (class = mohclasses; class; class = class->next) {
01143 ast_cli(fd, "Class: %s\n", class->name);
01144 ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
01145 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
01146 if (ast_test_flag(class, MOH_CUSTOM))
01147 ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
01148 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01149 }
01150 ast_mutex_unlock(&moh_lock);
01151
01152 return 0;
01153 }
01154
01155 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
01156
01157 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
01158
01159 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};
01160
01161 static int init_classes(int reload)
01162 {
01163 struct mohclass *moh;
01164
01165 if (!load_moh_classes(reload))
01166 return 0;
01167 moh = mohclasses;
01168 while (moh) {
01169 if (moh->total_files)
01170 moh_scan_files(moh);
01171 moh = moh->next;
01172 }
01173 return 1;
01174 }
01175
01176 int load_module(void)
01177 {
01178 int res;
01179
01180 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01181 ast_register_atexit(ast_moh_destroy);
01182 ast_cli_register(&cli_moh);
01183 ast_cli_register(&cli_moh_files_show);
01184 ast_cli_register(&cli_moh_classes_show);
01185 if (!res)
01186 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01187 if (!res)
01188 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01189 if (!res)
01190 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01191 if (!res)
01192 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01193
01194 if (!init_classes(0)) {
01195 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
01196 } else {
01197 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01198 }
01199
01200 return 0;
01201 }
01202
01203 int reload(void)
01204 {
01205 if (init_classes(1))
01206 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01207
01208 return 0;
01209 }
01210
01211 int unload_module(void)
01212 {
01213 return -1;
01214 }
01215
01216 char *description(void)
01217 {
01218 return "Music On Hold Resource";
01219 }
01220
01221 int usecount(void)
01222 {
01223
01224
01225 #if 0
01226 int res;
01227 STANDARD_USECOUNT(res);
01228 return res;
01229 #else
01230 return 1;
01231 #endif
01232 }
01233
01234 char *key()
01235 {
01236 return ASTERISK_GPL_KEY;
01237 }