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: 410043 $")
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 }
01435
01436
01437
01438
01439
01440
01441 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
01442 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
01443 return -1;
01444 }
01445 } else {
01446
01447
01448 time(&mohclass->start);
01449 mohclass->start -= respawn_time;
01450
01451 if (!strcasecmp(mohclass->mode, "files")) {
01452 if (!moh_scan_files(mohclass)) {
01453 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01454 return -1;
01455 }
01456 if (strchr(mohclass->args, 'r'))
01457 ast_set_flag(mohclass, MOH_RANDOMIZE);
01458 } 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")) {
01459
01460 if (!strcasecmp(mohclass->mode, "custom"))
01461 ast_set_flag(mohclass, MOH_CUSTOM);
01462 else if (!strcasecmp(mohclass->mode, "mp3nb"))
01463 ast_set_flag(mohclass, MOH_SINGLE);
01464 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01465 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01466 else if (!strcasecmp(mohclass->mode, "quietmp3"))
01467 ast_set_flag(mohclass, MOH_QUIET);
01468
01469 mohclass->srcfd = -1;
01470 if (!(mohclass->timer = ast_timer_open())) {
01471 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
01472 }
01473 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
01474 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
01475 ast_timer_close(mohclass->timer);
01476 mohclass->timer = NULL;
01477 }
01478
01479
01480 if (state && state->class) {
01481
01482 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01483 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01484
01485 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01486 mohclass = mohclass_ref(state->class, "using existing class from state");
01487 }
01488 } else {
01489 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01490 ast_log(LOG_WARNING, "Unable to create moh...\n");
01491 if (mohclass->timer) {
01492 ast_timer_close(mohclass->timer);
01493 mohclass->timer = NULL;
01494 }
01495 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01496 return -1;
01497 }
01498 }
01499 } else {
01500 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01501 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01502 return -1;
01503 }
01504 }
01505 } else {
01506 ast_variables_destroy(var);
01507 var = NULL;
01508 }
01509 }
01510
01511 if (!mohclass) {
01512 return -1;
01513 }
01514
01515
01516 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
01517 if (!moh_scan_files(mohclass)) {
01518 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01519 return -1;
01520 }
01521 }
01522
01523 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01524 "State: Start\r\n"
01525 "Channel: %s\r\n"
01526 "UniqueID: %s\r\n"
01527 "Class: %s\r\n",
01528 chan->name, chan->uniqueid,
01529 mohclass->name);
01530
01531 ast_set_flag(chan, AST_FLAG_MOH);
01532
01533 if (mohclass->total_files) {
01534 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01535 } else {
01536 res = ast_activate_generator(chan, &mohgen, mohclass);
01537 }
01538
01539 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01540
01541 return res;
01542 }
01543
01544 static void local_ast_moh_stop(struct ast_channel *chan)
01545 {
01546 ast_clear_flag(chan, AST_FLAG_MOH);
01547 ast_deactivate_generator(chan);
01548
01549 ast_channel_lock(chan);
01550 if (chan->music_state) {
01551 if (chan->stream) {
01552 ast_closestream(chan->stream);
01553 chan->stream = NULL;
01554 }
01555 }
01556
01557 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
01558 "State: Stop\r\n"
01559 "Channel: %s\r\n"
01560 "UniqueID: %s\r\n",
01561 chan->name, chan->uniqueid);
01562 ast_channel_unlock(chan);
01563 }
01564
01565 static void moh_class_destructor(void *obj)
01566 {
01567 struct mohclass *class = obj;
01568 struct mohdata *member;
01569 pthread_t tid = 0;
01570
01571 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01572
01573 ao2_lock(class);
01574 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01575 free(member);
01576 }
01577 ao2_unlock(class);
01578
01579
01580
01581 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01582 tid = class->thread;
01583 class->thread = AST_PTHREADT_NULL;
01584 pthread_cancel(tid);
01585
01586
01587 }
01588
01589 if (class->pid > 1) {
01590 char buff[8192];
01591 int bytes, tbytes = 0, stime = 0, pid = 0;
01592
01593 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01594
01595 stime = time(NULL) + 2;
01596 pid = class->pid;
01597 class->pid = 0;
01598
01599
01600
01601
01602 do {
01603 if (killpg(pid, SIGHUP) < 0) {
01604 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01605 }
01606 usleep(100000);
01607 if (killpg(pid, SIGTERM) < 0) {
01608 if (errno == ESRCH) {
01609 break;
01610 }
01611 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01612 }
01613 usleep(100000);
01614 if (killpg(pid, SIGKILL) < 0) {
01615 if (errno == ESRCH) {
01616 break;
01617 }
01618 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01619 }
01620 } while (0);
01621
01622 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01623 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01624 tbytes = tbytes + bytes;
01625 }
01626
01627 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01628
01629 close(class->srcfd);
01630 class->srcfd = -1;
01631 }
01632
01633 if (class->filearray) {
01634 int i;
01635 for (i = 0; i < class->total_files; i++) {
01636 free(class->filearray[i]);
01637 }
01638 free(class->filearray);
01639 class->filearray = NULL;
01640 }
01641
01642 if (class->timer) {
01643 ast_timer_close(class->timer);
01644 class->timer = NULL;
01645 }
01646
01647
01648 if (tid > 0) {
01649 pthread_join(tid, NULL);
01650 }
01651
01652 }
01653
01654 static int moh_class_mark(void *obj, void *arg, int flags)
01655 {
01656 struct mohclass *class = obj;
01657
01658 class->delete = 1;
01659
01660 return 0;
01661 }
01662
01663 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01664 {
01665 struct mohclass *class = obj;
01666
01667 return class->delete ? CMP_MATCH : 0;
01668 }
01669
01670 static int load_moh_classes(int reload)
01671 {
01672 struct ast_config *cfg;
01673 struct ast_variable *var;
01674 struct mohclass *class;
01675 char *cat;
01676 int numclasses = 0;
01677 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01678
01679 cfg = ast_config_load("musiconhold.conf", config_flags);
01680
01681 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01682 if (ast_check_realtime("musiconhold") && reload) {
01683 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01684 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
01685 }
01686 return 0;
01687 }
01688 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01689 moh_rescan_files();
01690 return 0;
01691 }
01692
01693 if (reload) {
01694 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01695 }
01696
01697 ast_clear_flag(global_flags, AST_FLAGS_ALL);
01698
01699 cat = ast_category_browse(cfg, NULL);
01700 for (; cat; cat = ast_category_browse(cfg, cat)) {
01701
01702 if (!strcasecmp(cat, "general")) {
01703 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01704 if (!strcasecmp(var->name, "cachertclasses")) {
01705 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01706 } else {
01707 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01708 }
01709 }
01710 }
01711
01712 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
01713 !strcasecmp(cat, "general")) {
01714 continue;
01715 }
01716
01717 if (!(class = moh_class_malloc())) {
01718 break;
01719 }
01720
01721 ast_copy_string(class->name, cat, sizeof(class->name));
01722 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01723 if (!strcasecmp(var->name, "mode"))
01724 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01725 else if (!strcasecmp(var->name, "directory"))
01726 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01727 else if (!strcasecmp(var->name, "application"))
01728 ast_copy_string(class->args, var->value, sizeof(class->args));
01729 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01730 class->digit = *var->value;
01731 else if (!strcasecmp(var->name, "random"))
01732 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01733 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01734 ast_set_flag(class, MOH_RANDOMIZE);
01735 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
01736 ast_set_flag(class, MOH_SORTALPHA);
01737 else if (!strcasecmp(var->name, "format")) {
01738 class->format = ast_getformatbyname(var->value);
01739 if (!class->format) {
01740 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01741 class->format = AST_FORMAT_SLINEAR;
01742 }
01743 }
01744 }
01745
01746 if (ast_strlen_zero(class->dir)) {
01747 if (!strcasecmp(class->mode, "custom")) {
01748 strcpy(class->dir, "nodir");
01749 } else {
01750 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01751 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01752 continue;
01753 }
01754 }
01755 if (ast_strlen_zero(class->mode)) {
01756 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01757 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01758 continue;
01759 }
01760 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01761 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01762 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01763 continue;
01764 }
01765
01766
01767 if (!moh_register(class, reload, HANDLE_REF)) {
01768 numclasses++;
01769 }
01770 }
01771
01772 ast_config_destroy(cfg);
01773
01774 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
01775 moh_classes_delete_marked, NULL, "Purge marked classes");
01776
01777 return numclasses;
01778 }
01779
01780 static void ast_moh_destroy(void)
01781 {
01782 ast_verb(2, "Destroying musiconhold processes\n");
01783 if (mohclasses) {
01784 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01785 ao2_ref(mohclasses, -1);
01786 mohclasses = NULL;
01787 }
01788 }
01789
01790 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01791 {
01792 switch (cmd) {
01793 case CLI_INIT:
01794 e->command = "moh reload";
01795 e->usage =
01796 "Usage: moh reload\n"
01797 " Reloads the MusicOnHold module.\n"
01798 " Alias for 'module reload res_musiconhold.so'\n";
01799 return NULL;
01800 case CLI_GENERATE:
01801 return NULL;
01802 }
01803
01804 if (a->argc != e->args)
01805 return CLI_SHOWUSAGE;
01806
01807 reload();
01808
01809 return CLI_SUCCESS;
01810 }
01811
01812 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01813 {
01814 struct mohclass *class;
01815 struct ao2_iterator i;
01816
01817 switch (cmd) {
01818 case CLI_INIT:
01819 e->command = "moh show files";
01820 e->usage =
01821 "Usage: moh show files\n"
01822 " Lists all loaded file-based MusicOnHold classes and their\n"
01823 " files.\n";
01824 return NULL;
01825 case CLI_GENERATE:
01826 return NULL;
01827 }
01828
01829 if (a->argc != e->args)
01830 return CLI_SHOWUSAGE;
01831
01832 i = ao2_iterator_init(mohclasses, 0);
01833 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01834 int x;
01835
01836 if (!class->total_files) {
01837 continue;
01838 }
01839
01840 ast_cli(a->fd, "Class: %s\n", class->name);
01841 for (x = 0; x < class->total_files; x++) {
01842 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01843 }
01844 }
01845 ao2_iterator_destroy(&i);
01846
01847 return CLI_SUCCESS;
01848 }
01849
01850 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01851 {
01852 struct mohclass *class;
01853 struct ao2_iterator i;
01854
01855 switch (cmd) {
01856 case CLI_INIT:
01857 e->command = "moh show classes";
01858 e->usage =
01859 "Usage: moh show classes\n"
01860 " Lists all MusicOnHold classes.\n";
01861 return NULL;
01862 case CLI_GENERATE:
01863 return NULL;
01864 }
01865
01866 if (a->argc != e->args)
01867 return CLI_SHOWUSAGE;
01868
01869 i = ao2_iterator_init(mohclasses, 0);
01870 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01871 ast_cli(a->fd, "Class: %s\n", class->name);
01872 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01873 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01874 if (ast_test_flag(class, MOH_CUSTOM)) {
01875 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01876 }
01877 if (strcasecmp(class->mode, "files")) {
01878 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01879 }
01880 }
01881 ao2_iterator_destroy(&i);
01882
01883 return CLI_SUCCESS;
01884 }
01885
01886 static struct ast_cli_entry cli_moh[] = {
01887 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
01888 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01889 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
01890 };
01891
01892 static int moh_class_hash(const void *obj, const int flags)
01893 {
01894 const struct mohclass *class = obj;
01895
01896 return ast_str_case_hash(class->name);
01897 }
01898
01899 static int moh_class_cmp(void *obj, void *arg, int flags)
01900 {
01901 struct mohclass *class = obj, *class2 = arg;
01902
01903 return strcasecmp(class->name, class2->name) ? 0 :
01904 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
01905 CMP_MATCH | CMP_STOP;
01906 }
01907
01908 static int load_module(void)
01909 {
01910 int res;
01911
01912 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01913 return AST_MODULE_LOAD_DECLINE;
01914 }
01915
01916 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) {
01917 ast_log(LOG_WARNING, "No music on hold classes configured, "
01918 "disabling music on hold.\n");
01919 } else {
01920 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01921 local_ast_moh_cleanup);
01922 }
01923
01924 res = ast_register_application_xml(play_moh, play_moh_exec);
01925 ast_register_atexit(ast_moh_destroy);
01926 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01927 if (!res)
01928 res = ast_register_application_xml(wait_moh, wait_moh_exec);
01929 if (!res)
01930 res = ast_register_application_xml(set_moh, set_moh_exec);
01931 if (!res)
01932 res = ast_register_application_xml(start_moh, start_moh_exec);
01933 if (!res)
01934 res = ast_register_application_xml(stop_moh, stop_moh_exec);
01935
01936 return AST_MODULE_LOAD_SUCCESS;
01937 }
01938
01939 static int reload(void)
01940 {
01941 if (load_moh_classes(1)) {
01942 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01943 local_ast_moh_cleanup);
01944 }
01945
01946 return AST_MODULE_LOAD_SUCCESS;
01947 }
01948
01949 static int moh_class_inuse(void *obj, void *arg, int flags)
01950 {
01951 struct mohclass *class = obj;
01952
01953 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01954 }
01955
01956 static int unload_module(void)
01957 {
01958 int res = 0;
01959 struct mohclass *class = NULL;
01960
01961
01962
01963 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01964 class = mohclass_unref(class, "unref of class from module unload callback");
01965 res = -1;
01966 }
01967
01968 if (res < 0) {
01969 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01970 return res;
01971 }
01972
01973 ast_uninstall_music_functions();
01974
01975 ast_moh_destroy();
01976 res = ast_unregister_application(play_moh);
01977 res |= ast_unregister_application(wait_moh);
01978 res |= ast_unregister_application(set_moh);
01979 res |= ast_unregister_application(start_moh);
01980 res |= ast_unregister_application(stop_moh);
01981 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01982 ast_unregister_atexit(ast_moh_destroy);
01983
01984 return res;
01985 }
01986
01987 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
01988 .load = load_module,
01989 .unload = unload_module,
01990 .reload = reload,
01991 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
01992 );