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