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: 352348 $")
00036
00037 #include <ctype.h>
00038 #include <signal.h>
00039 #include <sys/time.h>
00040 #include <sys/signal.h>
00041 #include <netinet/in.h>
00042 #include <sys/stat.h>
00043 #include <dirent.h>
00044
00045 #ifdef SOLARIS
00046 #include <thread.h>
00047 #endif
00048
00049 #include "asterisk/lock.h"
00050 #include "asterisk/file.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/app.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/translate.h"
00056 #include "asterisk/say.h"
00057 #include "asterisk/musiconhold.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/utils.h"
00060 #include "asterisk/cli.h"
00061 #include "asterisk/stringfields.h"
00062 #include "asterisk/linkedlists.h"
00063 #include "asterisk/manager.h"
00064 #include "asterisk/paths.h"
00065 #include "asterisk/astobj2.h"
00066 #include "asterisk/timing.h"
00067 #include "asterisk/time.h"
00068 #include "asterisk/poll-compat.h"
00069
00070 #define INITIAL_NUM_FILES 8
00071 #define HANDLE_REF 1
00072 #define DONT_UNREF 0
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147 static const char play_moh[] = "MusicOnHold";
00148 static const char wait_moh[] = "WaitMusicOnHold";
00149 static const char set_moh[] = "SetMusicOnHold";
00150 static const char start_moh[] = "StartMusicOnHold";
00151 static const char stop_moh[] = "StopMusicOnHold";
00152
00153 static int respawn_time = 20;
00154
00155 struct moh_files_state {
00156
00157 struct mohclass *class;
00158 char name[MAX_MUSICCLASS];
00159 struct ast_format origwfmt;
00160 struct ast_format mohwfmt;
00161 int announcement;
00162 int samples;
00163 int sample_queue;
00164 int pos;
00165 int save_pos;
00166 int save_total;
00167 char save_pos_filename[PATH_MAX];
00168 };
00169
00170 #define MOH_QUIET (1 << 0)
00171 #define MOH_SINGLE (1 << 1)
00172 #define MOH_CUSTOM (1 << 2)
00173 #define MOH_RANDOMIZE (1 << 3)
00174 #define MOH_SORTALPHA (1 << 4)
00175
00176 #define MOH_CACHERTCLASSES (1 << 5)
00177 #define MOH_ANNOUNCEMENT (1 << 6)
00178
00179
00180 #define MOH_NOTDELETED (1 << 30)
00181
00182 static struct ast_flags global_flags[1] = {{0}};
00183
00184 struct mohclass {
00185 char name[MAX_MUSICCLASS];
00186 char dir[256];
00187 char args[256];
00188 char announcement[256];
00189 char mode[80];
00190 char digit;
00191
00192 char **filearray;
00193
00194 int allowed_files;
00195
00196 int total_files;
00197 unsigned int flags;
00198
00199 struct ast_format format;
00200
00201 int pid;
00202 time_t start;
00203 pthread_t thread;
00204
00205 int srcfd;
00206
00207 struct ast_timer *timer;
00208
00209 unsigned int realtime:1;
00210 unsigned int delete:1;
00211 AST_LIST_HEAD_NOLOCK(, mohdata) members;
00212 AST_LIST_ENTRY(mohclass) list;
00213 };
00214
00215 struct mohdata {
00216 int pipe[2];
00217 struct ast_format origwfmt;
00218 struct mohclass *parent;
00219 struct ast_frame f;
00220 AST_LIST_ENTRY(mohdata) list;
00221 };
00222
00223 static struct ao2_container *mohclasses;
00224
00225 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00226 #define MPG_123 "/usr/bin/mpg123"
00227 #define MAX_MP3S 256
00228
00229 static int reload(void);
00230
00231 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
00232
00233 #ifndef REF_DEBUG
00234 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
00235 #else
00236 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
00237 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
00238 {
00239 struct mohclass *dup;
00240 if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
00241 if (__ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
00242 FILE *ref = fopen("/tmp/refs", "a");
00243 if (ref) {
00244 fprintf(ref, "%p =1 %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
00245 fclose(ref);
00246 }
00247 ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
00248 class, class->name, file, line, funcname);
00249 } else {
00250 ao2_ref(class, -1);
00251 }
00252 } else {
00253 ao2_t_ref(class, -1, (char *) tag);
00254 }
00255 return NULL;
00256 }
00257 #endif
00258
00259 static void moh_files_release(struct ast_channel *chan, void *data)
00260 {
00261 struct moh_files_state *state;
00262
00263 if (!chan || !chan->music_state) {
00264 return;
00265 }
00266
00267 state = chan->music_state;
00268
00269 if (chan->stream) {
00270 ast_closestream(chan->stream);
00271 chan->stream = NULL;
00272 }
00273
00274 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
00275
00276 ast_format_clear(&state->mohwfmt);
00277 if (state->origwfmt.id && ast_set_write_format(chan, &state->origwfmt)) {
00278 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan), ast_getformatname(&state->origwfmt));
00279 }
00280
00281 state->save_pos = state->pos;
00282 state->announcement = 0;
00283
00284 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00285 }
00286
00287 static int ast_moh_files_next(struct ast_channel *chan)
00288 {
00289 struct moh_files_state *state = chan->music_state;
00290 int tries;
00291
00292
00293 if (chan->stream) {
00294 ast_closestream(chan->stream);
00295 chan->stream = NULL;
00296 }
00297
00298 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
00299 state->announcement = 1;
00300 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
00301 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
00302 return 0;
00303 }
00304 } else {
00305 state->announcement = 0;
00306 }
00307
00308 if (!state->class->total_files) {
00309 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00310 return -1;
00311 }
00312
00313 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
00314
00315 state->save_pos = -1;
00316 } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
00317
00318 state->pos = state->save_pos;
00319 state->save_pos = -1;
00320 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00321
00322 for (tries = 0; tries < 20; tries++) {
00323 state->pos = ast_random() % state->class->total_files;
00324 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00325 break;
00326 }
00327 }
00328 state->save_pos = -1;
00329 state->samples = 0;
00330 } else {
00331
00332 state->pos++;
00333 state->pos %= state->class->total_files;
00334 state->save_pos = -1;
00335 state->samples = 0;
00336 }
00337
00338 for (tries = 0; tries < state->class->total_files; ++tries) {
00339 if (ast_openstream_full(chan, state->class->filearray[state->pos], ast_channel_language(chan), 1)) {
00340 break;
00341 }
00342
00343 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00344 state->pos++;
00345 state->pos %= state->class->total_files;
00346 }
00347
00348 if (tries == state->class->total_files) {
00349 return -1;
00350 }
00351
00352
00353 ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
00354
00355 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->class->filearray[state->pos]);
00356
00357 if (state->samples) {
00358 size_t loc;
00359
00360 ast_seekstream(chan->stream, state->samples, SEEK_SET);
00361
00362
00363 loc = ast_tellstream(chan->stream);
00364 if (state->samples > loc && loc) {
00365
00366 ast_seekstream(chan->stream, 1, SEEK_END);
00367 }
00368 }
00369
00370 return 0;
00371 }
00372
00373 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
00374 {
00375 struct ast_frame *f = NULL;
00376
00377 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00378 if (!ast_moh_files_next(chan))
00379 f = ast_readframe(chan->stream);
00380 }
00381
00382 return f;
00383 }
00384
00385 static void moh_files_write_format_change(struct ast_channel *chan, void *data)
00386 {
00387 struct moh_files_state *state = chan->music_state;
00388
00389
00390
00391
00392 if (&state->origwfmt.id) {
00393 struct ast_format tmp;
00394
00395 ast_format_copy(&tmp, &chan->writeformat);
00396 if (state->mohwfmt.id) {
00397 ast_format_clear(&state->origwfmt);
00398 ast_set_write_format(chan, &state->mohwfmt);
00399 }
00400 ast_format_copy(&state->origwfmt, &tmp);
00401 }
00402 }
00403
00404 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00405 {
00406 struct moh_files_state *state = chan->music_state;
00407 struct ast_frame *f = NULL;
00408 int res = 0;
00409
00410 state->sample_queue += samples;
00411
00412 while (state->sample_queue > 0) {
00413 ast_channel_lock(chan);
00414 if ((f = moh_files_readframe(chan))) {
00415
00416
00417
00418
00419
00420
00421 ast_channel_unlock(chan);
00422 state->samples += f->samples;
00423 state->sample_queue -= f->samples;
00424 if (ast_format_cmp(&f->subclass.format, &state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
00425 ast_format_copy(&state->mohwfmt, &f->subclass.format);
00426 }
00427 res = ast_write(chan, f);
00428 ast_frfree(f);
00429 if (res < 0) {
00430 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
00431 return -1;
00432 }
00433 } else {
00434 ast_channel_unlock(chan);
00435 return -1;
00436 }
00437 }
00438 return res;
00439 }
00440
00441 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00442 {
00443 struct moh_files_state *state;
00444 struct mohclass *class = params;
00445
00446 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00447 chan->music_state = state;
00448 ast_module_ref(ast_module_info->self);
00449 } else {
00450 state = chan->music_state;
00451 if (!state) {
00452 return NULL;
00453 }
00454 if (state->class) {
00455 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00456 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00457 }
00458 }
00459
00460
00461
00462
00463
00464
00465 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00466 memset(state, 0, sizeof(*state));
00467 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00468 state->pos = ast_random() % class->total_files;
00469 }
00470 }
00471
00472 state->class = mohclass_ref(class, "Reffing music class for channel");
00473 ast_format_copy(&state->origwfmt, &chan->writeformat);
00474 ast_format_copy(&state->mohwfmt, &chan->writeformat);
00475
00476
00477 ast_copy_string(state->name, class->name, sizeof(state->name));
00478 state->save_total = class->total_files;
00479
00480 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, ast_channel_name(chan));
00481
00482 return chan->music_state;
00483 }
00484
00485 static int moh_digit_match(void *obj, void *arg, int flags)
00486 {
00487 char *digit = arg;
00488 struct mohclass *class = obj;
00489
00490 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00491 }
00492
00493
00494 static struct mohclass *get_mohbydigit(char digit)
00495 {
00496 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00497 }
00498
00499 static void moh_handle_digit(struct ast_channel *chan, char digit)
00500 {
00501 struct mohclass *class;
00502 const char *classname = NULL;
00503
00504 if ((class = get_mohbydigit(digit))) {
00505 classname = ast_strdupa(class->name);
00506 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00507 ast_channel_musicclass_set(chan, classname);
00508 ast_moh_stop(chan);
00509 ast_moh_start(chan, classname, NULL);
00510 }
00511 }
00512
00513 static struct ast_generator moh_file_stream =
00514 {
00515 .alloc = moh_files_alloc,
00516 .release = moh_files_release,
00517 .generate = moh_files_generator,
00518 .digit = moh_handle_digit,
00519 .write_format_change = moh_files_write_format_change,
00520 };
00521
00522 static int spawn_mp3(struct mohclass *class)
00523 {
00524 int fds[2];
00525 int files = 0;
00526 char fns[MAX_MP3S][80];
00527 char *argv[MAX_MP3S + 50];
00528 char xargs[256];
00529 char *argptr;
00530 int argc = 0;
00531 DIR *dir = NULL;
00532 struct dirent *de;
00533
00534
00535 if (!strcasecmp(class->dir, "nodir")) {
00536 files = 1;
00537 } else {
00538 dir = opendir(class->dir);
00539 if (!dir && strncasecmp(class->dir, "http://", 7)) {
00540 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00541 return -1;
00542 }
00543 }
00544
00545 if (!ast_test_flag(class, MOH_CUSTOM)) {
00546 argv[argc++] = "mpg123";
00547 argv[argc++] = "-q";
00548 argv[argc++] = "-s";
00549 argv[argc++] = "--mono";
00550 argv[argc++] = "-r";
00551 argv[argc++] = "8000";
00552
00553 if (!ast_test_flag(class, MOH_SINGLE)) {
00554 argv[argc++] = "-b";
00555 argv[argc++] = "2048";
00556 }
00557
00558 argv[argc++] = "-f";
00559
00560 if (ast_test_flag(class, MOH_QUIET))
00561 argv[argc++] = "4096";
00562 else
00563 argv[argc++] = "8192";
00564
00565
00566 ast_copy_string(xargs, class->args, sizeof(xargs));
00567 argptr = xargs;
00568 while (!ast_strlen_zero(argptr)) {
00569 argv[argc++] = argptr;
00570 strsep(&argptr, ",");
00571 }
00572 } else {
00573
00574 ast_copy_string(xargs, class->args, sizeof(xargs));
00575 argptr = xargs;
00576 while (!ast_strlen_zero(argptr)) {
00577 argv[argc++] = argptr;
00578 strsep(&argptr, " ");
00579 }
00580 }
00581
00582 if (!strncasecmp(class->dir, "http://", 7)) {
00583 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00584 argv[argc++] = fns[files];
00585 files++;
00586 } else if (dir) {
00587 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00588 if ((strlen(de->d_name) > 3) &&
00589 ((ast_test_flag(class, MOH_CUSTOM) &&
00590 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
00591 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00592 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00593 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00594 argv[argc++] = fns[files];
00595 files++;
00596 }
00597 }
00598 }
00599 argv[argc] = NULL;
00600 if (dir) {
00601 closedir(dir);
00602 }
00603 if (pipe(fds)) {
00604 ast_log(LOG_WARNING, "Pipe failed\n");
00605 return -1;
00606 }
00607 if (!files) {
00608 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00609 close(fds[0]);
00610 close(fds[1]);
00611 return -1;
00612 }
00613 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00614 sleep(respawn_time - (time(NULL) - class->start));
00615 }
00616
00617 time(&class->start);
00618 class->pid = ast_safe_fork(0);
00619 if (class->pid < 0) {
00620 close(fds[0]);
00621 close(fds[1]);
00622 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00623 return -1;
00624 }
00625 if (!class->pid) {
00626 if (ast_opt_high_priority)
00627 ast_set_priority(0);
00628
00629 close(fds[0]);
00630
00631 dup2(fds[1], STDOUT_FILENO);
00632
00633
00634 ast_close_fds_above_n(STDERR_FILENO);
00635
00636
00637 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00638 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00639 _exit(1);
00640 }
00641 setpgid(0, getpid());
00642 if (ast_test_flag(class, MOH_CUSTOM)) {
00643 execv(argv[0], argv);
00644 } else {
00645
00646 execv(LOCAL_MPG_123, argv);
00647
00648 execv(MPG_123, argv);
00649
00650 execvp("mpg123", argv);
00651 }
00652
00653 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00654 close(fds[1]);
00655 _exit(1);
00656 } else {
00657
00658 close(fds[1]);
00659 }
00660 return fds[0];
00661 }
00662
00663 static void *monmp3thread(void *data)
00664 {
00665 #define MOH_MS_INTERVAL 100
00666
00667 struct mohclass *class = data;
00668 struct mohdata *moh;
00669 short sbuf[8192];
00670 int res = 0, res2;
00671 int len;
00672 struct timeval deadline, tv_tmp;
00673
00674 deadline.tv_sec = 0;
00675 deadline.tv_usec = 0;
00676 for(;;) {
00677 pthread_testcancel();
00678
00679 if (class->srcfd < 0) {
00680 if ((class->srcfd = spawn_mp3(class)) < 0) {
00681 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00682
00683 sleep(500);
00684 pthread_testcancel();
00685 }
00686 }
00687 if (class->timer) {
00688 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
00689
00690 #ifdef SOLARIS
00691 thr_yield();
00692 #endif
00693
00694 if (ast_poll(&pfd, 1, -1) > 0) {
00695 ast_timer_ack(class->timer, 1);
00696 res = 320;
00697 } else {
00698 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00699 res = 0;
00700 }
00701 pthread_testcancel();
00702 } else {
00703 long delta;
00704
00705 tv_tmp = ast_tvnow();
00706 if (ast_tvzero(deadline))
00707 deadline = tv_tmp;
00708 delta = ast_tvdiff_ms(tv_tmp, deadline);
00709 if (delta < MOH_MS_INTERVAL) {
00710 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));
00711 usleep(1000 * (MOH_MS_INTERVAL - delta));
00712 pthread_testcancel();
00713 } else {
00714 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00715 deadline = tv_tmp;
00716 }
00717 res = 8 * MOH_MS_INTERVAL;
00718 }
00719 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00720 continue;
00721
00722 len = ast_codec_get_len(&class->format, res);
00723
00724 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00725 if (!res2) {
00726 close(class->srcfd);
00727 class->srcfd = -1;
00728 pthread_testcancel();
00729 if (class->pid > 1) {
00730 do {
00731 if (killpg(class->pid, SIGHUP) < 0) {
00732 if (errno == ESRCH) {
00733 break;
00734 }
00735 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00736 }
00737 usleep(100000);
00738 if (killpg(class->pid, SIGTERM) < 0) {
00739 if (errno == ESRCH) {
00740 break;
00741 }
00742 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00743 }
00744 usleep(100000);
00745 if (killpg(class->pid, SIGKILL) < 0) {
00746 if (errno == ESRCH) {
00747 break;
00748 }
00749 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00750 }
00751 } while (0);
00752 class->pid = 0;
00753 }
00754 } else {
00755 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00756 }
00757 continue;
00758 }
00759
00760 pthread_testcancel();
00761
00762 ao2_lock(class);
00763 AST_LIST_TRAVERSE(&class->members, moh, list) {
00764
00765 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00766 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00767 }
00768 }
00769 ao2_unlock(class);
00770 }
00771 return NULL;
00772 }
00773
00774 static int play_moh_exec(struct ast_channel *chan, const char *data)
00775 {
00776 char *parse;
00777 char *class;
00778 int timeout = -1;
00779 int res;
00780 AST_DECLARE_APP_ARGS(args,
00781 AST_APP_ARG(class);
00782 AST_APP_ARG(duration);
00783 );
00784
00785 parse = ast_strdupa(data);
00786
00787 AST_STANDARD_APP_ARGS(args, parse);
00788
00789 if (!ast_strlen_zero(args.duration)) {
00790 if (sscanf(args.duration, "%30d", &timeout) == 1) {
00791 timeout *= 1000;
00792 } else {
00793 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00794 }
00795 }
00796
00797 class = S_OR(args.class, NULL);
00798 if (ast_moh_start(chan, class, NULL)) {
00799 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00800 return 0;
00801 }
00802
00803 if (timeout > 0)
00804 res = ast_safe_sleep(chan, timeout);
00805 else {
00806 while (!(res = ast_safe_sleep(chan, 10000)));
00807 }
00808
00809 ast_moh_stop(chan);
00810
00811 return res;
00812 }
00813
00814 static int wait_moh_exec(struct ast_channel *chan, const char *data)
00815 {
00816 static int deprecation_warning = 0;
00817 int res;
00818
00819 if (!deprecation_warning) {
00820 deprecation_warning = 1;
00821 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00822 }
00823
00824 if (!data || !atoi(data)) {
00825 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00826 return -1;
00827 }
00828 if (ast_moh_start(chan, NULL, NULL)) {
00829 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), ast_channel_name(chan));
00830 return 0;
00831 }
00832 res = ast_safe_sleep(chan, atoi(data) * 1000);
00833 ast_moh_stop(chan);
00834 return res;
00835 }
00836
00837 static int set_moh_exec(struct ast_channel *chan, const char *data)
00838 {
00839 static int deprecation_warning = 0;
00840
00841 if (!deprecation_warning) {
00842 deprecation_warning = 1;
00843 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00844 }
00845
00846 if (ast_strlen_zero(data)) {
00847 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00848 return -1;
00849 }
00850 ast_channel_musicclass_set(chan, data);
00851 return 0;
00852 }
00853
00854 static int start_moh_exec(struct ast_channel *chan, const char *data)
00855 {
00856 char *parse;
00857 char *class;
00858 AST_DECLARE_APP_ARGS(args,
00859 AST_APP_ARG(class);
00860 );
00861
00862 parse = ast_strdupa(data);
00863
00864 AST_STANDARD_APP_ARGS(args, parse);
00865
00866 class = S_OR(args.class, NULL);
00867 if (ast_moh_start(chan, class, NULL))
00868 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00869
00870 return 0;
00871 }
00872
00873 static int stop_moh_exec(struct ast_channel *chan, const char *data)
00874 {
00875 ast_moh_stop(chan);
00876
00877 return 0;
00878 }
00879
00880 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00881
00882 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00883 {
00884 struct mohclass *moh = NULL;
00885 struct mohclass tmp_class = {
00886 .flags = 0,
00887 };
00888
00889 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00890
00891 #ifdef REF_DEBUG
00892 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00893 #else
00894 moh = __ao2_find(mohclasses, &tmp_class, flags);
00895 #endif
00896
00897 if (!moh && warn) {
00898 ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
00899 }
00900
00901 return moh;
00902 }
00903
00904 static struct mohdata *mohalloc(struct mohclass *cl)
00905 {
00906 struct mohdata *moh;
00907 long flags;
00908
00909 if (!(moh = ast_calloc(1, sizeof(*moh))))
00910 return NULL;
00911
00912 if (pipe(moh->pipe)) {
00913 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00914 ast_free(moh);
00915 return NULL;
00916 }
00917
00918
00919 flags = fcntl(moh->pipe[0], F_GETFL);
00920 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00921 flags = fcntl(moh->pipe[1], F_GETFL);
00922 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00923
00924 moh->f.frametype = AST_FRAME_VOICE;
00925 ast_format_copy(&moh->f.subclass.format, &cl->format);
00926 moh->f.offset = AST_FRIENDLY_OFFSET;
00927
00928 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00929
00930 ao2_lock(cl);
00931 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00932 ao2_unlock(cl);
00933
00934 return moh;
00935 }
00936
00937 static void moh_release(struct ast_channel *chan, void *data)
00938 {
00939 struct mohdata *moh = data;
00940 struct mohclass *class = moh->parent;
00941 struct ast_format oldwfmt;
00942
00943 ao2_lock(class);
00944 AST_LIST_REMOVE(&moh->parent->members, moh, list);
00945 ao2_unlock(class);
00946
00947 close(moh->pipe[0]);
00948 close(moh->pipe[1]);
00949
00950 ast_format_copy(&oldwfmt, &moh->origwfmt);
00951
00952 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00953
00954 ast_free(moh);
00955
00956 if (chan) {
00957 struct moh_files_state *state;
00958
00959 state = chan->music_state;
00960 if (state && state->class) {
00961 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00962 }
00963 if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
00964 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00965 ast_channel_name(chan), ast_getformatname(&oldwfmt));
00966 }
00967
00968 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
00969 }
00970 }
00971
00972 static void *moh_alloc(struct ast_channel *chan, void *params)
00973 {
00974 struct mohdata *res;
00975 struct mohclass *class = params;
00976 struct moh_files_state *state;
00977
00978
00979 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00980 chan->music_state = state;
00981 ast_module_ref(ast_module_info->self);
00982 } else {
00983 state = chan->music_state;
00984 if (!state) {
00985 return NULL;
00986 }
00987 if (state->class) {
00988 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00989 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00990 }
00991 memset(state, 0, sizeof(*state));
00992 }
00993
00994 if ((res = mohalloc(class))) {
00995 ast_format_copy(&res->origwfmt, &chan->writeformat);
00996 if (ast_set_write_format(chan, &class->format)) {
00997 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan), ast_codec2str(&class->format));
00998 moh_release(NULL, res);
00999 res = NULL;
01000 } else {
01001 state->class = mohclass_ref(class, "Placing reference into state container");
01002 }
01003 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, ast_channel_name(chan));
01004 }
01005 return res;
01006 }
01007
01008 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
01009 {
01010 struct mohdata *moh = data;
01011 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
01012 int res;
01013
01014 len = ast_codec_get_len(&moh->parent->format, samples);
01015
01016 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
01017 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
01018 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
01019 }
01020 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
01021 if (res <= 0)
01022 return 0;
01023
01024 moh->f.datalen = res;
01025 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
01026 moh->f.samples = ast_codec_get_samples(&moh->f);
01027
01028 if (ast_write(chan, &moh->f) < 0) {
01029 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
01030 return -1;
01031 }
01032
01033 return 0;
01034 }
01035
01036 static struct ast_generator mohgen = {
01037 .alloc = moh_alloc,
01038 .release = moh_release,
01039 .generate = moh_generate,
01040 .digit = moh_handle_digit,
01041 };
01042
01043 static int moh_add_file(struct mohclass *class, const char *filepath)
01044 {
01045 if (!class->allowed_files) {
01046 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
01047 return -1;
01048 class->allowed_files = INITIAL_NUM_FILES;
01049 } else if (class->total_files == class->allowed_files) {
01050 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
01051 class->allowed_files = 0;
01052 class->total_files = 0;
01053 return -1;
01054 }
01055 class->allowed_files *= 2;
01056 }
01057
01058 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
01059 return -1;
01060
01061 class->total_files++;
01062
01063 return 0;
01064 }
01065
01066 static int moh_sort_compare(const void *i1, const void *i2)
01067 {
01068 char *s1, *s2;
01069
01070 s1 = ((char **)i1)[0];
01071 s2 = ((char **)i2)[0];
01072
01073 return strcasecmp(s1, s2);
01074 }
01075
01076 static int moh_scan_files(struct mohclass *class) {
01077
01078 DIR *files_DIR;
01079 struct dirent *files_dirent;
01080 char dir_path[PATH_MAX];
01081 char path[PATH_MAX];
01082 char filepath[PATH_MAX];
01083 char *ext;
01084 struct stat statbuf;
01085 int i;
01086
01087 if (class->dir[0] != '/') {
01088 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01089 strncat(dir_path, "/", sizeof(dir_path) - 1);
01090 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01091 } else {
01092 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01093 }
01094 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01095 files_DIR = opendir(dir_path);
01096 if (!files_DIR) {
01097 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01098 return -1;
01099 }
01100
01101 for (i = 0; i < class->total_files; i++)
01102 ast_free(class->filearray[i]);
01103
01104 class->total_files = 0;
01105 if (!getcwd(path, sizeof(path))) {
01106 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01107 return -1;
01108 }
01109 if (chdir(dir_path) < 0) {
01110 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01111 return -1;
01112 }
01113 while ((files_dirent = readdir(files_DIR))) {
01114
01115 if ((strlen(files_dirent->d_name) < 4))
01116 continue;
01117
01118
01119 if (files_dirent->d_name[0] == '.')
01120 continue;
01121
01122
01123 if (!strchr(files_dirent->d_name, '.'))
01124 continue;
01125
01126 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01127
01128 if (stat(filepath, &statbuf))
01129 continue;
01130
01131 if (!S_ISREG(statbuf.st_mode))
01132 continue;
01133
01134 if ((ext = strrchr(filepath, '.')))
01135 *ext = '\0';
01136
01137
01138 for (i = 0; i < class->total_files; i++)
01139 if (!strcmp(filepath, class->filearray[i]))
01140 break;
01141
01142 if (i == class->total_files) {
01143 if (moh_add_file(class, filepath))
01144 break;
01145 }
01146 }
01147
01148 closedir(files_DIR);
01149 if (chdir(path) < 0) {
01150 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01151 return -1;
01152 }
01153 if (ast_test_flag(class, MOH_SORTALPHA))
01154 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01155 return class->total_files;
01156 }
01157
01158 static int init_files_class(struct mohclass *class)
01159 {
01160 int res;
01161
01162 res = moh_scan_files(class);
01163
01164 if (res < 0) {
01165 return -1;
01166 }
01167
01168 if (!res) {
01169 ast_verb(3, "Files not found in %s for moh class:%s\n",
01170 class->dir, class->name);
01171 return -1;
01172 }
01173
01174 #if 0
01175
01176 if (strchr(class->args, 'r')) {
01177 ast_set_flag(class, MOH_RANDOMIZE);
01178 }
01179 #endif
01180
01181 return 0;
01182 }
01183
01184 static void moh_rescan_files(void) {
01185 struct ao2_iterator i;
01186 struct mohclass *c;
01187
01188 i = ao2_iterator_init(mohclasses, 0);
01189
01190 while ((c = ao2_iterator_next(&i))) {
01191 if (!strcasecmp(c->mode, "files")) {
01192 moh_scan_files(c);
01193 }
01194 ao2_ref(c, -1);
01195 }
01196
01197 ao2_iterator_destroy(&i);
01198 }
01199
01200 static int moh_diff(struct mohclass *old, struct mohclass *new)
01201 {
01202 if (!old || !new) {
01203 return -1;
01204 }
01205
01206 if (strcmp(old->dir, new->dir)) {
01207 return -1;
01208 } else if (strcmp(old->mode, new->mode)) {
01209 return -1;
01210 } else if (strcmp(old->args, new->args)) {
01211 return -1;
01212 } else if (old->flags != new->flags) {
01213 return -1;
01214 }
01215
01216 return 0;
01217 }
01218
01219 static int init_app_class(struct mohclass *class)
01220 {
01221 if (!strcasecmp(class->mode, "custom")) {
01222 ast_set_flag(class, MOH_CUSTOM);
01223 } else if (!strcasecmp(class->mode, "mp3nb")) {
01224 ast_set_flag(class, MOH_SINGLE);
01225 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01226 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01227 } else if (!strcasecmp(class->mode, "quietmp3")) {
01228 ast_set_flag(class, MOH_QUIET);
01229 }
01230
01231 class->srcfd = -1;
01232
01233 if (!(class->timer = ast_timer_open())) {
01234 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01235 return -1;
01236 }
01237 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01238 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01239 ast_timer_close(class->timer);
01240 class->timer = NULL;
01241 }
01242
01243 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01244 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01245 if (class->timer) {
01246 ast_timer_close(class->timer);
01247 class->timer = NULL;
01248 }
01249 return -1;
01250 }
01251
01252 return 0;
01253 }
01254
01255
01256
01257
01258 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01259 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01260 {
01261 struct mohclass *mohclass = NULL;
01262
01263 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01264
01265 if (mohclass && !moh_diff(mohclass, moh)) {
01266 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01267 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01268 if (unref) {
01269 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01270 }
01271 return -1;
01272 } else if (mohclass) {
01273
01274 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01275 }
01276
01277 time(&moh->start);
01278 moh->start -= respawn_time;
01279
01280 if (!strcasecmp(moh->mode, "files")) {
01281 if (init_files_class(moh)) {
01282 if (unref) {
01283 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01284 }
01285 return -1;
01286 }
01287 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
01288 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
01289 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01290 if (init_app_class(moh)) {
01291 if (unref) {
01292 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01293 }
01294 return -1;
01295 }
01296 } else {
01297 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01298 if (unref) {
01299 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01300 }
01301 return -1;
01302 }
01303
01304 ao2_t_link(mohclasses, moh, "Adding class to container");
01305
01306 if (unref) {
01307 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01308 }
01309
01310 return 0;
01311 }
01312
01313 static void local_ast_moh_cleanup(struct ast_channel *chan)
01314 {
01315 struct moh_files_state *state = chan->music_state;
01316
01317 if (state) {
01318 if (state->class) {
01319
01320 state->class =
01321 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01322 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01323 }
01324 ast_free(chan->music_state);
01325 chan->music_state = NULL;
01326
01327 ast_module_unref(ast_module_info->self);
01328 }
01329 }
01330
01331 static void moh_class_destructor(void *obj);
01332
01333 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01334
01335 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01336 {
01337 struct mohclass *class;
01338
01339 if ((class =
01340 #ifdef REF_DEBUG
01341 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
01342 #elif defined(__AST_DEBUG_MALLOC)
01343 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
01344 #else
01345 ao2_alloc(sizeof(*class), moh_class_destructor)
01346 #endif
01347 )) {
01348 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01349 class->srcfd = -1;
01350 }
01351
01352 return class;
01353 }
01354
01355 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01356 {
01357 struct mohclass *mohclass = NULL;
01358 struct moh_files_state *state = chan->music_state;
01359 struct ast_variable *var = NULL;
01360 int res;
01361 int realtime_possible = ast_check_realtime("musiconhold");
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374 if (!ast_strlen_zero(ast_channel_musicclass(chan))) {
01375 mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
01376 if (!mohclass && realtime_possible) {
01377 var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), SENTINEL);
01378 }
01379 }
01380 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01381 mohclass = get_mohbyname(mclass, 1, 0);
01382 if (!mohclass && realtime_possible) {
01383 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01384 }
01385 }
01386 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01387 mohclass = get_mohbyname(interpclass, 1, 0);
01388 if (!mohclass && realtime_possible) {
01389 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01390 }
01391 }
01392
01393 if (!mohclass && !var) {
01394 mohclass = get_mohbyname("default", 1, 0);
01395 if (!mohclass && realtime_possible) {
01396 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01397 }
01398 }
01399
01400
01401
01402
01403 if (var) {
01404 struct ast_variable *tmp = NULL;
01405
01406 if ((mohclass = moh_class_malloc())) {
01407 mohclass->realtime = 1;
01408 for (tmp = var; tmp; tmp = tmp->next) {
01409 if (!strcasecmp(tmp->name, "name"))
01410 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01411 else if (!strcasecmp(tmp->name, "mode"))
01412 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
01413 else if (!strcasecmp(tmp->name, "directory"))
01414 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01415 else if (!strcasecmp(tmp->name, "application"))
01416 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01417 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01418 mohclass->digit = *tmp->value;
01419 else if (!strcasecmp(tmp->name, "random"))
01420 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01421 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01422 ast_set_flag(mohclass, MOH_RANDOMIZE);
01423 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
01424 ast_set_flag(mohclass, MOH_SORTALPHA);
01425 else if (!strcasecmp(tmp->name, "format")) {
01426 ast_getformatbyname(tmp->value, &mohclass->format);
01427 if (!mohclass->format.id) {
01428 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01429 ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
01430 }
01431 }
01432 }
01433 ast_variables_destroy(var);
01434 if (ast_strlen_zero(mohclass->dir)) {
01435 if (!strcasecmp(mohclass->mode, "custom")) {
01436 strcpy(mohclass->dir, "nodir");
01437 } else {
01438 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01439 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01440 return -1;
01441 }
01442 }
01443 if (ast_strlen_zero(mohclass->mode)) {
01444 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01445 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01446 return -1;
01447 }
01448 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01449 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01450 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01451 return -1;
01452 }
01453
01454 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01455
01456 if (state && state->class) {
01457
01458 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01459 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01460
01461
01462 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01463 mohclass = state->class;
01464 }
01465 }
01466
01467
01468
01469
01470
01471
01472 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01473 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01474 return -1;
01475 }
01476 } else {
01477
01478
01479 time(&mohclass->start);
01480 mohclass->start -= respawn_time;
01481
01482 if (!strcasecmp(mohclass->mode, "files")) {
01483 if (!moh_scan_files(mohclass)) {
01484 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01485 return -1;
01486 }
01487 if (strchr(mohclass->args, 'r'))
01488 ast_set_flag(mohclass, MOH_RANDOMIZE);
01489 } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
01490
01491 if (!strcasecmp(mohclass->mode, "custom"))
01492 ast_set_flag(mohclass, MOH_CUSTOM);
01493 else if (!strcasecmp(mohclass->mode, "mp3nb"))
01494 ast_set_flag(mohclass, MOH_SINGLE);
01495 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01496 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01497 else if (!strcasecmp(mohclass->mode, "quietmp3"))
01498 ast_set_flag(mohclass, MOH_QUIET);
01499
01500 mohclass->srcfd = -1;
01501 if (!(mohclass->timer = ast_timer_open())) {
01502 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01503 }
01504 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01505 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01506 ast_timer_close(mohclass->timer);
01507 mohclass->timer = NULL;
01508 }
01509
01510
01511 if (state && state->class) {
01512
01513 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01514 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01515
01516 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01517 mohclass = state->class;
01518 }
01519 } else {
01520 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01521 ast_log(LOG_WARNING, "Unable to create moh...\n");
01522 if (mohclass->timer) {
01523 ast_timer_close(mohclass->timer);
01524 mohclass->timer = NULL;
01525 }
01526 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01527 return -1;
01528 }
01529 }
01530 } else {
01531 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01532 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01533 return -1;
01534 }
01535 }
01536 } else {
01537 ast_variables_destroy(var);
01538 var = NULL;
01539 }
01540 }
01541
01542 if (!mohclass) {
01543 return -1;
01544 }
01545
01546
01547 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01548 if (!moh_scan_files(mohclass)) {
01549 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01550 return -1;
01551 }
01552 }
01553
01554 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01555 "State: Start\r\n"
01556 "Channel: %s\r\n"
01557 "UniqueID: %s\r\n"
01558 "Class: %s\r\n",
01559 ast_channel_name(chan), ast_channel_uniqueid(chan),
01560 mohclass->name);
01561
01562 ast_set_flag(chan, AST_FLAG_MOH);
01563
01564 if (mohclass->total_files) {
01565 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01566 } else {
01567 res = ast_activate_generator(chan, &mohgen, mohclass);
01568 }
01569
01570 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01571
01572 return res;
01573 }
01574
01575 static void local_ast_moh_stop(struct ast_channel *chan)
01576 {
01577 ast_clear_flag(chan, AST_FLAG_MOH);
01578 ast_deactivate_generator(chan);
01579
01580 ast_channel_lock(chan);
01581 if (chan->music_state) {
01582 if (chan->stream) {
01583 ast_closestream(chan->stream);
01584 chan->stream = NULL;
01585 }
01586 }
01587
01588 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01589 "State: Stop\r\n"
01590 "Channel: %s\r\n"
01591 "UniqueID: %s\r\n",
01592 ast_channel_name(chan), ast_channel_uniqueid(chan));
01593 ast_channel_unlock(chan);
01594 }
01595
01596 static void moh_class_destructor(void *obj)
01597 {
01598 struct mohclass *class = obj;
01599 struct mohdata *member;
01600 pthread_t tid = 0;
01601
01602 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01603
01604 ao2_lock(class);
01605 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01606 free(member);
01607 }
01608 ao2_unlock(class);
01609
01610
01611
01612 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01613 tid = class->thread;
01614 class->thread = AST_PTHREADT_NULL;
01615 pthread_cancel(tid);
01616
01617
01618 }
01619
01620 if (class->pid > 1) {
01621 char buff[8192];
01622 int bytes, tbytes = 0, stime = 0, pid = 0;
01623
01624 ast_debug(1, "killing %d!\n", class->pid);
01625
01626 stime = time(NULL) + 2;
01627 pid = class->pid;
01628 class->pid = 0;
01629
01630
01631
01632
01633 do {
01634 if (killpg(pid, SIGHUP) < 0) {
01635 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01636 }
01637 usleep(100000);
01638 if (killpg(pid, SIGTERM) < 0) {
01639 if (errno == ESRCH) {
01640 break;
01641 }
01642 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01643 }
01644 usleep(100000);
01645 if (killpg(pid, SIGKILL) < 0) {
01646 if (errno == ESRCH) {
01647 break;
01648 }
01649 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01650 }
01651 } while (0);
01652
01653 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01654 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01655 tbytes = tbytes + bytes;
01656 }
01657
01658 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01659
01660 close(class->srcfd);
01661 class->srcfd = -1;
01662 }
01663
01664 if (class->filearray) {
01665 int i;
01666 for (i = 0; i < class->total_files; i++) {
01667 free(class->filearray[i]);
01668 }
01669 free(class->filearray);
01670 class->filearray = NULL;
01671 }
01672
01673 if (class->timer) {
01674 ast_timer_close(class->timer);
01675 class->timer = NULL;
01676 }
01677
01678
01679 if (tid > 0) {
01680 pthread_join(tid, NULL);
01681 }
01682
01683 }
01684
01685 static int moh_class_mark(void *obj, void *arg, int flags)
01686 {
01687 struct mohclass *class = obj;
01688
01689 class->delete = 1;
01690
01691 return 0;
01692 }
01693
01694 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01695 {
01696 struct mohclass *class = obj;
01697
01698 return class->delete ? CMP_MATCH : 0;
01699 }
01700
01701 static int load_moh_classes(int reload)
01702 {
01703 struct ast_config *cfg;
01704 struct ast_variable *var;
01705 struct mohclass *class;
01706 char *cat;
01707 int numclasses = 0;
01708 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01709
01710 cfg = ast_config_load("musiconhold.conf", config_flags);
01711
01712 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01713 if (ast_check_realtime("musiconhold") && reload) {
01714 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01715 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01716 }
01717 return 0;
01718 }
01719 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01720 moh_rescan_files();
01721 return 0;
01722 }
01723
01724 if (reload) {
01725 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01726 }
01727
01728 ast_clear_flag(global_flags, AST_FLAGS_ALL);
01729
01730 cat = ast_category_browse(cfg, NULL);
01731 for (; cat; cat = ast_category_browse(cfg, cat)) {
01732
01733 if (!strcasecmp(cat, "general")) {
01734 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01735 if (!strcasecmp(var->name, "cachertclasses")) {
01736 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01737 } else {
01738 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01739 }
01740 }
01741 }
01742
01743 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
01744 !strcasecmp(cat, "general")) {
01745 continue;
01746 }
01747
01748 if (!(class = moh_class_malloc())) {
01749 break;
01750 }
01751
01752 ast_copy_string(class->name, cat, sizeof(class->name));
01753 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01754 if (!strcasecmp(var->name, "mode")) {
01755 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01756 } else if (!strcasecmp(var->name, "directory")) {
01757 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01758 } else if (!strcasecmp(var->name, "application")) {
01759 ast_copy_string(class->args, var->value, sizeof(class->args));
01760 } else if (!strcasecmp(var->name, "announcement")) {
01761 ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
01762 ast_set_flag(class, MOH_ANNOUNCEMENT);
01763 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
01764 class->digit = *var->value;
01765 } else if (!strcasecmp(var->name, "random")) {
01766 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01767 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
01768 ast_set_flag(class, MOH_RANDOMIZE);
01769 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
01770 ast_set_flag(class, MOH_SORTALPHA);
01771 } else if (!strcasecmp(var->name, "format")) {
01772 ast_getformatbyname(var->value, &class->format);
01773 if (!class->format.id) {
01774 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01775 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01776 }
01777 }
01778 }
01779
01780 if (ast_strlen_zero(class->dir)) {
01781 if (!strcasecmp(class->mode, "custom")) {
01782 strcpy(class->dir, "nodir");
01783 } else {
01784 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01785 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01786 continue;
01787 }
01788 }
01789 if (ast_strlen_zero(class->mode)) {
01790 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01791 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01792 continue;
01793 }
01794 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01795 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01796 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01797 continue;
01798 }
01799
01800
01801 if (!moh_register(class, reload, HANDLE_REF)) {
01802 numclasses++;
01803 }
01804 }
01805
01806 ast_config_destroy(cfg);
01807
01808 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
01809 moh_classes_delete_marked, NULL, "Purge marked classes");
01810
01811 return numclasses;
01812 }
01813
01814 static void ast_moh_destroy(void)
01815 {
01816 ast_verb(2, "Destroying musiconhold processes\n");
01817 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01818 }
01819
01820 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01821 {
01822 switch (cmd) {
01823 case CLI_INIT:
01824 e->command = "moh reload";
01825 e->usage =
01826 "Usage: moh reload\n"
01827 " Reloads the MusicOnHold module.\n"
01828 " Alias for 'module reload res_musiconhold.so'\n";
01829 return NULL;
01830 case CLI_GENERATE:
01831 return NULL;
01832 }
01833
01834 if (a->argc != e->args)
01835 return CLI_SHOWUSAGE;
01836
01837 reload();
01838
01839 return CLI_SUCCESS;
01840 }
01841
01842 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01843 {
01844 struct mohclass *class;
01845 struct ao2_iterator i;
01846
01847 switch (cmd) {
01848 case CLI_INIT:
01849 e->command = "moh show files";
01850 e->usage =
01851 "Usage: moh show files\n"
01852 " Lists all loaded file-based MusicOnHold classes and their\n"
01853 " files.\n";
01854 return NULL;
01855 case CLI_GENERATE:
01856 return NULL;
01857 }
01858
01859 if (a->argc != e->args)
01860 return CLI_SHOWUSAGE;
01861
01862 i = ao2_iterator_init(mohclasses, 0);
01863 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01864 int x;
01865
01866 if (!class->total_files) {
01867 continue;
01868 }
01869
01870 ast_cli(a->fd, "Class: %s\n", class->name);
01871 for (x = 0; x < class->total_files; x++) {
01872 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01873 }
01874 }
01875 ao2_iterator_destroy(&i);
01876
01877 return CLI_SUCCESS;
01878 }
01879
01880 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01881 {
01882 struct mohclass *class;
01883 struct ao2_iterator i;
01884
01885 switch (cmd) {
01886 case CLI_INIT:
01887 e->command = "moh show classes";
01888 e->usage =
01889 "Usage: moh show classes\n"
01890 " Lists all MusicOnHold classes.\n";
01891 return NULL;
01892 case CLI_GENERATE:
01893 return NULL;
01894 }
01895
01896 if (a->argc != e->args)
01897 return CLI_SHOWUSAGE;
01898
01899 i = ao2_iterator_init(mohclasses, 0);
01900 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01901 ast_cli(a->fd, "Class: %s\n", class->name);
01902 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01903 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01904 if (ast_test_flag(class, MOH_CUSTOM)) {
01905 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01906 }
01907 if (strcasecmp(class->mode, "files")) {
01908 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(&class->format));
01909 }
01910 }
01911 ao2_iterator_destroy(&i);
01912
01913 return CLI_SUCCESS;
01914 }
01915
01916 static struct ast_cli_entry cli_moh[] = {
01917 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
01918 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01919 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
01920 };
01921
01922 static int moh_class_hash(const void *obj, const int flags)
01923 {
01924 const struct mohclass *class = obj;
01925
01926 return ast_str_case_hash(class->name);
01927 }
01928
01929 static int moh_class_cmp(void *obj, void *arg, int flags)
01930 {
01931 struct mohclass *class = obj, *class2 = arg;
01932
01933 return strcasecmp(class->name, class2->name) ? 0 :
01934 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01935 CMP_MATCH | CMP_STOP;
01936 }
01937
01938 static int load_module(void)
01939 {
01940 int res;
01941
01942 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01943 return AST_MODULE_LOAD_DECLINE;
01944 }
01945
01946 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {
01947 ast_log(LOG_WARNING, "No music on hold classes configured, "
01948 "disabling music on hold.\n");
01949 } else {
01950 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01951 local_ast_moh_cleanup);
01952 }
01953
01954 res = ast_register_application_xml(play_moh, play_moh_exec);
01955 ast_register_atexit(ast_moh_destroy);
01956 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01957 if (!res)
01958 res = ast_register_application_xml(wait_moh, wait_moh_exec);
01959 if (!res)
01960 res = ast_register_application_xml(set_moh, set_moh_exec);
01961 if (!res)
01962 res = ast_register_application_xml(start_moh, start_moh_exec);
01963 if (!res)
01964 res = ast_register_application_xml(stop_moh, stop_moh_exec);
01965
01966 return AST_MODULE_LOAD_SUCCESS;
01967 }
01968
01969 static int reload(void)
01970 {
01971 if (load_moh_classes(1)) {
01972 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01973 local_ast_moh_cleanup);
01974 }
01975
01976 return AST_MODULE_LOAD_SUCCESS;
01977 }
01978
01979 static int moh_class_inuse(void *obj, void *arg, int flags)
01980 {
01981 struct mohclass *class = obj;
01982
01983 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01984 }
01985
01986 static int unload_module(void)
01987 {
01988 int res = 0;
01989 struct mohclass *class = NULL;
01990
01991
01992
01993 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01994 class = mohclass_unref(class, "unref of class from module unload callback");
01995 res = -1;
01996 }
01997
01998 if (res < 0) {
01999 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
02000 return res;
02001 }
02002
02003 ast_uninstall_music_functions();
02004
02005 ast_moh_destroy();
02006 res = ast_unregister_application(play_moh);
02007 res |= ast_unregister_application(wait_moh);
02008 res |= ast_unregister_application(set_moh);
02009 res |= ast_unregister_application(start_moh);
02010 res |= ast_unregister_application(stop_moh);
02011 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
02012 ast_unregister_atexit(ast_moh_destroy);
02013
02014 return res;
02015 }
02016
02017 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
02018 .load = load_module,
02019 .unload = unload_module,
02020 .reload = reload,
02021 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
02022 );