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