2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, 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>
36 #include <sys/signal.h>
37 #include <netinet/in.h>
42 #include <linux/zaptel.h>
45 #endif /* __linux__ */
48 #include <sys/ioctl.h>
52 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
54 #include "asterisk/lock.h"
55 #include "asterisk/file.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/pbx.h"
59 #include "asterisk/options.h"
60 #include "asterisk/module.h"
61 #include "asterisk/translate.h"
62 #include "asterisk/say.h"
63 #include "asterisk/musiconhold.h"
64 #include "asterisk/config.h"
65 #include "asterisk/utils.h"
66 #include "asterisk/cli.h"
67 #include "asterisk/stringfields.h"
69 #define MAX_MOHFILES 512
70 #define MAX_MOHFILE_LEN 128
72 static char *app0 = "MusicOnHold";
73 static char *app1 = "WaitMusicOnHold";
74 static char *app2 = "SetMusicOnHold";
75 static char *app3 = "StartMusicOnHold";
76 static char *app4 = "StopMusicOnHold";
78 static char *synopsis0 = "Play Music On Hold indefinitely";
79 static char *synopsis1 = "Wait, playing Music On Hold";
80 static char *synopsis2 = "Set default Music On Hold class";
81 static char *synopsis3 = "Play Music On Hold";
82 static char *synopsis4 = "Stop Playing Music On Hold";
84 static char *descrip0 = "MusicOnHold(class): "
85 "Plays hold music specified by class. If omitted, the default\n"
86 "music source for the channel will be used. Set the default \n"
87 "class with the SetMusicOnHold() application.\n"
88 "Returns -1 on hangup.\n"
89 "Never returns otherwise.\n";
91 static char *descrip1 = "WaitMusicOnHold(delay): "
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 static char *descrip2 = "SetMusicOnHold(class): "
97 "Sets the default class for music on hold for a given channel. When\n"
98 "music on hold is activated, this class will be used to select which\n"
101 static char *descrip3 = "StartMusicOnHold(class): "
102 "Starts playing music on hold, uses default music class for channel.\n"
103 "Starts playing music specified by class. If omitted, the default\n"
104 "music source for the channel will be used. Always returns 0.\n";
106 static char *descrip4 = "StopMusicOnHold: "
107 "Stops playing music on hold.\n";
109 static int respawn_time = 20;
111 struct moh_files_state {
112 struct mohclass *class;
117 unsigned char save_pos;
120 #define MOH_QUIET (1 << 0)
121 #define MOH_SINGLE (1 << 1)
122 #define MOH_CUSTOM (1 << 2)
123 #define MOH_RANDOMIZE (1 << 3)
126 char name[MAX_MUSICCLASS];
130 char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
134 int pid; /* PID of mpg123 */
137 struct mohdata *members;
138 /* Source of audio */
140 /* FD for timing source */
142 struct mohclass *next;
148 struct mohclass *parent;
149 struct mohdata *next;
152 static struct mohclass *mohclasses;
154 AST_MUTEX_DEFINE_STATIC(moh_lock);
156 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
157 #define MPG_123 "/usr/bin/mpg123"
161 static void ast_moh_free_class(struct mohclass **class)
163 struct mohdata *members, *mtmp;
165 members = (*class)->members;
168 members = members->next;
171 if ((*class)->thread) {
172 pthread_cancel((*class)->thread);
173 (*class)->thread = 0;
180 static void moh_files_release(struct ast_channel *chan, void *data)
182 struct moh_files_state *state = chan->music_state;
185 if (option_verbose > 2)
186 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
188 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
189 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
191 state->save_pos = state->pos + 1;
196 static int ast_moh_files_next(struct ast_channel *chan)
198 struct moh_files_state *state = chan->music_state;
201 if (state->save_pos) {
202 state->pos = state->save_pos - 1;
205 /* Try 20 times to find something good */
206 for (tries=0;tries < 20;tries++) {
209 ast_closestream(chan->stream);
214 if (ast_test_flag(state->class, MOH_RANDOMIZE))
215 state->pos = ast_random();
217 /* check to see if this file's format can be opened */
218 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
224 state->pos = state->pos % state->class->total_files;
226 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
227 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
230 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
231 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
237 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
240 ast_seekstream(chan->stream, state->samples, SEEK_SET);
246 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
248 struct ast_frame *f = NULL;
250 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
251 if (!ast_moh_files_next(chan))
252 f = ast_readframe(chan->stream);
258 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
260 struct moh_files_state *state = chan->music_state;
261 struct ast_frame *f = NULL;
264 state->sample_queue += samples;
266 while (state->sample_queue > 0) {
267 if ((f = moh_files_readframe(chan))) {
268 state->samples += f->samples;
269 res = ast_write(chan, f);
270 state->sample_queue -= f->samples;
273 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
283 static void *moh_files_alloc(struct ast_channel *chan, void *params)
285 struct moh_files_state *state;
286 struct mohclass *class = params;
288 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
289 chan->music_state = state;
290 state->class = class;
292 state = chan->music_state;
295 if (state->class != class) {
297 memset(state, 0, sizeof(*state));
298 state->class = class;
301 state->origwfmt = chan->writeformat;
303 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
304 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
305 free(chan->music_state);
306 chan->music_state = NULL;
308 if (option_verbose > 2)
309 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
313 return chan->music_state;
316 static struct ast_generator moh_file_stream =
318 alloc: moh_files_alloc,
319 release: moh_files_release,
320 generate: moh_files_generator,
323 static int spawn_mp3(struct mohclass *class)
327 char fns[MAX_MP3S][80];
328 char *argv[MAX_MP3S + 50];
336 if (!strcasecmp(class->dir, "nodir")) {
339 dir = opendir(class->dir);
340 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
341 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
346 if (!ast_test_flag(class, MOH_CUSTOM)) {
347 argv[argc++] = "mpg123";
350 argv[argc++] = "--mono";
352 argv[argc++] = "8000";
354 if (!ast_test_flag(class, MOH_SINGLE)) {
356 argv[argc++] = "2048";
361 if (ast_test_flag(class, MOH_QUIET))
362 argv[argc++] = "4096";
364 argv[argc++] = "8192";
366 /* Look for extra arguments and add them to the list */
367 ast_copy_string(xargs, class->args, sizeof(xargs));
369 while (!ast_strlen_zero(argptr)) {
370 argv[argc++] = argptr;
371 strsep(&argptr, ",");
374 /* Format arguments for argv vector */
375 ast_copy_string(xargs, class->args, sizeof(xargs));
377 while (!ast_strlen_zero(argptr)) {
378 argv[argc++] = argptr;
379 strsep(&argptr, " ");
384 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
385 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
386 argv[argc++] = fns[files];
389 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
390 if ((strlen(de->d_name) > 3) &&
391 ((ast_test_flag(class, MOH_CUSTOM) &&
392 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
393 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
394 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
395 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
396 argv[argc++] = fns[files];
406 ast_log(LOG_WARNING, "Pipe failed\n");
410 printf("%d files total, %d args total\n", files, argc);
413 for (x=0;argv[x];x++)
414 printf("arg%d: %s\n", x, argv[x]);
418 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
423 if (time(NULL) - class->start < respawn_time) {
424 sleep(respawn_time - (time(NULL) - class->start));
428 if (class->pid < 0) {
431 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
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 */
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_mutex_lock(&moh_lock);
537 moh = class->members;
540 if ((res = write(moh->pipe[1], sbuf, res2)) != res2)
542 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
545 ast_mutex_unlock(&moh_lock);
550 static int moh0_exec(struct ast_channel *chan, void *data)
552 if (ast_moh_start(chan, data)) {
553 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
556 while (!ast_safe_sleep(chan, 10000));
561 static int moh1_exec(struct ast_channel *chan, void *data)
564 if (!data || !atoi(data)) {
565 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
568 if (ast_moh_start(chan, NULL)) {
569 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
572 res = ast_safe_sleep(chan, atoi(data) * 1000);
577 static int moh2_exec(struct ast_channel *chan, void *data)
579 if (ast_strlen_zero(data)) {
580 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
583 ast_string_field_set(chan, musicclass, data);
587 static int moh3_exec(struct ast_channel *chan, void *data)
590 if (data && strlen(data))
592 if (ast_moh_start(chan, class))
593 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
598 static int moh4_exec(struct ast_channel *chan, void *data)
605 static struct mohclass *get_mohbyname(const char *name)
607 struct mohclass *moh;
610 if (!strcasecmp(name, moh->name))
617 static struct mohdata *mohalloc(struct mohclass *cl)
621 if (!(moh = ast_calloc(1, sizeof(*moh))))
623 if (pipe(moh->pipe)) {
624 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
628 /* Make entirely non-blocking */
629 flags = fcntl(moh->pipe[0], F_GETFL);
630 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
631 flags = fcntl(moh->pipe[1], F_GETFL);
632 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
634 moh->next = cl->members;
639 static void moh_release(struct ast_channel *chan, void *data)
641 struct mohdata *moh = data, *prev, *cur;
643 ast_mutex_lock(&moh_lock);
646 cur = moh->parent->members;
650 prev->next = cur->next;
652 moh->parent->members = cur->next;
658 ast_mutex_unlock(&moh_lock);
661 oldwfmt = moh->origwfmt;
664 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
665 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
666 if (option_verbose > 2)
667 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
671 static void *moh_alloc(struct ast_channel *chan, void *params)
674 struct mohclass *class = params;
676 if ((res = mohalloc(class))) {
677 res->origwfmt = chan->writeformat;
678 if (ast_set_write_format(chan, class->format)) {
679 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
680 moh_release(NULL, res);
683 if (option_verbose > 2)
684 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
689 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
692 struct mohdata *moh = data;
693 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
696 if (!moh->parent->pid)
699 len = ast_codec_get_len(moh->parent->format, samples);
701 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
702 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
703 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
705 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
708 ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
714 memset(&f, 0, sizeof(f));
716 f.frametype = AST_FRAME_VOICE;
717 f.subclass = moh->parent->format;
720 f.data = buf + AST_FRIENDLY_OFFSET / 2;
721 f.offset = AST_FRIENDLY_OFFSET;
722 f.samples = ast_codec_get_samples(&f);
724 if (ast_write(chan, &f) < 0) {
725 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
732 static struct ast_generator mohgen =
735 release: moh_release,
736 generate: moh_generate,
739 static int moh_scan_files(struct mohclass *class) {
742 struct dirent *files_dirent;
744 char filepath[MAX_MOHFILE_LEN];
750 files_DIR = opendir(class->dir);
752 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
756 class->total_files = 0;
757 dirnamelen = strlen(class->dir) + 2;
760 memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
761 while ((files_dirent = readdir(files_DIR))) {
762 if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
765 snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
767 if (stat(filepath, &statbuf))
770 if (!S_ISREG(statbuf.st_mode))
773 if ((ext = strrchr(filepath, '.'))) {
778 /* if the file is present in multiple formats, ensure we only put it into the list once */
779 for (i = 0; i < class->total_files; i++)
780 if (!strcmp(filepath, class->filearray[i]))
783 if (i == class->total_files)
784 strcpy(class->filearray[class->total_files++], filepath);
786 /* If the new total files is equal to the maximum allowed, stop adding new ones */
787 if (class->total_files == MAX_MOHFILES)
794 return class->total_files;
797 static int moh_register(struct mohclass *moh, int reload)
802 ast_mutex_lock(&moh_lock);
803 if (get_mohbyname(moh->name)) {
805 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
807 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
810 ast_mutex_unlock(&moh_lock);
813 ast_mutex_unlock(&moh_lock);
816 moh->start -= respawn_time;
818 if (!strcasecmp(moh->mode, "files")) {
819 if (!moh_scan_files(moh)) {
820 ast_moh_free_class(&moh);
823 if (strchr(moh->args, 'r'))
824 ast_set_flag(moh, MOH_RANDOMIZE);
825 } 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")) {
827 if (!strcasecmp(moh->mode, "custom"))
828 ast_set_flag(moh, MOH_CUSTOM);
829 else if (!strcasecmp(moh->mode, "mp3nb"))
830 ast_set_flag(moh, MOH_SINGLE);
831 else if (!strcasecmp(moh->mode, "quietmp3nb"))
832 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
833 else if (!strcasecmp(moh->mode, "quietmp3"))
834 ast_set_flag(moh, MOH_QUIET);
838 /* Open /dev/zap/pseudo for timing... Is
839 there a better, yet reliable way to do this? */
840 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
841 if (moh->pseudofd < 0) {
842 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
845 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
850 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
851 ast_log(LOG_WARNING, "Unable to create moh...\n");
852 if (moh->pseudofd > -1)
853 close(moh->pseudofd);
854 ast_moh_free_class(&moh);
858 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
859 ast_moh_free_class(&moh);
862 ast_mutex_lock(&moh_lock);
863 moh->next = mohclasses;
865 ast_mutex_unlock(&moh_lock);
869 static void local_ast_moh_cleanup(struct ast_channel *chan)
871 if (chan->music_state) {
872 free(chan->music_state);
873 chan->music_state = NULL;
877 static int local_ast_moh_start(struct ast_channel *chan, const char *class)
879 struct mohclass *mohclass;
881 if (ast_strlen_zero(class))
882 class = chan->musicclass;
883 if (ast_strlen_zero(class))
885 ast_mutex_lock(&moh_lock);
886 mohclass = get_mohbyname(class);
887 ast_mutex_unlock(&moh_lock);
890 ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
894 ast_set_flag(chan, AST_FLAG_MOH);
895 if (mohclass->total_files) {
896 return ast_activate_generator(chan, &moh_file_stream, mohclass);
898 return ast_activate_generator(chan, &mohgen, mohclass);
901 static void local_ast_moh_stop(struct ast_channel *chan)
903 ast_clear_flag(chan, AST_FLAG_MOH);
904 ast_deactivate_generator(chan);
906 if (chan->music_state) {
908 ast_closestream(chan->stream);
914 static struct mohclass *moh_class_malloc(void)
916 struct mohclass *class;
918 if ((class = ast_calloc(1, sizeof(*class))))
919 class->format = AST_FORMAT_SLINEAR;
924 static int load_moh_classes(int reload)
926 struct ast_config *cfg;
927 struct ast_variable *var;
928 struct mohclass *class;
933 static int dep_warning = 0;
935 cfg = ast_config_load("musiconhold.conf");
940 cat = ast_category_browse(cfg, NULL);
941 for (; cat; cat = ast_category_browse(cfg, cat)) {
942 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
943 if (!(class = moh_class_malloc())) {
946 ast_copy_string(class->name, cat, sizeof(class->name));
947 var = ast_variable_browse(cfg, cat);
949 if (!strcasecmp(var->name, "mode"))
950 ast_copy_string(class->mode, var->value, sizeof(class->mode));
951 else if (!strcasecmp(var->name, "directory"))
952 ast_copy_string(class->dir, var->value, sizeof(class->dir));
953 else if (!strcasecmp(var->name, "application"))
954 ast_copy_string(class->args, var->value, sizeof(class->args));
955 else if (!strcasecmp(var->name, "random"))
956 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
957 else if (!strcasecmp(var->name, "format")) {
958 class->format = ast_getformatbyname(var->value);
959 if (!class->format) {
960 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
961 class->format = AST_FORMAT_SLINEAR;
967 if (ast_strlen_zero(class->dir)) {
968 if (!strcasecmp(class->mode, "custom")) {
969 strcpy(class->dir, "nodir");
971 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
976 if (ast_strlen_zero(class->mode)) {
977 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
981 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
982 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
987 /* Don't leak a class when it's already registered */
988 moh_register(class, reload);
995 /* Deprecated Old-School Configuration */
996 var = ast_variable_browse(cfg, "classes");
999 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");
1002 data = strchr(var->value, ':');
1005 args = strchr(data, ',');
1008 if (!(get_mohbyname(var->name))) {
1009 if (!(class = moh_class_malloc())) {
1013 ast_copy_string(class->name, var->name, sizeof(class->name));
1014 ast_copy_string(class->dir, data, sizeof(class->dir));
1015 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1017 ast_copy_string(class->args, args, sizeof(class->args));
1019 moh_register(class, reload);
1025 var = ast_variable_browse(cfg, "moh_files");
1028 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");
1031 if (!(get_mohbyname(var->name))) {
1032 args = strchr(var->value, ',');
1035 if (!(class = moh_class_malloc())) {
1039 ast_copy_string(class->name, var->name, sizeof(class->name));
1040 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1041 strcpy(class->mode, "files");
1043 ast_copy_string(class->args, args, sizeof(class->args));
1045 moh_register(class, reload);
1051 ast_config_destroy(cfg);
1056 static void ast_moh_destroy(void)
1058 struct mohclass *moh, *tmp;
1060 int bytes, tbytes=0, stime = 0, pid = 0;
1062 if (option_verbose > 1)
1063 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1064 ast_mutex_lock(&moh_lock);
1069 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1070 stime = time(NULL) + 2;
1073 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1074 * to give the process a reason and time enough to kill off its
1081 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
1082 tbytes = tbytes + bytes;
1084 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1089 ast_moh_free_class(&tmp);
1092 ast_mutex_unlock(&moh_lock);
1095 static void moh_on_off(int on)
1097 struct ast_channel *chan = NULL;
1099 while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1100 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1102 local_ast_moh_start(chan, NULL);
1104 ast_deactivate_generator(chan);
1106 ast_mutex_unlock(&chan->lock);
1110 static int moh_cli(int fd, int argc, char *argv[])
1116 x = load_moh_classes(1);
1118 ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1122 static int cli_files_show(int fd, int argc, char *argv[])
1125 struct mohclass *class;
1127 ast_mutex_lock(&moh_lock);
1128 for (class = mohclasses; class; class = class->next) {
1129 if (!class->total_files)
1132 ast_cli(fd, "Class: %s\n", class->name);
1133 for (i = 0; i < class->total_files; i++)
1134 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1136 ast_mutex_unlock(&moh_lock);
1141 static int moh_classes_show(int fd, int argc, char *argv[])
1143 struct mohclass *class;
1145 ast_mutex_lock(&moh_lock);
1146 for (class = mohclasses; class; class = class->next) {
1147 ast_cli(fd, "Class: %s\n", class->name);
1148 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode,"<none>"));
1149 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1150 if (ast_test_flag(class, MOH_CUSTOM))
1151 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1152 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1154 ast_mutex_unlock(&moh_lock);
1159 static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1161 static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1163 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};
1165 static int init_classes(int reload)
1167 struct mohclass *moh;
1169 if (!load_moh_classes(reload)) /* Load classes from config */
1170 return 0; /* Return if nothing is found */
1173 if (moh->total_files)
1174 moh_scan_files(moh);
1180 static int load_module(void *mod)
1184 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1185 ast_register_atexit(ast_moh_destroy);
1186 ast_cli_register(&cli_moh);
1187 ast_cli_register(&cli_moh_files_show);
1188 ast_cli_register(&cli_moh_classes_show);
1190 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1192 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1194 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1196 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1198 if (!init_classes(0)) { /* No music classes configured, so skip it */
1199 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1201 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1207 static int reload(void *mod)
1209 if (init_classes(1))
1210 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1215 static int unload_module(void *mod)
1220 static const char *description(void)
1222 return "Music On Hold Resource";
1225 static const char *key(void)
1227 return ASTERISK_GPL_KEY;
1230 STD_MOD(MOD_0 | NO_USECOUNT | NO_UNLOAD, reload, NULL, NULL);