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