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 ast_variables_destroy(var);
1112 if (ast_strlen_zero(mohclass->dir)) {
1113 if (!strcasecmp(mohclass->mode, "custom")) {
1114 strcpy(mohclass->dir, "nodir");
1116 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1121 if (ast_strlen_zero(mohclass->mode)) {
1122 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1126 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1127 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1132 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1133 /* CACHERTCLASSES enabled, let's add this class to default tree */
1134 if (state && state->class) {
1135 /* Class already exist for this channel */
1136 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1137 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1138 /* we found RT class with the same name, seems like we should continue playing existing one */
1139 ast_moh_free_class(&mohclass);
1140 mohclass = state->class;
1143 moh_register(mohclass, 0);
1146 /* We don't register RT moh class, so let's init it manualy */
1148 time(&mohclass->start);
1149 mohclass->start -= respawn_time;
1151 if (!strcasecmp(mohclass->mode, "files")) {
1152 if (!moh_scan_files(mohclass)) {
1153 ast_moh_free_class(&mohclass);
1156 if (strchr(mohclass->args, 'r'))
1157 ast_set_flag(mohclass, MOH_RANDOMIZE);
1158 } 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")) {
1160 if (!strcasecmp(mohclass->mode, "custom"))
1161 ast_set_flag(mohclass, MOH_CUSTOM);
1162 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1163 ast_set_flag(mohclass, MOH_SINGLE);
1164 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1165 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1166 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1167 ast_set_flag(mohclass, MOH_QUIET);
1169 mohclass->srcfd = -1;
1171 /* Open /dev/zap/pseudo for timing... Is
1172 there a better, yet reliable way to do this? */
1173 mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
1174 if (mohclass->pseudofd < 0) {
1175 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1178 ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x);
1181 mohclass->pseudofd = -1;
1183 /* Let's check if this channel already had a moh class before */
1184 if (state && state->class) {
1185 /* Class already exist for this channel */
1186 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1187 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1188 /* we found RT class with the same name, seems like we should continue playing existing one */
1189 ast_moh_free_class(&mohclass);
1190 mohclass = state->class;
1194 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1195 ast_log(LOG_WARNING, "Unable to create moh...\n");
1196 if (mohclass->pseudofd > -1)
1197 close(mohclass->pseudofd);
1198 ast_moh_free_class(&mohclass);
1203 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1204 ast_moh_free_class(&mohclass);
1211 ast_variables_destroy(var);
1216 /* Requested MOH class not found, check for 'default' class in musiconhold.conf */
1218 AST_RWLIST_RDLOCK(&mohclasses);
1219 mohclass = get_mohbyname("default", 1);
1221 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1222 AST_RWLIST_UNLOCK(&mohclasses);
1224 AST_RWLIST_RDLOCK(&mohclasses);
1225 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1226 AST_RWLIST_UNLOCK(&mohclasses);
1232 ast_set_flag(chan, AST_FLAG_MOH);
1233 if (mohclass->total_files) {
1234 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1236 return ast_activate_generator(chan, &mohgen, mohclass);
1239 static void local_ast_moh_stop(struct ast_channel *chan)
1241 struct moh_files_state *state = chan->music_state;
1242 ast_clear_flag(chan, AST_FLAG_MOH);
1243 ast_deactivate_generator(chan);
1247 ast_closestream(chan->stream);
1248 chan->stream = NULL;
1253 static int load_moh_classes(int reload)
1255 struct ast_config *cfg;
1256 struct ast_variable *var;
1257 struct mohclass *class;
1260 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1262 cfg = ast_config_load("musiconhold.conf", config_flags);
1264 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1268 AST_RWLIST_WRLOCK(&mohclasses);
1269 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1272 AST_RWLIST_UNLOCK(&mohclasses);
1275 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1277 cat = ast_category_browse(cfg, NULL);
1278 for (; cat; cat = ast_category_browse(cfg, cat)) {
1279 /* Setup common options from [general] section */
1280 if (!strcasecmp(cat, "general")) {
1281 var = ast_variable_browse(cfg, cat);
1283 if (!strcasecmp(var->name, "cachertclasses")) {
1284 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1286 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1291 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1292 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1293 if (!(class = moh_class_malloc()))
1296 ast_copy_string(class->name, cat, sizeof(class->name));
1297 var = ast_variable_browse(cfg, cat);
1299 if (!strcasecmp(var->name, "mode"))
1300 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1301 else if (!strcasecmp(var->name, "directory"))
1302 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1303 else if (!strcasecmp(var->name, "application"))
1304 ast_copy_string(class->args, var->value, sizeof(class->args));
1305 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1306 class->digit = *var->value;
1307 else if (!strcasecmp(var->name, "random"))
1308 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1309 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1310 ast_set_flag(class, MOH_RANDOMIZE);
1311 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1312 ast_set_flag(class, MOH_SORTALPHA);
1313 else if (!strcasecmp(var->name, "format")) {
1314 class->format = ast_getformatbyname(var->value);
1315 if (!class->format) {
1316 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1317 class->format = AST_FORMAT_SLINEAR;
1323 if (ast_strlen_zero(class->dir)) {
1324 if (!strcasecmp(class->mode, "custom")) {
1325 strcpy(class->dir, "nodir");
1327 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1332 if (ast_strlen_zero(class->mode)) {
1333 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1337 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1338 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1343 /* Don't leak a class when it's already registered */
1344 moh_register(class, reload);
1350 ast_config_destroy(cfg);
1355 static int ast_moh_destroy_one(struct mohclass *moh)
1358 int bytes, tbytes = 0, stime = 0, pid = 0;
1362 ast_debug(1, "killing %d!\n", moh->pid);
1363 stime = time(NULL) + 2;
1366 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1367 * to give the process a reason and time enough to kill off its
1374 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1375 tbytes = tbytes + bytes;
1376 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1379 ast_moh_free_class(&moh);
1385 static void ast_moh_destroy(void)
1387 struct mohclass *moh;
1389 ast_verb(2, "Destroying musiconhold processes\n");
1391 AST_RWLIST_WRLOCK(&mohclasses);
1392 while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1393 ast_moh_destroy_one(moh);
1395 AST_RWLIST_UNLOCK(&mohclasses);
1398 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1402 e->command = "moh reload";
1404 "Usage: moh reload\n"
1405 " Reloads the MusicOnHold module.\n"
1406 " Alias for 'module reload res_musiconhold.so'\n";
1412 if (a->argc != e->args)
1413 return CLI_SHOWUSAGE;
1420 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1423 struct mohclass *class;
1427 e->command = "moh show files";
1429 "Usage: moh show files\n"
1430 " Lists all loaded file-based MusicOnHold classes and their\n"
1437 if (a->argc != e->args)
1438 return CLI_SHOWUSAGE;
1440 AST_RWLIST_RDLOCK(&mohclasses);
1441 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1442 if (!class->total_files)
1445 ast_cli(a->fd, "Class: %s\n", class->name);
1446 for (i = 0; i < class->total_files; i++)
1447 ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1449 AST_RWLIST_UNLOCK(&mohclasses);
1454 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1456 struct mohclass *class;
1460 e->command = "moh show classes";
1462 "Usage: moh show classes\n"
1463 " Lists all MusicOnHold classes.\n";
1469 if (a->argc != e->args)
1470 return CLI_SHOWUSAGE;
1472 AST_RWLIST_RDLOCK(&mohclasses);
1473 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1474 ast_cli(a->fd, "Class: %s\n", class->name);
1475 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1476 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1477 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1479 ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1480 if (ast_test_flag(class, MOH_CUSTOM))
1481 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1482 if (strcasecmp(class->mode, "files"))
1483 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1485 AST_RWLIST_UNLOCK(&mohclasses);
1490 static struct ast_cli_entry cli_moh[] = {
1491 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1492 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1493 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1496 static int init_classes(int reload)
1498 struct mohclass *moh;
1500 if (!load_moh_classes(reload)) /* Load classes from config */
1501 return 0; /* Return if nothing is found */
1503 AST_RWLIST_WRLOCK(&mohclasses);
1504 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1505 if (reload && moh->delete) {
1506 AST_RWLIST_REMOVE_CURRENT(list);
1508 ast_moh_destroy_one(moh);
1509 } else if (moh->total_files) {
1510 if (moh_scan_files(moh) <= 0) {
1511 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1513 AST_LIST_REMOVE_CURRENT(list);
1515 ast_moh_destroy_one(moh);
1519 AST_RWLIST_TRAVERSE_SAFE_END
1520 AST_RWLIST_UNLOCK(&mohclasses);
1525 static int load_module(void)
1529 res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1530 ast_register_atexit(ast_moh_destroy);
1531 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1533 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1535 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1537 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1539 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1541 if (!init_classes(0)) { /* No music classes configured, so skip it */
1542 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1544 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1547 return AST_MODULE_LOAD_SUCCESS;
1550 static int reload(void)
1552 if (init_classes(1))
1553 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1558 static int unload_module(void)
1563 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1564 .load = load_module,
1565 .unload = unload_module,