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