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