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>
40 #include <sys/signal.h>
41 #include <netinet/in.h>
45 #include <sys/ioctl.h>
49 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
53 #include <linux/zaptel.h>
56 #endif /* __linux__ */
59 #include "asterisk/lock.h"
60 #include "asterisk/file.h"
61 #include "asterisk/logger.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/options.h"
65 #include "asterisk/module.h"
66 #include "asterisk/translate.h"
67 #include "asterisk/say.h"
68 #include "asterisk/musiconhold.h"
69 #include "asterisk/config.h"
70 #include "asterisk/utils.h"
71 #include "asterisk/cli.h"
72 #include "asterisk/stringfields.h"
73 #include "asterisk/linkedlists.h"
75 #define MAX_MOHFILES 512
76 #define MAX_MOHFILE_LEN 128
78 static char *app0 = "MusicOnHold";
79 static char *app1 = "WaitMusicOnHold";
80 static char *app2 = "SetMusicOnHold";
81 static char *app3 = "StartMusicOnHold";
82 static char *app4 = "StopMusicOnHold";
84 static char *synopsis0 = "Play Music On Hold indefinitely";
85 static char *synopsis1 = "Wait, playing Music On Hold";
86 static char *synopsis2 = "Set default Music On Hold class";
87 static char *synopsis3 = "Play Music On Hold";
88 static char *synopsis4 = "Stop Playing Music On Hold";
90 static char *descrip0 = "MusicOnHold(class): "
91 "Plays hold music specified by class. If omitted, the default\n"
92 "music source for the channel will be used. Set the default \n"
93 "class with the SetMusicOnHold() application.\n"
94 "Returns -1 on hangup.\n"
95 "Never returns otherwise.\n";
97 static char *descrip1 = "WaitMusicOnHold(delay): "
98 "Plays hold music specified number of seconds. Returns 0 when\n"
99 "done, or -1 on hangup. If no hold music is available, the delay will\n"
100 "still occur with no sound.\n";
102 static char *descrip2 = "SetMusicOnHold(class): "
103 "Sets the default class for music on hold for a given channel. When\n"
104 "music on hold is activated, this class will be used to select which\n"
105 "music is played.\n";
107 static char *descrip3 = "StartMusicOnHold(class): "
108 "Starts playing music on hold, uses default music class for channel.\n"
109 "Starts playing music specified by class. If omitted, the default\n"
110 "music source for the channel will be used. Always returns 0.\n";
112 static char *descrip4 = "StopMusicOnHold: "
113 "Stops playing music on hold.\n";
115 static int respawn_time = 20;
117 struct moh_files_state {
118 struct mohclass *class;
123 unsigned char save_pos;
126 #define MOH_QUIET (1 << 0)
127 #define MOH_SINGLE (1 << 1)
128 #define MOH_CUSTOM (1 << 2)
129 #define MOH_RANDOMIZE (1 << 3)
132 char name[MAX_MUSICCLASS];
136 /* XXX This means that we are allocating 64KB of memory for every musiconhold class XXX */
137 char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
141 int pid; /* PID of mpg123 */
144 /* Source of audio */
146 /* FD for timing source */
148 AST_LIST_HEAD_NOLOCK(, mohdata) members;
149 AST_LIST_ENTRY(mohclass) list;
155 struct mohclass *parent;
157 AST_LIST_ENTRY(mohdata) list;
160 AST_LIST_HEAD_STATIC(mohclasses, mohclass);
162 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
163 #define MPG_123 "/usr/bin/mpg123"
167 static void ast_moh_free_class(struct mohclass **class)
169 struct mohdata *member;
171 while ((member = AST_LIST_REMOVE_HEAD(&((*class)->members), list)))
174 if ((*class)->thread) {
175 pthread_cancel((*class)->thread);
176 (*class)->thread = 0;
184 static void moh_files_release(struct ast_channel *chan, void *data)
186 struct moh_files_state *state = chan->music_state;
189 if (option_verbose > 2)
190 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
192 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
193 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
195 state->save_pos = state->pos + 1;
200 static int ast_moh_files_next(struct ast_channel *chan)
202 struct moh_files_state *state = chan->music_state;
205 if (state->save_pos) {
206 state->pos = state->save_pos - 1;
209 /* Try 20 times to find something good */
210 for (tries=0;tries < 20;tries++) {
213 ast_closestream(chan->stream);
218 if (ast_test_flag(state->class, MOH_RANDOMIZE))
219 state->pos = ast_random();
221 state->pos %= state->class->total_files;
223 /* check to see if this file's format can be opened */
224 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
230 state->pos = state->pos % state->class->total_files;
232 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
233 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
239 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
242 ast_seekstream(chan->stream, state->samples, SEEK_SET);
248 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
250 struct ast_frame *f = NULL;
252 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
253 if (!ast_moh_files_next(chan))
254 f = ast_readframe(chan->stream);
260 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
262 struct moh_files_state *state = chan->music_state;
263 struct ast_frame *f = NULL;
266 state->sample_queue += samples;
268 while (state->sample_queue > 0) {
269 if ((f = moh_files_readframe(chan))) {
270 state->samples += f->samples;
271 res = ast_write(chan, f);
272 state->sample_queue -= f->samples;
275 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
285 static void *moh_files_alloc(struct ast_channel *chan, void *params)
287 struct moh_files_state *state;
288 struct mohclass *class = params;
290 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
291 chan->music_state = state;
292 state->class = class;
294 state = chan->music_state;
297 if (state->class != class) {
299 memset(state, 0, sizeof(*state));
300 state->class = class;
303 state->origwfmt = chan->writeformat;
305 if (option_verbose > 2)
306 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
309 return chan->music_state;
312 static struct ast_generator moh_file_stream =
314 alloc: moh_files_alloc,
315 release: moh_files_release,
316 generate: moh_files_generator,
319 static int spawn_mp3(struct mohclass *class)
323 char fns[MAX_MP3S][80];
324 char *argv[MAX_MP3S + 50];
332 if (!strcasecmp(class->dir, "nodir")) {
335 dir = opendir(class->dir);
336 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
337 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
342 if (!ast_test_flag(class, MOH_CUSTOM)) {
343 argv[argc++] = "mpg123";
346 argv[argc++] = "--mono";
348 argv[argc++] = "8000";
350 if (!ast_test_flag(class, MOH_SINGLE)) {
352 argv[argc++] = "2048";
357 if (ast_test_flag(class, MOH_QUIET))
358 argv[argc++] = "4096";
360 argv[argc++] = "8192";
362 /* Look for extra arguments and add them to the list */
363 ast_copy_string(xargs, class->args, sizeof(xargs));
365 while (!ast_strlen_zero(argptr)) {
366 argv[argc++] = argptr;
367 strsep(&argptr, ",");
370 /* Format arguments for argv vector */
371 ast_copy_string(xargs, class->args, sizeof(xargs));
373 while (!ast_strlen_zero(argptr)) {
374 argv[argc++] = argptr;
375 strsep(&argptr, " ");
380 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
381 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
382 argv[argc++] = fns[files];
385 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
386 if ((strlen(de->d_name) > 3) &&
387 ((ast_test_flag(class, MOH_CUSTOM) &&
388 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
389 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
390 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
391 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
392 argv[argc++] = fns[files];
402 ast_log(LOG_WARNING, "Pipe failed\n");
406 printf("%d files total, %d args total\n", files, argc);
409 for (x=0;argv[x];x++)
410 printf("arg%d: %s\n", x, argv[x]);
414 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
419 if (time(NULL) - class->start < respawn_time) {
420 sleep(respawn_time - (time(NULL) - class->start));
424 if (class->pid < 0) {
427 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
433 if (ast_opt_high_priority)
437 /* Stdout goes to pipe */
438 dup2(fds[1], STDOUT_FILENO);
439 /* Close unused file descriptors */
440 for (x=3;x<8192;x++) {
441 if (-1 != fcntl(x, F_GETFL)) {
447 if (ast_test_flag(class, MOH_CUSTOM)) {
448 execv(argv[0], argv);
450 /* Default install is /usr/local/bin */
451 execv(LOCAL_MPG_123, argv);
452 /* Many places have it in /usr/bin */
453 execv(MPG_123, argv);
454 /* Check PATH as a last-ditch effort */
455 execvp("mpg123", argv);
457 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
467 static void *monmp3thread(void *data)
469 #define MOH_MS_INTERVAL 100
471 struct mohclass *class = data;
477 struct timeval tv, tv_tmp;
482 pthread_testcancel();
483 /* Spawn mp3 player if it's not there */
484 if (class->srcfd < 0) {
485 if ((class->srcfd = spawn_mp3(class)) < 0) {
486 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
487 /* Try again later */
489 pthread_testcancel();
492 if (class->pseudofd > -1) {
493 /* Pause some amount of time */
494 res = read(class->pseudofd, buf, sizeof(buf));
495 pthread_testcancel();
499 tv_tmp = ast_tvnow();
502 delta = ast_tvdiff_ms(tv_tmp, tv);
503 if (delta < MOH_MS_INTERVAL) { /* too early */
504 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
505 usleep(1000 * (MOH_MS_INTERVAL - delta));
506 pthread_testcancel();
508 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
511 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
513 if (AST_LIST_EMPTY(&class->members))
516 len = ast_codec_get_len(class->format, res);
518 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
522 pthread_testcancel();
524 kill(class->pid, SIGHUP);
526 kill(class->pid, SIGTERM);
528 kill(class->pid, SIGKILL);
532 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
535 pthread_testcancel();
536 AST_LIST_LOCK(&mohclasses);
537 AST_LIST_TRAVERSE(&class->members, moh, list) {
539 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
541 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
544 AST_LIST_UNLOCK(&mohclasses);
549 static int moh0_exec(struct ast_channel *chan, void *data)
551 if (ast_moh_start(chan, data)) {
552 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
555 while (!ast_safe_sleep(chan, 10000));
560 static int moh1_exec(struct ast_channel *chan, void *data)
563 if (!data || !atoi(data)) {
564 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
567 if (ast_moh_start(chan, NULL)) {
568 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
571 res = ast_safe_sleep(chan, atoi(data) * 1000);
576 static int moh2_exec(struct ast_channel *chan, void *data)
578 if (ast_strlen_zero(data)) {
579 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
582 ast_string_field_set(chan, musicclass, data);
586 static int moh3_exec(struct ast_channel *chan, void *data)
589 if (data && strlen(data))
591 if (ast_moh_start(chan, class))
592 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
597 static int moh4_exec(struct ast_channel *chan, void *data)
604 /*! \note This function should be called with the mohclasses list locked */
605 static struct mohclass *get_mohbyname(const char *name)
607 struct mohclass *moh = NULL;
609 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
610 if (!strcasecmp(name, moh->name))
617 static struct mohdata *mohalloc(struct mohclass *cl)
622 if (!(moh = ast_calloc(1, sizeof(*moh))))
625 if (pipe(moh->pipe)) {
626 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
631 /* Make entirely non-blocking */
632 flags = fcntl(moh->pipe[0], F_GETFL);
633 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
634 flags = fcntl(moh->pipe[1], F_GETFL);
635 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
637 moh->f.frametype = AST_FRAME_VOICE;
638 moh->f.subclass = cl->format;
639 moh->f.offset = AST_FRIENDLY_OFFSET;
642 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
647 static void moh_release(struct ast_channel *chan, void *data)
649 struct mohdata *moh = data;
652 AST_LIST_LOCK(&mohclasses);
653 AST_LIST_REMOVE(&moh->parent->members, moh, list);
654 AST_LIST_UNLOCK(&mohclasses);
658 oldwfmt = moh->origwfmt;
661 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
662 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
663 if (option_verbose > 2)
664 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
668 static void *moh_alloc(struct ast_channel *chan, void *params)
671 struct mohclass *class = params;
673 if ((res = mohalloc(class))) {
674 res->origwfmt = chan->writeformat;
675 if (ast_set_write_format(chan, class->format)) {
676 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
677 moh_release(NULL, res);
680 if (option_verbose > 2)
681 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
686 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
688 struct mohdata *moh = data;
689 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
692 if (!moh->parent->pid)
695 len = ast_codec_get_len(moh->parent->format, samples);
697 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
698 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
699 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
701 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
705 moh->f.datalen = res;
706 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
707 moh->f.samples = ast_codec_get_samples(&moh->f);
709 if (ast_write(chan, &moh->f) < 0) {
710 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
717 static struct ast_generator mohgen =
720 release: moh_release,
721 generate: moh_generate,
724 static int moh_scan_files(struct mohclass *class) {
727 struct dirent *files_dirent;
729 char filepath[MAX_MOHFILE_LEN];
735 files_DIR = opendir(class->dir);
737 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
741 class->total_files = 0;
742 dirnamelen = strlen(class->dir) + 2;
745 memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
746 while ((files_dirent = readdir(files_DIR))) {
747 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
750 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
752 if (stat(filepath, &statbuf))
755 if (!S_ISREG(statbuf.st_mode))
758 if ((ext = strrchr(filepath, '.'))) {
763 /* if the file is present in multiple formats, ensure we only put it into the list once */
764 for (i = 0; i < class->total_files; i++)
765 if (!strcmp(filepath, class->filearray[i]))
768 if (i == class->total_files)
769 strcpy(class->filearray[class->total_files++], filepath);
771 /* If the new total files is equal to the maximum allowed, stop adding new ones */
772 if (class->total_files == MAX_MOHFILES)
779 return class->total_files;
782 static int moh_register(struct mohclass *moh, int reload)
787 AST_LIST_LOCK(&mohclasses);
788 if (get_mohbyname(moh->name)) {
790 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
792 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
795 AST_LIST_UNLOCK(&mohclasses);
798 AST_LIST_UNLOCK(&mohclasses);
801 moh->start -= respawn_time;
803 if (!strcasecmp(moh->mode, "files")) {
804 if (!moh_scan_files(moh)) {
805 ast_moh_free_class(&moh);
808 if (strchr(moh->args, 'r'))
809 ast_set_flag(moh, MOH_RANDOMIZE);
810 } 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")) {
812 if (!strcasecmp(moh->mode, "custom"))
813 ast_set_flag(moh, MOH_CUSTOM);
814 else if (!strcasecmp(moh->mode, "mp3nb"))
815 ast_set_flag(moh, MOH_SINGLE);
816 else if (!strcasecmp(moh->mode, "quietmp3nb"))
817 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
818 else if (!strcasecmp(moh->mode, "quietmp3"))
819 ast_set_flag(moh, MOH_QUIET);
823 /* Open /dev/zap/pseudo for timing... Is
824 there a better, yet reliable way to do this? */
825 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
826 if (moh->pseudofd < 0) {
827 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
830 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
835 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
836 ast_log(LOG_WARNING, "Unable to create moh...\n");
837 if (moh->pseudofd > -1)
838 close(moh->pseudofd);
839 ast_moh_free_class(&moh);
843 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
844 ast_moh_free_class(&moh);
848 AST_LIST_LOCK(&mohclasses);
849 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
850 AST_LIST_UNLOCK(&mohclasses);
855 static void local_ast_moh_cleanup(struct ast_channel *chan)
857 if (chan->music_state) {
858 free(chan->music_state);
859 chan->music_state = NULL;
863 static int local_ast_moh_start(struct ast_channel *chan, const char *class)
865 struct mohclass *mohclass;
867 if (ast_strlen_zero(class))
868 class = chan->musicclass;
869 if (ast_strlen_zero(class))
871 AST_LIST_LOCK(&mohclasses);
872 mohclass = get_mohbyname(class);
873 AST_LIST_UNLOCK(&mohclasses);
876 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
880 ast_set_flag(chan, AST_FLAG_MOH);
881 if (mohclass->total_files) {
882 return ast_activate_generator(chan, &moh_file_stream, mohclass);
884 return ast_activate_generator(chan, &mohgen, mohclass);
887 static void local_ast_moh_stop(struct ast_channel *chan)
889 ast_clear_flag(chan, AST_FLAG_MOH);
890 ast_deactivate_generator(chan);
892 if (chan->music_state) {
894 ast_closestream(chan->stream);
900 static struct mohclass *moh_class_malloc(void)
902 struct mohclass *class;
904 if ((class = ast_calloc(1, sizeof(*class))))
905 class->format = AST_FORMAT_SLINEAR;
910 static int load_moh_classes(int reload)
912 struct ast_config *cfg;
913 struct ast_variable *var;
914 struct mohclass *class;
919 static int dep_warning = 0;
921 cfg = ast_config_load("musiconhold.conf");
926 cat = ast_category_browse(cfg, NULL);
927 for (; cat; cat = ast_category_browse(cfg, cat)) {
928 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
929 if (!(class = moh_class_malloc())) {
932 ast_copy_string(class->name, cat, sizeof(class->name));
933 var = ast_variable_browse(cfg, cat);
935 if (!strcasecmp(var->name, "mode"))
936 ast_copy_string(class->mode, var->value, sizeof(class->mode));
937 else if (!strcasecmp(var->name, "directory"))
938 ast_copy_string(class->dir, var->value, sizeof(class->dir));
939 else if (!strcasecmp(var->name, "application"))
940 ast_copy_string(class->args, var->value, sizeof(class->args));
941 else if (!strcasecmp(var->name, "random"))
942 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
943 else if (!strcasecmp(var->name, "format")) {
944 class->format = ast_getformatbyname(var->value);
945 if (!class->format) {
946 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
947 class->format = AST_FORMAT_SLINEAR;
953 if (ast_strlen_zero(class->dir)) {
954 if (!strcasecmp(class->mode, "custom")) {
955 strcpy(class->dir, "nodir");
957 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
962 if (ast_strlen_zero(class->mode)) {
963 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
967 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
968 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
973 /* Don't leak a class when it's already registered */
974 moh_register(class, reload);
981 /* Deprecated Old-School Configuration */
982 var = ast_variable_browse(cfg, "classes");
985 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
988 data = strchr(var->value, ':');
991 args = strchr(data, ',');
994 if (!(get_mohbyname(var->name))) {
995 if (!(class = moh_class_malloc())) {
999 ast_copy_string(class->name, var->name, sizeof(class->name));
1000 ast_copy_string(class->dir, data, sizeof(class->dir));
1001 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1003 ast_copy_string(class->args, args, sizeof(class->args));
1005 moh_register(class, reload);
1011 var = ast_variable_browse(cfg, "moh_files");
1014 ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n");
1017 if (!(get_mohbyname(var->name))) {
1018 args = strchr(var->value, ',');
1021 if (!(class = moh_class_malloc())) {
1025 ast_copy_string(class->name, var->name, sizeof(class->name));
1026 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1027 strcpy(class->mode, "files");
1029 ast_copy_string(class->args, args, sizeof(class->args));
1031 moh_register(class, reload);
1037 ast_config_destroy(cfg);
1042 static void ast_moh_destroy(void)
1044 struct mohclass *moh;
1046 int bytes, tbytes = 0, stime = 0, pid = 0;
1048 if (option_verbose > 1)
1049 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1051 AST_LIST_LOCK(&mohclasses);
1052 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1054 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1055 stime = time(NULL) + 2;
1058 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1059 * to give the process a reason and time enough to kill off its
1066 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1067 tbytes = tbytes + bytes;
1068 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1071 ast_moh_free_class(&moh);
1073 AST_LIST_UNLOCK(&mohclasses);
1076 static void moh_on_off(int on)
1078 struct ast_channel *chan = NULL;
1080 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1081 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1083 local_ast_moh_start(chan, NULL);
1085 ast_deactivate_generator(chan);
1087 ast_channel_unlock(chan);
1091 static int moh_cli(int fd, int argc, char *argv[])
1097 x = load_moh_classes(1);
1099 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1103 static int cli_files_show(int fd, int argc, char *argv[])
1106 struct mohclass *class;
1108 AST_LIST_LOCK(&mohclasses);
1109 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1110 if (!class->total_files)
1113 ast_cli(fd, "Class: %s\n", class->name);
1114 for (i = 0; i < class->total_files; i++)
1115 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1117 AST_LIST_UNLOCK(&mohclasses);
1122 static int moh_classes_show(int fd, int argc, char *argv[])
1124 struct mohclass *class;
1126 AST_LIST_LOCK(&mohclasses);
1127 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1128 ast_cli(fd, "Class: %s\n", class->name);
1129 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1130 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1131 if (ast_test_flag(class, MOH_CUSTOM))
1132 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1133 if (strcasecmp(class->mode, "files"))
1134 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1136 AST_LIST_UNLOCK(&mohclasses);
1141 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1143 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1145 static struct ast_cli_entry cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL};
1147 static int init_classes(int reload)
1149 struct mohclass *moh;
1151 if (!load_moh_classes(reload)) /* Load classes from config */
1152 return 0; /* Return if nothing is found */
1154 AST_LIST_LOCK(&mohclasses);
1155 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
1156 if (moh->total_files)
1157 moh_scan_files(moh);
1159 AST_LIST_UNLOCK(&mohclasses);
1164 static int load_module(void *mod)
1168 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1169 ast_register_atexit(ast_moh_destroy);
1170 ast_cli_register(&cli_moh);
1171 ast_cli_register(&cli_moh_files_show);
1172 ast_cli_register(&cli_moh_classes_show);
1174 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1176 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1178 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1180 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1182 if (!init_classes(0)) { /* No music classes configured, so skip it */
1183 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1185 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1191 static int reload(void *mod)
1193 if (init_classes(1))
1194 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1199 static int unload_module(void *mod)
1204 static const char *description(void)
1206 return "Music On Hold Resource";
1209 static const char *key(void)
1211 return ASTERISK_GPL_KEY;
1214 STD_MOD(MOD_0 | NO_USECOUNT | NO_UNLOAD, reload, NULL, NULL);