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: 305471 $")
00036
00037 #include <stdlib.h>
00038 #include <errno.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <signal.h>
00042 #include <stdlib.h>
00043 #include <stdio.h>
00044
00045 #include <sys/time.h>
00046 #include <sys/signal.h>
00047 #include <netinet/in.h>
00048 #include <sys/stat.h>
00049 #include <dirent.h>
00050 #include <unistd.h>
00051 #include <sys/ioctl.h>
00052 #ifdef SOLARIS
00053 #include <thread.h>
00054 #endif
00055
00056 #include "asterisk/dahdi_compat.h"
00057
00058 #ifdef HAVE_CAP
00059 #include <sys/capability.h>
00060 #endif
00061
00062 #include "asterisk/lock.h"
00063 #include "asterisk/file.h"
00064 #include "asterisk/logger.h"
00065 #include "asterisk/channel.h"
00066 #include "asterisk/pbx.h"
00067 #include "asterisk/options.h"
00068 #include "asterisk/module.h"
00069 #include "asterisk/translate.h"
00070 #include "asterisk/say.h"
00071 #include "asterisk/musiconhold.h"
00072 #include "asterisk/config.h"
00073 #include "asterisk/utils.h"
00074 #include "asterisk/cli.h"
00075 #include "asterisk/stringfields.h"
00076 #include "asterisk/linkedlists.h"
00077 #include "asterisk/astobj2.h"
00078
00079
00080 #define INITIAL_NUM_FILES 8
00081
00082 static char *app0 = "MusicOnHold";
00083 static char *app1 = "WaitMusicOnHold";
00084 static char *app2 = "SetMusicOnHold";
00085 static char *app3 = "StartMusicOnHold";
00086 static char *app4 = "StopMusicOnHold";
00087
00088 static char *synopsis0 = "Play Music On Hold indefinitely";
00089 static char *synopsis1 = "Wait, playing Music On Hold";
00090 static char *synopsis2 = "Set default Music On Hold class";
00091 static char *synopsis3 = "Play Music On Hold";
00092 static char *synopsis4 = "Stop Playing Music On Hold";
00093
00094 static char *descrip0 = "MusicOnHold(class): "
00095 "Plays hold music specified by class. If omitted, the default\n"
00096 "music source for the channel will be used. Set the default \n"
00097 "class with the SetMusicOnHold() application.\n"
00098 "Returns -1 on hangup.\n"
00099 "Never returns otherwise.\n";
00100
00101 static char *descrip1 = "WaitMusicOnHold(delay): "
00102 "Plays hold music specified number of seconds. Returns 0 when\n"
00103 "done, or -1 on hangup. If no hold music is available, the delay will\n"
00104 "still occur with no sound.\n";
00105
00106 static char *descrip2 = "SetMusicOnHold(class): "
00107 "Sets the default class for music on hold for a given channel. When\n"
00108 "music on hold is activated, this class will be used to select which\n"
00109 "music is played.\n";
00110
00111 static char *descrip3 = "StartMusicOnHold(class): "
00112 "Starts playing music on hold, uses default music class for channel.\n"
00113 "Starts playing music specified by class. If omitted, the default\n"
00114 "music source for the channel will be used. Always returns 0.\n";
00115
00116 static char *descrip4 = "StopMusicOnHold: "
00117 "Stops playing music on hold.\n";
00118
00119 static int respawn_time = 20;
00120
00121 struct moh_files_state {
00122 struct mohclass *class;
00123 int origwfmt;
00124 int samples;
00125 int sample_queue;
00126 int pos;
00127 int save_pos;
00128 char *save_pos_filename;
00129 };
00130
00131 #define MOH_QUIET (1 << 0)
00132 #define MOH_SINGLE (1 << 1)
00133 #define MOH_CUSTOM (1 << 2)
00134 #define MOH_RANDOMIZE (1 << 3)
00135 #define MOH_CACHERTCLASSES (1 << 4)
00136
00137 static struct ast_flags global_flags[1] = {{0}};
00138
00139 struct mohclass {
00140 char name[MAX_MUSICCLASS];
00141 char dir[256];
00142 char args[256];
00143 char mode[80];
00144
00145 char **filearray;
00146
00147 int allowed_files;
00148
00149 int total_files;
00150 unsigned int flags;
00151
00152 int format;
00153
00154 int pid;
00155 time_t start;
00156 pthread_t thread;
00157
00158 int srcfd;
00159
00160 int pseudofd;
00161
00162 int realtime;
00163 unsigned int delete:1;
00164 unsigned int deprecated:1;
00165 AST_LIST_HEAD_NOLOCK(, mohdata) members;
00166 AST_LIST_ENTRY(mohclass) list;
00167 };
00168
00169 struct mohdata {
00170 int pipe[2];
00171 int origwfmt;
00172 struct mohclass *parent;
00173 struct ast_frame f;
00174 AST_LIST_ENTRY(mohdata) list;
00175 };
00176
00177 static struct ao2_container *mohclasses;
00178
00179 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00180 #define MPG_123 "/usr/bin/mpg123"
00181 #define MAX_MP3S 256
00182
00183 static int reload(void);
00184
00185 #define mohclass_ref(class) (ao2_ref((class), +1), class)
00186 #define mohclass_unref(class) (ao2_ref((class), -1), (struct mohclass *) NULL)
00187
00188 static void moh_files_release(struct ast_channel *chan, void *data)
00189 {
00190 struct moh_files_state *state;
00191
00192 if (!chan || !chan->music_state) {
00193 return;
00194 }
00195
00196 state = chan->music_state;
00197
00198 if (chan->stream) {
00199 ast_closestream(chan->stream);
00200 chan->stream = NULL;
00201 }
00202
00203 if (option_verbose > 2) {
00204 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00205 }
00206
00207 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00208 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00209 }
00210
00211 state->save_pos = state->pos;
00212
00213 mohclass_unref(state->class);
00214 }
00215
00216
00217 static int ast_moh_files_next(struct ast_channel *chan)
00218 {
00219 struct moh_files_state *state = chan->music_state;
00220 int tries;
00221
00222
00223 if (chan->stream) {
00224 ast_closestream(chan->stream);
00225 chan->stream = NULL;
00226 }
00227
00228 if (!state->class->total_files) {
00229 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00230 return -1;
00231 }
00232
00233 if (state->pos == 0 && state->save_pos_filename == NULL) {
00234
00235 state->save_pos = -1;
00236 } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00237
00238 state->pos = state->save_pos;
00239 state->save_pos = -1;
00240 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00241
00242 for (tries = 0; tries < 20; tries++) {
00243 state->pos = ast_random() % state->class->total_files;
00244 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
00245 break;
00246 }
00247 }
00248 state->save_pos = -1;
00249 state->samples = 0;
00250 } else {
00251
00252 state->pos++;
00253 state->pos %= state->class->total_files;
00254 state->save_pos = -1;
00255 state->samples = 0;
00256 }
00257
00258 for (tries = 0; tries < state->class->total_files; ++tries) {
00259 if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00260 break;
00261 }
00262
00263 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00264 state->pos++;
00265 state->pos %= state->class->total_files;
00266 }
00267
00268 if (tries == state->class->total_files) {
00269 return -1;
00270 }
00271
00272
00273 state->save_pos_filename = state->class->filearray[state->pos];
00274
00275 if (option_debug)
00276 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00277
00278 if (state->samples) {
00279 ast_seekstream(chan->stream, state->samples, SEEK_SET);
00280 }
00281
00282 return 0;
00283 }
00284
00285
00286 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
00287 {
00288 struct ast_frame *f = NULL;
00289
00290 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00291 if (!ast_moh_files_next(chan))
00292 f = ast_readframe(chan->stream);
00293 }
00294
00295 return f;
00296 }
00297
00298 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00299 {
00300 struct moh_files_state *state = chan->music_state;
00301 struct ast_frame *f = NULL;
00302 int res = 0;
00303
00304 state->sample_queue += samples;
00305
00306 while (state->sample_queue > 0) {
00307 ast_channel_lock(chan);
00308 if ((f = moh_files_readframe(chan))) {
00309
00310
00311
00312
00313
00314
00315 ast_channel_unlock(chan);
00316 state->samples += f->samples;
00317 state->sample_queue -= f->samples;
00318 res = ast_write(chan, f);
00319 ast_frfree(f);
00320 if (res < 0) {
00321 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00322 return -1;
00323 }
00324 } else {
00325 ast_channel_unlock(chan);
00326 return -1;
00327 }
00328 }
00329 return res;
00330 }
00331
00332
00333 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00334 {
00335 struct moh_files_state *state;
00336 struct mohclass *class = params;
00337
00338 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00339 chan->music_state = state;
00340 } else {
00341 state = chan->music_state;
00342 }
00343
00344 if (!state) {
00345 return NULL;
00346 }
00347
00348 if (state->class != class) {
00349 memset(state, 0, sizeof(*state));
00350 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
00351 state->pos = ast_random() % class->total_files;
00352 }
00353 }
00354
00355 state->class = mohclass_ref(class);
00356 state->origwfmt = chan->writeformat;
00357
00358 if (option_verbose > 2) {
00359 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n",
00360 class->name, chan->name);
00361 }
00362
00363 return chan->music_state;
00364 }
00365
00366 static struct ast_generator moh_file_stream = {
00367 .alloc = moh_files_alloc,
00368 .release = moh_files_release,
00369 .generate = moh_files_generator,
00370 };
00371
00372 #ifdef HAVE_WORKING_FORK
00373 static int spawn_mp3(struct mohclass *class)
00374 {
00375 int fds[2];
00376 int files = 0;
00377 char fns[MAX_MP3S][80];
00378 char *argv[MAX_MP3S + 50];
00379 char xargs[256];
00380 char *argptr;
00381 int argc = 0;
00382 DIR *dir = NULL;
00383 struct dirent *de;
00384 sigset_t signal_set, old_set;
00385
00386
00387 if (!strcasecmp(class->dir, "nodir")) {
00388 files = 1;
00389 } else {
00390 dir = opendir(class->dir);
00391 if (!dir && strncasecmp(class->dir, "http://", 7)) {
00392 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00393 return -1;
00394 }
00395 }
00396
00397 if (!ast_test_flag(class, MOH_CUSTOM)) {
00398 argv[argc++] = "mpg123";
00399 argv[argc++] = "-q";
00400 argv[argc++] = "-s";
00401 argv[argc++] = "--mono";
00402 argv[argc++] = "-r";
00403 argv[argc++] = "8000";
00404
00405 if (!ast_test_flag(class, MOH_SINGLE)) {
00406 argv[argc++] = "-b";
00407 argv[argc++] = "2048";
00408 }
00409
00410 argv[argc++] = "-f";
00411
00412 if (ast_test_flag(class, MOH_QUIET))
00413 argv[argc++] = "4096";
00414 else
00415 argv[argc++] = "8192";
00416
00417
00418 ast_copy_string(xargs, class->args, sizeof(xargs));
00419 argptr = xargs;
00420 while (!ast_strlen_zero(argptr)) {
00421 argv[argc++] = argptr;
00422 strsep(&argptr, ",");
00423 }
00424 } else {
00425
00426 ast_copy_string(xargs, class->args, sizeof(xargs));
00427 argptr = xargs;
00428 while (!ast_strlen_zero(argptr)) {
00429 argv[argc++] = argptr;
00430 strsep(&argptr, " ");
00431 }
00432 }
00433
00434 if (!strncasecmp(class->dir, "http://", 7)) {
00435 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00436 argv[argc++] = fns[files];
00437 files++;
00438 } else if (dir) {
00439 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00440 if ((strlen(de->d_name) > 3) &&
00441 ((ast_test_flag(class, MOH_CUSTOM) &&
00442 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
00443 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00444 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00445 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00446 argv[argc++] = fns[files];
00447 files++;
00448 }
00449 }
00450 }
00451 argv[argc] = NULL;
00452 if (dir) {
00453 closedir(dir);
00454 }
00455 if (pipe(fds)) {
00456 ast_log(LOG_WARNING, "Pipe failed\n");
00457 return -1;
00458 }
00459 if (!files) {
00460 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00461 close(fds[0]);
00462 close(fds[1]);
00463 return -1;
00464 }
00465 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00466 sleep(respawn_time - (time(NULL) - class->start));
00467 }
00468
00469
00470 sigfillset(&signal_set);
00471 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00472
00473 time(&class->start);
00474 class->pid = fork();
00475 if (class->pid < 0) {
00476 close(fds[0]);
00477 close(fds[1]);
00478 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00479 return -1;
00480 }
00481 if (!class->pid) {
00482
00483 int x;
00484 #ifdef HAVE_CAP
00485 cap_t cap;
00486 #endif
00487 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00488 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00489 _exit(1);
00490 }
00491
00492 if (ast_opt_high_priority)
00493 ast_set_priority(0);
00494
00495
00496 signal(SIGPIPE, SIG_DFL);
00497 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
00498
00499 #ifdef HAVE_CAP
00500 cap = cap_from_text("cap_net_admin-eip");
00501
00502 if (cap_set_proc(cap)) {
00503 ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00504 }
00505 cap_free(cap);
00506 #endif
00507 close(fds[0]);
00508
00509 dup2(fds[1], STDOUT_FILENO);
00510
00511 for (x=3;x<8192;x++) {
00512 if (-1 != fcntl(x, F_GETFL)) {
00513 close(x);
00514 }
00515 }
00516 setpgid(0, getpid());
00517
00518 if (ast_test_flag(class, MOH_CUSTOM)) {
00519 execv(argv[0], argv);
00520 } else {
00521
00522 execv(LOCAL_MPG_123, argv);
00523
00524 execv(MPG_123, argv);
00525
00526 execvp("mpg123", argv);
00527 }
00528 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00529 close(fds[1]);
00530 _exit(1);
00531 } else {
00532
00533 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00534 close(fds[1]);
00535 }
00536 return fds[0];
00537 }
00538
00539 static void *monmp3thread(void *data)
00540 {
00541 #define MOH_MS_INTERVAL 100
00542
00543 struct mohclass *class = data;
00544 struct mohdata *moh;
00545 char buf[8192];
00546 short sbuf[8192];
00547 int res, res2;
00548 int len;
00549 struct timeval tv, tv_tmp;
00550
00551 tv.tv_sec = 0;
00552 tv.tv_usec = 0;
00553 for(;;) {
00554 pthread_testcancel();
00555
00556 if (class->srcfd < 0) {
00557 if ((class->srcfd = spawn_mp3(class)) < 0) {
00558 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00559
00560 sleep(500);
00561 pthread_testcancel();
00562 }
00563 }
00564 if (class->pseudofd > -1) {
00565 #ifdef SOLARIS
00566 thr_yield();
00567 #endif
00568
00569 res = read(class->pseudofd, buf, sizeof(buf));
00570 pthread_testcancel();
00571 } else {
00572 long delta;
00573
00574 tv_tmp = ast_tvnow();
00575 if (ast_tvzero(tv))
00576 tv = tv_tmp;
00577 delta = ast_tvdiff_ms(tv_tmp, tv);
00578 if (delta < MOH_MS_INTERVAL) {
00579 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));
00580 usleep(1000 * (MOH_MS_INTERVAL - delta));
00581 pthread_testcancel();
00582 } else {
00583 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00584 tv = tv_tmp;
00585 }
00586 res = 8 * MOH_MS_INTERVAL;
00587 }
00588 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
00589 continue;
00590
00591 len = ast_codec_get_len(class->format, res);
00592
00593 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00594 if (!res2) {
00595 close(class->srcfd);
00596 class->srcfd = -1;
00597 pthread_testcancel();
00598 if (class->pid > 1) {
00599 killpg(class->pid, SIGHUP);
00600 usleep(100000);
00601 killpg(class->pid, SIGTERM);
00602 usleep(100000);
00603 killpg(class->pid, SIGKILL);
00604 class->pid = 0;
00605 }
00606 } else
00607 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
00608 continue;
00609 }
00610
00611 pthread_testcancel();
00612
00613 ao2_lock(class);
00614 AST_LIST_TRAVERSE(&class->members, moh, list) {
00615
00616 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00617 if (option_debug)
00618 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
00619 }
00620 }
00621 ao2_unlock(class);
00622 }
00623 return NULL;
00624 }
00625 #endif
00626
00627 static int moh0_exec(struct ast_channel *chan, void *data)
00628 {
00629 if (ast_moh_start(chan, data, NULL)) {
00630 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00631 return 0;
00632 }
00633 while (!ast_safe_sleep(chan, 10000));
00634 ast_moh_stop(chan);
00635 return -1;
00636 }
00637
00638 static int moh1_exec(struct ast_channel *chan, void *data)
00639 {
00640 int res;
00641 if (!data || !atoi(data)) {
00642 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00643 return -1;
00644 }
00645 if (ast_moh_start(chan, NULL, NULL)) {
00646 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00647 return 0;
00648 }
00649 res = ast_safe_sleep(chan, atoi(data) * 1000);
00650 ast_moh_stop(chan);
00651 return res;
00652 }
00653
00654 static int moh2_exec(struct ast_channel *chan, void *data)
00655 {
00656 if (ast_strlen_zero(data)) {
00657 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00658 return -1;
00659 }
00660 ast_string_field_set(chan, musicclass, data);
00661 return 0;
00662 }
00663
00664 static int moh3_exec(struct ast_channel *chan, void *data)
00665 {
00666 char *class = NULL;
00667 if (data && strlen(data))
00668 class = data;
00669 if (ast_moh_start(chan, class, NULL))
00670 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00671
00672 return 0;
00673 }
00674
00675 static int moh4_exec(struct ast_channel *chan, void *data)
00676 {
00677 ast_moh_stop(chan);
00678
00679 return 0;
00680 }
00681
00682 static struct mohclass *get_mohbyname(const char *name, int warn)
00683 {
00684 struct mohclass *moh = NULL;
00685 struct mohclass tmp_class = {
00686 .flags = 0,
00687 };
00688
00689 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00690
00691 moh = ao2_find(mohclasses, &tmp_class, 0);
00692
00693 if (!moh && warn) {
00694 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
00695 }
00696
00697 return moh;
00698 }
00699
00700 static struct mohdata *mohalloc(struct mohclass *cl)
00701 {
00702 struct mohdata *moh;
00703 long flags;
00704
00705 if (!(moh = ast_calloc(1, sizeof(*moh))))
00706 return NULL;
00707
00708 if (pipe(moh->pipe)) {
00709 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00710 free(moh);
00711 return NULL;
00712 }
00713
00714
00715 flags = fcntl(moh->pipe[0], F_GETFL);
00716 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00717 flags = fcntl(moh->pipe[1], F_GETFL);
00718 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00719
00720 moh->f.frametype = AST_FRAME_VOICE;
00721 moh->f.subclass = cl->format;
00722 moh->f.offset = AST_FRIENDLY_OFFSET;
00723
00724 moh->parent = mohclass_ref(cl);
00725
00726 ao2_lock(cl);
00727 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00728 ao2_unlock(cl);
00729
00730 return moh;
00731 }
00732
00733 static void moh_release(struct ast_channel *chan, void *data)
00734 {
00735 struct mohdata *moh = data;
00736 struct mohclass *class = moh->parent;
00737 int oldwfmt;
00738 struct moh_files_state *state;
00739
00740 ao2_lock(class);
00741 AST_LIST_REMOVE(&moh->parent->members, moh, list);
00742 ao2_unlock(class);
00743
00744 close(moh->pipe[0]);
00745 close(moh->pipe[1]);
00746
00747 oldwfmt = moh->origwfmt;
00748 state = chan->music_state;
00749 moh->parent = class = mohclass_unref(class);
00750
00751 free(moh);
00752
00753 if (chan) {
00754 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
00755 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
00756 chan->name, ast_getformatname(oldwfmt));
00757 }
00758 if (option_verbose > 2) {
00759 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00760 }
00761 }
00762 }
00763
00764 static void *moh_alloc(struct ast_channel *chan, void *params)
00765 {
00766 struct mohdata *res;
00767 struct mohclass *class = params;
00768
00769 if ((res = mohalloc(class))) {
00770 res->origwfmt = chan->writeformat;
00771 if (ast_set_write_format(chan, class->format)) {
00772 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00773 moh_release(NULL, res);
00774 res = NULL;
00775 }
00776 if (option_verbose > 2)
00777 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00778 }
00779 return res;
00780 }
00781
00782 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00783 {
00784 struct mohdata *moh = data;
00785 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00786 int res;
00787
00788 len = ast_codec_get_len(moh->parent->format, samples);
00789
00790 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00791 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00792 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00793 }
00794 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00795 if (res <= 0)
00796 return 0;
00797
00798 moh->f.datalen = res;
00799 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00800 moh->f.samples = ast_codec_get_samples(&moh->f);
00801
00802 if (ast_write(chan, &moh->f) < 0) {
00803 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00804 return -1;
00805 }
00806
00807 return 0;
00808 }
00809
00810 static struct ast_generator mohgen = {
00811 .alloc = moh_alloc,
00812 .release = moh_release,
00813 .generate = moh_generate,
00814 };
00815
00816 static int moh_add_file(struct mohclass *class, const char *filepath)
00817 {
00818 if (!class->allowed_files) {
00819 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00820 return -1;
00821 class->allowed_files = INITIAL_NUM_FILES;
00822 } else if (class->total_files == class->allowed_files) {
00823 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00824 class->allowed_files = 0;
00825 class->total_files = 0;
00826 return -1;
00827 }
00828 class->allowed_files *= 2;
00829 }
00830
00831 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00832 return -1;
00833
00834 class->total_files++;
00835
00836 return 0;
00837 }
00838
00839 static int moh_scan_files(struct mohclass *class) {
00840
00841 DIR *files_DIR;
00842 struct dirent *files_dirent;
00843 char path[PATH_MAX];
00844 char filepath[PATH_MAX];
00845 char *ext;
00846 struct stat statbuf;
00847 int dirnamelen;
00848 int i;
00849
00850 files_DIR = opendir(class->dir);
00851 if (!files_DIR) {
00852 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00853 return -1;
00854 }
00855
00856 for (i = 0; i < class->total_files; i++)
00857 free(class->filearray[i]);
00858
00859 class->total_files = 0;
00860 dirnamelen = strlen(class->dir) + 2;
00861 if (!getcwd(path, sizeof(path))) {
00862 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00863 return -1;
00864 }
00865 if (chdir(class->dir) < 0) {
00866 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00867 return -1;
00868 }
00869 while ((files_dirent = readdir(files_DIR))) {
00870
00871 if ((strlen(files_dirent->d_name) < 4))
00872 continue;
00873
00874
00875 if (files_dirent->d_name[0] == '.')
00876 continue;
00877
00878
00879 if (!strchr(files_dirent->d_name, '.'))
00880 continue;
00881
00882 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00883
00884 if (stat(filepath, &statbuf))
00885 continue;
00886
00887 if (!S_ISREG(statbuf.st_mode))
00888 continue;
00889
00890 if ((ext = strrchr(filepath, '.'))) {
00891 *ext = '\0';
00892 ext++;
00893 }
00894
00895
00896 for (i = 0; i < class->total_files; i++)
00897 if (!strcmp(filepath, class->filearray[i]))
00898 break;
00899
00900 if (i == class->total_files) {
00901 if (moh_add_file(class, filepath))
00902 break;
00903 }
00904 }
00905
00906 closedir(files_DIR);
00907 if (chdir(path) < 0) {
00908 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00909 return -1;
00910 }
00911 return class->total_files;
00912 }
00913
00914 static int init_files_class(struct mohclass *class)
00915 {
00916 int res;
00917
00918 res = moh_scan_files(class);
00919
00920 if (res < 0) {
00921 return -1;
00922 }
00923
00924 if (!res) {
00925 if (option_verbose > 2) {
00926 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00927 class->dir, class->name);
00928 }
00929 return -1;
00930 }
00931
00932 if (strchr(class->args, 'r')) {
00933 ast_set_flag(class, MOH_RANDOMIZE);
00934 }
00935
00936 return 0;
00937 }
00938
00939 #ifdef HAVE_WORKING_FORK
00940 static int init_app_class(struct mohclass *class)
00941 {
00942 #ifdef HAVE_DAHDI
00943 int x;
00944 #endif
00945
00946 if (!strcasecmp(class->mode, "custom")) {
00947 ast_set_flag(class, MOH_CUSTOM);
00948 } else if (!strcasecmp(class->mode, "mp3nb")) {
00949 ast_set_flag(class, MOH_SINGLE);
00950 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
00951 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
00952 } else if (!strcasecmp(class->mode, "quietmp3")) {
00953 ast_set_flag(class, MOH_QUIET);
00954 }
00955
00956 class->srcfd = -1;
00957 class->pseudofd = -1;
00958
00959 #ifdef HAVE_DAHDI
00960
00961
00962 class->pseudofd = open(DAHDI_FILE_PSEUDO, O_RDONLY);
00963 if (class->pseudofd < 0) {
00964 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
00965 } else {
00966 x = 320;
00967 ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
00968 }
00969 #endif
00970
00971 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
00972 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
00973 if (class->pseudofd > -1) {
00974 close(class->pseudofd);
00975 class->pseudofd = -1;
00976 }
00977 return -1;
00978 }
00979
00980 return 0;
00981 }
00982 #endif
00983
00984
00985
00986
00987 static int moh_register(struct mohclass *moh, int reload)
00988 {
00989 struct mohclass *mohclass = NULL;
00990
00991 if ((mohclass = get_mohbyname(moh->name, 0))) {
00992 if (!mohclass->delete) {
00993 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00994 mohclass = mohclass_unref(mohclass);
00995 moh = mohclass_unref(moh);
00996 return -1;
00997 }
00998 mohclass = mohclass_unref(mohclass);
00999 }
01000
01001 time(&moh->start);
01002 moh->start -= respawn_time;
01003
01004 if (!strcasecmp(moh->mode, "files")) {
01005 if (init_files_class(moh)) {
01006 moh = mohclass_unref(moh);
01007 return -1;
01008 }
01009 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
01010 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
01011 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
01012 #ifdef HAVE_WORKING_FORK
01013 if (init_app_class(moh)) {
01014 moh = mohclass_unref(moh);
01015 return -1;
01016 }
01017 #else
01018 ast_log(LOG_WARNING, "Cannot use mode '%s' music on hold, as there is no working fork().\n", moh->mode);
01019 moh = mohclass_unref(moh);
01020 return -1;
01021 #endif
01022 } else {
01023 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
01024 moh = mohclass_unref(moh);
01025 return -1;
01026 }
01027
01028 ao2_link(mohclasses, moh);
01029
01030 moh = mohclass_unref(moh);
01031
01032 return 0;
01033 }
01034
01035 static void local_ast_moh_cleanup(struct ast_channel *chan)
01036 {
01037 if (chan->music_state) {
01038 free(chan->music_state);
01039 chan->music_state = NULL;
01040 }
01041 }
01042
01043 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
01044 {
01045 struct mohclass *mohclass = NULL;
01046 int res;
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059 if (!ast_strlen_zero(chan->musicclass)) {
01060 mohclass = get_mohbyname(chan->musicclass, 1);
01061 }
01062 if (!mohclass && !ast_strlen_zero(mclass)) {
01063 mohclass = get_mohbyname(mclass, 1);
01064 }
01065 if (!mohclass && !ast_strlen_zero(interpclass)) {
01066 mohclass = get_mohbyname(interpclass, 1);
01067 }
01068 if (!mohclass) {
01069 mohclass = get_mohbyname("default", 1);
01070 }
01071
01072
01073 if (!mohclass) {
01074 return -1;
01075 }
01076
01077 ast_set_flag(chan, AST_FLAG_MOH);
01078
01079 if (mohclass->total_files) {
01080 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01081 } else {
01082 res = ast_activate_generator(chan, &mohgen, mohclass);
01083 }
01084
01085 mohclass = mohclass_unref(mohclass);
01086
01087 return res;
01088 }
01089
01090 static void local_ast_moh_stop(struct ast_channel *chan)
01091 {
01092 ast_clear_flag(chan, AST_FLAG_MOH);
01093 ast_deactivate_generator(chan);
01094
01095 ast_channel_lock(chan);
01096 if (chan->music_state) {
01097 if (chan->stream) {
01098 ast_closestream(chan->stream);
01099 chan->stream = NULL;
01100 }
01101 }
01102 ast_channel_unlock(chan);
01103 }
01104
01105 static void moh_class_destructor(void *obj)
01106 {
01107 struct mohclass *class = obj;
01108 struct mohdata *member;
01109
01110 if (option_debug) {
01111 ast_log(LOG_DEBUG, "Destroying MOH class '%s'\n", class->name);
01112 }
01113
01114 if (class->pid > 1) {
01115 char buff[8192];
01116 int bytes, tbytes = 0, stime = 0, pid = 0;
01117
01118 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01119
01120 stime = time(NULL) + 2;
01121 pid = class->pid;
01122 class->pid = 0;
01123
01124
01125
01126
01127 killpg(pid, SIGHUP);
01128 usleep(100000);
01129 killpg(pid, SIGTERM);
01130 usleep(100000);
01131 killpg(pid, SIGKILL);
01132
01133 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
01134 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01135 tbytes = tbytes + bytes;
01136 }
01137
01138 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01139
01140 close(class->srcfd);
01141 }
01142
01143 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01144 free(member);
01145 }
01146
01147 if (class->thread) {
01148 pthread_cancel(class->thread);
01149 pthread_join(class->thread, NULL);
01150 class->thread = AST_PTHREADT_NULL;
01151 }
01152
01153 if (class->pseudofd > -1) {
01154 close(class->pseudofd);
01155 class->pseudofd = -1;
01156 }
01157
01158 if (class->filearray) {
01159 int i;
01160 for (i = 0; i < class->total_files; i++) {
01161 free(class->filearray[i]);
01162 }
01163 free(class->filearray);
01164 class->filearray = NULL;
01165 }
01166 }
01167
01168 static struct mohclass *moh_class_malloc(void)
01169 {
01170 struct mohclass *class;
01171
01172 if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01173 class->format = AST_FORMAT_SLINEAR;
01174 class->srcfd = -1;
01175 class->pseudofd = -1;
01176 }
01177
01178 return class;
01179 }
01180
01181 static int moh_class_mark(void *obj, void *arg, int flags)
01182 {
01183 struct mohclass *class = obj;
01184
01185 class->delete = 1;
01186
01187 return 0;
01188 }
01189
01190 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
01191 {
01192 struct mohclass *class = obj;
01193
01194 return class->delete ? CMP_MATCH : 0;
01195 }
01196
01197 static int load_moh_classes(int reload)
01198 {
01199 struct ast_config *cfg;
01200 struct ast_variable *var;
01201 struct mohclass *class;
01202 char *data;
01203 char *args;
01204 char *cat;
01205 int numclasses = 0;
01206 static int dep_warning = 0;
01207
01208 cfg = ast_config_load("musiconhold.conf");
01209
01210 if (!cfg) {
01211 return 0;
01212 }
01213
01214 if (reload) {
01215 ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
01216 }
01217
01218 ast_clear_flag(global_flags, AST_FLAGS_ALL);
01219
01220 cat = ast_category_browse(cfg, NULL);
01221 for (; cat; cat = ast_category_browse(cfg, cat)) {
01222
01223 if (!strcasecmp(cat, "general")) {
01224 var = ast_variable_browse(cfg, cat);
01225 while (var) {
01226 if (!strcasecmp(var->name, "cachertclasses")) {
01227 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
01228 } else {
01229 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
01230 }
01231 var = var->next;
01232 }
01233 }
01234
01235
01236 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || !strcasecmp(cat, "general")) {
01237 continue;
01238 }
01239
01240 if (!(class = moh_class_malloc())) {
01241 break;
01242 }
01243
01244 ast_copy_string(class->name, cat, sizeof(class->name));
01245
01246 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01247 if (!strcasecmp(var->name, "mode")) {
01248 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01249 } else if (!strcasecmp(var->name, "directory")) {
01250 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01251 } else if (!strcasecmp(var->name, "application")) {
01252 ast_copy_string(class->args, var->value, sizeof(class->args));
01253 } else if (!strcasecmp(var->name, "random")) {
01254 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01255 } else if (!strcasecmp(var->name, "format")) {
01256 class->format = ast_getformatbyname(var->value);
01257 if (!class->format) {
01258 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01259 class->format = AST_FORMAT_SLINEAR;
01260 }
01261 }
01262 }
01263
01264 if (ast_strlen_zero(class->dir)) {
01265 if (!strcasecmp(class->mode, "custom")) {
01266 ast_copy_string(class->dir, "nodir", sizeof(class->dir));
01267 } else {
01268 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01269 class = mohclass_unref(class);
01270 continue;
01271 }
01272 }
01273
01274 if (ast_strlen_zero(class->mode)) {
01275 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01276 class = mohclass_unref(class);
01277 continue;
01278 }
01279
01280 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01281 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01282 class = mohclass_unref(class);
01283 continue;
01284 }
01285
01286
01287 if (!moh_register(class, reload)) {
01288 numclasses++;
01289 }
01290 }
01291
01292
01293
01294 for (var = ast_variable_browse(cfg, "classes"); var; var = var->next) {
01295 struct mohclass *tmp_class;
01296
01297 if (!dep_warning) {
01298 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
01299 dep_warning = 1;
01300 }
01301
01302 if (!(data = strchr(var->value, ':'))) {
01303 continue;
01304 }
01305 *data++ = '\0';
01306
01307 if ((args = strchr(data, ','))) {
01308 *args++ = '\0';
01309 }
01310
01311
01312 if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01313 tmp_class = mohclass_unref(tmp_class);
01314 continue;
01315 }
01316
01317 if (!(class = moh_class_malloc())) {
01318 break;
01319 }
01320
01321 class->deprecated = 1;
01322 ast_copy_string(class->name, var->name, sizeof(class->name));
01323 ast_copy_string(class->dir, data, sizeof(class->dir));
01324 ast_copy_string(class->mode, var->value, sizeof(class->mode));
01325 if (args) {
01326 ast_copy_string(class->args, args, sizeof(class->args));
01327 }
01328
01329 moh_register(class, reload);
01330 class = NULL;
01331
01332 numclasses++;
01333 }
01334
01335 for (var = ast_variable_browse(cfg, "moh_files"); var; var = var->next) {
01336 struct mohclass *tmp_class;
01337
01338 if (!dep_warning) {
01339 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
01340 dep_warning = 1;
01341 }
01342
01343
01344 if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
01345 tmp_class = mohclass_unref(tmp_class);
01346 continue;
01347 }
01348
01349 if ((args = strchr(var->value, ','))) {
01350 *args++ = '\0';
01351 }
01352
01353 if (!(class = moh_class_malloc())) {
01354 break;
01355 }
01356
01357 class->deprecated = 1;
01358 ast_copy_string(class->name, var->name, sizeof(class->name));
01359 ast_copy_string(class->dir, var->value, sizeof(class->dir));
01360 ast_copy_string(class->mode, "files", sizeof(class->mode));
01361 if (args) {
01362 ast_copy_string(class->args, args, sizeof(class->args));
01363 }
01364
01365 moh_register(class, reload);
01366 class = NULL;
01367
01368 numclasses++;
01369 }
01370
01371 ast_config_destroy(cfg);
01372
01373 ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
01374 moh_classes_delete_marked, NULL);
01375
01376 return numclasses;
01377 }
01378
01379 static void ast_moh_destroy(void)
01380 {
01381 if (option_verbose > 1) {
01382 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01383 }
01384
01385 ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01386 }
01387
01388 static int moh_cli(int fd, int argc, char *argv[])
01389 {
01390 reload();
01391 return 0;
01392 }
01393
01394 static int cli_files_show(int fd, int argc, char *argv[])
01395 {
01396 struct mohclass *class;
01397 struct ao2_iterator i;
01398
01399 i = ao2_iterator_init(mohclasses, 0);
01400
01401 for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01402 int x;
01403
01404 if (!class->total_files) {
01405 continue;
01406 }
01407
01408 ast_cli(fd, "Class: %s\n", class->name);
01409
01410 for (x = 0; x < class->total_files; x++) {
01411 ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01412 }
01413 }
01414
01415 ao2_iterator_destroy(&i);
01416
01417 return 0;
01418 }
01419
01420 static int moh_classes_show(int fd, int argc, char *argv[])
01421 {
01422 struct mohclass *class;
01423 struct ao2_iterator i;
01424
01425 i = ao2_iterator_init(mohclasses, 0);
01426
01427 for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01428 ast_cli(fd, "Class: %s\n", class->name);
01429 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01430 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01431 if (ast_test_flag(class, MOH_CUSTOM)) {
01432 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01433 }
01434 if (strcasecmp(class->mode, "files")) {
01435 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01436 }
01437 }
01438
01439 ao2_iterator_destroy(&i);
01440
01441 return 0;
01442 }
01443
01444 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
01445 { "moh", "classes", "show"},
01446 moh_classes_show, NULL,
01447 NULL };
01448
01449 static struct ast_cli_entry cli_moh_files_show_deprecated = {
01450 { "moh", "files", "show"},
01451 cli_files_show, NULL,
01452 NULL };
01453
01454 static struct ast_cli_entry cli_moh[] = {
01455 { { "moh", "reload"},
01456 moh_cli, "Music On Hold",
01457 "Usage: moh reload\n Rereads configuration\n" },
01458
01459 { { "moh", "show", "classes"},
01460 moh_classes_show, "List MOH classes",
01461 "Usage: moh show classes\n Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
01462
01463 { { "moh", "show", "files"},
01464 cli_files_show, "List MOH file-based classes",
01465 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
01466 };
01467
01468 static int moh_class_hash(const void *obj, const int flags)
01469 {
01470 const struct mohclass *class = obj;
01471
01472 return ast_str_case_hash(class->name);
01473 }
01474
01475 static int moh_class_cmp(void *obj, void *arg, int flags)
01476 {
01477 struct mohclass *class = obj, *class2 = arg;
01478
01479 return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01480 }
01481
01482 static int load_module(void)
01483 {
01484 int res;
01485
01486 if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01487 return AST_MODULE_LOAD_DECLINE;
01488 }
01489
01490 if (!load_moh_classes(0)) {
01491 ast_log(LOG_WARNING, "No music on hold classes configured, "
01492 "disabling music on hold.\n");
01493 } else {
01494 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01495 local_ast_moh_cleanup);
01496 }
01497
01498 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01499 ast_register_atexit(ast_moh_destroy);
01500 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01501 if (!res)
01502 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01503 if (!res)
01504 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01505 if (!res)
01506 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01507 if (!res)
01508 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01509
01510 return AST_MODULE_LOAD_SUCCESS;
01511 }
01512
01513 static int reload(void)
01514 {
01515 if (load_moh_classes(1)) {
01516 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01517 local_ast_moh_cleanup);
01518 }
01519
01520 return 0;
01521 }
01522
01523 static int moh_class_inuse(void *obj, void *arg, int flags)
01524 {
01525 struct mohclass *class = obj;
01526
01527 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01528 }
01529
01530 static int unload_module(void)
01531 {
01532 int res = 0;
01533 struct mohclass *class = NULL;
01534
01535
01536
01537 if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01538 class = mohclass_unref(class);
01539 res = -1;
01540 }
01541
01542 if (res < 0) {
01543 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01544 return res;
01545 }
01546
01547 ast_uninstall_music_functions();
01548
01549 ast_moh_destroy();
01550
01551 res = ast_unregister_application(app0);
01552 res |= ast_unregister_application(app1);
01553 res |= ast_unregister_application(app2);
01554 res |= ast_unregister_application(app3);
01555 res |= ast_unregister_application(app4);
01556
01557 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01558
01559 return res;
01560 }
01561
01562 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
01563 .load = load_module,
01564 .unload = unload_module,
01565 .reload = reload,
01566 );