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: 75108 $")
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 0;
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 0;
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, int warn)
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 if (!moh && warn)
00638 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
00639
00640 return moh;
00641 }
00642
00643 static struct mohdata *mohalloc(struct mohclass *cl)
00644 {
00645 struct mohdata *moh;
00646 long flags;
00647
00648 if (!(moh = ast_calloc(1, sizeof(*moh))))
00649 return NULL;
00650
00651 if (pipe(moh->pipe)) {
00652 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00653 free(moh);
00654 return NULL;
00655 }
00656
00657
00658 flags = fcntl(moh->pipe[0], F_GETFL);
00659 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00660 flags = fcntl(moh->pipe[1], F_GETFL);
00661 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00662
00663 moh->f.frametype = AST_FRAME_VOICE;
00664 moh->f.subclass = cl->format;
00665 moh->f.offset = AST_FRIENDLY_OFFSET;
00666
00667 moh->parent = cl;
00668
00669 AST_LIST_LOCK(&mohclasses);
00670 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00671 AST_LIST_UNLOCK(&mohclasses);
00672
00673 return moh;
00674 }
00675
00676 static void moh_release(struct ast_channel *chan, void *data)
00677 {
00678 struct mohdata *moh = data;
00679 int oldwfmt;
00680
00681 AST_LIST_LOCK(&mohclasses);
00682 AST_LIST_REMOVE(&moh->parent->members, moh, list);
00683 AST_LIST_UNLOCK(&mohclasses);
00684
00685 close(moh->pipe[0]);
00686 close(moh->pipe[1]);
00687 oldwfmt = moh->origwfmt;
00688 free(moh);
00689 if (chan) {
00690 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
00691 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
00692 if (option_verbose > 2)
00693 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00694 }
00695 }
00696
00697 static void *moh_alloc(struct ast_channel *chan, void *params)
00698 {
00699 struct mohdata *res;
00700 struct mohclass *class = params;
00701
00702 if ((res = mohalloc(class))) {
00703 res->origwfmt = chan->writeformat;
00704 if (ast_set_write_format(chan, class->format)) {
00705 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00706 moh_release(NULL, res);
00707 res = NULL;
00708 }
00709 if (option_verbose > 2)
00710 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00711 }
00712 return res;
00713 }
00714
00715 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00716 {
00717 struct mohdata *moh = data;
00718 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00719 int res;
00720
00721 if (!moh->parent->pid)
00722 return -1;
00723
00724 len = ast_codec_get_len(moh->parent->format, samples);
00725
00726 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00727 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00728 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00729 }
00730 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00731 if (res <= 0)
00732 return 0;
00733
00734 moh->f.datalen = res;
00735 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00736 moh->f.samples = ast_codec_get_samples(&moh->f);
00737
00738 if (ast_write(chan, &moh->f) < 0) {
00739 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00740 return -1;
00741 }
00742
00743 return 0;
00744 }
00745
00746 static struct ast_generator mohgen =
00747 {
00748 alloc: moh_alloc,
00749 release: moh_release,
00750 generate: moh_generate,
00751 };
00752
00753 static int moh_add_file(struct mohclass *class, const char *filepath)
00754 {
00755 if (!class->allowed_files) {
00756 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00757 return -1;
00758 class->allowed_files = INITIAL_NUM_FILES;
00759 } else if (class->total_files == class->allowed_files) {
00760 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00761 class->allowed_files = 0;
00762 class->total_files = 0;
00763 return -1;
00764 }
00765 class->allowed_files *= 2;
00766 }
00767
00768 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00769 return -1;
00770
00771 class->total_files++;
00772
00773 return 0;
00774 }
00775
00776 static int moh_scan_files(struct mohclass *class) {
00777
00778 DIR *files_DIR;
00779 struct dirent *files_dirent;
00780 char path[PATH_MAX];
00781 char filepath[PATH_MAX];
00782 char *ext;
00783 struct stat statbuf;
00784 int dirnamelen;
00785 int i;
00786
00787 files_DIR = opendir(class->dir);
00788 if (!files_DIR) {
00789 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00790 return -1;
00791 }
00792
00793 for (i = 0; i < class->total_files; i++)
00794 free(class->filearray[i]);
00795
00796 class->total_files = 0;
00797 dirnamelen = strlen(class->dir) + 2;
00798 getcwd(path, sizeof(path));
00799 chdir(class->dir);
00800 while ((files_dirent = readdir(files_DIR))) {
00801
00802 if ((strlen(files_dirent->d_name) < 4))
00803 continue;
00804
00805
00806 if (files_dirent->d_name[0] == '.')
00807 continue;
00808
00809
00810 if (!strchr(files_dirent->d_name, '.'))
00811 continue;
00812
00813 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00814
00815 if (stat(filepath, &statbuf))
00816 continue;
00817
00818 if (!S_ISREG(statbuf.st_mode))
00819 continue;
00820
00821 if ((ext = strrchr(filepath, '.'))) {
00822 *ext = '\0';
00823 ext++;
00824 }
00825
00826
00827 for (i = 0; i < class->total_files; i++)
00828 if (!strcmp(filepath, class->filearray[i]))
00829 break;
00830
00831 if (i == class->total_files) {
00832 if (moh_add_file(class, filepath))
00833 break;
00834 }
00835 }
00836
00837 closedir(files_DIR);
00838 chdir(path);
00839 return class->total_files;
00840 }
00841
00842 static int moh_register(struct mohclass *moh, int reload)
00843 {
00844 #ifdef HAVE_ZAPTEL
00845 int x;
00846 #endif
00847 AST_LIST_LOCK(&mohclasses);
00848 if (get_mohbyname(moh->name, 0)) {
00849 if (reload) {
00850 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
00851 } else {
00852 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00853 }
00854 free(moh);
00855 AST_LIST_UNLOCK(&mohclasses);
00856 return -1;
00857 }
00858 AST_LIST_UNLOCK(&mohclasses);
00859
00860 time(&moh->start);
00861 moh->start -= respawn_time;
00862
00863 if (!strcasecmp(moh->mode, "files")) {
00864 if (!moh_scan_files(moh)) {
00865 ast_moh_free_class(&moh);
00866 return -1;
00867 }
00868 if (strchr(moh->args, 'r'))
00869 ast_set_flag(moh, MOH_RANDOMIZE);
00870 } 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")) {
00871
00872 if (!strcasecmp(moh->mode, "custom"))
00873 ast_set_flag(moh, MOH_CUSTOM);
00874 else if (!strcasecmp(moh->mode, "mp3nb"))
00875 ast_set_flag(moh, MOH_SINGLE);
00876 else if (!strcasecmp(moh->mode, "quietmp3nb"))
00877 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00878 else if (!strcasecmp(moh->mode, "quietmp3"))
00879 ast_set_flag(moh, MOH_QUIET);
00880
00881 moh->srcfd = -1;
00882 #ifdef HAVE_ZAPTEL
00883
00884
00885 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00886 if (moh->pseudofd < 0) {
00887 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00888 } else {
00889 x = 320;
00890 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00891 }
00892 #else
00893 moh->pseudofd = -1;
00894 #endif
00895 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
00896 ast_log(LOG_WARNING, "Unable to create moh...\n");
00897 if (moh->pseudofd > -1)
00898 close(moh->pseudofd);
00899 ast_moh_free_class(&moh);
00900 return -1;
00901 }
00902 } else {
00903 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00904 ast_moh_free_class(&moh);
00905 return -1;
00906 }
00907
00908 AST_LIST_LOCK(&mohclasses);
00909 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
00910 AST_LIST_UNLOCK(&mohclasses);
00911
00912 return 0;
00913 }
00914
00915 static void local_ast_moh_cleanup(struct ast_channel *chan)
00916 {
00917 if (chan->music_state) {
00918 free(chan->music_state);
00919 chan->music_state = NULL;
00920 }
00921 }
00922
00923 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
00924 {
00925 struct mohclass *mohclass = NULL;
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938 AST_LIST_LOCK(&mohclasses);
00939 if (!ast_strlen_zero(chan->musicclass))
00940 mohclass = get_mohbyname(chan->musicclass, 1);
00941 if (!mohclass && !ast_strlen_zero(mclass))
00942 mohclass = get_mohbyname(mclass, 1);
00943 if (!mohclass && !ast_strlen_zero(interpclass))
00944 mohclass = get_mohbyname(interpclass, 1);
00945 if (!mohclass)
00946 mohclass = get_mohbyname("default", 1);
00947 AST_LIST_UNLOCK(&mohclasses);
00948
00949 if (!mohclass)
00950 return -1;
00951
00952 ast_set_flag(chan, AST_FLAG_MOH);
00953 if (mohclass->total_files) {
00954 return ast_activate_generator(chan, &moh_file_stream, mohclass);
00955 } else
00956 return ast_activate_generator(chan, &mohgen, mohclass);
00957 }
00958
00959 static void local_ast_moh_stop(struct ast_channel *chan)
00960 {
00961 ast_clear_flag(chan, AST_FLAG_MOH);
00962 ast_deactivate_generator(chan);
00963
00964 if (chan->music_state) {
00965 if (chan->stream) {
00966 ast_closestream(chan->stream);
00967 chan->stream = NULL;
00968 }
00969 }
00970 }
00971
00972 static struct mohclass *moh_class_malloc(void)
00973 {
00974 struct mohclass *class;
00975
00976 if ((class = ast_calloc(1, sizeof(*class))))
00977 class->format = AST_FORMAT_SLINEAR;
00978
00979 return class;
00980 }
00981
00982 static int load_moh_classes(int reload)
00983 {
00984 struct ast_config *cfg;
00985 struct ast_variable *var;
00986 struct mohclass *class;
00987 char *data;
00988 char *args;
00989 char *cat;
00990 int numclasses = 0;
00991 static int dep_warning = 0;
00992
00993 cfg = ast_config_load("musiconhold.conf");
00994
00995 if (!cfg)
00996 return 0;
00997
00998 cat = ast_category_browse(cfg, NULL);
00999 for (; cat; cat = ast_category_browse(cfg, cat)) {
01000 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
01001 if (!(class = moh_class_malloc())) {
01002 break;
01003 }
01004 ast_copy_string(class->name, cat, sizeof(class->name));
01005 var = ast_variable_browse(cfg, cat);
01006 while (var) {
01007 if (!strcasecmp(var->name, "mode"))
01008 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01009 else if (!strcasecmp(var->name, "directory"))
01010 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01011 else if (!strcasecmp(var->name, "application"))
01012 ast_copy_string(class->args, var->value, sizeof(class->args));
01013 else if (!strcasecmp(var->name, "random"))
01014 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01015 else if (!strcasecmp(var->name, "format")) {
01016 class->format = ast_getformatbyname(var->value);
01017 if (!class->format) {
01018 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01019 class->format = AST_FORMAT_SLINEAR;
01020 }
01021 }
01022 var = var->next;
01023 }
01024
01025 if (ast_strlen_zero(class->dir)) {
01026 if (!strcasecmp(class->mode, "custom")) {
01027 strcpy(class->dir, "nodir");
01028 } else {
01029 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01030 free(class);
01031 continue;
01032 }
01033 }
01034 if (ast_strlen_zero(class->mode)) {
01035 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01036 free(class);
01037 continue;
01038 }
01039 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01040 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01041 free(class);
01042 continue;
01043 }
01044
01045
01046 moh_register(class, reload);
01047
01048 numclasses++;
01049 }
01050 }
01051
01052
01053
01054 var = ast_variable_browse(cfg, "classes");
01055 while (var) {
01056 if (!dep_warning) {
01057 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");
01058 dep_warning = 1;
01059 }
01060 data = strchr(var->value, ':');
01061 if (data) {
01062 *data++ = '\0';
01063 args = strchr(data, ',');
01064 if (args)
01065 *args++ = '\0';
01066 if (!(get_mohbyname(var->name, 0))) {
01067 if (!(class = moh_class_malloc())) {
01068 break;
01069 }
01070
01071 ast_copy_string(class->name, var->name, sizeof(class->name));
01072 ast_copy_string(class->dir, data, sizeof(class->dir));
01073 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01074 if (args)
01075 ast_copy_string(class->args, args, sizeof(class->args));
01076
01077 moh_register(class, reload);
01078 numclasses++;
01079 }
01080 }
01081 var = var->next;
01082 }
01083 var = ast_variable_browse(cfg, "moh_files");
01084 while (var) {
01085 if (!dep_warning) {
01086 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");
01087 dep_warning = 1;
01088 }
01089 if (!(get_mohbyname(var->name, 0))) {
01090 args = strchr(var->value, ',');
01091 if (args)
01092 *args++ = '\0';
01093 if (!(class = moh_class_malloc())) {
01094 break;
01095 }
01096
01097 ast_copy_string(class->name, var->name, sizeof(class->name));
01098 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01099 strcpy(class->mode, "files");
01100 if (args)
01101 ast_copy_string(class->args, args, sizeof(class->args));
01102
01103 moh_register(class, reload);
01104 numclasses++;
01105 }
01106 var = var->next;
01107 }
01108
01109 ast_config_destroy(cfg);
01110
01111 return numclasses;
01112 }
01113
01114 static void ast_moh_destroy(void)
01115 {
01116 struct mohclass *moh;
01117 char buff[8192];
01118 int bytes, tbytes = 0, stime = 0, pid = 0;
01119
01120 if (option_verbose > 1)
01121 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01122
01123 AST_LIST_LOCK(&mohclasses);
01124 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
01125 if (moh->pid > 1) {
01126 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01127 stime = time(NULL) + 2;
01128 pid = moh->pid;
01129 moh->pid = 0;
01130
01131
01132
01133 kill(pid, SIGHUP);
01134 usleep(100000);
01135 kill(pid, SIGTERM);
01136 usleep(100000);
01137 kill(pid, SIGKILL);
01138 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
01139 tbytes = tbytes + bytes;
01140 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01141 close(moh->srcfd);
01142 }
01143 ast_moh_free_class(&moh);
01144 }
01145 AST_LIST_UNLOCK(&mohclasses);
01146 }
01147
01148 static void moh_on_off(int on)
01149 {
01150 struct ast_channel *chan = NULL;
01151
01152 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
01153 if (ast_test_flag(chan, AST_FLAG_MOH)) {
01154 if (on)
01155 local_ast_moh_start(chan, NULL, NULL);
01156 else
01157 ast_deactivate_generator(chan);
01158 }
01159 ast_channel_unlock(chan);
01160 }
01161 }
01162
01163 static int moh_cli(int fd, int argc, char *argv[])
01164 {
01165 int x;
01166
01167 moh_on_off(0);
01168 ast_moh_destroy();
01169 x = load_moh_classes(1);
01170 moh_on_off(1);
01171 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
01172 return 0;
01173 }
01174
01175 static int cli_files_show(int fd, int argc, char *argv[])
01176 {
01177 int i;
01178 struct mohclass *class;
01179
01180 AST_LIST_LOCK(&mohclasses);
01181 AST_LIST_TRAVERSE(&mohclasses, class, list) {
01182 if (!class->total_files)
01183 continue;
01184
01185 ast_cli(fd, "Class: %s\n", class->name);
01186 for (i = 0; i < class->total_files; i++)
01187 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01188 }
01189 AST_LIST_UNLOCK(&mohclasses);
01190
01191 return 0;
01192 }
01193
01194 static int moh_classes_show(int fd, int argc, char *argv[])
01195 {
01196 struct mohclass *class;
01197
01198 AST_LIST_LOCK(&mohclasses);
01199 AST_LIST_TRAVERSE(&mohclasses, class, list) {
01200 ast_cli(fd, "Class: %s\n", class->name);
01201 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01202 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01203 if (ast_test_flag(class, MOH_CUSTOM))
01204 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01205 if (strcasecmp(class->mode, "files"))
01206 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01207 }
01208 AST_LIST_UNLOCK(&mohclasses);
01209
01210 return 0;
01211 }
01212
01213 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
01214 { "moh", "classes", "show"},
01215 moh_classes_show, NULL,
01216 NULL };
01217
01218 static struct ast_cli_entry cli_moh_files_show_deprecated = {
01219 { "moh", "files", "show"},
01220 cli_files_show, NULL,
01221 NULL };
01222
01223 static struct ast_cli_entry cli_moh[] = {
01224 { { "moh", "reload"},
01225 moh_cli, "Music On Hold",
01226 "Music On Hold" },
01227
01228 { { "moh", "show", "classes"},
01229 moh_classes_show, "List MOH classes",
01230 "Lists all MOH classes", NULL, &cli_moh_classes_show_deprecated },
01231
01232 { { "moh", "show", "files"},
01233 cli_files_show, "List MOH file-based classes",
01234 "Lists all loaded file-based MOH classes and their files", NULL, &cli_moh_files_show_deprecated },
01235 };
01236
01237 static int init_classes(int reload)
01238 {
01239 struct mohclass *moh;
01240
01241 if (!load_moh_classes(reload))
01242 return 0;
01243
01244 AST_LIST_LOCK(&mohclasses);
01245 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
01246 if (moh->total_files)
01247 moh_scan_files(moh);
01248 }
01249 AST_LIST_UNLOCK(&mohclasses);
01250
01251 return 1;
01252 }
01253
01254 static int load_module(void)
01255 {
01256 int res;
01257
01258 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01259 ast_register_atexit(ast_moh_destroy);
01260 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01261 if (!res)
01262 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01263 if (!res)
01264 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01265 if (!res)
01266 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01267 if (!res)
01268 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01269
01270 if (!init_classes(0)) {
01271 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
01272 } else {
01273 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01274 }
01275
01276 return 0;
01277 }
01278
01279 static int reload(void)
01280 {
01281 if (init_classes(1))
01282 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01283
01284 return 0;
01285 }
01286
01287 static int unload_module(void)
01288 {
01289 return -1;
01290 }
01291
01292 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
01293 .load = load_module,
01294 .unload = unload_module,
01295 .reload = reload,
01296 );