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