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