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