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