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: 16534 $")
00052
00053 #include "asterisk/lock.h"
00054 #include "asterisk/file.h"
00055 #include "asterisk/logger.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/options.h"
00059 #include "asterisk/module.h"
00060 #include "asterisk/translate.h"
00061 #include "asterisk/say.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/config.h"
00064 #include "asterisk/utils.h"
00065 #include "asterisk/cli.h"
00066
00067 #define MAX_MOHFILES 512
00068 #define MAX_MOHFILE_LEN 128
00069
00070 static char *app0 = "MusicOnHold";
00071 static char *app1 = "WaitMusicOnHold";
00072 static char *app2 = "SetMusicOnHold";
00073 static char *app3 = "StartMusicOnHold";
00074 static char *app4 = "StopMusicOnHold";
00075
00076 static char *synopsis0 = "Play Music On Hold indefinitely";
00077 static char *synopsis1 = "Wait, playing Music On Hold";
00078 static char *synopsis2 = "Set default Music On Hold class";
00079 static char *synopsis3 = "Play Music On Hold";
00080 static char *synopsis4 = "Stop Playing Music On Hold";
00081
00082 static char *descrip0 = "MusicOnHold(class): "
00083 "Plays hold music specified by class. If omitted, the default\n"
00084 "music source for the channel will be used. Set the default \n"
00085 "class with the SetMusicOnHold() application.\n"
00086 "Returns -1 on hangup.\n"
00087 "Never returns otherwise.\n";
00088
00089 static char *descrip1 = "WaitMusicOnHold(delay): "
00090 "Plays hold music specified number of seconds. Returns 0 when\n"
00091 "done, or -1 on hangup. If no hold music is available, the delay will\n"
00092 "still occur with no sound.\n";
00093
00094 static char *descrip2 = "SetMusicOnHold(class): "
00095 "Sets the default class for music on hold for a given channel. When\n"
00096 "music on hold is activated, this class will be used to select which\n"
00097 "music is played.\n";
00098
00099 static char *descrip3 = "StartMusicOnHold(class): "
00100 "Starts playing music on hold, uses default music class for channel.\n"
00101 "Starts playing music specified by class. If omitted, the default\n"
00102 "music source for the channel will be used. Always returns 0.\n";
00103
00104 static char *descrip4 = "StopMusicOnHold: "
00105 "Stops playing music on hold.\n";
00106
00107 static int respawn_time = 20;
00108
00109 struct moh_files_state {
00110 struct mohclass *class;
00111 int origwfmt;
00112 int samples;
00113 int sample_queue;
00114 unsigned char pos;
00115 unsigned char save_pos;
00116 };
00117
00118 #define MOH_QUIET (1 << 0)
00119 #define MOH_SINGLE (1 << 1)
00120 #define MOH_CUSTOM (1 << 2)
00121 #define MOH_RANDOMIZE (1 << 3)
00122
00123 struct mohclass {
00124 char name[MAX_MUSICCLASS];
00125 char dir[256];
00126 char args[256];
00127 char mode[80];
00128 char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
00129 unsigned int flags;
00130 int total_files;
00131 int format;
00132 int pid;
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 if (class->total_files == MAX_MOHFILES)
00784 break;
00785
00786 }
00787
00788 closedir(files_DIR);
00789 chdir(path);
00790 return class->total_files;
00791 }
00792
00793 static int moh_register(struct mohclass *moh, int reload)
00794 {
00795 #ifdef ZAPATA_MOH
00796 int x;
00797 #endif
00798 ast_mutex_lock(&moh_lock);
00799 if (get_mohbyname(moh->name)) {
00800 if (reload) {
00801 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
00802 } else {
00803 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00804 }
00805 free(moh);
00806 ast_mutex_unlock(&moh_lock);
00807 return -1;
00808 }
00809 ast_mutex_unlock(&moh_lock);
00810
00811 time(&moh->start);
00812 moh->start -= respawn_time;
00813
00814 if (!strcasecmp(moh->mode, "files")) {
00815 if (!moh_scan_files(moh)) {
00816 ast_moh_free_class(&moh);
00817 return -1;
00818 }
00819 if (strchr(moh->args, 'r'))
00820 ast_set_flag(moh, MOH_RANDOMIZE);
00821 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
00822
00823 if (!strcasecmp(moh->mode, "custom"))
00824 ast_set_flag(moh, MOH_CUSTOM);
00825 else if (!strcasecmp(moh->mode, "mp3nb"))
00826 ast_set_flag(moh, MOH_SINGLE);
00827 else if (!strcasecmp(moh->mode, "quietmp3nb"))
00828 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00829 else if (!strcasecmp(moh->mode, "quietmp3"))
00830 ast_set_flag(moh, MOH_QUIET);
00831
00832 moh->srcfd = -1;
00833 #ifdef ZAPATA_MOH
00834
00835
00836 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00837 if (moh->pseudofd < 0) {
00838 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00839 } else {
00840 x = 320;
00841 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00842 }
00843 #else
00844 moh->pseudofd = -1;
00845 #endif
00846 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
00847 ast_log(LOG_WARNING, "Unable to create moh...\n");
00848 if (moh->pseudofd > -1)
00849 close(moh->pseudofd);
00850 ast_moh_free_class(&moh);
00851 return -1;
00852 }
00853 } else {
00854 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00855 ast_moh_free_class(&moh);
00856 return -1;
00857 }
00858 ast_mutex_lock(&moh_lock);
00859 moh->next = mohclasses;
00860 mohclasses = moh;
00861 ast_mutex_unlock(&moh_lock);
00862 return 0;
00863 }
00864
00865 static void local_ast_moh_cleanup(struct ast_channel *chan)
00866 {
00867 if (chan->music_state) {
00868 free(chan->music_state);
00869 chan->music_state = NULL;
00870 }
00871 }
00872
00873 static int local_ast_moh_start(struct ast_channel *chan, char *class)
00874 {
00875 struct mohclass *mohclass;
00876
00877 if (ast_strlen_zero(class))
00878 class = chan->musicclass;
00879 if (ast_strlen_zero(class))
00880 class = "default";
00881 ast_mutex_lock(&moh_lock);
00882 mohclass = get_mohbyname(class);
00883 ast_mutex_unlock(&moh_lock);
00884
00885 if (!mohclass) {
00886 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
00887 return -1;
00888 }
00889
00890 ast_set_flag(chan, AST_FLAG_MOH);
00891 if (mohclass->total_files) {
00892 return ast_activate_generator(chan, &moh_file_stream, mohclass);
00893 } else
00894 return ast_activate_generator(chan, &mohgen, mohclass);
00895 }
00896
00897 static void local_ast_moh_stop(struct ast_channel *chan)
00898 {
00899 ast_clear_flag(chan, AST_FLAG_MOH);
00900 ast_deactivate_generator(chan);
00901
00902 if (chan->music_state) {
00903 if (chan->stream) {
00904 ast_closestream(chan->stream);
00905 chan->stream = NULL;
00906 }
00907 }
00908 }
00909
00910 static struct mohclass *moh_class_malloc(void)
00911 {
00912 struct mohclass *class;
00913
00914 class = malloc(sizeof(struct mohclass));
00915
00916 if (!class)
00917 return NULL;
00918
00919 memset(class, 0, sizeof(struct mohclass));
00920
00921 class->format = AST_FORMAT_SLINEAR;
00922
00923 return class;
00924 }
00925
00926 static int load_moh_classes(int reload)
00927 {
00928 struct ast_config *cfg;
00929 struct ast_variable *var;
00930 struct mohclass *class;
00931 char *data;
00932 char *args;
00933 char *cat;
00934 int numclasses = 0;
00935 static int dep_warning = 0;
00936
00937 cfg = ast_config_load("musiconhold.conf");
00938
00939 if (!cfg)
00940 return 0;
00941
00942 cat = ast_category_browse(cfg, NULL);
00943 for (; cat; cat = ast_category_browse(cfg, cat)) {
00944 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
00945 class = moh_class_malloc();
00946 if (!class) {
00947 ast_log(LOG_WARNING, "Out of memory!\n");
00948 break;
00949 }
00950 ast_copy_string(class->name, cat, sizeof(class->name));
00951 var = ast_variable_browse(cfg, cat);
00952 while (var) {
00953 if (!strcasecmp(var->name, "mode"))
00954 ast_copy_string(class->mode, var->value, sizeof(class->mode));
00955 else if (!strcasecmp(var->name, "directory"))
00956 ast_copy_string(class->dir, var->value, sizeof(class->dir));
00957 else if (!strcasecmp(var->name, "application"))
00958 ast_copy_string(class->args, var->value, sizeof(class->args));
00959 else if (!strcasecmp(var->name, "random"))
00960 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
00961 else if (!strcasecmp(var->name, "format")) {
00962 class->format = ast_getformatbyname(var->value);
00963 if (!class->format) {
00964 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
00965 class->format = AST_FORMAT_SLINEAR;
00966 }
00967 }
00968 var = var->next;
00969 }
00970
00971 if (ast_strlen_zero(class->dir)) {
00972 if (!strcasecmp(class->mode, "custom")) {
00973 strcpy(class->dir, "nodir");
00974 } else {
00975 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
00976 free(class);
00977 continue;
00978 }
00979 }
00980 if (ast_strlen_zero(class->mode)) {
00981 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
00982 free(class);
00983 continue;
00984 }
00985 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
00986 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
00987 free(class);
00988 continue;
00989 }
00990
00991
00992 moh_register(class, reload);
00993
00994 numclasses++;
00995 }
00996 }
00997
00998
00999
01000 var = ast_variable_browse(cfg, "classes");
01001 while (var) {
01002 if (!dep_warning) {
01003 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
01004 dep_warning = 1;
01005 }
01006 data = strchr(var->value, ':');
01007 if (data) {
01008 *data++ = '\0';
01009 args = strchr(data, ',');
01010 if (args)
01011 *args++ = '\0';
01012 if (!(get_mohbyname(var->name))) {
01013 class = moh_class_malloc();
01014 if (!class) {
01015 ast_log(LOG_WARNING, "Out of memory!\n");
01016 return numclasses;
01017 }
01018
01019 ast_copy_string(class->name, var->name, sizeof(class->name));
01020 ast_copy_string(class->dir, data, sizeof(class->dir));
01021 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01022 if (args)
01023 ast_copy_string(class->args, args, sizeof(class->args));
01024
01025 moh_register(class, reload);
01026 numclasses++;
01027 }
01028 }
01029 var = var->next;
01030 }
01031 var = ast_variable_browse(cfg, "moh_files");
01032 while (var) {
01033 if (!dep_warning) {
01034 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
01035 dep_warning = 1;
01036 }
01037 if (!(get_mohbyname(var->name))) {
01038 args = strchr(var->value, ',');
01039 if (args)
01040 *args++ = '\0';
01041 class = moh_class_malloc();
01042 if (!class) {
01043 ast_log(LOG_WARNING, "Out of memory!\n");
01044 return numclasses;
01045 }
01046
01047 ast_copy_string(class->name, var->name, sizeof(class->name));
01048 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01049 strcpy(class->mode, "files");
01050 if (args)
01051 ast_copy_string(class->args, args, sizeof(class->args));
01052
01053 moh_register(class, reload);
01054 numclasses++;
01055 }
01056 var = var->next;
01057 }
01058
01059 ast_config_destroy(cfg);
01060
01061 return numclasses;
01062 }
01063
01064 static void ast_moh_destroy(void)
01065 {
01066 struct mohclass *moh, *tmp;
01067 char buff[8192];
01068 int bytes, tbytes=0, stime = 0, pid = 0;
01069
01070 if (option_verbose > 1)
01071 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01072 ast_mutex_lock(&moh_lock);
01073 moh = mohclasses;
01074
01075 while (moh) {
01076 if (moh->pid) {
01077 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01078 stime = time(NULL) + 2;
01079 pid = moh->pid;
01080 moh->pid = 0;
01081 kill(pid, SIGKILL);
01082 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
01083 tbytes = tbytes + bytes;
01084 }
01085 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01086 close(moh->srcfd);
01087 }
01088 tmp = moh;
01089 moh = moh->next;
01090 ast_moh_free_class(&tmp);
01091 }
01092 mohclasses = NULL;
01093 ast_mutex_unlock(&moh_lock);
01094 }
01095
01096 static void moh_on_off(int on)
01097 {
01098 struct ast_channel *chan = NULL;
01099
01100 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
01101 if (ast_test_flag(chan, AST_FLAG_MOH)) {
01102 if (on)
01103 local_ast_moh_start(chan, NULL);
01104 else
01105 ast_deactivate_generator(chan);
01106 }
01107 ast_mutex_unlock(&chan->lock);
01108 }
01109 }
01110
01111 static int moh_cli(int fd, int argc, char *argv[])
01112 {
01113 int x;
01114
01115 moh_on_off(0);
01116 ast_moh_destroy();
01117 x = load_moh_classes(1);
01118 moh_on_off(1);
01119 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
01120 return 0;
01121 }
01122
01123 static int cli_files_show(int fd, int argc, char *argv[])
01124 {
01125 int i;
01126 struct mohclass *class;
01127
01128 ast_mutex_lock(&moh_lock);
01129 for (class = mohclasses; class; class = class->next) {
01130 if (!class->total_files)
01131 continue;
01132
01133 ast_cli(fd, "Class: %s\n", class->name);
01134 for (i = 0; i < class->total_files; i++)
01135 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01136 }
01137 ast_mutex_unlock(&moh_lock);
01138
01139 return 0;
01140 }
01141
01142 static int moh_classes_show(int fd, int argc, char *argv[])
01143 {
01144 struct mohclass *class;
01145
01146 ast_mutex_lock(&moh_lock);
01147 for (class = mohclasses; class; class = class->next) {
01148 ast_cli(fd, "Class: %s\n", class->name);
01149 ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
01150 ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
01151 if (ast_test_flag(class, MOH_CUSTOM))
01152 ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
01153 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01154 }
01155 ast_mutex_unlock(&moh_lock);
01156
01157 return 0;
01158 }
01159
01160 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
01161
01162 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
01163
01164 static struct ast_cli_entry cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL};
01165
01166 static int init_classes(int reload)
01167 {
01168 struct mohclass *moh;
01169
01170 if (!load_moh_classes(reload))
01171 return 0;
01172 moh = mohclasses;
01173 while (moh) {
01174 if (moh->total_files)
01175 moh_scan_files(moh);
01176 moh = moh->next;
01177 }
01178 return 1;
01179 }
01180
01181 int load_module(void)
01182 {
01183 int res;
01184
01185 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01186 ast_register_atexit(ast_moh_destroy);
01187 ast_cli_register(&cli_moh);
01188 ast_cli_register(&cli_moh_files_show);
01189 ast_cli_register(&cli_moh_classes_show);
01190 if (!res)
01191 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01192 if (!res)
01193 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01194 if (!res)
01195 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01196 if (!res)
01197 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01198
01199 if (!init_classes(0)) {
01200 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
01201 } else {
01202 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01203 }
01204
01205 return 0;
01206 }
01207
01208 int reload(void)
01209 {
01210 if (init_classes(1))
01211 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01212
01213 return 0;
01214 }
01215
01216 int unload_module(void)
01217 {
01218 return -1;
01219 }
01220
01221 char *description(void)
01222 {
01223 return "Music On Hold Resource";
01224 }
01225
01226 int usecount(void)
01227 {
01228
01229
01230 #if 0
01231 int res;
01232 STANDARD_USECOUNT(res);
01233 return res;
01234 #else
01235 return 1;
01236 #endif
01237 }
01238
01239 char *key()
01240 {
01241 return ASTERISK_GPL_KEY;
01242 }