2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Routines implementing music on hold
23 * \arg See also \ref Config_moh
25 * \author Mark Spencer <markster@digium.com>
29 <conflict>win32</conflict>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45 #include <sys/signal.h>
46 #include <netinet/in.h>
50 #include <sys/ioctl.h>
53 #include <zaptel/zaptel.h>
56 #include "asterisk/lock.h"
57 #include "asterisk/file.h"
58 #include "asterisk/logger.h"
59 #include "asterisk/channel.h"
60 #include "asterisk/pbx.h"
61 #include "asterisk/options.h"
62 #include "asterisk/module.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/say.h"
65 #include "asterisk/musiconhold.h"
66 #include "asterisk/config.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/cli.h"
69 #include "asterisk/stringfields.h"
70 #include "asterisk/linkedlists.h"
72 #define INITIAL_NUM_FILES 8
74 static char *app0 = "MusicOnHold";
75 static char *app1 = "WaitMusicOnHold";
76 static char *app2 = "SetMusicOnHold";
77 static char *app3 = "StartMusicOnHold";
78 static char *app4 = "StopMusicOnHold";
80 static char *synopsis0 = "Play Music On Hold indefinitely";
81 static char *synopsis1 = "Wait, playing Music On Hold";
82 static char *synopsis2 = "Set default Music On Hold class";
83 static char *synopsis3 = "Play Music On Hold";
84 static char *synopsis4 = "Stop Playing Music On Hold";
86 static char *descrip0 = "MusicOnHold(class): "
87 "Plays hold music specified by class. If omitted, the default\n"
88 "music source for the channel will be used. Set the default \n"
89 "class with the SetMusicOnHold() application.\n"
90 "Returns -1 on hangup.\n"
91 "Never returns otherwise.\n";
93 static char *descrip1 = "WaitMusicOnHold(delay): "
94 "Plays hold music specified number of seconds. Returns 0 when\n"
95 "done, or -1 on hangup. If no hold music is available, the delay will\n"
96 "still occur with no sound.\n";
98 static char *descrip2 = "SetMusicOnHold(class): "
99 "Sets the default class for music on hold for a given channel. When\n"
100 "music on hold is activated, this class will be used to select which\n"
101 "music is played.\n";
103 static char *descrip3 = "StartMusicOnHold(class): "
104 "Starts playing music on hold, uses default music class for channel.\n"
105 "Starts playing music specified by class. If omitted, the default\n"
106 "music source for the channel will be used. Always returns 0.\n";
108 static char *descrip4 = "StopMusicOnHold: "
109 "Stops playing music on hold.\n";
111 static int respawn_time = 20;
113 struct moh_files_state {
114 struct mohclass *class;
119 unsigned char save_pos;
122 #define MOH_QUIET (1 << 0)
123 #define MOH_SINGLE (1 << 1)
124 #define MOH_CUSTOM (1 << 2)
125 #define MOH_RANDOMIZE (1 << 3)
128 char name[MAX_MUSICCLASS];
132 /*! A dynamically sized array to hold the list of filenames in "files" mode */
134 /*! The current size of the filearray */
136 /*! The current number of files loaded into the filearray */
139 /*! The format from the MOH source, not applicable to "files" mode */
141 /*! The pid of the external application delivering MOH */
145 /*! Source of audio */
147 /*! FD for timing source */
149 AST_LIST_HEAD_NOLOCK(, mohdata) members;
150 AST_LIST_ENTRY(mohclass) list;
156 struct mohclass *parent;
158 AST_LIST_ENTRY(mohdata) list;
161 AST_LIST_HEAD_STATIC(mohclasses, mohclass);
163 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
164 #define MPG_123 "/usr/bin/mpg123"
168 static void ast_moh_free_class(struct mohclass **mohclass)
170 struct mohdata *member;
171 struct mohclass *class = *mohclass;
174 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
178 pthread_cancel(class->thread);
182 if (class->filearray) {
183 for (i = 0; i < class->total_files; i++)
184 free(class->filearray[i]);
185 free(class->filearray);
193 static void moh_files_release(struct ast_channel *chan, void *data)
195 struct moh_files_state *state = chan->music_state;
199 ast_closestream(chan->stream);
202 if (option_verbose > 2)
203 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
205 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
206 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
208 state->save_pos = state->pos + 1;
213 static int ast_moh_files_next(struct ast_channel *chan)
215 struct moh_files_state *state = chan->music_state;
218 if (state->save_pos) {
219 state->pos = state->save_pos - 1;
222 /* Try 20 times to find something good */
223 for (tries=0;tries < 20;tries++) {
226 ast_closestream(chan->stream);
231 if (ast_test_flag(state->class, MOH_RANDOMIZE))
232 state->pos = ast_random();
234 state->pos %= state->class->total_files;
236 /* check to see if this file's format can be opened */
237 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
243 state->pos = state->pos % state->class->total_files;
245 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
246 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
252 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
255 ast_seekstream(chan->stream, state->samples, SEEK_SET);
261 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
263 struct ast_frame *f = NULL;
265 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
266 if (!ast_moh_files_next(chan))
267 f = ast_readframe(chan->stream);
273 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
275 struct moh_files_state *state = chan->music_state;
276 struct ast_frame *f = NULL;
279 state->sample_queue += samples;
281 while (state->sample_queue > 0) {
282 if ((f = moh_files_readframe(chan))) {
283 state->samples += f->samples;
284 res = ast_write(chan, f);
285 state->sample_queue -= f->samples;
288 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
298 static void *moh_files_alloc(struct ast_channel *chan, void *params)
300 struct moh_files_state *state;
301 struct mohclass *class = params;
303 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
304 chan->music_state = state;
305 state->class = class;
307 state = chan->music_state;
310 if (state->class != class) {
312 memset(state, 0, sizeof(*state));
313 state->class = class;
316 state->origwfmt = chan->writeformat;
318 if (option_verbose > 2)
319 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
322 return chan->music_state;
325 static struct ast_generator moh_file_stream =
327 alloc: moh_files_alloc,
328 release: moh_files_release,
329 generate: moh_files_generator,
332 static int spawn_mp3(struct mohclass *class)
336 char fns[MAX_MP3S][80];
337 char *argv[MAX_MP3S + 50];
345 if (!strcasecmp(class->dir, "nodir")) {
348 dir = opendir(class->dir);
349 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
350 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
355 if (!ast_test_flag(class, MOH_CUSTOM)) {
356 argv[argc++] = "mpg123";
359 argv[argc++] = "--mono";
361 argv[argc++] = "8000";
363 if (!ast_test_flag(class, MOH_SINGLE)) {
365 argv[argc++] = "2048";
370 if (ast_test_flag(class, MOH_QUIET))
371 argv[argc++] = "4096";
373 argv[argc++] = "8192";
375 /* Look for extra arguments and add them to the list */
376 ast_copy_string(xargs, class->args, sizeof(xargs));
378 while (!ast_strlen_zero(argptr)) {
379 argv[argc++] = argptr;
380 strsep(&argptr, ",");
383 /* Format arguments for argv vector */
384 ast_copy_string(xargs, class->args, sizeof(xargs));
386 while (!ast_strlen_zero(argptr)) {
387 argv[argc++] = argptr;
388 strsep(&argptr, " ");
393 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
394 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
395 argv[argc++] = fns[files];
398 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
399 if ((strlen(de->d_name) > 3) &&
400 ((ast_test_flag(class, MOH_CUSTOM) &&
401 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
402 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
403 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
404 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
405 argv[argc++] = fns[files];
415 ast_log(LOG_WARNING, "Pipe failed\n");
419 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
424 if (time(NULL) - class->start < respawn_time) {
425 sleep(respawn_time - (time(NULL) - class->start));
429 if (class->pid < 0) {
432 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
438 if (ast_opt_high_priority)
442 /* Stdout goes to pipe */
443 dup2(fds[1], STDOUT_FILENO);
444 /* Close unused file descriptors */
445 for (x=3;x<8192;x++) {
446 if (-1 != fcntl(x, F_GETFL)) {
452 if (ast_test_flag(class, MOH_CUSTOM)) {
453 execv(argv[0], argv);
455 /* Default install is /usr/local/bin */
456 execv(LOCAL_MPG_123, argv);
457 /* Many places have it in /usr/bin */
458 execv(MPG_123, argv);
459 /* Check PATH as a last-ditch effort */
460 execvp("mpg123", argv);
462 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
472 static void *monmp3thread(void *data)
474 #define MOH_MS_INTERVAL 100
476 struct mohclass *class = data;
482 struct timeval tv, tv_tmp;
487 pthread_testcancel();
488 /* Spawn mp3 player if it's not there */
489 if (class->srcfd < 0) {
490 if ((class->srcfd = spawn_mp3(class)) < 0) {
491 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
492 /* Try again later */
494 pthread_testcancel();
497 if (class->pseudofd > -1) {
498 /* Pause some amount of time */
499 res = read(class->pseudofd, buf, sizeof(buf));
500 pthread_testcancel();
504 tv_tmp = ast_tvnow();
507 delta = ast_tvdiff_ms(tv_tmp, tv);
508 if (delta < MOH_MS_INTERVAL) { /* too early */
509 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
510 usleep(1000 * (MOH_MS_INTERVAL - delta));
511 pthread_testcancel();
513 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
516 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
518 if (AST_LIST_EMPTY(&class->members))
521 len = ast_codec_get_len(class->format, res);
523 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
527 pthread_testcancel();
529 kill(class->pid, SIGHUP);
531 kill(class->pid, SIGTERM);
533 kill(class->pid, SIGKILL);
537 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
540 pthread_testcancel();
541 AST_LIST_LOCK(&mohclasses);
542 AST_LIST_TRAVERSE(&class->members, moh, list) {
544 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
546 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
549 AST_LIST_UNLOCK(&mohclasses);
554 static int moh0_exec(struct ast_channel *chan, void *data)
556 if (ast_moh_start(chan, data, NULL)) {
557 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
560 while (!ast_safe_sleep(chan, 10000));
565 static int moh1_exec(struct ast_channel *chan, void *data)
568 if (!data || !atoi(data)) {
569 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
572 if (ast_moh_start(chan, NULL, NULL)) {
573 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
576 res = ast_safe_sleep(chan, atoi(data) * 1000);
581 static int moh2_exec(struct ast_channel *chan, void *data)
583 if (ast_strlen_zero(data)) {
584 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
587 ast_string_field_set(chan, musicclass, data);
591 static int moh3_exec(struct ast_channel *chan, void *data)
594 if (data && strlen(data))
596 if (ast_moh_start(chan, class, NULL))
597 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
602 static int moh4_exec(struct ast_channel *chan, void *data)
609 /*! \note This function should be called with the mohclasses list locked */
610 static struct mohclass *get_mohbyname(const char *name)
612 struct mohclass *moh = NULL;
614 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
615 if (!strcasecmp(name, moh->name))
622 static struct mohdata *mohalloc(struct mohclass *cl)
627 if (!(moh = ast_calloc(1, sizeof(*moh))))
630 if (pipe(moh->pipe)) {
631 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
636 /* Make entirely non-blocking */
637 flags = fcntl(moh->pipe[0], F_GETFL);
638 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
639 flags = fcntl(moh->pipe[1], F_GETFL);
640 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
642 moh->f.frametype = AST_FRAME_VOICE;
643 moh->f.subclass = cl->format;
644 moh->f.offset = AST_FRIENDLY_OFFSET;
647 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
652 static void moh_release(struct ast_channel *chan, void *data)
654 struct mohdata *moh = data;
657 AST_LIST_LOCK(&mohclasses);
658 AST_LIST_REMOVE(&moh->parent->members, moh, list);
659 AST_LIST_UNLOCK(&mohclasses);
663 oldwfmt = moh->origwfmt;
666 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
667 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
668 if (option_verbose > 2)
669 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
673 static void *moh_alloc(struct ast_channel *chan, void *params)
676 struct mohclass *class = params;
678 if ((res = mohalloc(class))) {
679 res->origwfmt = chan->writeformat;
680 if (ast_set_write_format(chan, class->format)) {
681 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
682 moh_release(NULL, res);
685 if (option_verbose > 2)
686 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
691 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
693 struct mohdata *moh = data;
694 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
697 if (!moh->parent->pid)
700 len = ast_codec_get_len(moh->parent->format, samples);
702 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
703 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
704 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
706 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
710 moh->f.datalen = res;
711 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
712 moh->f.samples = ast_codec_get_samples(&moh->f);
714 if (ast_write(chan, &moh->f) < 0) {
715 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
722 static struct ast_generator mohgen =
725 release: moh_release,
726 generate: moh_generate,
729 static int moh_add_file(struct mohclass *class, const char *filepath)
731 if (!class->allowed_files) {
732 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
734 class->allowed_files = INITIAL_NUM_FILES;
735 } else if (class->total_files == class->allowed_files) {
736 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
737 class->allowed_files = 0;
738 class->total_files = 0;
741 class->allowed_files *= 2;
744 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
747 class->total_files++;
752 static int moh_scan_files(struct mohclass *class) {
755 struct dirent *files_dirent;
757 char filepath[PATH_MAX];
763 files_DIR = opendir(class->dir);
765 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
769 for (i = 0; i < class->total_files; i++)
770 free(class->filearray[i]);
772 class->total_files = 0;
773 dirnamelen = strlen(class->dir) + 2;
774 getcwd(path, sizeof(path));
776 while ((files_dirent = readdir(files_DIR))) {
777 /* The file name must be at least long enough to have the file type extension */
778 if ((strlen(files_dirent->d_name) < 4))
781 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
783 if (stat(filepath, &statbuf))
786 if (!S_ISREG(statbuf.st_mode))
789 if ((ext = strrchr(filepath, '.'))) {
794 /* if the file is present in multiple formats, ensure we only put it into the list once */
795 for (i = 0; i < class->total_files; i++)
796 if (!strcmp(filepath, class->filearray[i]))
799 if (i == class->total_files) {
800 if (moh_add_file(class, filepath))
807 return class->total_files;
810 static int moh_register(struct mohclass *moh, int reload)
815 AST_LIST_LOCK(&mohclasses);
816 if (get_mohbyname(moh->name)) {
818 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
820 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
823 AST_LIST_UNLOCK(&mohclasses);
826 AST_LIST_UNLOCK(&mohclasses);
829 moh->start -= respawn_time;
831 if (!strcasecmp(moh->mode, "files")) {
832 if (!moh_scan_files(moh)) {
833 ast_moh_free_class(&moh);
836 if (strchr(moh->args, 'r'))
837 ast_set_flag(moh, MOH_RANDOMIZE);
838 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
840 if (!strcasecmp(moh->mode, "custom"))
841 ast_set_flag(moh, MOH_CUSTOM);
842 else if (!strcasecmp(moh->mode, "mp3nb"))
843 ast_set_flag(moh, MOH_SINGLE);
844 else if (!strcasecmp(moh->mode, "quietmp3nb"))
845 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
846 else if (!strcasecmp(moh->mode, "quietmp3"))
847 ast_set_flag(moh, MOH_QUIET);
851 /* Open /dev/zap/pseudo for timing... Is
852 there a better, yet reliable way to do this? */
853 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
854 if (moh->pseudofd < 0) {
855 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
858 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
863 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
864 ast_log(LOG_WARNING, "Unable to create moh...\n");
865 if (moh->pseudofd > -1)
866 close(moh->pseudofd);
867 ast_moh_free_class(&moh);
871 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
872 ast_moh_free_class(&moh);
876 AST_LIST_LOCK(&mohclasses);
877 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
878 AST_LIST_UNLOCK(&mohclasses);
883 static void local_ast_moh_cleanup(struct ast_channel *chan)
885 if (chan->music_state) {
886 free(chan->music_state);
887 chan->music_state = NULL;
891 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
893 struct mohclass *mohclass;
896 /* The following is the order of preference for which class to use:
897 * 1) The channels explicitly set musicclass, which should *only* be
898 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
899 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
900 * result of receiving a HOLD control frame, this should be the
901 * payload that came with the frame.
902 * 3) The interpclass argument. This would be from the mohinterpret
903 * option from channel drivers. This is the same as the old musicclass
905 * 4) The default class.
907 if (!ast_strlen_zero(chan->musicclass))
908 class = chan->musicclass;
909 else if (!ast_strlen_zero(mclass))
911 else if (!ast_strlen_zero(interpclass))
916 AST_LIST_LOCK(&mohclasses);
917 mohclass = get_mohbyname(class);
918 AST_LIST_UNLOCK(&mohclasses);
921 ast_log(LOG_WARNING, "No class: %s\n", class);
925 ast_set_flag(chan, AST_FLAG_MOH);
926 if (mohclass->total_files) {
927 return ast_activate_generator(chan, &moh_file_stream, mohclass);
929 return ast_activate_generator(chan, &mohgen, mohclass);
932 static void local_ast_moh_stop(struct ast_channel *chan)
934 ast_clear_flag(chan, AST_FLAG_MOH);
935 ast_deactivate_generator(chan);
937 if (chan->music_state) {
939 ast_closestream(chan->stream);
945 static struct mohclass *moh_class_malloc(void)
947 struct mohclass *class;
949 if ((class = ast_calloc(1, sizeof(*class))))
950 class->format = AST_FORMAT_SLINEAR;
955 static int load_moh_classes(int reload)
957 struct ast_config *cfg;
958 struct ast_variable *var;
959 struct mohclass *class;
964 static int dep_warning = 0;
966 cfg = ast_config_load("musiconhold.conf");
971 cat = ast_category_browse(cfg, NULL);
972 for (; cat; cat = ast_category_browse(cfg, cat)) {
973 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
974 if (!(class = moh_class_malloc())) {
977 ast_copy_string(class->name, cat, sizeof(class->name));
978 var = ast_variable_browse(cfg, cat);
980 if (!strcasecmp(var->name, "mode"))
981 ast_copy_string(class->mode, var->value, sizeof(class->mode));
982 else if (!strcasecmp(var->name, "directory"))
983 ast_copy_string(class->dir, var->value, sizeof(class->dir));
984 else if (!strcasecmp(var->name, "application"))
985 ast_copy_string(class->args, var->value, sizeof(class->args));
986 else if (!strcasecmp(var->name, "random"))
987 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
988 else if (!strcasecmp(var->name, "format")) {
989 class->format = ast_getformatbyname(var->value);
990 if (!class->format) {
991 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
992 class->format = AST_FORMAT_SLINEAR;
998 if (ast_strlen_zero(class->dir)) {
999 if (!strcasecmp(class->mode, "custom")) {
1000 strcpy(class->dir, "nodir");
1002 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1007 if (ast_strlen_zero(class->mode)) {
1008 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1012 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1013 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1018 /* Don't leak a class when it's already registered */
1019 moh_register(class, reload);
1026 /* Deprecated Old-School Configuration */
1027 var = ast_variable_browse(cfg, "classes");
1030 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");
1033 data = strchr(var->value, ':');
1036 args = strchr(data, ',');
1039 if (!(get_mohbyname(var->name))) {
1040 if (!(class = moh_class_malloc())) {
1044 ast_copy_string(class->name, var->name, sizeof(class->name));
1045 ast_copy_string(class->dir, data, sizeof(class->dir));
1046 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1048 ast_copy_string(class->args, args, sizeof(class->args));
1050 moh_register(class, reload);
1056 var = ast_variable_browse(cfg, "moh_files");
1059 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");
1062 if (!(get_mohbyname(var->name))) {
1063 args = strchr(var->value, ',');
1066 if (!(class = moh_class_malloc())) {
1070 ast_copy_string(class->name, var->name, sizeof(class->name));
1071 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1072 strcpy(class->mode, "files");
1074 ast_copy_string(class->args, args, sizeof(class->args));
1076 moh_register(class, reload);
1082 ast_config_destroy(cfg);
1087 static void ast_moh_destroy(void)
1089 struct mohclass *moh;
1091 int bytes, tbytes = 0, stime = 0, pid = 0;
1093 if (option_verbose > 1)
1094 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1096 AST_LIST_LOCK(&mohclasses);
1097 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1099 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1100 stime = time(NULL) + 2;
1103 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1104 * to give the process a reason and time enough to kill off its
1111 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1112 tbytes = tbytes + bytes;
1113 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1116 ast_moh_free_class(&moh);
1118 AST_LIST_UNLOCK(&mohclasses);
1121 static void moh_on_off(int on)
1123 struct ast_channel *chan = NULL;
1125 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1126 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1128 local_ast_moh_start(chan, NULL, NULL);
1130 ast_deactivate_generator(chan);
1132 ast_channel_unlock(chan);
1136 static int moh_cli(int fd, int argc, char *argv[])
1142 x = load_moh_classes(1);
1144 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1148 static int cli_files_show(int fd, int argc, char *argv[])
1151 struct mohclass *class;
1153 AST_LIST_LOCK(&mohclasses);
1154 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1155 if (!class->total_files)
1158 ast_cli(fd, "Class: %s\n", class->name);
1159 for (i = 0; i < class->total_files; i++)
1160 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1162 AST_LIST_UNLOCK(&mohclasses);
1167 static int moh_classes_show(int fd, int argc, char *argv[])
1169 struct mohclass *class;
1171 AST_LIST_LOCK(&mohclasses);
1172 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1173 ast_cli(fd, "Class: %s\n", class->name);
1174 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1175 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1176 if (ast_test_flag(class, MOH_CUSTOM))
1177 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1178 if (strcasecmp(class->mode, "files"))
1179 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1181 AST_LIST_UNLOCK(&mohclasses);
1186 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1188 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1190 static struct ast_cli_entry cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL};
1192 static int init_classes(int reload)
1194 struct mohclass *moh;
1196 if (!load_moh_classes(reload)) /* Load classes from config */
1197 return 0; /* Return if nothing is found */
1199 AST_LIST_LOCK(&mohclasses);
1200 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
1201 if (moh->total_files)
1202 moh_scan_files(moh);
1204 AST_LIST_UNLOCK(&mohclasses);
1209 static int load_module(void *mod)
1213 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1214 ast_register_atexit(ast_moh_destroy);
1215 ast_cli_register(&cli_moh);
1216 ast_cli_register(&cli_moh_files_show);
1217 ast_cli_register(&cli_moh_classes_show);
1219 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1221 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1223 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1225 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1227 if (!init_classes(0)) { /* No music classes configured, so skip it */
1228 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1230 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1236 static int reload(void *mod)
1238 if (init_classes(1))
1239 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1244 static int unload_module(void *mod)
1249 static const char *description(void)
1251 return "Music On Hold Resource";
1254 static const char *key(void)
1256 return ASTERISK_GPL_KEY;
1259 STD_MOD(MOD_0 | NO_USECOUNT | NO_UNLOAD, reload, NULL, NULL);