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