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$")
40 #include <sys/signal.h>
41 #include <netinet/in.h>
44 #include <sys/ioctl.h>
49 #include "asterisk/zapata.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/file.h"
53 #include "asterisk/channel.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/module.h"
56 #include "asterisk/translate.h"
57 #include "asterisk/say.h"
58 #include "asterisk/musiconhold.h"
59 #include "asterisk/config.h"
60 #include "asterisk/utils.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/linkedlists.h"
65 #define INITIAL_NUM_FILES 8
67 static char *play_moh = "MusicOnHold";
68 static char *wait_moh = "WaitMusicOnHold";
69 static char *set_moh = "SetMusicOnHold";
70 static char *start_moh = "StartMusicOnHold";
71 static char *stop_moh = "StopMusicOnHold";
73 static char *play_moh_syn = "Play Music On Hold indefinitely";
74 static char *wait_moh_syn = "Wait, playing Music On Hold";
75 static char *set_moh_syn = "Set default Music On Hold class";
76 static char *start_moh_syn = "Play Music On Hold";
77 static char *stop_moh_syn = "Stop Playing Music On Hold";
79 static char *play_moh_desc = " MusicOnHold(class):\n"
80 "Plays hold music specified by class. If omitted, the default\n"
81 "music source for the channel will be used. Set the default \n"
82 "class with the SetMusicOnHold() application.\n"
83 "Returns -1 on hangup.\n"
84 "Never returns otherwise.\n";
86 static char *wait_moh_desc = " WaitMusicOnHold(delay):\n"
87 "Plays hold music specified number of seconds. Returns 0 when\n"
88 "done, or -1 on hangup. If no hold music is available, the delay will\n"
89 "still occur with no sound.\n";
91 static char *set_moh_desc = " SetMusicOnHold(class):\n"
92 "Sets the default class for music on hold for a given channel. When\n"
93 "music on hold is activated, this class will be used to select which\n"
96 static char *start_moh_desc = " StartMusicOnHold(class):\n"
97 "Starts playing music on hold, uses default music class for channel.\n"
98 "Starts playing music specified by class. If omitted, the default\n"
99 "music source for the channel will be used. Always returns 0.\n";
101 static char *stop_moh_desc = " StopMusicOnHold(): "
102 "Stops playing music on hold.\n";
104 static int respawn_time = 20;
106 struct moh_files_state {
107 struct mohclass *class;
115 #define MOH_QUIET (1 << 0)
116 #define MOH_SINGLE (1 << 1)
117 #define MOH_CUSTOM (1 << 2)
118 #define MOH_RANDOMIZE (1 << 3)
119 #define MOH_SORTALPHA (1 << 4)
121 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
123 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
126 char name[MAX_MUSICCLASS];
131 /*! A dynamically sized array to hold the list of filenames in "files" mode */
133 /*! The current size of the filearray */
135 /*! The current number of files loaded into the filearray */
138 /*! The format from the MOH source, not applicable to "files" mode */
140 /*! The pid of the external application delivering MOH */
144 /*! Source of audio */
146 /*! FD for timing source */
148 /*! Number of users */
150 /*! Created on the fly, from RT engine */
152 unsigned int delete:1;
153 AST_LIST_HEAD_NOLOCK(, mohdata) members;
154 AST_LIST_ENTRY(mohclass) list;
160 struct mohclass *parent;
162 AST_LIST_ENTRY(mohdata) list;
165 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
167 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
168 #define MPG_123 "/usr/bin/mpg123"
171 static int ast_moh_destroy_one(struct mohclass *moh);
172 static int reload(void);
174 static void ast_moh_free_class(struct mohclass **mohclass)
176 struct mohdata *member;
177 struct mohclass *class = *mohclass;
180 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
184 pthread_cancel(class->thread);
188 if (class->filearray) {
189 for (i = 0; i < class->total_files; i++)
190 ast_free(class->filearray[i]);
191 ast_free(class->filearray);
199 static void moh_files_release(struct ast_channel *chan, void *data)
201 struct moh_files_state *state = chan->music_state;
205 ast_closestream(chan->stream);
208 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
210 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
211 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
213 state->save_pos = state->pos;
215 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
216 ast_moh_destroy_one(state->class);
220 static int ast_moh_files_next(struct ast_channel *chan)
222 struct moh_files_state *state = chan->music_state;
225 /* Discontinue a stream if it is running already */
227 ast_closestream(chan->stream);
231 if (!state->class->total_files) {
232 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
236 /* If a specific file has been saved, use it */
237 if (state->save_pos >= 0) {
238 state->pos = state->save_pos;
239 state->save_pos = -1;
240 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
241 /* Get a random file and ensure we can open it */
242 for (tries = 0; tries < 20; tries++) {
243 state->pos = rand() % state->class->total_files;
244 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
249 /* This is easy, just increment our position and make sure we don't exceed the total file count */
251 state->pos %= state->class->total_files;
255 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
256 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
258 state->pos %= state->class->total_files;
262 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
265 ast_seekstream(chan->stream, state->samples, SEEK_SET);
271 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
273 struct ast_frame *f = NULL;
275 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
276 if (!ast_moh_files_next(chan))
277 f = ast_readframe(chan->stream);
283 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
285 struct moh_files_state *state = chan->music_state;
286 struct ast_frame *f = NULL;
289 state->sample_queue += samples;
291 while (state->sample_queue > 0) {
292 if ((f = moh_files_readframe(chan))) {
293 state->samples += f->samples;
294 res = ast_write(chan, f);
295 state->sample_queue -= f->samples;
298 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
308 static void *moh_files_alloc(struct ast_channel *chan, void *params)
310 struct moh_files_state *state;
311 struct mohclass *class = params;
313 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
314 chan->music_state = state;
315 state->class = class;
316 state->save_pos = -1;
318 state = chan->music_state;
321 if (state->class != class) {
323 memset(state, 0, sizeof(*state));
324 state->class = class;
325 if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
326 state->pos = ast_random() % class->total_files;
329 state->origwfmt = chan->writeformat;
331 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
334 return chan->music_state;
337 /*! \note This function should be called with the mohclasses list locked */
338 static struct mohclass *get_mohbydigit(char digit)
340 struct mohclass *moh = NULL;
342 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
343 if (digit == moh->digit)
350 static void moh_handle_digit(struct ast_channel *chan, char digit)
352 struct mohclass *moh;
353 const char *classname = NULL;
355 AST_RWLIST_RDLOCK(&mohclasses);
356 if ((moh = get_mohbydigit(digit)))
357 classname = ast_strdupa(moh->name);
358 AST_RWLIST_UNLOCK(&mohclasses);
364 ast_moh_start(chan, classname, NULL);
367 static struct ast_generator moh_file_stream =
369 alloc: moh_files_alloc,
370 release: moh_files_release,
371 generate: moh_files_generator,
372 digit: moh_handle_digit,
375 static int spawn_mp3(struct mohclass *class)
379 char fns[MAX_MP3S][80];
380 char *argv[MAX_MP3S + 50];
386 sigset_t signal_set, old_set;
389 if (!strcasecmp(class->dir, "nodir")) {
392 dir = opendir(class->dir);
393 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
394 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
399 if (!ast_test_flag(class, MOH_CUSTOM)) {
400 argv[argc++] = "mpg123";
403 argv[argc++] = "--mono";
405 argv[argc++] = "8000";
407 if (!ast_test_flag(class, MOH_SINGLE)) {
409 argv[argc++] = "2048";
414 if (ast_test_flag(class, MOH_QUIET))
415 argv[argc++] = "4096";
417 argv[argc++] = "8192";
419 /* Look for extra arguments and add them to the list */
420 ast_copy_string(xargs, class->args, sizeof(xargs));
422 while (!ast_strlen_zero(argptr)) {
423 argv[argc++] = argptr;
424 strsep(&argptr, ",");
427 /* Format arguments for argv vector */
428 ast_copy_string(xargs, class->args, sizeof(xargs));
430 while (!ast_strlen_zero(argptr)) {
431 argv[argc++] = argptr;
432 strsep(&argptr, " ");
437 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
438 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
439 argv[argc++] = fns[files];
442 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
443 if ((strlen(de->d_name) > 3) &&
444 ((ast_test_flag(class, MOH_CUSTOM) &&
445 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
446 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
447 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
448 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
449 argv[argc++] = fns[files];
459 ast_log(LOG_WARNING, "Pipe failed\n");
463 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
468 if (time(NULL) - class->start < respawn_time) {
469 sleep(respawn_time - (time(NULL) - class->start));
472 /* Block signals during the fork() */
473 sigfillset(&signal_set);
474 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
478 if (class->pid < 0) {
481 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
487 if (ast_opt_high_priority)
490 /* Reset ignored signals back to default */
491 signal(SIGPIPE, SIG_DFL);
492 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
495 /* Stdout goes to pipe */
496 dup2(fds[1], STDOUT_FILENO);
497 /* Close unused file descriptors */
498 for (x=3;x<8192;x++) {
499 if (-1 != fcntl(x, F_GETFL)) {
505 if (ast_test_flag(class, MOH_CUSTOM)) {
506 execv(argv[0], argv);
508 /* Default install is /usr/local/bin */
509 execv(LOCAL_MPG_123, argv);
510 /* Many places have it in /usr/bin */
511 execv(MPG_123, argv);
512 /* Check PATH as a last-ditch effort */
513 execvp("mpg123", argv);
515 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
520 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
526 static void *monmp3thread(void *data)
528 #define MOH_MS_INTERVAL 100
530 struct mohclass *class = data;
536 struct timeval tv, tv_tmp;
541 pthread_testcancel();
542 /* Spawn mp3 player if it's not there */
543 if (class->srcfd < 0) {
544 if ((class->srcfd = spawn_mp3(class)) < 0) {
545 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
546 /* Try again later */
548 pthread_testcancel();
551 if (class->pseudofd > -1) {
555 /* Pause some amount of time */
556 res = read(class->pseudofd, buf, sizeof(buf));
557 pthread_testcancel();
561 tv_tmp = ast_tvnow();
564 delta = ast_tvdiff_ms(tv_tmp, tv);
565 if (delta < MOH_MS_INTERVAL) { /* too early */
566 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
567 usleep(1000 * (MOH_MS_INTERVAL - delta));
568 pthread_testcancel();
570 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
573 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
575 if (AST_LIST_EMPTY(&class->members))
578 len = ast_codec_get_len(class->format, res);
580 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
584 pthread_testcancel();
585 if (class->pid > 1) {
586 kill(class->pid, SIGHUP);
588 kill(class->pid, SIGTERM);
590 kill(class->pid, SIGKILL);
594 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
598 pthread_testcancel();
599 AST_RWLIST_RDLOCK(&mohclasses);
600 AST_RWLIST_TRAVERSE(&class->members, moh, list) {
602 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
603 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
606 AST_RWLIST_UNLOCK(&mohclasses);
611 static int play_moh_exec(struct ast_channel *chan, void *data)
613 if (ast_moh_start(chan, data, NULL)) {
614 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
617 while (!ast_safe_sleep(chan, 10000));
622 static int wait_moh_exec(struct ast_channel *chan, void *data)
625 if (!data || !atoi(data)) {
626 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
629 if (ast_moh_start(chan, NULL, NULL)) {
630 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
633 res = ast_safe_sleep(chan, atoi(data) * 1000);
638 static int set_moh_exec(struct ast_channel *chan, void *data)
640 if (ast_strlen_zero(data)) {
641 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
644 ast_string_field_set(chan, musicclass, data);
648 static int start_moh_exec(struct ast_channel *chan, void *data)
651 if (data && strlen(data))
653 if (ast_moh_start(chan, class, NULL))
654 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
659 static int stop_moh_exec(struct ast_channel *chan, void *data)
666 /*! \note This function should be called with the mohclasses list locked */
667 static struct mohclass *get_mohbyname(const char *name, int warn)
669 struct mohclass *moh = NULL;
671 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
672 if (!strcasecmp(name, moh->name))
677 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
682 static struct mohdata *mohalloc(struct mohclass *cl)
687 if (!(moh = ast_calloc(1, sizeof(*moh))))
690 if (pipe(moh->pipe)) {
691 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
696 /* Make entirely non-blocking */
697 flags = fcntl(moh->pipe[0], F_GETFL);
698 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
699 flags = fcntl(moh->pipe[1], F_GETFL);
700 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
702 moh->f.frametype = AST_FRAME_VOICE;
703 moh->f.subclass = cl->format;
704 moh->f.offset = AST_FRIENDLY_OFFSET;
708 AST_RWLIST_WRLOCK(&mohclasses);
709 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
710 AST_RWLIST_UNLOCK(&mohclasses);
715 static void moh_release(struct ast_channel *chan, void *data)
717 struct mohdata *moh = data;
719 struct moh_files_state *state;
721 AST_RWLIST_WRLOCK(&mohclasses);
722 AST_RWLIST_REMOVE(&moh->parent->members, moh, list);
723 AST_RWLIST_UNLOCK(&mohclasses);
727 oldwfmt = moh->origwfmt;
728 state = chan->music_state;
729 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
730 ast_moh_destroy_one(moh->parent);
731 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
732 ast_moh_destroy_one(state->class);
736 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
737 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
738 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
742 static void *moh_alloc(struct ast_channel *chan, void *params)
745 struct mohclass *class = params;
746 struct moh_files_state *state;
748 /* Initiating music_state for current channel. Channel should know name of moh class */
749 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
750 chan->music_state = state;
751 memset(state, 0, sizeof(*state));
752 state->class = class;
754 state = chan->music_state;
755 if (state && state->class != class) {
756 memset(state, 0, sizeof(*state));
757 state->class = class;
760 if ((res = mohalloc(class))) {
761 res->origwfmt = chan->writeformat;
762 if (ast_set_write_format(chan, class->format)) {
763 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
764 moh_release(NULL, res);
767 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
772 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
774 struct mohdata *moh = data;
775 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
778 if (!moh->parent->pid)
781 len = ast_codec_get_len(moh->parent->format, samples);
783 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
784 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
785 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
787 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
791 moh->f.datalen = res;
792 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
793 moh->f.samples = ast_codec_get_samples(&moh->f);
795 if (ast_write(chan, &moh->f) < 0) {
796 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
803 static struct ast_generator mohgen =
806 release: moh_release,
807 generate: moh_generate,
808 digit: moh_handle_digit
811 static int moh_add_file(struct mohclass *class, const char *filepath)
813 if (!class->allowed_files) {
814 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
816 class->allowed_files = INITIAL_NUM_FILES;
817 } else if (class->total_files == class->allowed_files) {
818 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
819 class->allowed_files = 0;
820 class->total_files = 0;
823 class->allowed_files *= 2;
826 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
829 class->total_files++;
834 static int moh_sort_compare(const void *i1, const void *i2)
838 s1 = ((char **)i1)[0];
839 s2 = ((char **)i2)[0];
841 return strcasecmp(s1, s2);
844 static int moh_scan_files(struct mohclass *class) {
847 struct dirent *files_dirent;
849 char filepath[PATH_MAX];
855 files_DIR = opendir(class->dir);
857 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
861 for (i = 0; i < class->total_files; i++)
862 ast_free(class->filearray[i]);
864 class->total_files = 0;
865 dirnamelen = strlen(class->dir) + 2;
866 getcwd(path, sizeof(path));
868 while ((files_dirent = readdir(files_DIR))) {
869 /* The file name must be at least long enough to have the file type extension */
870 if ((strlen(files_dirent->d_name) < 4))
873 /* Skip files that starts with a dot */
874 if (files_dirent->d_name[0] == '.')
877 /* Skip files without extensions... they are not audio */
878 if (!strchr(files_dirent->d_name, '.'))
881 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
883 if (stat(filepath, &statbuf))
886 if (!S_ISREG(statbuf.st_mode))
889 if ((ext = strrchr(filepath, '.')))
892 /* if the file is present in multiple formats, ensure we only put it into the list once */
893 for (i = 0; i < class->total_files; i++)
894 if (!strcmp(filepath, class->filearray[i]))
897 if (i == class->total_files) {
898 if (moh_add_file(class, filepath))
905 if (ast_test_flag(class, MOH_SORTALPHA))
906 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
907 return class->total_files;
910 static int moh_diff(struct mohclass *old, struct mohclass *new)
915 if (strcmp(old->dir, new->dir))
917 else if (strcmp(old->mode, new->mode))
919 else if (strcmp(old->args, new->args))
921 else if (old->flags != new->flags)
927 static int moh_register(struct mohclass *moh, int reload)
932 struct mohclass *mohclass = NULL;
934 AST_RWLIST_WRLOCK(&mohclasses);
935 if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
936 mohclass->delete = 0;
938 ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
940 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
943 AST_RWLIST_UNLOCK(&mohclasses);
946 AST_RWLIST_UNLOCK(&mohclasses);
949 moh->start -= respawn_time;
951 if (!strcasecmp(moh->mode, "files")) {
952 if (!moh_scan_files(moh)) {
953 ast_moh_free_class(&moh);
956 if (strchr(moh->args, 'r'))
957 ast_set_flag(moh, MOH_RANDOMIZE);
958 } 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")) {
960 if (!strcasecmp(moh->mode, "custom"))
961 ast_set_flag(moh, MOH_CUSTOM);
962 else if (!strcasecmp(moh->mode, "mp3nb"))
963 ast_set_flag(moh, MOH_SINGLE);
964 else if (!strcasecmp(moh->mode, "quietmp3nb"))
965 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
966 else if (!strcasecmp(moh->mode, "quietmp3"))
967 ast_set_flag(moh, MOH_QUIET);
971 /* Open /dev/zap/pseudo for timing... Is
972 there a better, yet reliable way to do this? */
973 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
974 if (moh->pseudofd < 0) {
975 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
978 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
983 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
984 ast_log(LOG_WARNING, "Unable to create moh...\n");
985 if (moh->pseudofd > -1)
986 close(moh->pseudofd);
987 ast_moh_free_class(&moh);
991 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
992 ast_moh_free_class(&moh);
996 AST_RWLIST_WRLOCK(&mohclasses);
997 AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
998 AST_RWLIST_UNLOCK(&mohclasses);
1003 static void local_ast_moh_cleanup(struct ast_channel *chan)
1005 struct moh_files_state *state = chan->music_state;
1008 if (state->class->realtime) {
1009 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1010 /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
1011 if (!(state->class->inuse)) {
1012 /* Remove this class from list */
1013 AST_RWLIST_WRLOCK(&mohclasses);
1014 AST_RWLIST_REMOVE(&mohclasses, state->class, list);
1015 AST_RWLIST_UNLOCK(&mohclasses);
1017 /* Free some memory */
1018 ast_moh_destroy_one(state->class);
1021 ast_moh_destroy_one(state->class);
1024 ast_free(chan->music_state);
1025 chan->music_state = NULL;
1029 static struct mohclass *moh_class_malloc(void)
1031 struct mohclass *class;
1033 if ((class = ast_calloc(1, sizeof(*class)))) {
1034 class->format = AST_FORMAT_SLINEAR;
1035 class->realtime = 0;
1041 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1043 struct mohclass *mohclass = NULL;
1044 struct ast_variable *var = NULL;
1045 struct ast_variable *tmp = NULL;
1046 struct moh_files_state *state = chan->music_state;
1051 /* The following is the order of preference for which class to use:
1052 * 1) The channels explicitly set musicclass, which should *only* be
1053 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1054 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1055 * result of receiving a HOLD control frame, this should be the
1056 * payload that came with the frame.
1057 * 3) The interpclass argument. This would be from the mohinterpret
1058 * option from channel drivers. This is the same as the old musicclass
1060 * 4) The default class.
1063 /* First, let's check in memory for static and cached RT classes */
1064 AST_RWLIST_RDLOCK(&mohclasses);
1065 if (!ast_strlen_zero(chan->musicclass))
1066 mohclass = get_mohbyname(chan->musicclass, 1);
1067 if (!mohclass && !ast_strlen_zero(mclass))
1068 mohclass = get_mohbyname(mclass, 1);
1069 if (!mohclass && !ast_strlen_zero(interpclass))
1070 mohclass = get_mohbyname(interpclass, 1);
1071 AST_RWLIST_UNLOCK(&mohclasses);
1073 /* If no moh class found in memory, then check RT */
1074 if (!mohclass && ast_check_realtime("musiconhold")) {
1075 if (!ast_strlen_zero(chan->musicclass)) {
1076 var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL);
1078 if (!var && !ast_strlen_zero(mclass))
1079 var = ast_load_realtime("musiconhold", "name", mclass, NULL);
1080 if (!var && !ast_strlen_zero(interpclass))
1081 var = ast_load_realtime("musiconhold", "name", interpclass, NULL);
1083 var = ast_load_realtime("musiconhold", "name", "default", NULL);
1084 if (var && (mohclass = moh_class_malloc())) {
1085 mohclass->realtime = 1;
1086 for (tmp = var; tmp; tmp = tmp->next) {
1087 if (!strcasecmp(tmp->name, "name"))
1088 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1089 else if (!strcasecmp(tmp->name, "mode"))
1090 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1091 else if (!strcasecmp(tmp->name, "directory"))
1092 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1093 else if (!strcasecmp(tmp->name, "application"))
1094 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1095 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1096 mohclass->digit = *tmp->value;
1097 else if (!strcasecmp(tmp->name, "random"))
1098 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1099 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1100 ast_set_flag(mohclass, MOH_RANDOMIZE);
1101 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1102 ast_set_flag(mohclass, MOH_SORTALPHA);
1103 else if (!strcasecmp(tmp->name, "format")) {
1104 mohclass->format = ast_getformatbyname(tmp->value);
1105 if (!mohclass->format) {
1106 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1107 mohclass->format = AST_FORMAT_SLINEAR;
1111 if (ast_strlen_zero(mohclass->dir)) {
1112 if (!strcasecmp(mohclass->mode, "custom")) {
1113 strcpy(mohclass->dir, "nodir");
1115 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1120 if (ast_strlen_zero(mohclass->mode)) {
1121 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1125 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1126 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1131 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1132 /* CACHERTCLASSES enabled, let's add this class to default tree */
1133 if (state && state->class) {
1134 /* Class already exist for this channel */
1135 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1136 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1137 /* we found RT class with the same name, seems like we should continue playing existing one */
1138 ast_moh_free_class(&mohclass);
1139 mohclass = state->class;
1142 moh_register(mohclass, 0);
1145 /* We don't register RT moh class, so let's init it manualy */
1147 time(&mohclass->start);
1148 mohclass->start -= respawn_time;
1150 if (!strcasecmp(mohclass->mode, "files")) {
1151 if (!moh_scan_files(mohclass)) {
1152 ast_moh_free_class(&mohclass);
1155 if (strchr(mohclass->args, 'r'))
1156 ast_set_flag(mohclass, MOH_RANDOMIZE);
1157 } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
1159 if (!strcasecmp(mohclass->mode, "custom"))
1160 ast_set_flag(mohclass, MOH_CUSTOM);
1161 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1162 ast_set_flag(mohclass, MOH_SINGLE);
1163 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1164 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1165 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1166 ast_set_flag(mohclass, MOH_QUIET);
1168 mohclass->srcfd = -1;
1170 /* Open /dev/zap/pseudo for timing... Is
1171 there a better, yet reliable way to do this? */
1172 mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
1173 if (mohclass->pseudofd < 0) {
1174 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1177 ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x);
1180 mohclass->pseudofd = -1;
1182 /* Let's check if this channel already had a moh class before */
1183 if (state && state->class) {
1184 /* Class already exist for this channel */
1185 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1186 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1187 /* we found RT class with the same name, seems like we should continue playing existing one */
1188 ast_moh_free_class(&mohclass);
1189 mohclass = state->class;
1193 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1194 ast_log(LOG_WARNING, "Unable to create moh...\n");
1195 if (mohclass->pseudofd > -1)
1196 close(mohclass->pseudofd);
1197 ast_moh_free_class(&mohclass);
1202 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1203 ast_moh_free_class(&mohclass);
1214 /* Requested MOH class not found, check for 'default' class in musiconhold.conf */
1216 AST_RWLIST_RDLOCK(&mohclasses);
1217 mohclass = get_mohbyname("default", 1);
1219 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1220 AST_RWLIST_UNLOCK(&mohclasses);
1222 AST_RWLIST_RDLOCK(&mohclasses);
1223 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1224 AST_RWLIST_UNLOCK(&mohclasses);
1230 ast_set_flag(chan, AST_FLAG_MOH);
1231 if (mohclass->total_files) {
1232 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1234 return ast_activate_generator(chan, &mohgen, mohclass);
1237 static void local_ast_moh_stop(struct ast_channel *chan)
1239 struct moh_files_state *state = chan->music_state;
1240 ast_clear_flag(chan, AST_FLAG_MOH);
1241 ast_deactivate_generator(chan);
1245 ast_closestream(chan->stream);
1246 chan->stream = NULL;
1251 static int load_moh_classes(int reload)
1253 struct ast_config *cfg;
1254 struct ast_variable *var;
1255 struct mohclass *class;
1258 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1260 cfg = ast_config_load("musiconhold.conf", config_flags);
1262 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1266 AST_RWLIST_WRLOCK(&mohclasses);
1267 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1270 AST_RWLIST_UNLOCK(&mohclasses);
1273 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1275 cat = ast_category_browse(cfg, NULL);
1276 for (; cat; cat = ast_category_browse(cfg, cat)) {
1277 /* Setup common options from [general] section */
1278 if (!strcasecmp(cat, "general")) {
1279 var = ast_variable_browse(cfg, cat);
1281 if (!strcasecmp(var->name, "cachertclasses")) {
1282 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1284 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1289 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1290 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1291 if (!(class = moh_class_malloc()))
1294 ast_copy_string(class->name, cat, sizeof(class->name));
1295 var = ast_variable_browse(cfg, cat);
1297 if (!strcasecmp(var->name, "mode"))
1298 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1299 else if (!strcasecmp(var->name, "directory"))
1300 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1301 else if (!strcasecmp(var->name, "application"))
1302 ast_copy_string(class->args, var->value, sizeof(class->args));
1303 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1304 class->digit = *var->value;
1305 else if (!strcasecmp(var->name, "random"))
1306 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1307 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1308 ast_set_flag(class, MOH_RANDOMIZE);
1309 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1310 ast_set_flag(class, MOH_SORTALPHA);
1311 else if (!strcasecmp(var->name, "format")) {
1312 class->format = ast_getformatbyname(var->value);
1313 if (!class->format) {
1314 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1315 class->format = AST_FORMAT_SLINEAR;
1321 if (ast_strlen_zero(class->dir)) {
1322 if (!strcasecmp(class->mode, "custom")) {
1323 strcpy(class->dir, "nodir");
1325 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1330 if (ast_strlen_zero(class->mode)) {
1331 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1335 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1336 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1341 /* Don't leak a class when it's already registered */
1342 moh_register(class, reload);
1348 ast_config_destroy(cfg);
1353 static int ast_moh_destroy_one(struct mohclass *moh)
1356 int bytes, tbytes = 0, stime = 0, pid = 0;
1360 ast_debug(1, "killing %d!\n", moh->pid);
1361 stime = time(NULL) + 2;
1364 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1365 * to give the process a reason and time enough to kill off its
1372 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1373 tbytes = tbytes + bytes;
1374 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1377 ast_moh_free_class(&moh);
1383 static void ast_moh_destroy(void)
1385 struct mohclass *moh;
1387 ast_verb(2, "Destroying musiconhold processes\n");
1389 AST_RWLIST_WRLOCK(&mohclasses);
1390 while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1391 ast_moh_destroy_one(moh);
1393 AST_RWLIST_UNLOCK(&mohclasses);
1396 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1400 e->command = "moh reload";
1402 "Usage: moh reload\n"
1403 " Reloads the MusicOnHold module.\n"
1404 " Alias for 'module reload res_musiconhold.so'\n";
1410 if (a->argc != e->args)
1411 return CLI_SHOWUSAGE;
1418 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1421 struct mohclass *class;
1425 e->command = "moh show files";
1427 "Usage: moh show files\n"
1428 " Lists all loaded file-based MusicOnHold classes and their\n"
1435 if (a->argc != e->args)
1436 return CLI_SHOWUSAGE;
1438 AST_RWLIST_RDLOCK(&mohclasses);
1439 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1440 if (!class->total_files)
1443 ast_cli(a->fd, "Class: %s\n", class->name);
1444 for (i = 0; i < class->total_files; i++)
1445 ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1447 AST_RWLIST_UNLOCK(&mohclasses);
1452 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1454 struct mohclass *class;
1458 e->command = "moh show classes";
1460 "Usage: moh show classes\n"
1461 " Lists all MusicOnHold classes.\n";
1467 if (a->argc != e->args)
1468 return CLI_SHOWUSAGE;
1470 AST_RWLIST_RDLOCK(&mohclasses);
1471 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1472 ast_cli(a->fd, "Class: %s\n", class->name);
1473 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1474 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1475 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1477 ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1478 if (ast_test_flag(class, MOH_CUSTOM))
1479 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1480 if (strcasecmp(class->mode, "files"))
1481 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1483 AST_RWLIST_UNLOCK(&mohclasses);
1488 static struct ast_cli_entry cli_moh[] = {
1489 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1490 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1491 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1494 static int init_classes(int reload)
1496 struct mohclass *moh;
1498 if (!load_moh_classes(reload)) /* Load classes from config */
1499 return 0; /* Return if nothing is found */
1501 AST_RWLIST_WRLOCK(&mohclasses);
1502 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1503 if (reload && moh->delete) {
1504 AST_RWLIST_REMOVE_CURRENT(list);
1506 ast_moh_destroy_one(moh);
1507 } else if (moh->total_files) {
1508 if (moh_scan_files(moh) <= 0) {
1509 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1511 AST_LIST_REMOVE_CURRENT(list);
1513 ast_moh_destroy_one(moh);
1517 AST_RWLIST_TRAVERSE_SAFE_END
1518 AST_RWLIST_UNLOCK(&mohclasses);
1523 static int load_module(void)
1527 res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1528 ast_register_atexit(ast_moh_destroy);
1529 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1531 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1533 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1535 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1537 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1539 if (!init_classes(0)) { /* No music classes configured, so skip it */
1540 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1542 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1545 return AST_MODULE_LOAD_SUCCESS;
1548 static int reload(void)
1550 if (init_classes(1))
1551 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1556 static int unload_module(void)
1561 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1562 .load = load_module,
1563 .unload = unload_module,