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