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: 211569 $")
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, "%30d", &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 struct ast_variable *var = NULL;
01131 int res;
01132 int realtime_possible = ast_check_realtime("musiconhold");
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145 if (!ast_strlen_zero(chan->musicclass)) {
01146 mohclass = get_mohbyname(chan->musicclass, 1);
01147 if (!mohclass && realtime_possible) {
01148 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
01149 }
01150 }
01151 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
01152 mohclass = get_mohbyname(mclass, 1);
01153 if (!mohclass && realtime_possible) {
01154 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
01155 }
01156 }
01157 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
01158 mohclass = get_mohbyname(interpclass, 1);
01159 if (!mohclass && realtime_possible) {
01160 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
01161 }
01162 }
01163
01164 if (!mohclass && !var) {
01165 mohclass = get_mohbyname("default", 1);
01166 if (!mohclass && realtime_possible) {
01167 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
01168 }
01169 }
01170
01171
01172
01173
01174 if (var) {
01175 struct ast_variable *tmp = NULL;
01176
01177 if ((mohclass = moh_class_malloc())) {
01178 mohclass->realtime = 1;
01179 for (tmp = var; tmp; tmp = tmp->next) {
01180 if (!strcasecmp(tmp->name, "name"))
01181 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
01182 else if (!strcasecmp(tmp->name, "mode"))
01183 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
01184 else if (!strcasecmp(tmp->name, "directory"))
01185 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
01186 else if (!strcasecmp(tmp->name, "application"))
01187 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
01188 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
01189 mohclass->digit = *tmp->value;
01190 else if (!strcasecmp(tmp->name, "random"))
01191 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
01192 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
01193 ast_set_flag(mohclass, MOH_RANDOMIZE);
01194 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
01195 ast_set_flag(mohclass, MOH_SORTALPHA);
01196 else if (!strcasecmp(tmp->name, "format")) {
01197 mohclass->format = ast_getformatbyname(tmp->value);
01198 if (!mohclass->format) {
01199 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
01200 mohclass->format = AST_FORMAT_SLINEAR;
01201 }
01202 }
01203 }
01204 ast_variables_destroy(var);
01205 if (ast_strlen_zero(mohclass->dir)) {
01206 if (!strcasecmp(mohclass->mode, "custom")) {
01207 strcpy(mohclass->dir, "nodir");
01208 } else {
01209 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
01210 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
01211 return -1;
01212 }
01213 }
01214 if (ast_strlen_zero(mohclass->mode)) {
01215 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
01216 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
01217 return -1;
01218 }
01219 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
01220 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
01221 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
01222 return -1;
01223 }
01224
01225 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
01226
01227 if (state && state->class) {
01228
01229 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01230 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01231
01232
01233 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
01234 mohclass = state->class;
01235 }
01236 }
01237
01238
01239
01240
01241
01242
01243 moh_register(mohclass, 0, 0);
01244 } else {
01245
01246
01247 time(&mohclass->start);
01248 mohclass->start -= respawn_time;
01249
01250 if (!strcasecmp(mohclass->mode, "files")) {
01251 if (!moh_scan_files(mohclass)) {
01252 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
01253 return -1;
01254 }
01255 if (strchr(mohclass->args, 'r'))
01256 ast_set_flag(mohclass, MOH_RANDOMIZE);
01257 } 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")) {
01258
01259 if (!strcasecmp(mohclass->mode, "custom"))
01260 ast_set_flag(mohclass, MOH_CUSTOM);
01261 else if (!strcasecmp(mohclass->mode, "mp3nb"))
01262 ast_set_flag(mohclass, MOH_SINGLE);
01263 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
01264 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
01265 else if (!strcasecmp(mohclass->mode, "quietmp3"))
01266 ast_set_flag(mohclass, MOH_QUIET);
01267
01268 mohclass->srcfd = -1;
01269 #ifdef HAVE_DAHDI
01270
01271
01272 mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
01273 if (mohclass->pseudofd < 0) {
01274 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
01275 } else {
01276 int x = 320;
01277 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
01278 }
01279 #else
01280 mohclass->pseudofd = -1;
01281 #endif
01282
01283 if (state && state->class) {
01284
01285 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
01286 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
01287
01288 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
01289 mohclass = state->class;
01290 }
01291 } else {
01292 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
01293 ast_log(LOG_WARNING, "Unable to create moh...\n");
01294 if (mohclass->pseudofd > -1) {
01295 close(mohclass->pseudofd);
01296 mohclass->pseudofd = -1;
01297 }
01298 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
01299 return -1;
01300 }
01301 }
01302 } else {
01303 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
01304 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
01305 return -1;
01306 }
01307 }
01308 } else {
01309 ast_variables_destroy(var);
01310 }
01311 }
01312
01313 if (!mohclass) {
01314 return -1;
01315 }
01316
01317 manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01318 "State: Start\r\n"
01319 "Channel: %s\r\n"
01320 "UniqueID: %s\r\n",
01321 chan->name, chan->uniqueid);
01322
01323 ast_set_flag(chan, AST_FLAG_MOH);
01324
01325 if (mohclass->total_files) {
01326 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01327 } else {
01328 res = ast_activate_generator(chan, &mohgen, mohclass);
01329 }
01330
01331 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
01332
01333 return res;
01334 }
01335
01336 static void local_ast_moh_stop(struct ast_channel *chan)
01337 {
01338 struct moh_files_state *state = chan->music_state;
01339 ast_clear_flag(chan, AST_FLAG_MOH);
01340 ast_deactivate_generator(chan);
01341
01342 if (state) {
01343 if (chan->stream) {
01344 ast_closestream(chan->stream);
01345 chan->stream = NULL;
01346 }
01347 }
01348
01349 manager_event(EVENT_FLAG_CALL, "MusicOnHold",
01350 "State: Stop\r\n"
01351 "Channel: %s\r\n"
01352 "UniqueID: %s\r\n",
01353 chan->name, chan->uniqueid);
01354 }
01355
01356 static void moh_class_destructor(void *obj)
01357 {
01358 struct mohclass *class = obj;
01359 struct mohdata *member;
01360
01361 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01362
01363 if (class->pid > 1) {
01364 char buff[8192];
01365 int bytes, tbytes = 0, stime = 0, pid = 0;
01366
01367 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01368
01369 stime = time(NULL) + 2;
01370 pid = class->pid;
01371 class->pid = 0;
01372
01373
01374
01375
01376 killpg(pid, SIGHUP);
01377 usleep(100000);
01378 killpg(pid, SIGTERM);
01379 usleep(100000);
01380 killpg(pid, SIGKILL);
01381
01382 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01383 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01384 tbytes = tbytes + bytes;
01385 }
01386
01387 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01388
01389 close(class->srcfd);
01390 }
01391
01392 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01393 free(member);
01394 }
01395
01396 if (class->thread) {
01397 pthread_cancel(class->thread);
01398 pthread_join(class->thread, NULL);
01399 class->thread = AST_PTHREADT_NULL;
01400 }
01401
01402 if (class->filearray) {
01403 int i;
01404 for (i = 0; i < class->total_files; i++) {
01405 free(class->filearray[i]);
01406 }
01407 free(class->filearray);
01408 class->filearray = NULL;
01409 }
01410 }
01411
01412 static int moh_class_mark(void *obj, void *arg, int flags)
01413 {
01414 struct mohclass *class = obj;
01415
01416 class->delete = 1;
01417
01418 return 0;
01419 }
01420
01421 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01422 {
01423 struct mohclass *class = obj;
01424
01425 return class->delete ? CMP_MATCH : 0;
01426 }
01427
01428 static int load_moh_classes(int reload)
01429 {
01430 struct ast_config *cfg;
01431 struct ast_variable *var;
01432 struct mohclass *class;
01433 char *cat;
01434 int numclasses = 0;
01435 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01436
01437 cfg = ast_config_load("musiconhold.conf", config_flags);
01438
01439 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01440 return 0;
01441
01442 if (reload) {
01443 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
01444 }
01445
01446 ast_clear_flag(global_flags, AST_FLAGS_ALL);
01447
01448 cat = ast_category_browse(cfg, NULL);
01449 for (; cat; cat = ast_category_browse(cfg, cat)) {
01450
01451 if (!strcasecmp(cat, "general")) {
01452 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01453 if (!strcasecmp(var->name, "cachertclasses")) {
01454 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01455 } else {
01456 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01457 }
01458 }
01459 }
01460
01461 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
01462 !strcasecmp(cat, "general")) {
01463 continue;
01464 }
01465
01466 if (!(class = moh_class_malloc())) {
01467 break;
01468 }
01469
01470 ast_copy_string(class->name, cat, sizeof(class->name));
01471 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01472 if (!strcasecmp(var->name, "mode"))
01473 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01474 else if (!strcasecmp(var->name, "directory"))
01475 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01476 else if (!strcasecmp(var->name, "application"))
01477 ast_copy_string(class->args, var->value, sizeof(class->args));
01478 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
01479 class->digit = *var->value;
01480 else if (!strcasecmp(var->name, "random"))
01481 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01482 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
01483 ast_set_flag(class, MOH_RANDOMIZE);
01484 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
01485 ast_set_flag(class, MOH_SORTALPHA);
01486 else if (!strcasecmp(var->name, "format")) {
01487 class->format = ast_getformatbyname(var->value);
01488 if (!class->format) {
01489 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01490 class->format = AST_FORMAT_SLINEAR;
01491 }
01492 }
01493 }
01494
01495 if (ast_strlen_zero(class->dir)) {
01496 if (!strcasecmp(class->mode, "custom")) {
01497 strcpy(class->dir, "nodir");
01498 } else {
01499 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01500 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
01501 continue;
01502 }
01503 }
01504 if (ast_strlen_zero(class->mode)) {
01505 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01506 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
01507 continue;
01508 }
01509 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01510 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01511 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
01512 continue;
01513 }
01514
01515
01516 if (!moh_register(class, reload, 1)) {
01517 numclasses++;
01518 }
01519 }
01520
01521 ast_config_destroy(cfg);
01522
01523 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
01524 moh_classes_delete_marked, NULL, "Purge marked classes");
01525
01526 return numclasses;
01527 }
01528
01529 static void ast_moh_destroy(void)
01530 {
01531 ast_verb(2, "Destroying musiconhold processes\n");
01532 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
01533 }
01534
01535 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01536 {
01537 switch (cmd) {
01538 case CLI_INIT:
01539 e->command = "moh reload";
01540 e->usage =
01541 "Usage: moh reload\n"
01542 " Reloads the MusicOnHold module.\n"
01543 " Alias for 'module reload res_musiconhold.so'\n";
01544 return NULL;
01545 case CLI_GENERATE:
01546 return NULL;
01547 }
01548
01549 if (a->argc != e->args)
01550 return CLI_SHOWUSAGE;
01551
01552 reload();
01553
01554 return CLI_SUCCESS;
01555 }
01556
01557 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01558 {
01559 struct mohclass *class;
01560 struct ao2_iterator i;
01561
01562 switch (cmd) {
01563 case CLI_INIT:
01564 e->command = "moh show files";
01565 e->usage =
01566 "Usage: moh show files\n"
01567 " Lists all loaded file-based MusicOnHold classes and their\n"
01568 " files.\n";
01569 return NULL;
01570 case CLI_GENERATE:
01571 return NULL;
01572 }
01573
01574 if (a->argc != e->args)
01575 return CLI_SHOWUSAGE;
01576
01577 i = ao2_iterator_init(mohclasses, 0);
01578
01579 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
01580 int x;
01581
01582 if (!class->total_files) {
01583 continue;
01584 }
01585
01586 ast_cli(a->fd, "Class: %s\n", class->name);
01587 for (x = 0; x < class->total_files; x++) {
01588 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
01589 }
01590 }
01591
01592 return CLI_SUCCESS;
01593 }
01594
01595 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01596 {
01597 struct mohclass *class;
01598 struct ao2_iterator i;
01599
01600 switch (cmd) {
01601 case CLI_INIT:
01602 e->command = "moh show classes";
01603 e->usage =
01604 "Usage: moh show classes\n"
01605 " Lists all MusicOnHold classes.\n";
01606 return NULL;
01607 case CLI_GENERATE:
01608 return NULL;
01609 }
01610
01611 if (a->argc != e->args)
01612 return CLI_SHOWUSAGE;
01613
01614 i = ao2_iterator_init(mohclasses, 0);
01615
01616 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
01617 ast_cli(a->fd, "Class: %s\n", class->name);
01618 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01619 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01620 if (ast_test_flag(class, MOH_CUSTOM)) {
01621 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01622 }
01623 if (strcasecmp(class->mode, "files")) {
01624 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
01625 }
01626 }
01627
01628 return CLI_SUCCESS;
01629 }
01630
01631 static struct ast_cli_entry cli_moh[] = {
01632 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
01633 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
01634 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
01635 };
01636
01637 static int moh_class_hash(const void *obj, const int flags)
01638 {
01639 const struct mohclass *class = obj;
01640
01641 return ast_str_case_hash(class->name);
01642 }
01643
01644 static int moh_class_cmp(void *obj, void *arg, int flags)
01645 {
01646 struct mohclass *class = obj, *class2 = arg;
01647
01648 return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01649 }
01650
01651 static int load_module(void)
01652 {
01653 int res;
01654
01655 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
01656 return AST_MODULE_LOAD_DECLINE;
01657 }
01658
01659 if (!load_moh_classes(0)) {
01660 ast_log(LOG_WARNING, "No music on hold classes configured, "
01661 "disabling music on hold.\n");
01662 } else {
01663 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01664 local_ast_moh_cleanup);
01665 }
01666
01667 res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
01668 ast_register_atexit(ast_moh_destroy);
01669 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01670 if (!res)
01671 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
01672 if (!res)
01673 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
01674 if (!res)
01675 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
01676 if (!res)
01677 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
01678
01679 return AST_MODULE_LOAD_SUCCESS;
01680 }
01681
01682 static int reload(void)
01683 {
01684 if (load_moh_classes(1)) {
01685 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01686 local_ast_moh_cleanup);
01687 }
01688
01689 return AST_MODULE_LOAD_SUCCESS;
01690 }
01691
01692 static int moh_class_inuse(void *obj, void *arg, int flags)
01693 {
01694 struct mohclass *class = obj;
01695
01696 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01697 }
01698
01699 static int unload_module(void)
01700 {
01701 int res = 0;
01702 struct mohclass *class = NULL;
01703
01704
01705
01706 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
01707 class = mohclass_unref(class, "unref of class from module unload callback");
01708 res = -1;
01709 }
01710
01711 if (res < 0) {
01712 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01713 return res;
01714 }
01715
01716 ast_uninstall_music_functions();
01717
01718 ast_moh_destroy();
01719 res = ast_unregister_application(play_moh);
01720 res |= ast_unregister_application(wait_moh);
01721 res |= ast_unregister_application(set_moh);
01722 res |= ast_unregister_application(start_moh);
01723 res |= ast_unregister_application(stop_moh);
01724 ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
01725 ast_unregister_atexit(ast_moh_destroy);
01726
01727 return res;
01728 }
01729
01730 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
01731 .load = load_module,
01732 .unload = unload_module,
01733 .reload = reload,
01734 );