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