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: 115418 $")
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 = ast_random() % 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 state->sample_queue -= f->samples;
00308 res = ast_write(chan, f);
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 int res = 0;
00873
00874 AST_LIST_LOCK(&mohclasses);
00875 if ((mohclass = get_mohbyname(moh->name, 0))) {
00876 if (!mohclass->delete) {
00877 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00878 free(moh);
00879 AST_LIST_UNLOCK(&mohclasses);
00880 return -1;
00881 }
00882 }
00883 AST_LIST_UNLOCK(&mohclasses);
00884
00885 time(&moh->start);
00886 moh->start -= respawn_time;
00887
00888 if (!strcasecmp(moh->mode, "files")) {
00889 res = moh_scan_files(moh);
00890 if (res <= 0) {
00891 if (res == 0) {
00892 if (option_verbose > 2)
00893 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
00894 }
00895 ast_moh_free_class(&moh);
00896 return -1;
00897 }
00898 if (strchr(moh->args, 'r'))
00899 ast_set_flag(moh, MOH_RANDOMIZE);
00900 } 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")) {
00901
00902 if (!strcasecmp(moh->mode, "custom"))
00903 ast_set_flag(moh, MOH_CUSTOM);
00904 else if (!strcasecmp(moh->mode, "mp3nb"))
00905 ast_set_flag(moh, MOH_SINGLE);
00906 else if (!strcasecmp(moh->mode, "quietmp3nb"))
00907 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00908 else if (!strcasecmp(moh->mode, "quietmp3"))
00909 ast_set_flag(moh, MOH_QUIET);
00910
00911 moh->srcfd = -1;
00912 #ifdef HAVE_ZAPTEL
00913
00914
00915 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00916 if (moh->pseudofd < 0) {
00917 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00918 } else {
00919 x = 320;
00920 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00921 }
00922 #else
00923 moh->pseudofd = -1;
00924 #endif
00925 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
00926 ast_log(LOG_WARNING, "Unable to create moh...\n");
00927 if (moh->pseudofd > -1)
00928 close(moh->pseudofd);
00929 ast_moh_free_class(&moh);
00930 return -1;
00931 }
00932 } else {
00933 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00934 ast_moh_free_class(&moh);
00935 return -1;
00936 }
00937
00938 AST_LIST_LOCK(&mohclasses);
00939 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
00940 AST_LIST_UNLOCK(&mohclasses);
00941
00942 return 0;
00943 }
00944
00945 static void local_ast_moh_cleanup(struct ast_channel *chan)
00946 {
00947 if (chan->music_state) {
00948 free(chan->music_state);
00949 chan->music_state = NULL;
00950 }
00951 }
00952
00953 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
00954 {
00955 struct mohclass *mohclass = NULL;
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968 AST_LIST_LOCK(&mohclasses);
00969 if (!ast_strlen_zero(chan->musicclass))
00970 mohclass = get_mohbyname(chan->musicclass, 1);
00971 if (!mohclass && !ast_strlen_zero(mclass))
00972 mohclass = get_mohbyname(mclass, 1);
00973 if (!mohclass && !ast_strlen_zero(interpclass))
00974 mohclass = get_mohbyname(interpclass, 1);
00975 if (!mohclass)
00976 mohclass = get_mohbyname("default", 1);
00977 if (mohclass)
00978 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
00979 AST_LIST_UNLOCK(&mohclasses);
00980
00981 if (!mohclass)
00982 return -1;
00983
00984 ast_set_flag(chan, AST_FLAG_MOH);
00985 if (mohclass->total_files) {
00986 return ast_activate_generator(chan, &moh_file_stream, mohclass);
00987 } else
00988 return ast_activate_generator(chan, &mohgen, mohclass);
00989 }
00990
00991 static void local_ast_moh_stop(struct ast_channel *chan)
00992 {
00993 ast_clear_flag(chan, AST_FLAG_MOH);
00994 ast_deactivate_generator(chan);
00995
00996 if (chan->music_state) {
00997 if (chan->stream) {
00998 ast_closestream(chan->stream);
00999 chan->stream = NULL;
01000 }
01001 }
01002 }
01003
01004 static struct mohclass *moh_class_malloc(void)
01005 {
01006 struct mohclass *class;
01007
01008 if ((class = ast_calloc(1, sizeof(*class))))
01009 class->format = AST_FORMAT_SLINEAR;
01010
01011 return class;
01012 }
01013
01014 static int load_moh_classes(int reload)
01015 {
01016 struct ast_config *cfg;
01017 struct ast_variable *var;
01018 struct mohclass *class;
01019 char *data;
01020 char *args;
01021 char *cat;
01022 int numclasses = 0;
01023 static int dep_warning = 0;
01024
01025 cfg = ast_config_load("musiconhold.conf");
01026
01027 if (!cfg)
01028 return 0;
01029
01030 if (reload) {
01031 AST_LIST_LOCK(&mohclasses);
01032 AST_LIST_TRAVERSE(&mohclasses, class, list)
01033 class->delete = 1;
01034 AST_LIST_UNLOCK(&mohclasses);
01035 }
01036
01037 cat = ast_category_browse(cfg, NULL);
01038 for (; cat; cat = ast_category_browse(cfg, cat)) {
01039 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
01040 if (!(class = moh_class_malloc())) {
01041 break;
01042 }
01043 ast_copy_string(class->name, cat, sizeof(class->name));
01044 var = ast_variable_browse(cfg, cat);
01045 while (var) {
01046 if (!strcasecmp(var->name, "mode"))
01047 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01048 else if (!strcasecmp(var->name, "directory"))
01049 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01050 else if (!strcasecmp(var->name, "application"))
01051 ast_copy_string(class->args, var->value, sizeof(class->args));
01052 else if (!strcasecmp(var->name, "random"))
01053 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01054 else if (!strcasecmp(var->name, "format")) {
01055 class->format = ast_getformatbyname(var->value);
01056 if (!class->format) {
01057 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01058 class->format = AST_FORMAT_SLINEAR;
01059 }
01060 }
01061 var = var->next;
01062 }
01063
01064 if (ast_strlen_zero(class->dir)) {
01065 if (!strcasecmp(class->mode, "custom")) {
01066 strcpy(class->dir, "nodir");
01067 } else {
01068 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01069 free(class);
01070 continue;
01071 }
01072 }
01073 if (ast_strlen_zero(class->mode)) {
01074 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01075 free(class);
01076 continue;
01077 }
01078 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01079 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01080 free(class);
01081 continue;
01082 }
01083
01084
01085 moh_register(class, reload);
01086
01087 numclasses++;
01088 }
01089 }
01090
01091
01092
01093 var = ast_variable_browse(cfg, "classes");
01094 while (var) {
01095 if (!dep_warning) {
01096 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");
01097 dep_warning = 1;
01098 }
01099 data = strchr(var->value, ':');
01100 if (data) {
01101 *data++ = '\0';
01102 args = strchr(data, ',');
01103 if (args)
01104 *args++ = '\0';
01105 if (!(get_mohbyname(var->name, 0))) {
01106 if (!(class = moh_class_malloc())) {
01107 break;
01108 }
01109
01110 ast_copy_string(class->name, var->name, sizeof(class->name));
01111 ast_copy_string(class->dir, data, sizeof(class->dir));
01112 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01113 if (args)
01114 ast_copy_string(class->args, args, sizeof(class->args));
01115
01116 moh_register(class, reload);
01117 numclasses++;
01118 }
01119 }
01120 var = var->next;
01121 }
01122 var = ast_variable_browse(cfg, "moh_files");
01123 while (var) {
01124 if (!dep_warning) {
01125 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");
01126 dep_warning = 1;
01127 }
01128 if (!(get_mohbyname(var->name, 0))) {
01129 args = strchr(var->value, ',');
01130 if (args)
01131 *args++ = '\0';
01132 if (!(class = moh_class_malloc())) {
01133 break;
01134 }
01135
01136 ast_copy_string(class->name, var->name, sizeof(class->name));
01137 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01138 strcpy(class->mode, "files");
01139 if (args)
01140 ast_copy_string(class->args, args, sizeof(class->args));
01141
01142 moh_register(class, reload);
01143 numclasses++;
01144 }
01145 var = var->next;
01146 }
01147
01148 ast_config_destroy(cfg);
01149
01150 return numclasses;
01151 }
01152
01153 static int ast_moh_destroy_one(struct mohclass *moh)
01154 {
01155 char buff[8192];
01156 int bytes, tbytes = 0, stime = 0, pid = 0;
01157
01158 if (moh) {
01159 if (moh->pid > 1) {
01160 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01161 stime = time(NULL) + 2;
01162 pid = moh->pid;
01163 moh->pid = 0;
01164
01165
01166
01167 kill(pid, SIGHUP);
01168 usleep(100000);
01169 kill(pid, SIGTERM);
01170 usleep(100000);
01171 kill(pid, SIGKILL);
01172 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
01173 tbytes = tbytes + bytes;
01174 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01175 close(moh->srcfd);
01176 }
01177 ast_moh_free_class(&moh);
01178 }
01179
01180 return 0;
01181 }
01182
01183 static void ast_moh_destroy(void)
01184 {
01185 struct mohclass *moh;
01186
01187 if (option_verbose > 1)
01188 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01189
01190 AST_LIST_LOCK(&mohclasses);
01191 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
01192 ast_moh_destroy_one(moh);
01193 }
01194 AST_LIST_UNLOCK(&mohclasses);
01195 }
01196
01197 static int moh_cli(int fd, int argc, char *argv[])
01198 {
01199 reload();
01200 return 0;
01201 }
01202
01203 static int cli_files_show(int fd, int argc, char *argv[])
01204 {
01205 int i;
01206 struct mohclass *class;
01207
01208 AST_LIST_LOCK(&mohclasses);
01209 AST_LIST_TRAVERSE(&mohclasses, class, list) {
01210 if (!class->total_files)
01211 continue;
01212
01213 ast_cli(fd, "Class: %s\n", class->name);
01214 for (i = 0; i < class->total_files; i++)
01215 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01216 }
01217 AST_LIST_UNLOCK(&mohclasses);
01218
01219 return 0;
01220 }
01221
01222 static int moh_classes_show(int fd, int argc, char *argv[])
01223 {
01224 struct mohclass *class;
01225
01226 AST_LIST_LOCK(&mohclasses);
01227 AST_LIST_TRAVERSE(&mohclasses, class, list) {
01228 ast_cli(fd, "Class: %s\n", class->name);
01229 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01230 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01231 ast_cli(fd, "\tUse Count: %d\n", class->inuse);
01232 if (ast_test_flag(class, MOH_CUSTOM))
01233 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01234 if (strcasecmp(class->mode, "files"))
01235 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01236 }
01237 AST_LIST_UNLOCK(&mohclasses);
01238
01239 return 0;
01240 }
01241
01242 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
01243 { "moh", "classes", "show"},
01244 moh_classes_show, NULL,
01245 NULL };
01246
01247 static struct ast_cli_entry cli_moh_files_show_deprecated = {
01248 { "moh", "files", "show"},
01249 cli_files_show, NULL,
01250 NULL };
01251
01252 static struct ast_cli_entry cli_moh[] = {
01253 { { "moh", "reload"},
01254 moh_cli, "Music On Hold",
01255 "Usage: moh reload\n Rereads configuration\n" },
01256
01257 { { "moh", "show", "classes"},
01258 moh_classes_show, "List MOH classes",
01259 "Usage: moh show classes\n Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
01260
01261 { { "moh", "show", "files"},
01262 cli_files_show, "List MOH file-based classes",
01263 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
01264 };
01265
01266 static int init_classes(int reload)
01267 {
01268 struct mohclass *moh;
01269
01270 if (!load_moh_classes(reload))
01271 return 0;
01272
01273 AST_LIST_LOCK(&mohclasses);
01274 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
01275 if (reload && moh->delete) {
01276 AST_LIST_REMOVE_CURRENT(&mohclasses, list);
01277 if (!moh->inuse)
01278 ast_moh_destroy_one(moh);
01279 }
01280 }
01281 AST_LIST_TRAVERSE_SAFE_END
01282 AST_LIST_UNLOCK(&mohclasses);
01283
01284 return 1;
01285 }
01286
01287 static int load_module(void)
01288 {
01289 int res;
01290
01291 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01292 ast_register_atexit(ast_moh_destroy);
01293 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01294 if (!res)
01295 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01296 if (!res)
01297 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01298 if (!res)
01299 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01300 if (!res)
01301 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01302
01303 if (!init_classes(0)) {
01304 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
01305 } else {
01306 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01307 }
01308
01309 return 0;
01310 }
01311
01312 static int reload(void)
01313 {
01314 if (init_classes(1))
01315 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01316
01317 return 0;
01318 }
01319
01320 static int unload_module(void)
01321 {
01322 int res = 0;
01323 struct mohclass *class = NULL;
01324
01325 AST_LIST_LOCK(&mohclasses);
01326 AST_LIST_TRAVERSE(&mohclasses, class, list) {
01327 if (class->inuse > 0) {
01328 res = -1;
01329 break;
01330 }
01331 }
01332 AST_LIST_UNLOCK(&mohclasses);
01333 if (res < 0) {
01334 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01335 return res;
01336 }
01337
01338 ast_uninstall_music_functions();
01339 ast_moh_destroy();
01340 res = ast_unregister_application(app0);
01341 res |= ast_unregister_application(app1);
01342 res |= ast_unregister_application(app2);
01343 res |= ast_unregister_application(app3);
01344 res |= ast_unregister_application(app4);
01345 ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01346 return res;
01347 }
01348
01349 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
01350 .load = load_module,
01351 .unload = unload_module,
01352 .reload = reload,
01353 );