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