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