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