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$")
46 #include <sys/signal.h>
47 #include <netinet/in.h>
51 #include <sys/ioctl.h>
56 #include "asterisk/zapata.h"
58 #include "asterisk/lock.h"
59 #include "asterisk/file.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/channel.h"
62 #include "asterisk/pbx.h"
63 #include "asterisk/options.h"
64 #include "asterisk/module.h"
65 #include "asterisk/translate.h"
66 #include "asterisk/say.h"
67 #include "asterisk/musiconhold.h"
68 #include "asterisk/config.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/cli.h"
71 #include "asterisk/stringfields.h"
72 #include "asterisk/linkedlists.h"
74 #define INITIAL_NUM_FILES 8
76 static char *play_moh = "MusicOnHold";
77 static char *wait_moh = "WaitMusicOnHold";
78 static char *set_moh = "SetMusicOnHold";
79 static char *start_moh = "StartMusicOnHold";
80 static char *stop_moh = "StopMusicOnHold";
82 static char *play_moh_syn = "Play Music On Hold indefinitely";
83 static char *wait_moh_syn = "Wait, playing Music On Hold";
84 static char *set_moh_syn = "Set default Music On Hold class";
85 static char *start_moh_syn = "Play Music On Hold";
86 static char *stop_moh_syn = "Stop Playing Music On Hold";
88 static char *play_moh_desc = "MusicOnHold(class): "
89 "Plays hold music specified by class. If omitted, the default\n"
90 "music source for the channel will be used. Set the default \n"
91 "class with the SetMusicOnHold() application.\n"
92 "Returns -1 on hangup.\n"
93 "Never returns otherwise.\n";
95 static char *wait_moh_desc = "WaitMusicOnHold(delay): "
96 "Plays hold music specified number of seconds. Returns 0 when\n"
97 "done, or -1 on hangup. If no hold music is available, the delay will\n"
98 "still occur with no sound.\n";
100 static char *set_moh_desc = "SetMusicOnHold(class): "
101 "Sets the default class for music on hold for a given channel. When\n"
102 "music on hold is activated, this class will be used to select which\n"
103 "music is played.\n";
105 static char *start_moh_desc = "StartMusicOnHold(class): "
106 "Starts playing music on hold, uses default music class for channel.\n"
107 "Starts playing music specified by class. If omitted, the default\n"
108 "music source for the channel will be used. Always returns 0.\n";
110 static char *stop_moh_desc = "StopMusicOnHold: "
111 "Stops playing music on hold.\n";
113 static int respawn_time = 20;
115 struct moh_files_state {
116 struct mohclass *class;
124 #define MOH_QUIET (1 << 0)
125 #define MOH_SINGLE (1 << 1)
126 #define MOH_CUSTOM (1 << 2)
127 #define MOH_RANDOMIZE (1 << 3)
130 char name[MAX_MUSICCLASS];
135 /*! A dynamically sized array to hold the list of filenames in "files" mode */
137 /*! The current size of the filearray */
139 /*! The current number of files loaded into the filearray */
142 /*! The format from the MOH source, not applicable to "files" mode */
144 /*! The pid of the external application delivering MOH */
148 /*! Source of audio */
150 /*! FD for timing source */
152 /*! Number of users */
154 unsigned int delete:1;
155 AST_LIST_HEAD_NOLOCK(, mohdata) members;
156 AST_LIST_ENTRY(mohclass) list;
162 struct mohclass *parent;
164 AST_LIST_ENTRY(mohdata) list;
167 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
169 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
170 #define MPG_123 "/usr/bin/mpg123"
173 static int ast_moh_destroy_one(struct mohclass *moh);
174 static int reload(void);
176 static void ast_moh_free_class(struct mohclass **mohclass)
178 struct mohdata *member;
179 struct mohclass *class = *mohclass;
182 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
186 pthread_cancel(class->thread);
190 if (class->filearray) {
191 for (i = 0; i < class->total_files; i++)
192 ast_free(class->filearray[i]);
193 ast_free(class->filearray);
201 static void moh_files_release(struct ast_channel *chan, void *data)
203 struct moh_files_state *state = chan->music_state;
207 ast_closestream(chan->stream);
210 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
212 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
213 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
215 state->save_pos = state->pos;
217 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
218 ast_moh_destroy_one(state->class);
222 static int ast_moh_files_next(struct ast_channel *chan)
224 struct moh_files_state *state = chan->music_state;
227 /* Discontinue a stream if it is running already */
229 ast_closestream(chan->stream);
233 /* If a specific file has been saved, use it */
234 if (state->save_pos >= 0) {
235 state->pos = state->save_pos;
236 state->save_pos = -1;
237 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
238 /* Get a random file and ensure we can open it */
239 for (tries = 0; tries < 20; tries++) {
240 state->pos = rand() % state->class->total_files;
241 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
246 /* This is easy, just increment our position and make sure we don't exceed the total file count */
248 state->pos %= state->class->total_files;
252 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
253 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
255 state->pos %= state->class->total_files;
259 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
262 ast_seekstream(chan->stream, state->samples, SEEK_SET);
268 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
270 struct ast_frame *f = NULL;
272 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
273 if (!ast_moh_files_next(chan))
274 f = ast_readframe(chan->stream);
280 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
282 struct moh_files_state *state = chan->music_state;
283 struct ast_frame *f = NULL;
286 state->sample_queue += samples;
288 while (state->sample_queue > 0) {
289 if ((f = moh_files_readframe(chan))) {
290 state->samples += f->samples;
291 res = ast_write(chan, f);
292 state->sample_queue -= f->samples;
295 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
305 static void *moh_files_alloc(struct ast_channel *chan, void *params)
307 struct moh_files_state *state;
308 struct mohclass *class = params;
310 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
311 chan->music_state = state;
312 state->class = class;
314 state = chan->music_state;
317 if (state->class != class) {
319 memset(state, 0, sizeof(*state));
320 state->class = class;
321 if (ast_test_flag(state->class, MOH_RANDOMIZE))
322 state->pos = ast_random() % class->total_files;
325 state->origwfmt = chan->writeformat;
327 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
330 return chan->music_state;
333 /*! \note This function should be called with the mohclasses list locked */
334 static struct mohclass *get_mohbydigit(char digit)
336 struct mohclass *moh = NULL;
338 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
339 if (digit == moh->digit)
346 static void moh_handle_digit(struct ast_channel *chan, char digit)
348 struct mohclass *moh;
349 const char *classname = NULL;
351 AST_RWLIST_RDLOCK(&mohclasses);
352 if ((moh = get_mohbydigit(digit)))
353 classname = ast_strdupa(moh->name);
354 AST_RWLIST_UNLOCK(&mohclasses);
360 ast_moh_start(chan, classname, NULL);
363 static struct ast_generator moh_file_stream =
365 alloc: moh_files_alloc,
366 release: moh_files_release,
367 generate: moh_files_generator,
368 digit: moh_handle_digit,
371 static int spawn_mp3(struct mohclass *class)
375 char fns[MAX_MP3S][80];
376 char *argv[MAX_MP3S + 50];
382 sigset_t signal_set, old_set;
385 if (!strcasecmp(class->dir, "nodir")) {
388 dir = opendir(class->dir);
389 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
390 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
395 if (!ast_test_flag(class, MOH_CUSTOM)) {
396 argv[argc++] = "mpg123";
399 argv[argc++] = "--mono";
401 argv[argc++] = "8000";
403 if (!ast_test_flag(class, MOH_SINGLE)) {
405 argv[argc++] = "2048";
410 if (ast_test_flag(class, MOH_QUIET))
411 argv[argc++] = "4096";
413 argv[argc++] = "8192";
415 /* Look for extra arguments and add them to the list */
416 ast_copy_string(xargs, class->args, sizeof(xargs));
418 while (!ast_strlen_zero(argptr)) {
419 argv[argc++] = argptr;
420 strsep(&argptr, ",");
423 /* Format arguments for argv vector */
424 ast_copy_string(xargs, class->args, sizeof(xargs));
426 while (!ast_strlen_zero(argptr)) {
427 argv[argc++] = argptr;
428 strsep(&argptr, " ");
433 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
434 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
435 argv[argc++] = fns[files];
438 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
439 if ((strlen(de->d_name) > 3) &&
440 ((ast_test_flag(class, MOH_CUSTOM) &&
441 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
442 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
443 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
444 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
445 argv[argc++] = fns[files];
455 ast_log(LOG_WARNING, "Pipe failed\n");
459 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
464 if (time(NULL) - class->start < respawn_time) {
465 sleep(respawn_time - (time(NULL) - class->start));
468 /* Block signals during the fork() */
469 sigfillset(&signal_set);
470 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
474 if (class->pid < 0) {
477 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
483 if (ast_opt_high_priority)
486 /* Reset ignored signals back to default */
487 signal(SIGPIPE, SIG_DFL);
488 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
491 /* Stdout goes to pipe */
492 dup2(fds[1], STDOUT_FILENO);
493 /* Close unused file descriptors */
494 for (x=3;x<8192;x++) {
495 if (-1 != fcntl(x, F_GETFL)) {
501 if (ast_test_flag(class, MOH_CUSTOM)) {
502 execv(argv[0], argv);
504 /* Default install is /usr/local/bin */
505 execv(LOCAL_MPG_123, argv);
506 /* Many places have it in /usr/bin */
507 execv(MPG_123, argv);
508 /* Check PATH as a last-ditch effort */
509 execvp("mpg123", argv);
511 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
516 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
522 static void *monmp3thread(void *data)
524 #define MOH_MS_INTERVAL 100
526 struct mohclass *class = data;
532 struct timeval tv, tv_tmp;
537 pthread_testcancel();
538 /* Spawn mp3 player if it's not there */
539 if (class->srcfd < 0) {
540 if ((class->srcfd = spawn_mp3(class)) < 0) {
541 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
542 /* Try again later */
544 pthread_testcancel();
547 if (class->pseudofd > -1) {
551 /* Pause some amount of time */
552 res = read(class->pseudofd, buf, sizeof(buf));
553 pthread_testcancel();
557 tv_tmp = ast_tvnow();
560 delta = ast_tvdiff_ms(tv_tmp, tv);
561 if (delta < MOH_MS_INTERVAL) { /* too early */
562 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
563 usleep(1000 * (MOH_MS_INTERVAL - delta));
564 pthread_testcancel();
566 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
569 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
571 if (AST_LIST_EMPTY(&class->members))
574 len = ast_codec_get_len(class->format, res);
576 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
580 pthread_testcancel();
581 if (class->pid > 1) {
582 kill(class->pid, SIGHUP);
584 kill(class->pid, SIGTERM);
586 kill(class->pid, SIGKILL);
590 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
594 pthread_testcancel();
595 AST_RWLIST_RDLOCK(&mohclasses);
596 AST_RWLIST_TRAVERSE(&class->members, moh, list) {
598 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
599 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
602 AST_RWLIST_UNLOCK(&mohclasses);
607 static int play_moh_exec(struct ast_channel *chan, void *data)
609 if (ast_moh_start(chan, data, NULL)) {
610 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
613 while (!ast_safe_sleep(chan, 10000));
618 static int wait_moh_exec(struct ast_channel *chan, void *data)
621 if (!data || !atoi(data)) {
622 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
625 if (ast_moh_start(chan, NULL, NULL)) {
626 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
629 res = ast_safe_sleep(chan, atoi(data) * 1000);
634 static int set_moh_exec(struct ast_channel *chan, void *data)
636 if (ast_strlen_zero(data)) {
637 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
640 ast_string_field_set(chan, musicclass, data);
644 static int start_moh_exec(struct ast_channel *chan, void *data)
647 if (data && strlen(data))
649 if (ast_moh_start(chan, class, NULL))
650 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
655 static int stop_moh_exec(struct ast_channel *chan, void *data)
662 /*! \note This function should be called with the mohclasses list locked */
663 static struct mohclass *get_mohbyname(const char *name, int warn)
665 struct mohclass *moh = NULL;
667 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
668 if (!strcasecmp(name, moh->name))
673 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
678 static struct mohdata *mohalloc(struct mohclass *cl)
683 if (!(moh = ast_calloc(1, sizeof(*moh))))
686 if (pipe(moh->pipe)) {
687 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
692 /* Make entirely non-blocking */
693 flags = fcntl(moh->pipe[0], F_GETFL);
694 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
695 flags = fcntl(moh->pipe[1], F_GETFL);
696 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
698 moh->f.frametype = AST_FRAME_VOICE;
699 moh->f.subclass = cl->format;
700 moh->f.offset = AST_FRIENDLY_OFFSET;
704 AST_RWLIST_WRLOCK(&mohclasses);
705 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
706 AST_RWLIST_UNLOCK(&mohclasses);
711 static void moh_release(struct ast_channel *chan, void *data)
713 struct mohdata *moh = data;
716 AST_RWLIST_WRLOCK(&mohclasses);
717 AST_RWLIST_REMOVE(&moh->parent->members, moh, list);
718 AST_RWLIST_UNLOCK(&mohclasses);
722 oldwfmt = moh->origwfmt;
723 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
724 ast_moh_destroy_one(moh->parent);
727 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
728 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
729 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
733 static void *moh_alloc(struct ast_channel *chan, void *params)
736 struct mohclass *class = params;
738 if ((res = mohalloc(class))) {
739 res->origwfmt = chan->writeformat;
740 if (ast_set_write_format(chan, class->format)) {
741 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
742 moh_release(NULL, res);
745 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
750 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
752 struct mohdata *moh = data;
753 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
756 if (!moh->parent->pid)
759 len = ast_codec_get_len(moh->parent->format, samples);
761 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
762 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
763 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
765 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
769 moh->f.datalen = res;
770 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
771 moh->f.samples = ast_codec_get_samples(&moh->f);
773 if (ast_write(chan, &moh->f) < 0) {
774 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
781 static struct ast_generator mohgen =
784 release: moh_release,
785 generate: moh_generate,
786 digit: moh_handle_digit
789 static int moh_add_file(struct mohclass *class, const char *filepath)
791 if (!class->allowed_files) {
792 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
794 class->allowed_files = INITIAL_NUM_FILES;
795 } else if (class->total_files == class->allowed_files) {
796 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
797 class->allowed_files = 0;
798 class->total_files = 0;
801 class->allowed_files *= 2;
804 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
807 class->total_files++;
812 static int moh_scan_files(struct mohclass *class) {
815 struct dirent *files_dirent;
817 char filepath[PATH_MAX];
823 files_DIR = opendir(class->dir);
825 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
829 for (i = 0; i < class->total_files; i++)
830 ast_free(class->filearray[i]);
832 class->total_files = 0;
833 dirnamelen = strlen(class->dir) + 2;
834 getcwd(path, sizeof(path));
836 while ((files_dirent = readdir(files_DIR))) {
837 /* The file name must be at least long enough to have the file type extension */
838 if ((strlen(files_dirent->d_name) < 4))
841 /* Skip files that starts with a dot */
842 if (files_dirent->d_name[0] == '.')
845 /* Skip files without extensions... they are not audio */
846 if (!strchr(files_dirent->d_name, '.'))
849 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
851 if (stat(filepath, &statbuf))
854 if (!S_ISREG(statbuf.st_mode))
857 if ((ext = strrchr(filepath, '.'))) {
862 /* if the file is present in multiple formats, ensure we only put it into the list once */
863 for (i = 0; i < class->total_files; i++)
864 if (!strcmp(filepath, class->filearray[i]))
867 if (i == class->total_files) {
868 if (moh_add_file(class, filepath))
875 return class->total_files;
878 static int moh_register(struct mohclass *moh, int reload)
883 struct mohclass *mohclass = NULL;
885 AST_RWLIST_WRLOCK(&mohclasses);
886 if ((mohclass = get_mohbyname(moh->name, 0))) {
887 mohclass->delete = 0;
889 ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
891 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
894 AST_RWLIST_UNLOCK(&mohclasses);
897 AST_RWLIST_UNLOCK(&mohclasses);
900 moh->start -= respawn_time;
902 if (!strcasecmp(moh->mode, "files")) {
903 if (!moh_scan_files(moh)) {
904 ast_moh_free_class(&moh);
907 if (strchr(moh->args, 'r'))
908 ast_set_flag(moh, MOH_RANDOMIZE);
909 } 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")) {
911 if (!strcasecmp(moh->mode, "custom"))
912 ast_set_flag(moh, MOH_CUSTOM);
913 else if (!strcasecmp(moh->mode, "mp3nb"))
914 ast_set_flag(moh, MOH_SINGLE);
915 else if (!strcasecmp(moh->mode, "quietmp3nb"))
916 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
917 else if (!strcasecmp(moh->mode, "quietmp3"))
918 ast_set_flag(moh, MOH_QUIET);
922 /* Open /dev/zap/pseudo for timing... Is
923 there a better, yet reliable way to do this? */
924 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
925 if (moh->pseudofd < 0) {
926 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
929 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
934 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
935 ast_log(LOG_WARNING, "Unable to create moh...\n");
936 if (moh->pseudofd > -1)
937 close(moh->pseudofd);
938 ast_moh_free_class(&moh);
942 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
943 ast_moh_free_class(&moh);
947 AST_RWLIST_WRLOCK(&mohclasses);
948 AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
949 AST_RWLIST_UNLOCK(&mohclasses);
954 static void local_ast_moh_cleanup(struct ast_channel *chan)
956 if (chan->music_state) {
957 ast_free(chan->music_state);
958 chan->music_state = NULL;
962 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
964 struct mohclass *mohclass = NULL;
966 /* The following is the order of preference for which class to use:
967 * 1) The channels explicitly set musicclass, which should *only* be
968 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
969 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
970 * result of receiving a HOLD control frame, this should be the
971 * payload that came with the frame.
972 * 3) The interpclass argument. This would be from the mohinterpret
973 * option from channel drivers. This is the same as the old musicclass
975 * 4) The default class.
977 AST_RWLIST_RDLOCK(&mohclasses);
978 if (!ast_strlen_zero(chan->musicclass))
979 mohclass = get_mohbyname(chan->musicclass, 1);
980 if (!mohclass && !ast_strlen_zero(mclass))
981 mohclass = get_mohbyname(mclass, 1);
982 if (!mohclass && !ast_strlen_zero(interpclass))
983 mohclass = get_mohbyname(interpclass, 1);
985 mohclass = get_mohbyname("default", 1);
987 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
988 AST_RWLIST_UNLOCK(&mohclasses);
993 ast_set_flag(chan, AST_FLAG_MOH);
994 if (mohclass->total_files) {
995 return ast_activate_generator(chan, &moh_file_stream, mohclass);
997 return ast_activate_generator(chan, &mohgen, mohclass);
1000 static void local_ast_moh_stop(struct ast_channel *chan)
1002 ast_clear_flag(chan, AST_FLAG_MOH);
1003 ast_deactivate_generator(chan);
1005 if (chan->music_state) {
1007 ast_closestream(chan->stream);
1008 chan->stream = NULL;
1013 static struct mohclass *moh_class_malloc(void)
1015 struct mohclass *class;
1017 if ((class = ast_calloc(1, sizeof(*class))))
1018 class->format = AST_FORMAT_SLINEAR;
1023 static int load_moh_classes(int reload)
1025 struct ast_config *cfg;
1026 struct ast_variable *var;
1027 struct mohclass *class;
1030 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1032 cfg = ast_config_load("musiconhold.conf", config_flags);
1034 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1038 AST_RWLIST_WRLOCK(&mohclasses);
1039 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1042 AST_RWLIST_UNLOCK(&mohclasses);
1045 cat = ast_category_browse(cfg, NULL);
1046 for (; cat; cat = ast_category_browse(cfg, cat)) {
1047 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1048 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
1049 if (!(class = moh_class_malloc()))
1052 ast_copy_string(class->name, cat, sizeof(class->name));
1053 var = ast_variable_browse(cfg, cat);
1055 if (!strcasecmp(var->name, "mode"))
1056 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1057 else if (!strcasecmp(var->name, "directory"))
1058 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1059 else if (!strcasecmp(var->name, "application"))
1060 ast_copy_string(class->args, var->value, sizeof(class->args));
1061 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1062 class->digit = *var->value;
1063 else if (!strcasecmp(var->name, "random"))
1064 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1065 else if (!strcasecmp(var->name, "format")) {
1066 class->format = ast_getformatbyname(var->value);
1067 if (!class->format) {
1068 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1069 class->format = AST_FORMAT_SLINEAR;
1075 if (ast_strlen_zero(class->dir)) {
1076 if (!strcasecmp(class->mode, "custom")) {
1077 strcpy(class->dir, "nodir");
1079 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1084 if (ast_strlen_zero(class->mode)) {
1085 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1089 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1090 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1095 /* Don't leak a class when it's already registered */
1096 moh_register(class, reload);
1102 ast_config_destroy(cfg);
1107 static int ast_moh_destroy_one(struct mohclass *moh)
1110 int bytes, tbytes = 0, stime = 0, pid = 0;
1114 ast_debug(1, "killing %d!\n", moh->pid);
1115 stime = time(NULL) + 2;
1118 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1119 * to give the process a reason and time enough to kill off its
1126 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1127 tbytes = tbytes + bytes;
1128 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1131 ast_moh_free_class(&moh);
1137 static void ast_moh_destroy(void)
1139 struct mohclass *moh;
1141 ast_verb(2, "Destroying musiconhold processes\n");
1143 AST_RWLIST_WRLOCK(&mohclasses);
1144 while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1145 ast_moh_destroy_one(moh);
1147 AST_RWLIST_UNLOCK(&mohclasses);
1150 static int moh_cli(int fd, int argc, char *argv[])
1157 static int cli_files_show(int fd, int argc, char *argv[])
1160 struct mohclass *class;
1162 AST_RWLIST_RDLOCK(&mohclasses);
1163 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1164 if (!class->total_files)
1167 ast_cli(fd, "Class: %s\n", class->name);
1168 for (i = 0; i < class->total_files; i++)
1169 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1171 AST_RWLIST_UNLOCK(&mohclasses);
1176 static int moh_classes_show(int fd, int argc, char *argv[])
1178 struct mohclass *class;
1180 AST_RWLIST_RDLOCK(&mohclasses);
1181 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1182 ast_cli(fd, "Class: %s\n", class->name);
1183 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1184 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1185 ast_cli(fd, "\tUse Count: %d\n", class->inuse);
1187 ast_cli(fd, "\tDigit: %c\n", class->digit);
1188 if (ast_test_flag(class, MOH_CUSTOM))
1189 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1190 if (strcasecmp(class->mode, "files"))
1191 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1193 AST_RWLIST_UNLOCK(&mohclasses);
1198 static struct ast_cli_entry cli_moh[] = {
1199 { { "moh", "reload"},
1200 moh_cli, "Music On Hold",
1203 { { "moh", "show", "classes"},
1204 moh_classes_show, "List MOH classes",
1205 "Lists all MOH classes" },
1207 { { "moh", "show", "files"},
1208 cli_files_show, "List MOH file-based classes",
1209 "Lists all loaded file-based MOH classes and their files" },
1212 static int init_classes(int reload)
1214 struct mohclass *moh;
1216 if (!load_moh_classes(reload)) /* Load classes from config */
1217 return 0; /* Return if nothing is found */
1219 AST_RWLIST_WRLOCK(&mohclasses);
1220 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1221 if (reload && moh->delete) {
1222 AST_RWLIST_REMOVE_CURRENT(&mohclasses, list);
1224 ast_moh_destroy_one(moh);
1225 } else if (moh->total_files)
1226 moh_scan_files(moh);
1228 AST_RWLIST_TRAVERSE_SAFE_END
1229 AST_RWLIST_UNLOCK(&mohclasses);
1234 static int load_module(void)
1238 res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1239 ast_register_atexit(ast_moh_destroy);
1240 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1242 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1244 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1246 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1248 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1250 if (!init_classes(0)) { /* No music classes configured, so skip it */
1251 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1253 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1259 static int reload(void)
1261 if (init_classes(1))
1262 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1267 static int unload_module(void)
1272 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1273 .load = load_module,
1274 .unload = unload_module,