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