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