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: 410044 $")
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 || !ast_channel_music_state(chan)) {
00264 return;
00265 }
00266
00267 state = ast_channel_music_state(chan);
00268
00269 if (ast_channel_stream(chan)) {
00270 ast_closestream(ast_channel_stream(chan));
00271 ast_channel_stream_set(chan, 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 = ast_channel_music_state(chan);
00290 int tries;
00291
00292
00293 if (ast_channel_stream(chan)) {
00294 ast_closestream(ast_channel_stream(chan));
00295 ast_channel_stream_set(chan, 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(ast_channel_stream(chan), state->samples, SEEK_SET);
00361
00362
00363 loc = ast_tellstream(ast_channel_stream(chan));
00364 if (state->samples > loc && loc) {
00365
00366 ast_seekstream(ast_channel_stream(chan), 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 (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
00378 if (!ast_moh_files_next(chan))
00379 f = ast_readframe(ast_channel_stream(chan));
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 = ast_channel_music_state(chan);
00388
00389
00390
00391
00392 if (&state->origwfmt.id) {
00393 struct ast_format tmp;
00394
00395 ast_format_copy(&tmp, ast_channel_writeformat(chan));
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 = ast_channel_music_state(chan);
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 (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
00447 ast_channel_music_state_set(chan, state);
00448 ast_module_ref(ast_module_info->self);
00449 } else {
00450 state = ast_channel_music_state(chan);
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, ast_channel_writeformat(chan));
00474 ast_format_copy(&state->mohwfmt, ast_channel_writeformat(chan));
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 ast_channel_music_state(chan);
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 continue;
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 if (ast_timer_ack(class->timer, 1) < 0) {
00696 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
00697 return NULL;
00698 }
00699 res = 320;
00700 } else {
00701 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
00702 res = 0;
00703 }
00704 pthread_testcancel();
00705 } else {
00706 long delta;
00707
00708 tv_tmp = ast_tvnow();
00709 if (ast_tvzero(deadline))
00710 deadline = tv_tmp;
00711 delta = ast_tvdiff_ms(tv_tmp, deadline);
00712 if (delta < MOH_MS_INTERVAL) {
00713 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));
00714 usleep(1000 * (MOH_MS_INTERVAL - delta));
00715 pthread_testcancel();
00716 } else {
00717 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00718 deadline = tv_tmp;
00719 }
00720 res = 8 * MOH_MS_INTERVAL;
00721 }
00722 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00723 continue;
00724
00725 len = ast_codec_get_len(&class->format, res);
00726
00727 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00728 if (!res2) {
00729 close(class->srcfd);
00730 class->srcfd = -1;
00731 pthread_testcancel();
00732 if (class->pid > 1) {
00733 do {
00734 if (killpg(class->pid, SIGHUP) < 0) {
00735 if (errno == ESRCH) {
00736 break;
00737 }
00738 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00739 }
00740 usleep(100000);
00741 if (killpg(class->pid, SIGTERM) < 0) {
00742 if (errno == ESRCH) {
00743 break;
00744 }
00745 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00746 }
00747 usleep(100000);
00748 if (killpg(class->pid, SIGKILL) < 0) {
00749 if (errno == ESRCH) {
00750 break;
00751 }
00752 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00753 }
00754 } while (0);
00755 class->pid = 0;
00756 }
00757 } else {
00758 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00759 }
00760 continue;
00761 }
00762
00763 pthread_testcancel();
00764
00765 ao2_lock(class);
00766 AST_LIST_TRAVERSE(&class->members, moh, list) {
00767
00768 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00769 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00770 }
00771 }
00772 ao2_unlock(class);
00773 }
00774 return NULL;
00775 }
00776
00777 static int play_moh_exec(struct ast_channel *chan, const char *data)
00778 {
00779 char *parse;
00780 char *class;
00781 int timeout = -1;
00782 int res;
00783 AST_DECLARE_APP_ARGS(args,
00784 AST_APP_ARG(class);
00785 AST_APP_ARG(duration);
00786 );
00787
00788 parse = ast_strdupa(data);
00789
00790 AST_STANDARD_APP_ARGS(args, parse);
00791
00792 if (!ast_strlen_zero(args.duration)) {
00793 if (sscanf(args.duration, "%30d", &timeout) == 1) {
00794 timeout *= 1000;
00795 } else {
00796 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00797 }
00798 }
00799
00800 class = S_OR(args.class, NULL);
00801 if (ast_moh_start(chan, class, NULL)) {
00802 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00803 return 0;
00804 }
00805
00806 if (timeout > 0)
00807 res = ast_safe_sleep(chan, timeout);
00808 else {
00809 while (!(res = ast_safe_sleep(chan, 10000)));
00810 }
00811
00812 ast_moh_stop(chan);
00813
00814 return res;
00815 }
00816
00817 static int wait_moh_exec(struct ast_channel *chan, const char *data)
00818 {
00819 static int deprecation_warning = 0;
00820 int res;
00821
00822 if (!deprecation_warning) {
00823 deprecation_warning = 1;
00824 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00825 }
00826
00827 if (!data || !atoi(data)) {
00828 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00829 return -1;
00830 }
00831 if (ast_moh_start(chan, NULL, NULL)) {
00832 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), ast_channel_name(chan));
00833 return 0;
00834 }
00835 res = ast_safe_sleep(chan, atoi(data) * 1000);
00836 ast_moh_stop(chan);
00837 return res;
00838 }
00839
00840 static int set_moh_exec(struct ast_channel *chan, const char *data)
00841 {
00842 static int deprecation_warning = 0;
00843
00844 if (!deprecation_warning) {
00845 deprecation_warning = 1;
00846 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00847 }
00848
00849 if (ast_strlen_zero(data)) {
00850 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00851 return -1;
00852 }
00853 ast_channel_musicclass_set(chan, data);
00854 return 0;
00855 }
00856
00857 static int start_moh_exec(struct ast_channel *chan, const char *data)
00858 {
00859 char *parse;
00860 char *class;
00861 AST_DECLARE_APP_ARGS(args,
00862 AST_APP_ARG(class);
00863 );
00864
00865 parse = ast_strdupa(data);
00866
00867 AST_STANDARD_APP_ARGS(args, parse);
00868
00869 class = S_OR(args.class, NULL);
00870 if (ast_moh_start(chan, class, NULL))
00871 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
00872
00873 return 0;
00874 }
00875
00876 static int stop_moh_exec(struct ast_channel *chan, const char *data)
00877 {
00878 ast_moh_stop(chan);
00879
00880 return 0;
00881 }
00882
00883 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00884
00885 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00886 {
00887 struct mohclass *moh = NULL;
00888 struct mohclass tmp_class = {
00889 .flags = 0,
00890 };
00891
00892 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00893
00894 #ifdef REF_DEBUG
00895 moh = __ao2_find_debug(mohclasses, &tmp_class, flags,
00896 "get_mohbyname", file, lineno, funcname);
00897 #else
00898 moh = __ao2_find(mohclasses, &tmp_class, flags);
00899 #endif
00900
00901 if (!moh && warn) {
00902 ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
00903 }
00904
00905 return moh;
00906 }
00907
00908 static struct mohdata *mohalloc(struct mohclass *cl)
00909 {
00910 struct mohdata *moh;
00911 long flags;
00912
00913 if (!(moh = ast_calloc(1, sizeof(*moh))))
00914 return NULL;
00915
00916 if (pipe(moh->pipe)) {
00917 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00918 ast_free(moh);
00919 return NULL;
00920 }
00921
00922
00923 flags = fcntl(moh->pipe[0], F_GETFL);
00924 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00925 flags = fcntl(moh->pipe[1], F_GETFL);
00926 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00927
00928 moh->f.frametype = AST_FRAME_VOICE;
00929 ast_format_copy(&moh->f.subclass.format, &cl->format);
00930 moh->f.offset = AST_FRIENDLY_OFFSET;
00931
00932 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00933
00934 ao2_lock(cl);
00935 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00936 ao2_unlock(cl);
00937
00938 return moh;
00939 }
00940
00941 static void moh_release(struct ast_channel *chan, void *data)
00942 {
00943 struct mohdata *moh = data;
00944 struct mohclass *class = moh->parent;
00945 struct ast_format oldwfmt;
00946
00947 ao2_lock(class);
00948 AST_LIST_REMOVE(&moh->parent->members, moh, list);
00949 ao2_unlock(class);
00950
00951 close(moh->pipe[0]);
00952 close(moh->pipe[1]);
00953
00954 ast_format_copy(&oldwfmt, &moh->origwfmt);
00955
00956 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00957
00958 ast_free(moh);
00959
00960 if (chan) {
00961 struct moh_files_state *state;
00962
00963 state = ast_channel_music_state(chan);
00964 if (state && state->class) {
00965 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00966 }
00967 if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
00968 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00969 ast_channel_name(chan), ast_getformatname(&oldwfmt));
00970 }
00971
00972 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
00973 }
00974 }
00975
00976 static void *moh_alloc(struct ast_channel *chan, void *params)
00977 {
00978 struct mohdata *res;
00979 struct mohclass *class = params;
00980 struct moh_files_state *state;
00981
00982
00983 if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
00984 ast_channel_music_state_set(chan, state);
00985 ast_module_ref(ast_module_info->self);
00986 } else {
00987 state = ast_channel_music_state(chan);
00988 if (!state) {
00989 return NULL;
00990 }
00991 if (state->class) {
00992 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
00993 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
00994 }
00995 memset(state, 0, sizeof(*state));
00996 }
00997
00998 if ((res = mohalloc(class))) {
00999 ast_format_copy(&res->origwfmt, ast_channel_writeformat(chan));
01000 if (ast_set_write_format(chan, &class->format)) {
01001 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan), ast_codec2str(&class->format));
01002 moh_release(NULL, res);
01003 res = NULL;
01004 } else {
01005 state->class = mohclass_ref(class, "Placing reference into state container");
01006 }
01007 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, ast_channel_name(chan));
01008 }
01009 return res;
01010 }
01011
01012 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
01013 {
01014 struct mohdata *moh = data;
01015 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
01016 int res;
01017
01018 len = ast_codec_get_len(&moh->parent->format, samples);
01019
01020 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
01021 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
01022 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
01023 }
01024 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
01025 if (res <= 0)
01026 return 0;
01027
01028 moh->f.datalen = res;
01029 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
01030 moh->f.samples = ast_codec_get_samples(&moh->f);
01031
01032 if (ast_write(chan, &moh->f) < 0) {
01033 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
01034 return -1;
01035 }
01036
01037 return 0;
01038 }
01039
01040 static struct ast_generator mohgen = {
01041 .alloc = moh_alloc,
01042 .release = moh_release,
01043 .generate = moh_generate,
01044 .digit = moh_handle_digit,
01045 };
01046
01047 static int moh_add_file(struct mohclass *class, const char *filepath)
01048 {
01049 if (!class->allowed_files) {
01050 class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray));
01051 if (!class->filearray) {
01052 return -1;
01053 }
01054 class->allowed_files = INITIAL_NUM_FILES;
01055 } else if (class->total_files == class->allowed_files) {
01056 char **new_array;
01057
01058 new_array = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2);
01059 if (!new_array) {
01060 return -1;
01061 }
01062 class->filearray = new_array;
01063 class->allowed_files *= 2;
01064 }
01065
01066 class->filearray[class->total_files] = ast_strdup(filepath);
01067 if (!class->filearray[class->total_files]) {
01068 return -1;
01069 }
01070
01071 class->total_files++;
01072
01073 return 0;
01074 }
01075
01076 static int moh_sort_compare(const void *i1, const void *i2)
01077 {
01078 char *s1, *s2;
01079
01080 s1 = ((char **)i1)[0];
01081 s2 = ((char **)i2)[0];
01082
01083 return strcasecmp(s1, s2);
01084 }
01085
01086 static int moh_scan_files(struct mohclass *class) {
01087
01088 DIR *files_DIR;
01089 struct dirent *files_dirent;
01090 char dir_path[PATH_MAX];
01091 char path[PATH_MAX];
01092 char filepath[PATH_MAX];
01093 char *ext;
01094 struct stat statbuf;
01095 int i;
01096
01097 if (class->dir[0] != '/') {
01098 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
01099 strncat(dir_path, "/", sizeof(dir_path) - 1);
01100 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
01101 } else {
01102 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
01103 }
01104 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
01105 files_DIR = opendir(dir_path);
01106 if (!files_DIR) {
01107 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
01108 return -1;
01109 }
01110
01111 for (i = 0; i < class->total_files; i++) {
01112 ast_free(class->filearray[i]);
01113 }
01114 class->total_files = 0;
01115
01116 if (!getcwd(path, sizeof(path))) {
01117 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
01118 closedir(files_DIR);
01119 return -1;
01120 }
01121 if (chdir(dir_path) < 0) {
01122 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01123 closedir(files_DIR);
01124 return -1;
01125 }
01126 while ((files_dirent = readdir(files_DIR))) {
01127
01128 if ((strlen(files_dirent->d_name) < 4))
01129 continue;
01130
01131
01132 if (files_dirent->d_name[0] == '.')
01133 continue;
01134
01135
01136 if (!strchr(files_dirent->d_name, '.'))
01137 continue;
01138
01139 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
01140
01141 if (stat(filepath, &statbuf))
01142 continue;
01143
01144 if (!S_ISREG(statbuf.st_mode))
01145 continue;
01146
01147 if ((ext = strrchr(filepath, '.')))
01148 *ext = '\0';
01149
01150
01151 for (i = 0; i < class->total_files; i++)
01152 if (!strcmp(filepath, class->filearray[i]))
01153 break;
01154
01155 if (i == class->total_files) {
01156 if (moh_add_file(class, filepath))
01157 break;
01158 }
01159 }
01160
01161 closedir(files_DIR);
01162 if (chdir(path) < 0) {
01163 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
01164 return -1;
01165 }
01166 if (ast_test_flag(class, MOH_SORTALPHA))
01167 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
01168 return class->total_files;
01169 }
01170
01171 static int init_files_class(struct mohclass *class)
01172 {
01173 int res;
01174
01175 res = moh_scan_files(class);
01176
01177 if (res < 0) {
01178 return -1;
01179 }
01180
01181 if (!res) {
01182 ast_verb(3, "Files not found in %s for moh class:%s\n",
01183 class->dir, class->name);
01184 return -1;
01185 }
01186
01187 #if 0
01188
01189 if (strchr(class->args, 'r')) {
01190 ast_set_flag(class, MOH_RANDOMIZE);
01191 }
01192 #endif
01193
01194 return 0;
01195 }
01196
01197 static void moh_rescan_files(void) {
01198 struct ao2_iterator i;
01199 struct mohclass *c;
01200
01201 i = ao2_iterator_init(mohclasses, 0);
01202
01203 while ((c = ao2_iterator_next(&i))) {
01204 if (!strcasecmp(c->mode, "files")) {
01205 moh_scan_files(c);
01206 }
01207 ao2_ref(c, -1);
01208 }
01209
01210 ao2_iterator_destroy(&i);
01211 }
01212
01213 static int moh_diff(struct mohclass *old, struct mohclass *new)
01214 {
01215 if (!old || !new) {
01216 return -1;
01217 }
01218
01219 if (strcmp(old->dir, new->dir)) {
01220 return -1;
01221 } else if (strcmp(old->mode, new->mode)) {
01222 return -1;
01223 } else if (strcmp(old->args, new->args)) {
01224 return -1;
01225 } else if (old->flags != new->flags) {
01226 return -1;
01227 }
01228
01229 return 0;
01230 }
01231
01232 static int init_app_class(struct mohclass *class)
01233 {
01234 if (!strcasecmp(class->mode, "custom")) {
01235 ast_set_flag(class, MOH_CUSTOM);
01236 } else if (!strcasecmp(class->mode, "mp3nb")) {
01237 ast_set_flag(class, MOH_SINGLE);
01238 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
01239 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
01240 } else if (!strcasecmp(class->mode, "quietmp3")) {
01241 ast_set_flag(class, MOH_QUIET);
01242 }
01243
01244 class->srcfd = -1;
01245
01246 if (!(class->timer = ast_timer_open())) {
01247 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01248 return -1;
01249 }
01250 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
01251 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01252 ast_timer_close(class->timer);
01253 class->timer = NULL;
01254 }
01255
01256 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
01257 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
01258 if (class->timer) {
01259 ast_timer_close(class->timer);
01260 class->timer = NULL;
01261 }
01262 return -1;
01263 }
01264
01265 return 0;
01266 }
01267
01268
01269
01270
01271 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
01272 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
01273 {
01274 struct mohclass *mohclass = NULL;
01275
01276 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
01277
01278 if (mohclass && !moh_diff(mohclass, moh)) {
01279 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
01280 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01281 if (unref) {
01282 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
01283 }
01284 return -1;
01285 } else if (mohclass) {
01286
01287 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
01288 }
01289
01290 time(&moh->start);
01291 moh->start -= respawn_time;
01292
01293 if (!strcasecmp(moh->mode, "files")) {
01294 if (init_files_class(moh)) {
01295 if (unref) {
01296 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
01297 }
01298 return -1;
01299 }
01300 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
01301 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
01302 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01303 if (init_app_class(moh)) {
01304 if (unref) {
01305 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
01306 }
01307 return -1;
01308 }
01309 } else {
01310 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01311 if (unref) {
01312 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
01313 }
01314 return -1;
01315 }
01316
01317 ao2_t_link(mohclasses, moh, "Adding class to container");
01318
01319 if (unref) {
01320 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
01321 }
01322
01323 return 0;
01324 }
01325
01326 static void local_ast_moh_cleanup(struct ast_channel *chan)
01327 {
01328 struct moh_files_state *state = ast_channel_music_state(chan);
01329
01330 if (state) {
01331 if (state->class) {
01332
01333 state->class =
01334 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
01335 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
01336 }
01337 ast_free(ast_channel_music_state(chan));
01338 ast_channel_music_state_set(chan, NULL);
01339
01340 ast_module_unref(ast_module_info->self);
01341 }
01342 }
01343
01344 static void moh_class_destructor(void *obj);
01345
01346 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
01347
01348 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
01349 {
01350 struct mohclass *class;
01351
01352 if ((class =
01353 #ifdef REF_DEBUG
01354 __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01355 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
01356 #elif defined(__AST_DEBUG_MALLOC)
01357 __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
01358 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
01359 #else
01360 ao2_alloc(sizeof(*class), moh_class_destructor)
01361 #endif
01362 )) {
01363 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01364 class->srcfd = -1;
01365 }
01366
01367 return class;
01368 }
01369
01370 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01371 {
01372 struct mohclass *mohclass = NULL;
01373 struct moh_files_state *state = ast_channel_music_state(chan);
01374 struct ast_variable *var = NULL;
01375 int res;
01376 int realtime_possible = ast_check_realtime("musiconhold");
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389 if (!ast_strlen_zero(ast_channel_musicclass(chan))) {
01390 mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
01391 if (!mohclass && realtime_possible) {
01392 var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), SENTINEL);
01393 }
01394 }
01395 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01396 mohclass = get_mohbyname(mclass, 1, 0);
01397 if (!mohclass && realtime_possible) {
01398 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01399 }
01400 }
01401 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01402 mohclass = get_mohbyname(interpclass, 1, 0);
01403 if (!mohclass && realtime_possible) {
01404 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01405 }
01406 }
01407
01408 if (!mohclass && !var) {
01409 mohclass = get_mohbyname("default", 1, 0);
01410 if (!mohclass && realtime_possible) {
01411 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01412 }
01413 }
01414
01415
01416
01417
01418 if (var) {
01419 struct ast_variable *tmp = NULL;
01420
01421 if ((mohclass = moh_class_malloc())) {
01422 mohclass->realtime = 1;
01423 for (tmp = var; tmp; tmp = tmp->next) {
01424 if (!strcasecmp(tmp->name, "name"))
01425 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01426 else if (!strcasecmp(tmp->name, "mode"))
01427 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
01428 else if (!strcasecmp(tmp->name, "directory"))
01429 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01430 else if (!strcasecmp(tmp->name, "application"))
01431 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01432 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01433 mohclass->digit = *tmp->value;
01434 else if (!strcasecmp(tmp->name, "random"))
01435 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01436 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01437 ast_set_flag(mohclass, MOH_RANDOMIZE);
01438 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
01439 ast_set_flag(mohclass, MOH_SORTALPHA);
01440 else if (!strcasecmp(tmp->name, "format")) {
01441 ast_getformatbyname(tmp->value, &mohclass->format);
01442 if (!mohclass->format.id) {
01443 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01444 ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
01445 }
01446 }
01447 }
01448 ast_variables_destroy(var);
01449 if (ast_strlen_zero(mohclass->dir)) {
01450 if (!strcasecmp(mohclass->mode, "custom")) {
01451 strcpy(mohclass->dir, "nodir");
01452 } else {
01453 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01454 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01455 return -1;
01456 }
01457 }
01458 if (ast_strlen_zero(mohclass->mode)) {
01459 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01460 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01461 return -1;
01462 }
01463 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01464 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01465 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01466 return -1;
01467 }
01468
01469 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01470
01471 if (state && state->class) {
01472
01473 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01474 }
01475
01476
01477
01478
01479
01480
01481 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01482 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01483 return -1;
01484 }
01485 } else {
01486
01487
01488 time(&mohclass->start);
01489 mohclass->start -= respawn_time;
01490
01491 if (!strcasecmp(mohclass->mode, "files")) {
01492 if (!moh_scan_files(mohclass)) {
01493 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01494 return -1;
01495 }
01496 if (strchr(mohclass->args, 'r'))
01497 ast_set_flag(mohclass, MOH_RANDOMIZE);
01498 } 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")) {
01499
01500 if (!strcasecmp(mohclass->mode, "custom"))
01501 ast_set_flag(mohclass, MOH_CUSTOM);
01502 else if (!strcasecmp(mohclass->mode, "mp3nb"))
01503 ast_set_flag(mohclass, MOH_SINGLE);
01504 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01505 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01506 else if (!strcasecmp(mohclass->mode, "quietmp3"))
01507 ast_set_flag(mohclass, MOH_QUIET);
01508
01509 mohclass->srcfd = -1;
01510 if (!(mohclass->timer = ast_timer_open())) {
01511 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01512 }
01513 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01514 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01515 ast_timer_close(mohclass->timer);
01516 mohclass->timer = NULL;
01517 }
01518
01519
01520 if (state && state->class) {
01521
01522 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01523 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01524
01525 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01526 mohclass = mohclass_ref(state->class, "using existing class from state");
01527 }
01528 } else {
01529 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01530 ast_log(LOG_WARNING, "Unable to create moh...\n");
01531 if (mohclass->timer) {
01532 ast_timer_close(mohclass->timer);
01533 mohclass->timer = NULL;
01534 }
01535 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01536 return -1;
01537 }
01538 }
01539 } else {
01540 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01541 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01542 return -1;
01543 }
01544 }
01545 } else {
01546 ast_variables_destroy(var);
01547 var = NULL;
01548 }
01549 }
01550
01551 if (!mohclass) {
01552 return -1;
01553 }
01554
01555
01556 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01557 if (!moh_scan_files(mohclass)) {
01558 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01559 return -1;
01560 }
01561 }
01562
01563 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01564 "State: Start\r\n"
01565 "Channel: %s\r\n"
01566 "UniqueID: %s\r\n"
01567 "Class: %s\r\n",
01568 ast_channel_name(chan), ast_channel_uniqueid(chan),
01569 mohclass->name);
01570
01571 ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
01572
01573 if (mohclass->total_files) {
01574 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01575 } else {
01576 res = ast_activate_generator(chan, &mohgen, mohclass);
01577 }
01578
01579 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01580
01581 return res;
01582 }
01583
01584 static void local_ast_moh_stop(struct ast_channel *chan)
01585 {
01586 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
01587 ast_deactivate_generator(chan);
01588
01589 ast_channel_lock(chan);
01590 if (ast_channel_music_state(chan)) {
01591 if (ast_channel_stream(chan)) {
01592 ast_closestream(ast_channel_stream(chan));
01593 ast_channel_stream_set(chan, NULL);
01594 }
01595 }
01596
01597 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01598 "State: Stop\r\n"
01599 "Channel: %s\r\n"
01600 "UniqueID: %s\r\n",
01601 ast_channel_name(chan), ast_channel_uniqueid(chan));
01602 ast_channel_unlock(chan);
01603 }
01604
01605 static void moh_class_destructor(void *obj)
01606 {
01607 struct mohclass *class = obj;
01608 struct mohdata *member;
01609 pthread_t tid = 0;
01610
01611 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01612
01613 ao2_lock(class);
01614 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01615 free(member);
01616 }
01617 ao2_unlock(class);
01618
01619
01620
01621 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01622 tid = class->thread;
01623 class->thread = AST_PTHREADT_NULL;
01624 pthread_cancel(tid);
01625
01626
01627 }
01628
01629 if (class->pid > 1) {
01630 char buff[8192];
01631 int bytes, tbytes = 0, stime = 0, pid = 0;
01632
01633 ast_debug(1, "killing %d!\n", class->pid);
01634
01635 stime = time(NULL) + 2;
01636 pid = class->pid;
01637 class->pid = 0;
01638
01639
01640
01641
01642 do {
01643 if (killpg(pid, SIGHUP) < 0) {
01644 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01645 }
01646 usleep(100000);
01647 if (killpg(pid, SIGTERM) < 0) {
01648 if (errno == ESRCH) {
01649 break;
01650 }
01651 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01652 }
01653 usleep(100000);
01654 if (killpg(pid, SIGKILL) < 0) {
01655 if (errno == ESRCH) {
01656 break;
01657 }
01658 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01659 }
01660 } while (0);
01661
01662 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01663 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01664 tbytes = tbytes + bytes;
01665 }
01666
01667 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01668
01669 close(class->srcfd);
01670 class->srcfd = -1;
01671 }
01672
01673 if (class->filearray) {
01674 int i;
01675 for (i = 0; i < class->total_files; i++) {
01676 free(class->filearray[i]);
01677 }
01678 free(class->filearray);
01679 class->filearray = NULL;
01680 }
01681
01682 if (class->timer) {
01683 ast_timer_close(class->timer);
01684 class->timer = NULL;
01685 }
01686
01687
01688 if (tid > 0) {
01689 pthread_join(tid, NULL);
01690 }
01691
01692 }
01693
01694 static int moh_class_mark(void *obj, void *arg, int flags)
01695 {
01696 struct mohclass *class = obj;
01697
01698 class->delete = 1;
01699
01700 return 0;
01701 }
01702
01703 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01704 {
01705 struct mohclass *class = obj;
01706
01707 return class->delete ? CMP_MATCH : 0;
01708 }
01709
01710 static int load_moh_classes(int reload)
01711 {
01712 struct ast_config *cfg;
01713 struct ast_variable *var;
01714 struct mohclass *class;
01715 char *cat;
01716 int numclasses = 0;
01717 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01718
01719 cfg = ast_config_load("musiconhold.conf", config_flags);
01720
01721 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01722 if (ast_check_realtime("musiconhold") && reload) {
01723 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01724 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01725 }
01726 return 0;
01727 }
01728 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01729 moh_rescan_files();
01730 return 0;
01731 }
01732
01733 if (reload) {
01734 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01735 }
01736
01737 ast_clear_flag(global_flags, AST_FLAGS_ALL);
01738
01739 cat = ast_category_browse(cfg, NULL);
01740 for (; cat; cat = ast_category_browse(cfg, cat)) {
01741
01742 if (!strcasecmp(cat, "general")) {
01743 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01744 if (!strcasecmp(var->name, "cachertclasses")) {
01745 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01746 } else {
01747 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01748 }
01749 }
01750 }
01751
01752 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
01753 !strcasecmp(cat, "general")) {
01754 continue;
01755 }
01756
01757 if (!(class = moh_class_malloc())) {
01758 break;
01759 }
01760
01761 ast_copy_string(class->name, cat, sizeof(class->name));
01762 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01763 if (!strcasecmp(var->name, "mode")) {
01764 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01765 } else if (!strcasecmp(var->name, "directory")) {
01766 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01767 } else if (!strcasecmp(var->name, "application")) {
01768 ast_copy_string(class->args, var->value, sizeof(class->args));
01769 } else if (!strcasecmp(var->name, "announcement")) {
01770 ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
01771 ast_set_flag(class, MOH_ANNOUNCEMENT);
01772 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
01773 class->digit = *var->value;
01774 } else if (!strcasecmp(var->name, "random")) {
01775 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01776 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
01777 ast_set_flag(class, MOH_RANDOMIZE);
01778 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
01779 ast_set_flag(class, MOH_SORTALPHA);
01780 } else if (!strcasecmp(var->name, "format")) {
01781 ast_getformatbyname(var->value, &class->format);
01782 if (!class->format.id) {
01783 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01784 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
01785 }
01786 }
01787 }
01788
01789 if (ast_strlen_zero(class->dir)) {
01790 if (!strcasecmp(class->mode, "custom")) {
01791 strcpy(class->dir, "nodir");
01792 } else {
01793 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01794 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01795 continue;
01796 }
01797 }
01798 if (ast_strlen_zero(class->mode)) {
01799 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01800 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01801 continue;
01802 }
01803 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01804 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01805 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01806 continue;
01807 }
01808
01809
01810 if (!moh_register(class, reload, HANDLE_REF)) {
01811 numclasses++;
01812 }
01813 }
01814
01815 ast_config_destroy(cfg);
01816
01817 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
01818 moh_classes_delete_marked, NULL, "Purge marked classes");
01819
01820 return numclasses;
01821 }
01822
01823 static void ast_moh_destroy(void)
01824 {
01825 ast_verb(2, "Destroying musiconhold processes\n");
01826 if (mohclasses) {
01827 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01828 ao2_ref(mohclasses, -1);
01829 mohclasses = NULL;
01830 }
01831 }
01832
01833 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01834 {
01835 switch (cmd) {
01836 case CLI_INIT:
01837 e->command = "moh reload";
01838 e->usage =
01839 "Usage: moh reload\n"
01840 " Reloads the MusicOnHold module.\n"
01841 " Alias for 'module reload res_musiconhold.so'\n";
01842 return NULL;
01843 case CLI_GENERATE:
01844 return NULL;
01845 }
01846
01847 if (a->argc != e->args)
01848 return CLI_SHOWUSAGE;
01849
01850 reload();
01851
01852 return CLI_SUCCESS;
01853 }
01854
01855 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01856 {
01857 struct mohclass *class;
01858 struct ao2_iterator i;
01859
01860 switch (cmd) {
01861 case CLI_INIT:
01862 e->command = "moh show files";
01863 e->usage =
01864 "Usage: moh show files\n"
01865 " Lists all loaded file-based MusicOnHold classes and their\n"
01866 " files.\n";
01867 return NULL;
01868 case CLI_GENERATE:
01869 return NULL;
01870 }
01871
01872 if (a->argc != e->args)
01873 return CLI_SHOWUSAGE;
01874
01875 i = ao2_iterator_init(mohclasses, 0);
01876 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01877 int x;
01878
01879 if (!class->total_files) {
01880 continue;
01881 }
01882
01883 ast_cli(a->fd, "Class: %s\n", class->name);
01884 for (x = 0; x < class->total_files; x++) {
01885 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01886 }
01887 }
01888 ao2_iterator_destroy(&i);
01889
01890 return CLI_SUCCESS;
01891 }
01892
01893 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01894 {
01895 struct mohclass *class;
01896 struct ao2_iterator i;
01897
01898 switch (cmd) {
01899 case CLI_INIT:
01900 e->command = "moh show classes";
01901 e->usage =
01902 "Usage: moh show classes\n"
01903 " Lists all MusicOnHold classes.\n";
01904 return NULL;
01905 case CLI_GENERATE:
01906 return NULL;
01907 }
01908
01909 if (a->argc != e->args)
01910 return CLI_SHOWUSAGE;
01911
01912 i = ao2_iterator_init(mohclasses, 0);
01913 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01914 ast_cli(a->fd, "Class: %s\n", class->name);
01915 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01916 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01917 if (ast_test_flag(class, MOH_CUSTOM)) {
01918 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01919 }
01920 if (strcasecmp(class->mode, "files")) {
01921 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(&class->format));
01922 }
01923 }
01924 ao2_iterator_destroy(&i);
01925
01926 return CLI_SUCCESS;
01927 }
01928
01929 static struct ast_cli_entry cli_moh[] = {
01930 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
01931 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01932 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
01933 };
01934
01935 static int moh_class_hash(const void *obj, const int flags)
01936 {
01937 const struct mohclass *class = obj;
01938
01939 return ast_str_case_hash(class->name);
01940 }
01941
01942 static int moh_class_cmp(void *obj, void *arg, int flags)
01943 {
01944 struct mohclass *class = obj, *class2 = arg;
01945
01946 return strcasecmp(class->name, class2->name) ? 0 :
01947 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01948 CMP_MATCH | CMP_STOP;
01949 }
01950
01951 static int load_module(void)
01952 {
01953 int res;
01954
01955 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01956 return AST_MODULE_LOAD_DECLINE;
01957 }
01958
01959 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {
01960 ast_log(LOG_WARNING, "No music on hold classes configured, "
01961 "disabling music on hold.\n");
01962 } else {
01963 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01964 local_ast_moh_cleanup);
01965 }
01966
01967 res = ast_register_application_xml(play_moh, play_moh_exec);
01968 ast_register_atexit(ast_moh_destroy);
01969 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01970 if (!res)
01971 res = ast_register_application_xml(wait_moh, wait_moh_exec);
01972 if (!res)
01973 res = ast_register_application_xml(set_moh, set_moh_exec);
01974 if (!res)
01975 res = ast_register_application_xml(start_moh, start_moh_exec);
01976 if (!res)
01977 res = ast_register_application_xml(stop_moh, stop_moh_exec);
01978
01979 return AST_MODULE_LOAD_SUCCESS;
01980 }
01981
01982 static int reload(void)
01983 {
01984 if (load_moh_classes(1)) {
01985 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01986 local_ast_moh_cleanup);
01987 }
01988
01989 return AST_MODULE_LOAD_SUCCESS;
01990 }
01991
01992 static int moh_class_inuse(void *obj, void *arg, int flags)
01993 {
01994 struct mohclass *class = obj;
01995
01996 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01997 }
01998
01999 static int unload_module(void)
02000 {
02001 int res = 0;
02002 struct mohclass *class = NULL;
02003
02004
02005
02006 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
02007 class = mohclass_unref(class, "unref of class from module unload callback");
02008 res = -1;
02009 }
02010
02011 if (res < 0) {
02012 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
02013 return res;
02014 }
02015
02016 ast_uninstall_music_functions();
02017
02018 ast_moh_destroy();
02019 res = ast_unregister_application(play_moh);
02020 res |= ast_unregister_application(wait_moh);
02021 res |= ast_unregister_application(set_moh);
02022 res |= ast_unregister_application(start_moh);
02023 res |= ast_unregister_application(stop_moh);
02024 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
02025 ast_unregister_atexit(ast_moh_destroy);
02026
02027 return res;
02028 }
02029
02030 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
02031 .load = load_module,
02032 .unload = unload_module,
02033 .reload = reload,
02034 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
02035 );