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/app.h"
56 #include "asterisk/module.h"
57 #include "asterisk/translate.h"
58 #include "asterisk/say.h"
59 #include "asterisk/musiconhold.h"
60 #include "asterisk/config.h"
61 #include "asterisk/utils.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/stringfields.h"
64 #include "asterisk/linkedlists.h"
66 #define INITIAL_NUM_FILES 8
68 static char *play_moh = "MusicOnHold";
69 static char *wait_moh = "WaitMusicOnHold";
70 static char *set_moh = "SetMusicOnHold";
71 static char *start_moh = "StartMusicOnHold";
72 static char *stop_moh = "StopMusicOnHold";
74 static char *play_moh_syn = "Play Music On Hold indefinitely";
75 static char *wait_moh_syn = "Wait, playing Music On Hold";
76 static char *set_moh_syn = "Set default Music On Hold class";
77 static char *start_moh_syn = "Play Music On Hold";
78 static char *stop_moh_syn = "Stop Playing Music On Hold";
80 static char *play_moh_desc = " MusicOnHold(class[,duration]):\n"
81 "Plays hold music specified by class. If omitted, the default\n"
82 "music source for the channel will be used. Change the default \n"
83 "class with Set(CHANNEL(musicclass)=...).\n"
84 "If duration is given, hold music will be played specified number\n"
85 "of seconds. If duration is ommited, music plays indefinitely.\n"
86 "Returns 0 when done, -1 on hangup.\n";
88 static char *wait_moh_desc = " WaitMusicOnHold(delay):\n"
90 " !!! DEPRECATED. Use MusicOnHold instead !!!\n"
92 "Plays hold music specified number of seconds. Returns 0 when\n"
93 "done, or -1 on hangup. If no hold music is available, the delay will\n"
94 "still occur with no sound.\n"
96 " !!! DEPRECATED. Use MusicOnHold instead !!!\n";
98 static char *set_moh_desc = " SetMusicOnHold(class):\n"
100 " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n"
102 "Sets the default class for music on hold for a given channel. When\n"
103 "music on hold is activated, this class will be used to select which\n"
106 " !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n";
108 static char *start_moh_desc = " StartMusicOnHold(class):\n"
109 "Starts playing music on hold, uses default music class for channel.\n"
110 "Starts playing music specified by class. If omitted, the default\n"
111 "music source for the channel will be used. Always returns 0.\n";
113 static char *stop_moh_desc = " StopMusicOnHold(): "
114 "Stops playing music on hold.\n";
116 static int respawn_time = 20;
118 struct moh_files_state {
119 struct mohclass *class;
127 #define MOH_QUIET (1 << 0)
128 #define MOH_SINGLE (1 << 1)
129 #define MOH_CUSTOM (1 << 2)
130 #define MOH_RANDOMIZE (1 << 3)
131 #define MOH_SORTALPHA (1 << 4)
133 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
135 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
138 char name[MAX_MUSICCLASS];
143 /*! A dynamically sized array to hold the list of filenames in "files" mode */
145 /*! The current size of the filearray */
147 /*! The current number of files loaded into the filearray */
150 /*! The format from the MOH source, not applicable to "files" mode */
152 /*! The pid of the external application delivering MOH */
156 /*! Source of audio */
158 /*! FD for timing source */
160 /*! Number of users */
162 /*! Created on the fly, from RT engine */
164 unsigned int delete:1;
165 AST_LIST_HEAD_NOLOCK(, mohdata) members;
166 AST_LIST_ENTRY(mohclass) list;
172 struct mohclass *parent;
174 AST_LIST_ENTRY(mohdata) list;
177 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
179 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
180 #define MPG_123 "/usr/bin/mpg123"
183 static int ast_moh_destroy_one(struct mohclass *moh);
184 static int reload(void);
186 static void ast_moh_free_class(struct mohclass **mohclass)
188 struct mohdata *member;
189 struct mohclass *class = *mohclass;
192 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
196 pthread_cancel(class->thread);
200 if (class->filearray) {
201 for (i = 0; i < class->total_files; i++)
202 ast_free(class->filearray[i]);
203 ast_free(class->filearray);
211 static void moh_files_release(struct ast_channel *chan, void *data)
213 struct moh_files_state *state;
216 if ((state = chan->music_state)) {
218 ast_closestream(chan->stream);
221 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
223 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
224 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
226 state->save_pos = state->pos;
228 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
229 ast_moh_destroy_one(state->class);
235 static int ast_moh_files_next(struct ast_channel *chan)
237 struct moh_files_state *state = chan->music_state;
240 /* Discontinue a stream if it is running already */
242 ast_closestream(chan->stream);
246 if (!state->class->total_files) {
247 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
251 /* If a specific file has been saved, use it */
252 if (state->save_pos >= 0) {
253 state->pos = state->save_pos;
254 state->save_pos = -1;
255 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
256 /* Get a random file and ensure we can open it */
257 for (tries = 0; tries < 20; tries++) {
258 state->pos = rand() % state->class->total_files;
259 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
264 /* This is easy, just increment our position and make sure we don't exceed the total file count */
266 state->pos %= state->class->total_files;
270 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
271 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
273 state->pos %= state->class->total_files;
277 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
280 ast_seekstream(chan->stream, state->samples, SEEK_SET);
286 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
288 struct ast_frame *f = NULL;
290 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
291 if (!ast_moh_files_next(chan))
292 f = ast_readframe(chan->stream);
298 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
300 struct moh_files_state *state = chan->music_state;
301 struct ast_frame *f = NULL;
304 state->sample_queue += samples;
306 while (state->sample_queue > 0) {
307 if ((f = moh_files_readframe(chan))) {
308 state->samples += f->samples;
309 res = ast_write(chan, f);
310 state->sample_queue -= f->samples;
313 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
323 static void *moh_files_alloc(struct ast_channel *chan, void *params)
325 struct moh_files_state *state;
326 struct mohclass *class = params;
328 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
329 chan->music_state = state;
330 state->class = class;
331 state->save_pos = -1;
333 state = chan->music_state;
336 if (state->class != class) {
338 memset(state, 0, sizeof(*state));
339 state->class = class;
340 if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
341 state->pos = ast_random() % class->total_files;
344 state->origwfmt = chan->writeformat;
346 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
349 return chan->music_state;
352 /*! \note This function should be called with the mohclasses list locked */
353 static struct mohclass *get_mohbydigit(char digit)
355 struct mohclass *moh = NULL;
357 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
358 if (digit == moh->digit)
365 static void moh_handle_digit(struct ast_channel *chan, char digit)
367 struct mohclass *moh;
368 const char *classname = NULL;
370 AST_RWLIST_RDLOCK(&mohclasses);
371 if ((moh = get_mohbydigit(digit)))
372 classname = ast_strdupa(moh->name);
373 AST_RWLIST_UNLOCK(&mohclasses);
379 ast_moh_start(chan, classname, NULL);
382 static struct ast_generator moh_file_stream =
384 alloc: moh_files_alloc,
385 release: moh_files_release,
386 generate: moh_files_generator,
387 digit: moh_handle_digit,
390 static int spawn_mp3(struct mohclass *class)
394 char fns[MAX_MP3S][80];
395 char *argv[MAX_MP3S + 50];
401 sigset_t signal_set, old_set;
404 if (!strcasecmp(class->dir, "nodir")) {
407 dir = opendir(class->dir);
408 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
409 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
414 if (!ast_test_flag(class, MOH_CUSTOM)) {
415 argv[argc++] = "mpg123";
418 argv[argc++] = "--mono";
420 argv[argc++] = "8000";
422 if (!ast_test_flag(class, MOH_SINGLE)) {
424 argv[argc++] = "2048";
429 if (ast_test_flag(class, MOH_QUIET))
430 argv[argc++] = "4096";
432 argv[argc++] = "8192";
434 /* Look for extra arguments and add them to the list */
435 ast_copy_string(xargs, class->args, sizeof(xargs));
437 while (!ast_strlen_zero(argptr)) {
438 argv[argc++] = argptr;
439 strsep(&argptr, ",");
442 /* Format arguments for argv vector */
443 ast_copy_string(xargs, class->args, sizeof(xargs));
445 while (!ast_strlen_zero(argptr)) {
446 argv[argc++] = argptr;
447 strsep(&argptr, " ");
452 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
453 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
454 argv[argc++] = fns[files];
457 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
458 if ((strlen(de->d_name) > 3) &&
459 ((ast_test_flag(class, MOH_CUSTOM) &&
460 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
461 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
462 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
463 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
464 argv[argc++] = fns[files];
474 ast_log(LOG_WARNING, "Pipe failed\n");
478 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
483 if (time(NULL) - class->start < respawn_time) {
484 sleep(respawn_time - (time(NULL) - class->start));
487 /* Block signals during the fork() */
488 sigfillset(&signal_set);
489 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
493 if (class->pid < 0) {
496 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
502 if (ast_opt_high_priority)
505 /* Reset ignored signals back to default */
506 signal(SIGPIPE, SIG_DFL);
507 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
510 /* Stdout goes to pipe */
511 dup2(fds[1], STDOUT_FILENO);
512 /* Close unused file descriptors */
513 for (x=3;x<8192;x++) {
514 if (-1 != fcntl(x, F_GETFL)) {
520 if (ast_test_flag(class, MOH_CUSTOM)) {
521 execv(argv[0], argv);
523 /* Default install is /usr/local/bin */
524 execv(LOCAL_MPG_123, argv);
525 /* Many places have it in /usr/bin */
526 execv(MPG_123, argv);
527 /* Check PATH as a last-ditch effort */
528 execvp("mpg123", argv);
530 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
535 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
541 static void *monmp3thread(void *data)
543 #define MOH_MS_INTERVAL 100
545 struct mohclass *class = data;
551 struct timeval tv, tv_tmp;
556 pthread_testcancel();
557 /* Spawn mp3 player if it's not there */
558 if (class->srcfd < 0) {
559 if ((class->srcfd = spawn_mp3(class)) < 0) {
560 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
561 /* Try again later */
563 pthread_testcancel();
566 if (class->pseudofd > -1) {
570 /* Pause some amount of time */
571 res = read(class->pseudofd, buf, sizeof(buf));
572 pthread_testcancel();
576 tv_tmp = ast_tvnow();
579 delta = ast_tvdiff_ms(tv_tmp, tv);
580 if (delta < MOH_MS_INTERVAL) { /* too early */
581 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
582 usleep(1000 * (MOH_MS_INTERVAL - delta));
583 pthread_testcancel();
585 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
588 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
590 if (AST_LIST_EMPTY(&class->members))
593 len = ast_codec_get_len(class->format, res);
595 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
599 pthread_testcancel();
600 if (class->pid > 1) {
601 kill(class->pid, SIGHUP);
603 kill(class->pid, SIGTERM);
605 kill(class->pid, SIGKILL);
609 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
613 pthread_testcancel();
614 AST_RWLIST_RDLOCK(&mohclasses);
615 AST_RWLIST_TRAVERSE(&class->members, moh, list) {
617 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
618 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
621 AST_RWLIST_UNLOCK(&mohclasses);
626 static int play_moh_exec(struct ast_channel *chan, void *data)
632 AST_DECLARE_APP_ARGS(args,
634 AST_APP_ARG(duration);
637 parse = ast_strdupa(data);
639 AST_STANDARD_APP_ARGS(args, parse);
641 if (!ast_strlen_zero(args.duration)) {
642 if (sscanf(args.duration, "%d", &timeout) == 1) {
645 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
649 class = S_OR(args.class, NULL);
650 if (ast_moh_start(chan, class, NULL)) {
651 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
656 res = ast_safe_sleep(chan, timeout);
658 while (!(res = ast_safe_sleep(chan, 10000)));
666 static int wait_moh_exec(struct ast_channel *chan, void *data)
668 static int deprecation_warning = 0;
671 if (!deprecation_warning) {
672 deprecation_warning = 1;
673 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
676 if (!data || !atoi(data)) {
677 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
680 if (ast_moh_start(chan, NULL, NULL)) {
681 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
684 res = ast_safe_sleep(chan, atoi(data) * 1000);
689 static int set_moh_exec(struct ast_channel *chan, void *data)
691 static int deprecation_warning = 0;
693 if (!deprecation_warning) {
694 deprecation_warning = 1;
695 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
698 if (ast_strlen_zero(data)) {
699 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
702 ast_string_field_set(chan, musicclass, data);
706 static int start_moh_exec(struct ast_channel *chan, void *data)
710 AST_DECLARE_APP_ARGS(args,
714 parse = ast_strdupa(data);
716 AST_STANDARD_APP_ARGS(args, parse);
718 class = S_OR(args.class, NULL);
719 if (ast_moh_start(chan, class, NULL))
720 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
725 static int stop_moh_exec(struct ast_channel *chan, void *data)
732 /*! \note This function should be called with the mohclasses list locked */
733 static struct mohclass *get_mohbyname(const char *name, int warn)
735 struct mohclass *moh = NULL;
737 AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
738 if (!strcasecmp(name, moh->name))
743 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
748 static struct mohdata *mohalloc(struct mohclass *cl)
753 if (!(moh = ast_calloc(1, sizeof(*moh))))
756 if (pipe(moh->pipe)) {
757 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
762 /* Make entirely non-blocking */
763 flags = fcntl(moh->pipe[0], F_GETFL);
764 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
765 flags = fcntl(moh->pipe[1], F_GETFL);
766 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
768 moh->f.frametype = AST_FRAME_VOICE;
769 moh->f.subclass = cl->format;
770 moh->f.offset = AST_FRIENDLY_OFFSET;
774 AST_RWLIST_WRLOCK(&mohclasses);
775 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
776 AST_RWLIST_UNLOCK(&mohclasses);
781 static void moh_release(struct ast_channel *chan, void *data)
783 struct mohdata *moh = data;
785 struct moh_files_state *state;
787 AST_RWLIST_WRLOCK(&mohclasses);
788 AST_RWLIST_REMOVE(&moh->parent->members, moh, list);
789 AST_RWLIST_UNLOCK(&mohclasses);
793 oldwfmt = moh->origwfmt;
794 state = chan->music_state;
795 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
796 ast_moh_destroy_one(moh->parent);
797 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
798 ast_moh_destroy_one(state->class);
802 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
803 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
804 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
808 static void *moh_alloc(struct ast_channel *chan, void *params)
811 struct mohclass *class = params;
812 struct moh_files_state *state;
814 /* Initiating music_state for current channel. Channel should know name of moh class */
815 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
816 chan->music_state = state;
817 state->class = class;
819 state = chan->music_state;
820 if (state && state->class != class) {
821 memset(state, 0, sizeof(*state));
822 state->class = class;
825 if ((res = mohalloc(class))) {
826 res->origwfmt = chan->writeformat;
827 if (ast_set_write_format(chan, class->format)) {
828 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
829 moh_release(NULL, res);
832 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
837 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
839 struct mohdata *moh = data;
840 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
843 if (!moh->parent->pid)
846 len = ast_codec_get_len(moh->parent->format, samples);
848 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
849 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
850 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
852 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
856 moh->f.datalen = res;
857 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
858 moh->f.samples = ast_codec_get_samples(&moh->f);
860 if (ast_write(chan, &moh->f) < 0) {
861 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
868 static struct ast_generator mohgen =
871 release: moh_release,
872 generate: moh_generate,
873 digit: moh_handle_digit
876 static int moh_add_file(struct mohclass *class, const char *filepath)
878 if (!class->allowed_files) {
879 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
881 class->allowed_files = INITIAL_NUM_FILES;
882 } else if (class->total_files == class->allowed_files) {
883 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
884 class->allowed_files = 0;
885 class->total_files = 0;
888 class->allowed_files *= 2;
891 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
894 class->total_files++;
899 static int moh_sort_compare(const void *i1, const void *i2)
903 s1 = ((char **)i1)[0];
904 s2 = ((char **)i2)[0];
906 return strcasecmp(s1, s2);
909 static int moh_scan_files(struct mohclass *class) {
912 struct dirent *files_dirent;
914 char filepath[PATH_MAX];
920 files_DIR = opendir(class->dir);
922 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
926 for (i = 0; i < class->total_files; i++)
927 ast_free(class->filearray[i]);
929 class->total_files = 0;
930 dirnamelen = strlen(class->dir) + 2;
931 getcwd(path, sizeof(path));
933 while ((files_dirent = readdir(files_DIR))) {
934 /* The file name must be at least long enough to have the file type extension */
935 if ((strlen(files_dirent->d_name) < 4))
938 /* Skip files that starts with a dot */
939 if (files_dirent->d_name[0] == '.')
942 /* Skip files without extensions... they are not audio */
943 if (!strchr(files_dirent->d_name, '.'))
946 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
948 if (stat(filepath, &statbuf))
951 if (!S_ISREG(statbuf.st_mode))
954 if ((ext = strrchr(filepath, '.')))
957 /* if the file is present in multiple formats, ensure we only put it into the list once */
958 for (i = 0; i < class->total_files; i++)
959 if (!strcmp(filepath, class->filearray[i]))
962 if (i == class->total_files) {
963 if (moh_add_file(class, filepath))
970 if (ast_test_flag(class, MOH_SORTALPHA))
971 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
972 return class->total_files;
975 static int moh_diff(struct mohclass *old, struct mohclass *new)
980 if (strcmp(old->dir, new->dir))
982 else if (strcmp(old->mode, new->mode))
984 else if (strcmp(old->args, new->args))
986 else if (old->flags != new->flags)
992 static int moh_register(struct mohclass *moh, int reload)
997 struct mohclass *mohclass = NULL;
999 AST_RWLIST_WRLOCK(&mohclasses);
1000 if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
1001 mohclass->delete = 0;
1003 ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
1005 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1008 AST_RWLIST_UNLOCK(&mohclasses);
1011 AST_RWLIST_UNLOCK(&mohclasses);
1014 moh->start -= respawn_time;
1016 if (!strcasecmp(moh->mode, "files")) {
1017 if (!moh_scan_files(moh)) {
1018 ast_moh_free_class(&moh);
1021 if (strchr(moh->args, 'r'))
1022 ast_set_flag(moh, MOH_RANDOMIZE);
1023 } 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")) {
1025 if (!strcasecmp(moh->mode, "custom"))
1026 ast_set_flag(moh, MOH_CUSTOM);
1027 else if (!strcasecmp(moh->mode, "mp3nb"))
1028 ast_set_flag(moh, MOH_SINGLE);
1029 else if (!strcasecmp(moh->mode, "quietmp3nb"))
1030 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
1031 else if (!strcasecmp(moh->mode, "quietmp3"))
1032 ast_set_flag(moh, MOH_QUIET);
1036 /* Open /dev/zap/pseudo for timing... Is
1037 there a better, yet reliable way to do this? */
1038 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
1039 if (moh->pseudofd < 0) {
1040 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1043 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
1048 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
1049 ast_log(LOG_WARNING, "Unable to create moh...\n");
1050 if (moh->pseudofd > -1)
1051 close(moh->pseudofd);
1052 ast_moh_free_class(&moh);
1056 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1057 ast_moh_free_class(&moh);
1061 AST_RWLIST_WRLOCK(&mohclasses);
1062 AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
1063 AST_RWLIST_UNLOCK(&mohclasses);
1068 static void local_ast_moh_cleanup(struct ast_channel *chan)
1070 struct moh_files_state *state = chan->music_state;
1073 if (state->class->realtime) {
1074 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1075 /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
1076 if (!(state->class->inuse)) {
1077 /* Remove this class from list */
1078 AST_RWLIST_WRLOCK(&mohclasses);
1079 AST_RWLIST_REMOVE(&mohclasses, state->class, list);
1080 AST_RWLIST_UNLOCK(&mohclasses);
1082 /* Free some memory */
1083 ast_moh_destroy_one(state->class);
1086 ast_moh_destroy_one(state->class);
1089 ast_free(chan->music_state);
1090 chan->music_state = NULL;
1094 static struct mohclass *moh_class_malloc(void)
1096 struct mohclass *class;
1098 if ((class = ast_calloc(1, sizeof(*class)))) {
1099 class->format = AST_FORMAT_SLINEAR;
1100 class->realtime = 0;
1106 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1108 struct mohclass *mohclass = NULL;
1109 struct ast_variable *var = NULL;
1110 struct ast_variable *tmp = NULL;
1111 struct moh_files_state *state = chan->music_state;
1116 /* The following is the order of preference for which class to use:
1117 * 1) The channels explicitly set musicclass, which should *only* be
1118 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1119 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1120 * result of receiving a HOLD control frame, this should be the
1121 * payload that came with the frame.
1122 * 3) The interpclass argument. This would be from the mohinterpret
1123 * option from channel drivers. This is the same as the old musicclass
1125 * 4) The default class.
1128 /* First, let's check in memory for static and cached RT classes */
1129 AST_RWLIST_RDLOCK(&mohclasses);
1130 if (!ast_strlen_zero(chan->musicclass))
1131 mohclass = get_mohbyname(chan->musicclass, 1);
1132 if (!mohclass && !ast_strlen_zero(mclass))
1133 mohclass = get_mohbyname(mclass, 1);
1134 if (!mohclass && !ast_strlen_zero(interpclass))
1135 mohclass = get_mohbyname(interpclass, 1);
1136 AST_RWLIST_UNLOCK(&mohclasses);
1138 /* If no moh class found in memory, then check RT */
1139 if (!mohclass && ast_check_realtime("musiconhold")) {
1140 if (!ast_strlen_zero(chan->musicclass)) {
1141 var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL);
1143 if (!var && !ast_strlen_zero(mclass))
1144 var = ast_load_realtime("musiconhold", "name", mclass, NULL);
1145 if (!var && !ast_strlen_zero(interpclass))
1146 var = ast_load_realtime("musiconhold", "name", interpclass, NULL);
1148 var = ast_load_realtime("musiconhold", "name", "default", NULL);
1149 if (var && (mohclass = moh_class_malloc())) {
1150 mohclass->realtime = 1;
1151 for (tmp = var; tmp; tmp = tmp->next) {
1152 if (!strcasecmp(tmp->name, "name"))
1153 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1154 else if (!strcasecmp(tmp->name, "mode"))
1155 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1156 else if (!strcasecmp(tmp->name, "directory"))
1157 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1158 else if (!strcasecmp(tmp->name, "application"))
1159 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1160 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1161 mohclass->digit = *tmp->value;
1162 else if (!strcasecmp(tmp->name, "random"))
1163 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1164 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1165 ast_set_flag(mohclass, MOH_RANDOMIZE);
1166 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1167 ast_set_flag(mohclass, MOH_SORTALPHA);
1168 else if (!strcasecmp(tmp->name, "format")) {
1169 mohclass->format = ast_getformatbyname(tmp->value);
1170 if (!mohclass->format) {
1171 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1172 mohclass->format = AST_FORMAT_SLINEAR;
1176 ast_variables_destroy(var);
1177 if (ast_strlen_zero(mohclass->dir)) {
1178 if (!strcasecmp(mohclass->mode, "custom")) {
1179 strcpy(mohclass->dir, "nodir");
1181 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1186 if (ast_strlen_zero(mohclass->mode)) {
1187 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1191 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1192 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1197 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1198 /* CACHERTCLASSES enabled, let's add this class to default tree */
1199 if (state && state->class) {
1200 /* Class already exist for this channel */
1201 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1202 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1203 /* we found RT class with the same name, seems like we should continue playing existing one */
1204 ast_moh_free_class(&mohclass);
1205 mohclass = state->class;
1208 moh_register(mohclass, 0);
1211 /* We don't register RT moh class, so let's init it manualy */
1213 time(&mohclass->start);
1214 mohclass->start -= respawn_time;
1216 if (!strcasecmp(mohclass->mode, "files")) {
1217 if (!moh_scan_files(mohclass)) {
1218 ast_moh_free_class(&mohclass);
1221 if (strchr(mohclass->args, 'r'))
1222 ast_set_flag(mohclass, MOH_RANDOMIZE);
1223 } 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")) {
1225 if (!strcasecmp(mohclass->mode, "custom"))
1226 ast_set_flag(mohclass, MOH_CUSTOM);
1227 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1228 ast_set_flag(mohclass, MOH_SINGLE);
1229 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1230 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1231 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1232 ast_set_flag(mohclass, MOH_QUIET);
1234 mohclass->srcfd = -1;
1236 /* Open /dev/zap/pseudo for timing... Is
1237 there a better, yet reliable way to do this? */
1238 mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
1239 if (mohclass->pseudofd < 0) {
1240 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1243 ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x);
1246 mohclass->pseudofd = -1;
1248 /* Let's check if this channel already had a moh class before */
1249 if (state && state->class) {
1250 /* Class already exist for this channel */
1251 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1252 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1253 /* we found RT class with the same name, seems like we should continue playing existing one */
1254 ast_moh_free_class(&mohclass);
1255 mohclass = state->class;
1259 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1260 ast_log(LOG_WARNING, "Unable to create moh...\n");
1261 if (mohclass->pseudofd > -1)
1262 close(mohclass->pseudofd);
1263 ast_moh_free_class(&mohclass);
1268 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1269 ast_moh_free_class(&mohclass);
1276 ast_variables_destroy(var);
1281 /* Requested MOH class not found, check for 'default' class in musiconhold.conf */
1283 AST_RWLIST_RDLOCK(&mohclasses);
1284 mohclass = get_mohbyname("default", 1);
1286 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1287 AST_RWLIST_UNLOCK(&mohclasses);
1289 AST_RWLIST_RDLOCK(&mohclasses);
1290 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1291 AST_RWLIST_UNLOCK(&mohclasses);
1297 ast_set_flag(chan, AST_FLAG_MOH);
1298 if (mohclass->total_files) {
1299 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1301 return ast_activate_generator(chan, &mohgen, mohclass);
1304 static void local_ast_moh_stop(struct ast_channel *chan)
1306 struct moh_files_state *state = chan->music_state;
1307 ast_clear_flag(chan, AST_FLAG_MOH);
1308 ast_deactivate_generator(chan);
1312 ast_closestream(chan->stream);
1313 chan->stream = NULL;
1318 static int load_moh_classes(int reload)
1320 struct ast_config *cfg;
1321 struct ast_variable *var;
1322 struct mohclass *class;
1325 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1327 cfg = ast_config_load("musiconhold.conf", config_flags);
1329 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1333 AST_RWLIST_WRLOCK(&mohclasses);
1334 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1337 AST_RWLIST_UNLOCK(&mohclasses);
1340 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1342 cat = ast_category_browse(cfg, NULL);
1343 for (; cat; cat = ast_category_browse(cfg, cat)) {
1344 /* Setup common options from [general] section */
1345 if (!strcasecmp(cat, "general")) {
1346 var = ast_variable_browse(cfg, cat);
1348 if (!strcasecmp(var->name, "cachertclasses")) {
1349 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1351 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1356 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1357 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1358 if (!(class = moh_class_malloc()))
1361 ast_copy_string(class->name, cat, sizeof(class->name));
1362 var = ast_variable_browse(cfg, cat);
1364 if (!strcasecmp(var->name, "mode"))
1365 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1366 else if (!strcasecmp(var->name, "directory"))
1367 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1368 else if (!strcasecmp(var->name, "application"))
1369 ast_copy_string(class->args, var->value, sizeof(class->args));
1370 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1371 class->digit = *var->value;
1372 else if (!strcasecmp(var->name, "random"))
1373 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1374 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1375 ast_set_flag(class, MOH_RANDOMIZE);
1376 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1377 ast_set_flag(class, MOH_SORTALPHA);
1378 else if (!strcasecmp(var->name, "format")) {
1379 class->format = ast_getformatbyname(var->value);
1380 if (!class->format) {
1381 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1382 class->format = AST_FORMAT_SLINEAR;
1388 if (ast_strlen_zero(class->dir)) {
1389 if (!strcasecmp(class->mode, "custom")) {
1390 strcpy(class->dir, "nodir");
1392 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1397 if (ast_strlen_zero(class->mode)) {
1398 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1402 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1403 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1408 /* Don't leak a class when it's already registered */
1409 moh_register(class, reload);
1415 ast_config_destroy(cfg);
1420 static int ast_moh_destroy_one(struct mohclass *moh)
1423 int bytes, tbytes = 0, stime = 0, pid = 0;
1427 ast_debug(1, "killing %d!\n", moh->pid);
1428 stime = time(NULL) + 2;
1431 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1432 * to give the process a reason and time enough to kill off its
1439 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1440 tbytes = tbytes + bytes;
1441 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1444 ast_moh_free_class(&moh);
1450 static void ast_moh_destroy(void)
1452 struct mohclass *moh;
1454 ast_verb(2, "Destroying musiconhold processes\n");
1456 AST_RWLIST_WRLOCK(&mohclasses);
1457 while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1458 ast_moh_destroy_one(moh);
1460 AST_RWLIST_UNLOCK(&mohclasses);
1463 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1467 e->command = "moh reload";
1469 "Usage: moh reload\n"
1470 " Reloads the MusicOnHold module.\n"
1471 " Alias for 'module reload res_musiconhold.so'\n";
1477 if (a->argc != e->args)
1478 return CLI_SHOWUSAGE;
1485 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1488 struct mohclass *class;
1492 e->command = "moh show files";
1494 "Usage: moh show files\n"
1495 " Lists all loaded file-based MusicOnHold classes and their\n"
1502 if (a->argc != e->args)
1503 return CLI_SHOWUSAGE;
1505 AST_RWLIST_RDLOCK(&mohclasses);
1506 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1507 if (!class->total_files)
1510 ast_cli(a->fd, "Class: %s\n", class->name);
1511 for (i = 0; i < class->total_files; i++)
1512 ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1514 AST_RWLIST_UNLOCK(&mohclasses);
1519 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1521 struct mohclass *class;
1525 e->command = "moh show classes";
1527 "Usage: moh show classes\n"
1528 " Lists all MusicOnHold classes.\n";
1534 if (a->argc != e->args)
1535 return CLI_SHOWUSAGE;
1537 AST_RWLIST_RDLOCK(&mohclasses);
1538 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1539 ast_cli(a->fd, "Class: %s\n", class->name);
1540 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1541 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1542 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1544 ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1545 if (ast_test_flag(class, MOH_CUSTOM))
1546 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1547 if (strcasecmp(class->mode, "files"))
1548 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1550 AST_RWLIST_UNLOCK(&mohclasses);
1555 static struct ast_cli_entry cli_moh[] = {
1556 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1557 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1558 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1561 static int init_classes(int reload)
1563 struct mohclass *moh;
1565 if (!load_moh_classes(reload)) /* Load classes from config */
1566 return 0; /* Return if nothing is found */
1568 AST_RWLIST_WRLOCK(&mohclasses);
1569 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1570 if (reload && moh->delete) {
1571 AST_RWLIST_REMOVE_CURRENT(list);
1573 ast_moh_destroy_one(moh);
1574 } else if (moh->total_files) {
1575 if (moh_scan_files(moh) <= 0) {
1576 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1578 AST_LIST_REMOVE_CURRENT(list);
1580 ast_moh_destroy_one(moh);
1584 AST_RWLIST_TRAVERSE_SAFE_END
1585 AST_RWLIST_UNLOCK(&mohclasses);
1590 static int load_module(void)
1594 res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1595 ast_register_atexit(ast_moh_destroy);
1596 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1598 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1600 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1602 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1604 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1606 if (!init_classes(0)) { /* No music classes configured, so skip it */
1607 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1609 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1612 return AST_MODULE_LOAD_SUCCESS;
1615 static int reload(void)
1617 if (init_classes(1))
1618 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1623 static int unload_module(void)
1628 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1629 .load = load_module,
1630 .unload = unload_module,