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