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