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