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: 38825 $")
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 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 (chan->stream) {
00184 ast_closestream(chan->stream);
00185 chan->stream = NULL;
00186 }
00187 if (option_verbose > 2)
00188 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00189
00190 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00191 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00192 }
00193 state->save_pos = state->pos + 1;
00194 }
00195 }
00196
00197
00198 static int ast_moh_files_next(struct ast_channel *chan)
00199 {
00200 struct moh_files_state *state = chan->music_state;
00201 int tries;
00202
00203 if (state->save_pos) {
00204 state->pos = state->save_pos - 1;
00205 state->save_pos = 0;
00206 } else {
00207
00208 for (tries=0;tries < 20;tries++) {
00209 state->samples = 0;
00210 if (chan->stream) {
00211 ast_closestream(chan->stream);
00212 chan->stream = NULL;
00213 state->pos++;
00214 }
00215
00216 if (ast_test_flag(state->class, MOH_RANDOMIZE))
00217 state->pos = rand();
00218
00219 state->pos %= state->class->total_files;
00220
00221
00222 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00223 break;
00224
00225 }
00226 }
00227
00228 state->pos = state->pos % state->class->total_files;
00229
00230 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00231 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00232 state->pos++;
00233 return -1;
00234 }
00235
00236 if (option_debug)
00237 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00238
00239 if (state->samples)
00240 ast_seekstream(chan->stream, state->samples, SEEK_SET);
00241
00242 return 0;
00243 }
00244
00245
00246 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
00247 {
00248 struct ast_frame *f = NULL;
00249
00250 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00251 if (!ast_moh_files_next(chan))
00252 f = ast_readframe(chan->stream);
00253 }
00254
00255 return f;
00256 }
00257
00258 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00259 {
00260 struct moh_files_state *state = chan->music_state;
00261 struct ast_frame *f = NULL;
00262 int res = 0;
00263
00264 state->sample_queue += samples;
00265
00266 while (state->sample_queue > 0) {
00267 if ((f = moh_files_readframe(chan))) {
00268 state->samples += f->samples;
00269 res = ast_write(chan, f);
00270 state->sample_queue -= f->samples;
00271 ast_frfree(f);
00272 if (res < 0) {
00273 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00274 return -1;
00275 }
00276 } else
00277 return -1;
00278 }
00279 return res;
00280 }
00281
00282
00283 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00284 {
00285 struct moh_files_state *state;
00286 struct mohclass *class = params;
00287 int allocated = 0;
00288
00289 if (!chan->music_state && (state = malloc(sizeof(struct moh_files_state)))) {
00290 chan->music_state = state;
00291 allocated = 1;
00292 } else
00293 state = chan->music_state;
00294
00295 if (state) {
00296 if (allocated || state->class != class) {
00297
00298 memset(state, 0, sizeof(struct moh_files_state));
00299 state->class = class;
00300 }
00301
00302 state->origwfmt = chan->writeformat;
00303
00304 if (option_verbose > 2)
00305 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
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
00440 if (option_highpriority)
00441 ast_set_priority(0);
00442
00443 close(fds[0]);
00444
00445 dup2(fds[1], STDOUT_FILENO);
00446
00447 for (x=3;x<8192;x++) {
00448 if (-1 != fcntl(x, F_GETFL)) {
00449 close(x);
00450 }
00451 }
00452
00453 chdir(class->dir);
00454 if (ast_test_flag(class, MOH_CUSTOM)) {
00455 execv(argv[0], argv);
00456 } else {
00457
00458 execv(LOCAL_MPG_123, argv);
00459
00460 execv(MPG_123, argv);
00461
00462 execvp("mpg123", argv);
00463 }
00464 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00465 close(fds[1]);
00466 exit(1);
00467 } else {
00468
00469 close(fds[1]);
00470 }
00471 return fds[0];
00472 }
00473
00474 static void *monmp3thread(void *data)
00475 {
00476 #define MOH_MS_INTERVAL 100
00477
00478 struct mohclass *class = data;
00479 struct mohdata *moh;
00480 char buf[8192];
00481 short sbuf[8192];
00482 int res, res2;
00483 int len;
00484 struct timeval tv, tv_tmp;
00485
00486 tv.tv_sec = 0;
00487 tv.tv_usec = 0;
00488 for(;;) {
00489 pthread_testcancel();
00490
00491 if (class->srcfd < 0) {
00492 if ((class->srcfd = spawn_mp3(class)) < 0) {
00493 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00494
00495 sleep(500);
00496 pthread_testcancel();
00497 }
00498 }
00499 if (class->pseudofd > -1) {
00500
00501 res = read(class->pseudofd, buf, sizeof(buf));
00502 pthread_testcancel();
00503 } else {
00504 long delta;
00505
00506 tv_tmp = ast_tvnow();
00507 if (ast_tvzero(tv))
00508 tv = tv_tmp;
00509 delta = ast_tvdiff_ms(tv_tmp, tv);
00510 if (delta < MOH_MS_INTERVAL) {
00511 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));
00512 usleep(1000 * (MOH_MS_INTERVAL - delta));
00513 pthread_testcancel();
00514 } else {
00515 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00516 tv = tv_tmp;
00517 }
00518 res = 8 * MOH_MS_INTERVAL;
00519 }
00520 if (!class->members)
00521 continue;
00522
00523 len = ast_codec_get_len(class->format, res);
00524
00525 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00526 if (!res2) {
00527 close(class->srcfd);
00528 class->srcfd = -1;
00529 pthread_testcancel();
00530 if (class->pid) {
00531 kill(class->pid, SIGHUP);
00532 usleep(100000);
00533 kill(class->pid, SIGTERM);
00534 usleep(100000);
00535 kill(class->pid, SIGKILL);
00536 class->pid = 0;
00537 }
00538 } else
00539 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
00540 continue;
00541 }
00542 pthread_testcancel();
00543 ast_mutex_lock(&moh_lock);
00544 moh = class->members;
00545 while (moh) {
00546
00547 if ((res = write(moh->pipe[1], sbuf, res2)) != res2)
00548 if (option_debug)
00549 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
00550 moh = moh->next;
00551 }
00552 ast_mutex_unlock(&moh_lock);
00553 }
00554 return NULL;
00555 }
00556
00557 static int moh0_exec(struct ast_channel *chan, void *data)
00558 {
00559 if (ast_moh_start(chan, data)) {
00560 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00561 return -1;
00562 }
00563 while (!ast_safe_sleep(chan, 10000));
00564 ast_moh_stop(chan);
00565 return -1;
00566 }
00567
00568 static int moh1_exec(struct ast_channel *chan, void *data)
00569 {
00570 int res;
00571 if (!data || !atoi(data)) {
00572 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00573 return -1;
00574 }
00575 if (ast_moh_start(chan, NULL)) {
00576 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00577 return -1;
00578 }
00579 res = ast_safe_sleep(chan, atoi(data) * 1000);
00580 ast_moh_stop(chan);
00581 return res;
00582 }
00583
00584 static int moh2_exec(struct ast_channel *chan, void *data)
00585 {
00586 if (ast_strlen_zero(data)) {
00587 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00588 return -1;
00589 }
00590 strncpy(chan->musicclass, data, sizeof(chan->musicclass) - 1);
00591 return 0;
00592 }
00593
00594 static int moh3_exec(struct ast_channel *chan, void *data)
00595 {
00596 char *class = NULL;
00597 if (data && strlen(data))
00598 class = data;
00599 if (ast_moh_start(chan, class))
00600 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00601
00602 return 0;
00603 }
00604
00605 static int moh4_exec(struct ast_channel *chan, void *data)
00606 {
00607 ast_moh_stop(chan);
00608
00609 return 0;
00610 }
00611
00612 static struct mohclass *get_mohbyname(char *name)
00613 {
00614 struct mohclass *moh;
00615 moh = mohclasses;
00616 while (moh) {
00617 if (!strcasecmp(name, moh->name))
00618 return moh;
00619 moh = moh->next;
00620 }
00621 return NULL;
00622 }
00623
00624 static struct mohdata *mohalloc(struct mohclass *cl)
00625 {
00626 struct mohdata *moh;
00627 long flags;
00628 moh = malloc(sizeof(struct mohdata));
00629 if (!moh)
00630 return NULL;
00631 memset(moh, 0, sizeof(struct mohdata));
00632 if (pipe(moh->pipe)) {
00633 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00634 free(moh);
00635 return NULL;
00636 }
00637
00638 flags = fcntl(moh->pipe[0], F_GETFL);
00639 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00640 flags = fcntl(moh->pipe[1], F_GETFL);
00641 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00642 moh->parent = cl;
00643 moh->next = cl->members;
00644 cl->members = moh;
00645 return moh;
00646 }
00647
00648 static void moh_release(struct ast_channel *chan, void *data)
00649 {
00650 struct mohdata *moh = data, *prev, *cur;
00651 int oldwfmt;
00652 ast_mutex_lock(&moh_lock);
00653
00654 prev = NULL;
00655 cur = moh->parent->members;
00656 while (cur) {
00657 if (cur == moh) {
00658 if (prev)
00659 prev->next = cur->next;
00660 else
00661 moh->parent->members = cur->next;
00662 break;
00663 }
00664 prev = cur;
00665 cur = cur->next;
00666 }
00667 ast_mutex_unlock(&moh_lock);
00668 close(moh->pipe[0]);
00669 close(moh->pipe[1]);
00670 oldwfmt = moh->origwfmt;
00671 free(moh);
00672 if (chan) {
00673 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
00674 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
00675 if (option_verbose > 2)
00676 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00677 }
00678 }
00679
00680 static void *moh_alloc(struct ast_channel *chan, void *params)
00681 {
00682 struct mohdata *res;
00683 struct mohclass *class = params;
00684
00685 res = mohalloc(class);
00686 if (res) {
00687 res->origwfmt = chan->writeformat;
00688 if (ast_set_write_format(chan, class->format)) {
00689 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00690 moh_release(NULL, res);
00691 res = NULL;
00692 }
00693 if (option_verbose > 2)
00694 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00695 }
00696 return res;
00697 }
00698
00699 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00700 {
00701 struct ast_frame f;
00702 struct mohdata *moh = data;
00703 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00704 int res;
00705
00706 if (!moh->parent->pid)
00707 return -1;
00708
00709 len = ast_codec_get_len(moh->parent->format, samples);
00710
00711 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00712 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00713 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00714 }
00715 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00716 #if 0
00717 if (res != len) {
00718 ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
00719 }
00720 #endif
00721 if (res <= 0)
00722 return 0;
00723
00724 memset(&f, 0, sizeof(f));
00725
00726 f.frametype = AST_FRAME_VOICE;
00727 f.subclass = moh->parent->format;
00728 f.mallocd = 0;
00729 f.datalen = res;
00730 f.data = buf + AST_FRIENDLY_OFFSET / 2;
00731 f.offset = AST_FRIENDLY_OFFSET;
00732 f.samples = ast_codec_get_samples(&f);
00733
00734 if (ast_write(chan, &f) < 0) {
00735 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00736 return -1;
00737 }
00738
00739 return 0;
00740 }
00741
00742 static struct ast_generator mohgen =
00743 {
00744 alloc: moh_alloc,
00745 release: moh_release,
00746 generate: moh_generate,
00747 };
00748
00749 static int moh_scan_files(struct mohclass *class) {
00750
00751 DIR *files_DIR;
00752 struct dirent *files_dirent;
00753 char path[512];
00754 char filepath[MAX_MOHFILE_LEN];
00755 char *ext;
00756 struct stat statbuf;
00757 int dirnamelen;
00758 int i;
00759
00760 files_DIR = opendir(class->dir);
00761 if (!files_DIR) {
00762 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
00763 return -1;
00764 }
00765
00766 class->total_files = 0;
00767 dirnamelen = strlen(class->dir) + 2;
00768 getcwd(path, 512);
00769 chdir(class->dir);
00770 memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
00771 while ((files_dirent = readdir(files_DIR))) {
00772 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
00773 continue;
00774
00775 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
00776
00777 if (stat(filepath, &statbuf))
00778 continue;
00779
00780 if (!S_ISREG(statbuf.st_mode))
00781 continue;
00782
00783 if ((ext = strrchr(filepath, '.'))) {
00784 *ext = '\0';
00785 ext++;
00786 }
00787
00788
00789 for (i = 0; i < class->total_files; i++)
00790 if (!strcmp(filepath, class->filearray[i]))
00791 break;
00792
00793 if (i == class->total_files)
00794 strcpy(class->filearray[class->total_files++], filepath);
00795
00796
00797 if (class->total_files == MAX_MOHFILES)
00798 break;
00799
00800 }
00801
00802 closedir(files_DIR);
00803 chdir(path);
00804 return class->total_files;
00805 }
00806
00807 static int moh_register(struct mohclass *moh, int reload)
00808 {
00809 #ifdef ZAPATA_MOH
00810 int x;
00811 #endif
00812 ast_mutex_lock(&moh_lock);
00813 if (get_mohbyname(moh->name)) {
00814 if (reload) {
00815 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
00816 } else {
00817 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00818 }
00819 free(moh);
00820 ast_mutex_unlock(&moh_lock);
00821 return -1;
00822 }
00823 ast_mutex_unlock(&moh_lock);
00824
00825 time(&moh->start);
00826 moh->start -= respawn_time;
00827
00828 if (!strcasecmp(moh->mode, "files")) {
00829 if (!moh_scan_files(moh)) {
00830 ast_moh_free_class(&moh);
00831 return -1;
00832 }
00833 if (strchr(moh->args, 'r'))
00834 ast_set_flag(moh, MOH_RANDOMIZE);
00835 } 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")) {
00836
00837 if (!strcasecmp(moh->mode, "custom"))
00838 ast_set_flag(moh, MOH_CUSTOM);
00839 else if (!strcasecmp(moh->mode, "mp3nb"))
00840 ast_set_flag(moh, MOH_SINGLE);
00841 else if (!strcasecmp(moh->mode, "quietmp3nb"))
00842 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00843 else if (!strcasecmp(moh->mode, "quietmp3"))
00844 ast_set_flag(moh, MOH_QUIET);
00845
00846 moh->srcfd = -1;
00847 #ifdef ZAPATA_MOH
00848
00849
00850 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00851 if (moh->pseudofd < 0) {
00852 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00853 } else {
00854 x = 320;
00855 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00856 }
00857 #else
00858 moh->pseudofd = -1;
00859 #endif
00860 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
00861 ast_log(LOG_WARNING, "Unable to create moh...\n");
00862 if (moh->pseudofd > -1)
00863 close(moh->pseudofd);
00864 ast_moh_free_class(&moh);
00865 return -1;
00866 }
00867 } else {
00868 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00869 ast_moh_free_class(&moh);
00870 return -1;
00871 }
00872 ast_mutex_lock(&moh_lock);
00873 moh->next = mohclasses;
00874 mohclasses = moh;
00875 ast_mutex_unlock(&moh_lock);
00876 return 0;
00877 }
00878
00879 static void local_ast_moh_cleanup(struct ast_channel *chan)
00880 {
00881 if (chan->music_state) {
00882 free(chan->music_state);
00883 chan->music_state = NULL;
00884 }
00885 }
00886
00887 static int local_ast_moh_start(struct ast_channel *chan, char *class)
00888 {
00889 struct mohclass *mohclass;
00890
00891 if (ast_strlen_zero(class))
00892 class = chan->musicclass;
00893 if (ast_strlen_zero(class))
00894 class = "default";
00895 ast_mutex_lock(&moh_lock);
00896 mohclass = get_mohbyname(class);
00897 ast_mutex_unlock(&moh_lock);
00898
00899 if (!mohclass) {
00900 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
00901 return -1;
00902 }
00903
00904 ast_set_flag(chan, AST_FLAG_MOH);
00905 if (mohclass->total_files) {
00906 return ast_activate_generator(chan, &moh_file_stream, mohclass);
00907 } else
00908 return ast_activate_generator(chan, &mohgen, mohclass);
00909 }
00910
00911 static void local_ast_moh_stop(struct ast_channel *chan)
00912 {
00913 ast_clear_flag(chan, AST_FLAG_MOH);
00914 ast_deactivate_generator(chan);
00915
00916 if (chan->music_state) {
00917 if (chan->stream) {
00918 ast_closestream(chan->stream);
00919 chan->stream = NULL;
00920 }
00921 }
00922 }
00923
00924 static struct mohclass *moh_class_malloc(void)
00925 {
00926 struct mohclass *class;
00927
00928 class = malloc(sizeof(struct mohclass));
00929
00930 if (!class)
00931 return NULL;
00932
00933 memset(class, 0, sizeof(struct mohclass));
00934
00935 class->format = AST_FORMAT_SLINEAR;
00936
00937 return class;
00938 }
00939
00940 static int load_moh_classes(int reload)
00941 {
00942 struct ast_config *cfg;
00943 struct ast_variable *var;
00944 struct mohclass *class;
00945 char *data;
00946 char *args;
00947 char *cat;
00948 int numclasses = 0;
00949 static int dep_warning = 0;
00950
00951 cfg = ast_config_load("musiconhold.conf");
00952
00953 if (!cfg)
00954 return 0;
00955
00956 cat = ast_category_browse(cfg, NULL);
00957 for (; cat; cat = ast_category_browse(cfg, cat)) {
00958 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
00959 class = moh_class_malloc();
00960 if (!class) {
00961 ast_log(LOG_WARNING, "Out of memory!\n");
00962 break;
00963 }
00964 ast_copy_string(class->name, cat, sizeof(class->name));
00965 var = ast_variable_browse(cfg, cat);
00966 while (var) {
00967 if (!strcasecmp(var->name, "mode"))
00968 ast_copy_string(class->mode, var->value, sizeof(class->mode));
00969 else if (!strcasecmp(var->name, "directory"))
00970 ast_copy_string(class->dir, var->value, sizeof(class->dir));
00971 else if (!strcasecmp(var->name, "application"))
00972 ast_copy_string(class->args, var->value, sizeof(class->args));
00973 else if (!strcasecmp(var->name, "random"))
00974 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
00975 else if (!strcasecmp(var->name, "format")) {
00976 class->format = ast_getformatbyname(var->value);
00977 if (!class->format) {
00978 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
00979 class->format = AST_FORMAT_SLINEAR;
00980 }
00981 }
00982 var = var->next;
00983 }
00984
00985 if (ast_strlen_zero(class->dir)) {
00986 if (!strcasecmp(class->mode, "custom")) {
00987 strcpy(class->dir, "nodir");
00988 } else {
00989 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
00990 free(class);
00991 continue;
00992 }
00993 }
00994 if (ast_strlen_zero(class->mode)) {
00995 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
00996 free(class);
00997 continue;
00998 }
00999 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01000 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01001 free(class);
01002 continue;
01003 }
01004
01005
01006 moh_register(class, reload);
01007
01008 numclasses++;
01009 }
01010 }
01011
01012
01013
01014 var = ast_variable_browse(cfg, "classes");
01015 while (var) {
01016 if (!dep_warning) {
01017 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");
01018 dep_warning = 1;
01019 }
01020 data = strchr(var->value, ':');
01021 if (data) {
01022 *data++ = '\0';
01023 args = strchr(data, ',');
01024 if (args)
01025 *args++ = '\0';
01026 if (!(get_mohbyname(var->name))) {
01027 class = moh_class_malloc();
01028 if (!class) {
01029 ast_log(LOG_WARNING, "Out of memory!\n");
01030 return numclasses;
01031 }
01032
01033 ast_copy_string(class->name, var->name, sizeof(class->name));
01034 ast_copy_string(class->dir, data, sizeof(class->dir));
01035 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01036 if (args)
01037 ast_copy_string(class->args, args, sizeof(class->args));
01038
01039 moh_register(class, reload);
01040 numclasses++;
01041 }
01042 }
01043 var = var->next;
01044 }
01045 var = ast_variable_browse(cfg, "moh_files");
01046 while (var) {
01047 if (!dep_warning) {
01048 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");
01049 dep_warning = 1;
01050 }
01051 if (!(get_mohbyname(var->name))) {
01052 args = strchr(var->value, ',');
01053 if (args)
01054 *args++ = '\0';
01055 class = moh_class_malloc();
01056 if (!class) {
01057 ast_log(LOG_WARNING, "Out of memory!\n");
01058 return numclasses;
01059 }
01060
01061 ast_copy_string(class->name, var->name, sizeof(class->name));
01062 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01063 strcpy(class->mode, "files");
01064 if (args)
01065 ast_copy_string(class->args, args, sizeof(class->args));
01066
01067 moh_register(class, reload);
01068 numclasses++;
01069 }
01070 var = var->next;
01071 }
01072
01073 ast_config_destroy(cfg);
01074
01075 return numclasses;
01076 }
01077
01078 static void ast_moh_destroy(void)
01079 {
01080 struct mohclass *moh, *tmp;
01081 char buff[8192];
01082 int bytes, tbytes=0, stime = 0, pid = 0;
01083
01084 if (option_verbose > 1)
01085 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01086 ast_mutex_lock(&moh_lock);
01087 moh = mohclasses;
01088
01089 while (moh) {
01090 if (moh->pid) {
01091 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01092 stime = time(NULL) + 2;
01093 pid = moh->pid;
01094 moh->pid = 0;
01095
01096
01097
01098 kill(pid, SIGHUP);
01099 usleep(100000);
01100 kill(pid, SIGTERM);
01101 usleep(100000);
01102 kill(pid, SIGKILL);
01103 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
01104 tbytes = tbytes + bytes;
01105 }
01106 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01107 close(moh->srcfd);
01108 }
01109 tmp = moh;
01110 moh = moh->next;
01111 ast_moh_free_class(&tmp);
01112 }
01113 mohclasses = NULL;
01114 ast_mutex_unlock(&moh_lock);
01115 }
01116
01117 static void moh_on_off(int on)
01118 {
01119 struct ast_channel *chan = NULL;
01120
01121 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
01122 if (ast_test_flag(chan, AST_FLAG_MOH)) {
01123 if (on)
01124 local_ast_moh_start(chan, NULL);
01125 else
01126 ast_deactivate_generator(chan);
01127 }
01128 ast_mutex_unlock(&chan->lock);
01129 }
01130 }
01131
01132 static int moh_cli(int fd, int argc, char *argv[])
01133 {
01134 int x;
01135
01136 moh_on_off(0);
01137 ast_moh_destroy();
01138 x = load_moh_classes(1);
01139 moh_on_off(1);
01140 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
01141 return 0;
01142 }
01143
01144 static int cli_files_show(int fd, int argc, char *argv[])
01145 {
01146 int i;
01147 struct mohclass *class;
01148
01149 ast_mutex_lock(&moh_lock);
01150 for (class = mohclasses; class; class = class->next) {
01151 if (!class->total_files)
01152 continue;
01153
01154 ast_cli(fd, "Class: %s\n", class->name);
01155 for (i = 0; i < class->total_files; i++)
01156 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01157 }
01158 ast_mutex_unlock(&moh_lock);
01159
01160 return 0;
01161 }
01162
01163 static int moh_classes_show(int fd, int argc, char *argv[])
01164 {
01165 struct mohclass *class;
01166
01167 ast_mutex_lock(&moh_lock);
01168 for (class = mohclasses; class; class = class->next) {
01169 ast_cli(fd, "Class: %s\n", class->name);
01170 ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
01171 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
01172 if (ast_test_flag(class, MOH_CUSTOM))
01173 ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
01174 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01175 }
01176 ast_mutex_unlock(&moh_lock);
01177
01178 return 0;
01179 }
01180
01181 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
01182
01183 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
01184
01185 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};
01186
01187 static int init_classes(int reload)
01188 {
01189 struct mohclass *moh;
01190
01191 if (!load_moh_classes(reload))
01192 return 0;
01193 moh = mohclasses;
01194 while (moh) {
01195 if (moh->total_files)
01196 moh_scan_files(moh);
01197 moh = moh->next;
01198 }
01199 return 1;
01200 }
01201
01202 int load_module(void)
01203 {
01204 int res;
01205
01206 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01207 ast_register_atexit(ast_moh_destroy);
01208 ast_cli_register(&cli_moh);
01209 ast_cli_register(&cli_moh_files_show);
01210 ast_cli_register(&cli_moh_classes_show);
01211 if (!res)
01212 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01213 if (!res)
01214 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01215 if (!res)
01216 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01217 if (!res)
01218 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01219
01220 if (!init_classes(0)) {
01221 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
01222 } else {
01223 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01224 }
01225
01226 return 0;
01227 }
01228
01229 int reload(void)
01230 {
01231 if (init_classes(1))
01232 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01233
01234 return 0;
01235 }
01236
01237 int unload_module(void)
01238 {
01239 return -1;
01240 }
01241
01242 char *description(void)
01243 {
01244 return "Music On Hold Resource";
01245 }
01246
01247 int usecount(void)
01248 {
01249
01250
01251 #if 0
01252 int res;
01253 STANDARD_USECOUNT(res);
01254 return res;
01255 #else
01256 return 1;
01257 #endif
01258 }
01259
01260 char *key()
01261 {
01262 return ASTERISK_GPL_KEY;
01263 }