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