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