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