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: 240670 $")
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/paths.h"
00073 #include "asterisk/astobj2.h"
00074
00075 #define INITIAL_NUM_FILES 8
00076 #define HANDLE_REF 1
00077 #define DONT_UNREF 0
00078
00079 static char *play_moh = "MusicOnHold";
00080 static char *wait_moh = "WaitMusicOnHold";
00081 static char *set_moh = "SetMusicOnHold";
00082 static char *start_moh = "StartMusicOnHold";
00083 static char *stop_moh = "StopMusicOnHold";
00084
00085 static char *play_moh_syn = "Play Music On Hold indefinitely";
00086 static char *wait_moh_syn = "Wait, playing Music On Hold";
00087 static char *set_moh_syn = "Set default Music On Hold class";
00088 static char *start_moh_syn = "Play Music On Hold";
00089 static char *stop_moh_syn = "Stop Playing Music On Hold";
00090
00091 static char *play_moh_desc = " MusicOnHold(class[,duration]):\n"
00092 "Plays hold music specified by class. If omitted, the default\n"
00093 "music source for the channel will be used. Change the default \n"
00094 "class with Set(CHANNEL(musicclass)=...).\n"
00095 "If duration is given, hold music will be played specified number\n"
00096 "of seconds. If duration is ommited, music plays indefinitely.\n"
00097 "Returns 0 when done, -1 on hangup.\n";
00098
00099 static char *wait_moh_desc = " WaitMusicOnHold(delay):\n"
00100 "\n"
00101 " !!! DEPRECATED. Use MusicOnHold instead !!!\n"
00102 "\n"
00103 "Plays hold music specified number of seconds. Returns 0 when\n"
00104 "done, or -1 on hangup. If no hold music is available, the delay will\n"
00105 "still occur with no sound.\n"
00106 "\n"
00107 " !!! DEPRECATED. Use MusicOnHold instead !!!\n";
00108
00109 static char *set_moh_desc = " SetMusicOnHold(class):\n"
00110 "\n"
00111 " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n"
00112 "\n"
00113 "Sets the default class for music on hold for a given channel. When\n"
00114 "music on hold is activated, this class will be used to select which\n"
00115 "music is played.\n"
00116 "\n"
00117 " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n";
00118
00119 static char *start_moh_desc = " StartMusicOnHold(class):\n"
00120 "Starts playing music on hold, uses default music class for channel.\n"
00121 "Starts playing music specified by class. If omitted, the default\n"
00122 "music source for the channel will be used. Always returns 0.\n";
00123
00124 static char *stop_moh_desc = " StopMusicOnHold(): "
00125 "Stops playing music on hold.\n";
00126
00127 static int respawn_time = 20;
00128
00129 struct moh_files_state {
00130 struct mohclass *class;
00131 char name[MAX_MUSICCLASS];
00132 int origwfmt;
00133 int samples;
00134 int sample_queue;
00135 int pos;
00136 int save_pos;
00137 int save_total;
00138 char *save_pos_filename;
00139 };
00140
00141 #define MOH_QUIET (1 << 0)
00142 #define MOH_SINGLE (1 << 1)
00143 #define MOH_CUSTOM (1 << 2)
00144 #define MOH_RANDOMIZE (1 << 3)
00145 #define MOH_SORTALPHA (1 << 4)
00146
00147 #define MOH_CACHERTCLASSES (1 << 5)
00148
00149
00150 #define MOH_NOTDELETED (1 << 30)
00151
00152 static struct ast_flags global_flags[1] = {{0}};
00153
00154 struct mohclass {
00155 char name[MAX_MUSICCLASS];
00156 char dir[256];
00157 char args[256];
00158 char mode[80];
00159 char digit;
00160
00161 char **filearray;
00162
00163 int allowed_files;
00164
00165 int total_files;
00166 unsigned int flags;
00167
00168 int format;
00169
00170 int pid;
00171 time_t start;
00172 pthread_t thread;
00173
00174 int srcfd;
00175
00176 int pseudofd;
00177
00178 int realtime;
00179 unsigned int delete:1;
00180 AST_LIST_HEAD_NOLOCK(, mohdata) members;
00181 AST_LIST_ENTRY(mohclass) list;
00182 };
00183
00184 struct mohdata {
00185 int pipe[2];
00186 int origwfmt;
00187 struct mohclass *parent;
00188 struct ast_frame f;
00189 AST_LIST_ENTRY(mohdata) list;
00190 };
00191
00192 static struct ao2_container *mohclasses;
00193
00194 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00195 #define MPG_123 "/usr/bin/mpg123"
00196 #define MAX_MP3S 256
00197
00198 static int reload(void);
00199
00200 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
00201
00202 #ifndef REF_DEBUG
00203 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
00204 #else
00205 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
00206 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
00207 {
00208 struct mohclass *dup;
00209 if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
00210 if (_ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
00211 FILE *ref = fopen("/tmp/refs", "a");
00212 if (ref) {
00213 fprintf(ref, "%p =1 %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
00214 fclose(ref);
00215 }
00216 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",
00217 class, class->name, file, line, funcname);
00218 } else {
00219 ao2_ref(class, -1);
00220 }
00221 } else {
00222 ao2_t_ref(class, -1, (char *) tag);
00223 }
00224 return NULL;
00225 }
00226 #endif
00227
00228 static void moh_files_release(struct ast_channel *chan, void *data)
00229 {
00230 struct moh_files_state *state;
00231
00232 if (!chan || !chan->music_state) {
00233 return;
00234 }
00235
00236 state = chan->music_state;
00237
00238 if (chan->stream) {
00239 ast_closestream(chan->stream);
00240 chan->stream = NULL;
00241 }
00242
00243 if (option_verbose > 2) {
00244 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00245 }
00246
00247 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00248 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00249 }
00250
00251 state->save_pos = state->pos;
00252
00253 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
00254 }
00255
00256 static int ast_moh_files_next(struct ast_channel *chan)
00257 {
00258 struct moh_files_state *state = chan->music_state;
00259 int tries;
00260
00261
00262 if (chan->stream) {
00263 ast_closestream(chan->stream);
00264 chan->stream = NULL;
00265 }
00266
00267 if (!state->class->total_files) {
00268 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00269 return -1;
00270 }
00271
00272
00273 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00274 state->pos = state->save_pos;
00275 state->save_pos = -1;
00276 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00277
00278 for (tries = 0; tries < 20; tries++) {
00279 state->pos = ast_random() % state->class->total_files;
00280 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00281 break;
00282 }
00283 state->save_pos = -1;
00284 state->samples = 0;
00285 } else {
00286
00287 state->pos++;
00288 state->pos %= state->class->total_files;
00289 state->save_pos = -1;
00290 state->samples = 0;
00291 }
00292
00293 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00294 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00295 state->pos++;
00296 state->pos %= state->class->total_files;
00297 return -1;
00298 }
00299
00300
00301 state->save_pos_filename = state->class->filearray[state->pos];
00302
00303 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00304
00305 if (state->samples)
00306 ast_seekstream(chan->stream, state->samples, SEEK_SET);
00307
00308 return 0;
00309 }
00310
00311 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
00312 {
00313 struct ast_frame *f = NULL;
00314
00315 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00316 if (!ast_moh_files_next(chan))
00317 f = ast_readframe(chan->stream);
00318 }
00319
00320 return f;
00321 }
00322
00323 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00324 {
00325 struct moh_files_state *state = chan->music_state;
00326 struct ast_frame *f = NULL;
00327 int res = 0;
00328
00329 state->sample_queue += samples;
00330
00331 while (state->sample_queue > 0) {
00332 if ((f = moh_files_readframe(chan))) {
00333 state->samples += f->samples;
00334 state->sample_queue -= f->samples;
00335 res = ast_write(chan, f);
00336 ast_frfree(f);
00337 if (res < 0) {
00338 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00339 return -1;
00340 }
00341 } else
00342 return -1;
00343 }
00344 return res;
00345 }
00346
00347 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00348 {
00349 struct moh_files_state *state;
00350 struct mohclass *class = params;
00351
00352 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00353 chan->music_state = state;
00354 ast_module_ref(ast_module_info->self);
00355 } else {
00356 state = chan->music_state;
00357 }
00358
00359 if (!state) {
00360 return NULL;
00361 }
00362
00363
00364
00365
00366
00367
00368 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
00369 memset(state, 0, sizeof(*state));
00370 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00371 state->pos = ast_random() % class->total_files;
00372 }
00373 }
00374
00375 state->class = mohclass_ref(class, "Reffing music class for channel");
00376 state->origwfmt = chan->writeformat;
00377
00378 ast_copy_string(state->name, class->name, sizeof(state->name));
00379 state->save_total = class->total_files;
00380
00381 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00382
00383 return chan->music_state;
00384 }
00385
00386 static int moh_digit_match(void *obj, void *arg, int flags)
00387 {
00388 char *digit = arg;
00389 struct mohclass *class = obj;
00390
00391 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
00392 }
00393
00394
00395 static struct mohclass *get_mohbydigit(char digit)
00396 {
00397 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
00398 }
00399
00400 static void moh_handle_digit(struct ast_channel *chan, char digit)
00401 {
00402 struct mohclass *class;
00403 const char *classname = NULL;
00404
00405 if ((class = get_mohbydigit(digit))) {
00406 classname = ast_strdupa(class->name);
00407 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
00408 ast_string_field_set(chan,musicclass,classname);
00409 ast_moh_stop(chan);
00410 ast_moh_start(chan, classname, NULL);
00411 }
00412 }
00413
00414 static struct ast_generator moh_file_stream =
00415 {
00416 .alloc = moh_files_alloc,
00417 .release = moh_files_release,
00418 .generate = moh_files_generator,
00419 .digit = moh_handle_digit,
00420 };
00421
00422 static int spawn_mp3(struct mohclass *class)
00423 {
00424 int fds[2];
00425 int files = 0;
00426 char fns[MAX_MP3S][80];
00427 char *argv[MAX_MP3S + 50];
00428 char xargs[256];
00429 char *argptr;
00430 int argc = 0;
00431 DIR *dir = NULL;
00432 struct dirent *de;
00433
00434
00435 if (!strcasecmp(class->dir, "nodir")) {
00436 files = 1;
00437 } else {
00438 dir = opendir(class->dir);
00439 if (!dir && strncasecmp(class->dir, "http://", 7)) {
00440 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00441 return -1;
00442 }
00443 }
00444
00445 if (!ast_test_flag(class, MOH_CUSTOM)) {
00446 argv[argc++] = "mpg123";
00447 argv[argc++] = "-q";
00448 argv[argc++] = "-s";
00449 argv[argc++] = "--mono";
00450 argv[argc++] = "-r";
00451 argv[argc++] = "8000";
00452
00453 if (!ast_test_flag(class, MOH_SINGLE)) {
00454 argv[argc++] = "-b";
00455 argv[argc++] = "2048";
00456 }
00457
00458 argv[argc++] = "-f";
00459
00460 if (ast_test_flag(class, MOH_QUIET))
00461 argv[argc++] = "4096";
00462 else
00463 argv[argc++] = "8192";
00464
00465
00466 ast_copy_string(xargs, class->args, sizeof(xargs));
00467 argptr = xargs;
00468 while (!ast_strlen_zero(argptr)) {
00469 argv[argc++] = argptr;
00470 strsep(&argptr, ",");
00471 }
00472 } else {
00473
00474 ast_copy_string(xargs, class->args, sizeof(xargs));
00475 argptr = xargs;
00476 while (!ast_strlen_zero(argptr)) {
00477 argv[argc++] = argptr;
00478 strsep(&argptr, " ");
00479 }
00480 }
00481
00482 if (!strncasecmp(class->dir, "http://", 7)) {
00483 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00484 argv[argc++] = fns[files];
00485 files++;
00486 } else if (dir) {
00487 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00488 if ((strlen(de->d_name) > 3) &&
00489 ((ast_test_flag(class, MOH_CUSTOM) &&
00490 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
00491 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00492 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00493 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00494 argv[argc++] = fns[files];
00495 files++;
00496 }
00497 }
00498 }
00499 argv[argc] = NULL;
00500 if (dir) {
00501 closedir(dir);
00502 }
00503 if (pipe(fds)) {
00504 ast_log(LOG_WARNING, "Pipe failed\n");
00505 return -1;
00506 }
00507 if (!files) {
00508 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00509 close(fds[0]);
00510 close(fds[1]);
00511 return -1;
00512 }
00513 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00514 sleep(respawn_time - (time(NULL) - class->start));
00515 }
00516
00517 time(&class->start);
00518 class->pid = ast_safe_fork(0);
00519 if (class->pid < 0) {
00520 close(fds[0]);
00521 close(fds[1]);
00522 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00523 return -1;
00524 }
00525 if (!class->pid) {
00526 if (ast_opt_high_priority)
00527 ast_set_priority(0);
00528
00529 close(fds[0]);
00530
00531 dup2(fds[1], STDOUT_FILENO);
00532
00533
00534 ast_close_fds_above_n(STDERR_FILENO);
00535
00536
00537 if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00538 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00539 _exit(1);
00540 }
00541 setpgid(0, getpid());
00542 if (ast_test_flag(class, MOH_CUSTOM)) {
00543 execv(argv[0], argv);
00544 } else {
00545
00546 execv(LOCAL_MPG_123, argv);
00547
00548 execv(MPG_123, argv);
00549
00550 execvp("mpg123", argv);
00551 }
00552
00553 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
00554 close(fds[1]);
00555 _exit(1);
00556 } else {
00557
00558 close(fds[1]);
00559 }
00560 return fds[0];
00561 }
00562
00563 static void *monmp3thread(void *data)
00564 {
00565 #define MOH_MS_INTERVAL 100
00566
00567 struct mohclass *class = data;
00568 struct mohdata *moh;
00569 char buf[8192];
00570 short sbuf[8192];
00571 int res, res2;
00572 int len;
00573 struct timeval deadline, tv_tmp;
00574
00575 deadline.tv_sec = 0;
00576 deadline.tv_usec = 0;
00577 for(;;) {
00578 pthread_testcancel();
00579
00580 if (class->srcfd < 0) {
00581 if ((class->srcfd = spawn_mp3(class)) < 0) {
00582 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00583
00584 sleep(500);
00585 pthread_testcancel();
00586 }
00587 }
00588 if (class->pseudofd > -1) {
00589 #ifdef SOLARIS
00590 thr_yield();
00591 #endif
00592
00593 res = read(class->pseudofd, buf, sizeof(buf));
00594 pthread_testcancel();
00595 } else {
00596 long delta;
00597
00598 tv_tmp = ast_tvnow();
00599 if (ast_tvzero(deadline))
00600 deadline = tv_tmp;
00601 delta = ast_tvdiff_ms(tv_tmp, deadline);
00602 if (delta < MOH_MS_INTERVAL) {
00603 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));
00604 usleep(1000 * (MOH_MS_INTERVAL - delta));
00605 pthread_testcancel();
00606 } else {
00607 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00608 deadline = tv_tmp;
00609 }
00610 res = 8 * MOH_MS_INTERVAL;
00611 }
00612 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00613 continue;
00614
00615 len = ast_codec_get_len(class->format, res);
00616
00617 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00618 if (!res2) {
00619 close(class->srcfd);
00620 class->srcfd = -1;
00621 pthread_testcancel();
00622 if (class->pid > 1) {
00623 do {
00624 if (killpg(class->pid, SIGHUP) < 0) {
00625 if (errno == ESRCH) {
00626 break;
00627 }
00628 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
00629 }
00630 usleep(100000);
00631 if (killpg(class->pid, SIGTERM) < 0) {
00632 if (errno == ESRCH) {
00633 break;
00634 }
00635 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
00636 }
00637 usleep(100000);
00638 if (killpg(class->pid, SIGKILL) < 0) {
00639 if (errno == ESRCH) {
00640 break;
00641 }
00642 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
00643 }
00644 } while (0);
00645 class->pid = 0;
00646 }
00647 } else {
00648 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
00649 }
00650 continue;
00651 }
00652
00653 pthread_testcancel();
00654
00655 ao2_lock(class);
00656 AST_LIST_TRAVERSE(&class->members, moh, list) {
00657
00658 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00659 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
00660 }
00661 }
00662 ao2_unlock(class);
00663 }
00664 return NULL;
00665 }
00666
00667 static int play_moh_exec(struct ast_channel *chan, void *data)
00668 {
00669 char *parse;
00670 char *class;
00671 int timeout = -1;
00672 int res;
00673 AST_DECLARE_APP_ARGS(args,
00674 AST_APP_ARG(class);
00675 AST_APP_ARG(duration);
00676 );
00677
00678 parse = ast_strdupa(data);
00679
00680 AST_STANDARD_APP_ARGS(args, parse);
00681
00682 if (!ast_strlen_zero(args.duration)) {
00683 if (sscanf(args.duration, "%30d", &timeout) == 1) {
00684 timeout *= 1000;
00685 } else {
00686 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
00687 }
00688 }
00689
00690 class = S_OR(args.class, NULL);
00691 if (ast_moh_start(chan, class, NULL)) {
00692 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00693 return 0;
00694 }
00695
00696 if (timeout > 0)
00697 res = ast_safe_sleep(chan, timeout);
00698 else {
00699 while (!(res = ast_safe_sleep(chan, 10000)));
00700 }
00701
00702 ast_moh_stop(chan);
00703
00704 return res;
00705 }
00706
00707 static int wait_moh_exec(struct ast_channel *chan, void *data)
00708 {
00709 static int deprecation_warning = 0;
00710 int res;
00711
00712 if (!deprecation_warning) {
00713 deprecation_warning = 1;
00714 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
00715 }
00716
00717 if (!data || !atoi(data)) {
00718 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00719 return -1;
00720 }
00721 if (ast_moh_start(chan, NULL, NULL)) {
00722 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00723 return 0;
00724 }
00725 res = ast_safe_sleep(chan, atoi(data) * 1000);
00726 ast_moh_stop(chan);
00727 return res;
00728 }
00729
00730 static int set_moh_exec(struct ast_channel *chan, void *data)
00731 {
00732 static int deprecation_warning = 0;
00733
00734 if (!deprecation_warning) {
00735 deprecation_warning = 1;
00736 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
00737 }
00738
00739 if (ast_strlen_zero(data)) {
00740 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00741 return -1;
00742 }
00743 ast_string_field_set(chan, musicclass, data);
00744 return 0;
00745 }
00746
00747 static int start_moh_exec(struct ast_channel *chan, void *data)
00748 {
00749 char *parse;
00750 char *class;
00751 AST_DECLARE_APP_ARGS(args,
00752 AST_APP_ARG(class);
00753 );
00754
00755 parse = ast_strdupa(data);
00756
00757 AST_STANDARD_APP_ARGS(args, parse);
00758
00759 class = S_OR(args.class, NULL);
00760 if (ast_moh_start(chan, class, NULL))
00761 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
00762
00763 return 0;
00764 }
00765
00766 static int stop_moh_exec(struct ast_channel *chan, void *data)
00767 {
00768 ast_moh_stop(chan);
00769
00770 return 0;
00771 }
00772
00773 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
00774
00775 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
00776 {
00777 struct mohclass *moh = NULL;
00778 struct mohclass tmp_class = {
00779 .flags = 0,
00780 };
00781
00782 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00783
00784 #ifdef REF_DEBUG
00785 moh = _ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
00786 #else
00787 moh = _ao2_find(mohclasses, &tmp_class, flags);
00788 #endif
00789
00790 if (!moh && warn) {
00791 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00792 }
00793
00794 return moh;
00795 }
00796
00797 static struct mohdata *mohalloc(struct mohclass *cl)
00798 {
00799 struct mohdata *moh;
00800 long flags;
00801
00802 if (!(moh = ast_calloc(1, sizeof(*moh))))
00803 return NULL;
00804
00805 if (pipe(moh->pipe)) {
00806 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00807 ast_free(moh);
00808 return NULL;
00809 }
00810
00811
00812 flags = fcntl(moh->pipe[0], F_GETFL);
00813 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00814 flags = fcntl(moh->pipe[1], F_GETFL);
00815 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00816
00817 moh->f.frametype = AST_FRAME_VOICE;
00818 moh->f.subclass = cl->format;
00819 moh->f.offset = AST_FRIENDLY_OFFSET;
00820
00821 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
00822
00823 ao2_lock(cl);
00824 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00825 ao2_unlock(cl);
00826
00827 return moh;
00828 }
00829
00830 static void moh_release(struct ast_channel *chan, void *data)
00831 {
00832 struct mohdata *moh = data;
00833 struct mohclass *class = moh->parent;
00834 int oldwfmt;
00835
00836 ao2_lock(class);
00837 AST_LIST_REMOVE(&moh->parent->members, moh, list);
00838 ao2_unlock(class);
00839
00840 close(moh->pipe[0]);
00841 close(moh->pipe[1]);
00842
00843 oldwfmt = moh->origwfmt;
00844
00845 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
00846
00847 ast_free(moh);
00848
00849 if (chan) {
00850 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00851 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00852 chan->name, ast_getformatname(oldwfmt));
00853 }
00854
00855 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
00856 }
00857 }
00858
00859 static void *moh_alloc(struct ast_channel *chan, void *params)
00860 {
00861 struct mohdata *res;
00862 struct mohclass *class = params;
00863 struct moh_files_state *state;
00864
00865
00866 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00867 chan->music_state = state;
00868 state->class = mohclass_ref(class, "Copying reference into state container");
00869 ast_module_ref(ast_module_info->self);
00870 } else
00871 state = chan->music_state;
00872 if (state && state->class != class) {
00873 memset(state, 0, sizeof(*state));
00874 state->class = class;
00875 }
00876
00877 if ((res = mohalloc(class))) {
00878 res->origwfmt = chan->writeformat;
00879 if (ast_set_write_format(chan, class->format)) {
00880 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00881 moh_release(NULL, res);
00882 res = NULL;
00883 }
00884 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00885 }
00886 return res;
00887 }
00888
00889 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00890 {
00891 struct mohdata *moh = data;
00892 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00893 int res;
00894
00895 len = ast_codec_get_len(moh->parent->format, samples);
00896
00897 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00898 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00899 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00900 }
00901 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00902 if (res <= 0)
00903 return 0;
00904
00905 moh->f.datalen = res;
00906 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
00907 moh->f.samples = ast_codec_get_samples(&moh->f);
00908
00909 if (ast_write(chan, &moh->f) < 0) {
00910 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00911 return -1;
00912 }
00913
00914 return 0;
00915 }
00916
00917 static struct ast_generator mohgen = {
00918 .alloc = moh_alloc,
00919 .release = moh_release,
00920 .generate = moh_generate,
00921 .digit = moh_handle_digit,
00922 };
00923
00924 static int moh_add_file(struct mohclass *class, const char *filepath)
00925 {
00926 if (!class->allowed_files) {
00927 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00928 return -1;
00929 class->allowed_files = INITIAL_NUM_FILES;
00930 } else if (class->total_files == class->allowed_files) {
00931 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00932 class->allowed_files = 0;
00933 class->total_files = 0;
00934 return -1;
00935 }
00936 class->allowed_files *= 2;
00937 }
00938
00939 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00940 return -1;
00941
00942 class->total_files++;
00943
00944 return 0;
00945 }
00946
00947 static int moh_sort_compare(const void *i1, const void *i2)
00948 {
00949 char *s1, *s2;
00950
00951 s1 = ((char **)i1)[0];
00952 s2 = ((char **)i2)[0];
00953
00954 return strcasecmp(s1, s2);
00955 }
00956
00957 static int moh_scan_files(struct mohclass *class) {
00958
00959 DIR *files_DIR;
00960 struct dirent *files_dirent;
00961 char dir_path[PATH_MAX];
00962 char path[PATH_MAX];
00963 char filepath[PATH_MAX];
00964 char *ext;
00965 struct stat statbuf;
00966 int dirnamelen;
00967 int i;
00968
00969 if (class->dir[0] != '/') {
00970 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
00971 strncat(dir_path, "/", sizeof(dir_path) - 1);
00972 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
00973 } else {
00974 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
00975 }
00976 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
00977 files_DIR = opendir(dir_path);
00978 if (!files_DIR) {
00979 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
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(dir_path) + 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(dir_path) < 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", dir_path, 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 struct moh_files_state *state = chan->music_state;
01436 ast_clear_flag(chan, AST_FLAG_MOH);
01437 ast_deactivate_generator(chan);
01438
01439 if (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 }
01452
01453 static void moh_class_destructor(void *obj)
01454 {
01455 struct mohclass *class = obj;
01456 struct mohdata *member;
01457 pthread_t tid = 0;
01458
01459 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
01460
01461
01462
01463 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
01464 tid = class->thread;
01465 class->thread = AST_PTHREADT_NULL;
01466 pthread_cancel(tid);
01467
01468
01469 }
01470
01471 if (class->pid > 1) {
01472 char buff[8192];
01473 int bytes, tbytes = 0, stime = 0, pid = 0;
01474
01475 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01476
01477 stime = time(NULL) + 2;
01478 pid = class->pid;
01479 class->pid = 0;
01480
01481
01482
01483
01484 do {
01485 if (killpg(pid, SIGHUP) < 0) {
01486 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
01487 }
01488 usleep(100000);
01489 if (killpg(pid, SIGTERM) < 0) {
01490 if (errno == ESRCH) {
01491 break;
01492 }
01493 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
01494 }
01495 usleep(100000);
01496 if (killpg(pid, SIGKILL) < 0) {
01497 if (errno == ESRCH) {
01498 break;
01499 }
01500 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
01501 }
01502 } while (0);
01503
01504 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01505 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01506 tbytes = tbytes + bytes;
01507 }
01508
01509 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01510
01511 close(class->srcfd);
01512 }
01513
01514 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01515 free(member);
01516 }
01517
01518 if (class->filearray) {
01519 int i;
01520 for (i = 0; i < class->total_files; i++) {
01521 free(class->filearray[i]);
01522 }
01523 free(class->filearray);
01524 class->filearray = NULL;
01525 }
01526
01527
01528 if (tid > 0) {
01529 pthread_join(tid, NULL);
01530 }
01531 }
01532
01533 static int moh_class_mark(void *obj, void *arg, int flags)
01534 {
01535 struct mohclass *class = obj;
01536
01537 class->delete = 1;
01538
01539 return 0;
01540 }
01541
01542 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01543 {
01544 struct mohclass *class = obj;
01545
01546 return class->delete ? CMP_MATCH : 0;
01547 }
01548
01549 static int load_moh_classes(int reload)
01550 {
01551 struct ast_config *cfg;
01552 struct ast_variable *var;
01553 struct mohclass *class;
01554 char *cat;
01555 int numclasses = 0;
01556 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01557
01558 cfg = ast_config_load("musiconhold.conf", config_flags);
01559
01560 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01561 return 0;
01562 }
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, ARRAY_LEN(cli_moh));
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, ARRAY_LEN(cli_moh));
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 );