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$")
45 #include <sys/signal.h>
46 #include <netinet/in.h>
50 #include <sys/ioctl.h>
56 #include <zaptel/zaptel.h>
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 INITIAL_NUM_FILES 8
77 static char *app0
= "MusicOnHold";
78 static char *app1
= "WaitMusicOnHold";
79 static char *app2
= "SetMusicOnHold";
80 static char *app3
= "StartMusicOnHold";
81 static char *app4
= "StopMusicOnHold";
83 static char *synopsis0
= "Play Music On Hold indefinitely";
84 static char *synopsis1
= "Wait, playing Music On Hold";
85 static char *synopsis2
= "Set default Music On Hold class";
86 static char *synopsis3
= "Play Music On Hold";
87 static char *synopsis4
= "Stop Playing Music On Hold";
89 static char *descrip0
= "MusicOnHold(class): "
90 "Plays hold music specified by class. If omitted, the default\n"
91 "music source for the channel will be used. Set the default \n"
92 "class with the SetMusicOnHold() application.\n"
93 "Returns -1 on hangup.\n"
94 "Never returns otherwise.\n";
96 static char *descrip1
= "WaitMusicOnHold(delay): "
97 "Plays hold music specified number of seconds. Returns 0 when\n"
98 "done, or -1 on hangup. If no hold music is available, the delay will\n"
99 "still occur with no sound.\n";
101 static char *descrip2
= "SetMusicOnHold(class): "
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"
104 "music is played.\n";
106 static char *descrip3
= "StartMusicOnHold(class): "
107 "Starts playing music on hold, uses default music class for channel.\n"
108 "Starts playing music specified by class. If omitted, the default\n"
109 "music source for the channel will be used. Always returns 0.\n";
111 static char *descrip4
= "StopMusicOnHold: "
112 "Stops playing music on hold.\n";
114 static int respawn_time
= 20;
116 struct moh_files_state
{
117 struct mohclass
*class;
125 #define MOH_QUIET (1 << 0)
126 #define MOH_SINGLE (1 << 1)
127 #define MOH_CUSTOM (1 << 2)
128 #define MOH_RANDOMIZE (1 << 3)
131 char name
[MAX_MUSICCLASS
];
135 /*! A dynamically sized array to hold the list of filenames in "files" mode */
137 /*! The current size of the filearray */
139 /*! The current number of files loaded into the filearray */
142 /*! The format from the MOH source, not applicable to "files" mode */
144 /*! The pid of the external application delivering MOH */
148 /*! Source of audio */
150 /*! FD for timing source */
152 /*! Number of users */
154 unsigned int delete:1;
155 AST_LIST_HEAD_NOLOCK(, mohdata
) members
;
156 AST_LIST_ENTRY(mohclass
) list
;
162 struct mohclass
*parent
;
164 AST_LIST_ENTRY(mohdata
) list
;
167 AST_LIST_HEAD_STATIC(mohclasses
, mohclass
);
169 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
170 #define MPG_123 "/usr/bin/mpg123"
173 static int ast_moh_destroy_one(struct mohclass
*moh
);
174 static int reload(void);
176 static void ast_moh_free_class(struct mohclass
**mohclass
)
178 struct mohdata
*member
;
179 struct mohclass
*class = *mohclass
;
182 while ((member
= AST_LIST_REMOVE_HEAD(&class->members
, list
)))
186 pthread_cancel(class->thread
);
190 if (class->filearray
) {
191 for (i
= 0; i
< class->total_files
; i
++)
192 free(class->filearray
[i
]);
193 free(class->filearray
);
201 static void moh_files_release(struct ast_channel
*chan
, void *data
)
203 struct moh_files_state
*state
= chan
->music_state
;
207 ast_closestream(chan
->stream
);
210 if (option_verbose
> 2)
211 ast_verbose(VERBOSE_PREFIX_3
"Stopped music on hold on %s\n", chan
->name
);
213 if (state
->origwfmt
&& ast_set_write_format(chan
, state
->origwfmt
)) {
214 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format '%d'\n", chan
->name
, state
->origwfmt
);
216 state
->save_pos
= state
->pos
;
218 if (ast_atomic_dec_and_test(&state
->class->inuse
) && state
->class->delete)
219 ast_moh_destroy_one(state
->class);
223 static int ast_moh_files_next(struct ast_channel
*chan
)
225 struct moh_files_state
*state
= chan
->music_state
;
228 /* Discontinue a stream if it is running already */
230 ast_closestream(chan
->stream
);
234 if (!state
->class->total_files
) {
235 ast_log(LOG_WARNING
, "No files available for class '%s'\n", state
->class->name
);
239 /* If a specific file has been saved, use it */
240 if (state
->save_pos
>= 0) {
241 state
->pos
= state
->save_pos
;
242 state
->save_pos
= -1;
243 } else if (ast_test_flag(state
->class, MOH_RANDOMIZE
)) {
244 /* Get a random file and ensure we can open it */
245 for (tries
= 0; tries
< 20; tries
++) {
246 state
->pos
= rand() % state
->class->total_files
;
247 if (ast_fileexists(state
->class->filearray
[state
->pos
], NULL
, NULL
) > 0)
252 /* This is easy, just increment our position and make sure we don't exceed the total file count */
254 state
->pos
%= state
->class->total_files
;
258 if (!ast_openstream_full(chan
, state
->class->filearray
[state
->pos
], chan
->language
, 1)) {
259 ast_log(LOG_WARNING
, "Unable to open file '%s': %s\n", state
->class->filearray
[state
->pos
], strerror(errno
));
261 state
->pos
%= state
->class->total_files
;
266 ast_log(LOG_DEBUG
, "%s Opened file %d '%s'\n", chan
->name
, state
->pos
, state
->class->filearray
[state
->pos
]);
269 ast_seekstream(chan
->stream
, state
->samples
, SEEK_SET
);
275 static struct ast_frame
*moh_files_readframe(struct ast_channel
*chan
)
277 struct ast_frame
*f
= NULL
;
279 if (!(chan
->stream
&& (f
= ast_readframe(chan
->stream
)))) {
280 if (!ast_moh_files_next(chan
))
281 f
= ast_readframe(chan
->stream
);
287 static int moh_files_generator(struct ast_channel
*chan
, void *data
, int len
, int samples
)
289 struct moh_files_state
*state
= chan
->music_state
;
290 struct ast_frame
*f
= NULL
;
293 state
->sample_queue
+= samples
;
295 while (state
->sample_queue
> 0) {
296 if ((f
= moh_files_readframe(chan
))) {
297 state
->samples
+= f
->samples
;
298 res
= ast_write(chan
, f
);
299 state
->sample_queue
-= f
->samples
;
302 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
312 static void *moh_files_alloc(struct ast_channel
*chan
, void *params
)
314 struct moh_files_state
*state
;
315 struct mohclass
*class = params
;
317 if (!chan
->music_state
&& (state
= ast_calloc(1, sizeof(*state
)))) {
318 chan
->music_state
= state
;
319 state
->class = class;
320 state
->save_pos
= -1;
322 state
= chan
->music_state
;
325 if (state
->class != class) {
327 memset(state
, 0, sizeof(*state
));
328 state
->class = class;
329 if (ast_test_flag(state
->class, MOH_RANDOMIZE
) && class->total_files
)
330 state
->pos
= ast_random() % class->total_files
;
333 state
->origwfmt
= chan
->writeformat
;
335 if (option_verbose
> 2)
336 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on %s\n", class->name
, chan
->name
);
339 return chan
->music_state
;
342 static struct ast_generator moh_file_stream
=
344 alloc
: moh_files_alloc
,
345 release
: moh_files_release
,
346 generate
: moh_files_generator
,
349 static int spawn_mp3(struct mohclass
*class)
353 char fns
[MAX_MP3S
][80];
354 char *argv
[MAX_MP3S
+ 50];
360 sigset_t signal_set
, old_set
;
363 if (!strcasecmp(class->dir
, "nodir")) {
366 dir
= opendir(class->dir
);
367 if (!dir
&& !strstr(class->dir
,"http://") && !strstr(class->dir
,"HTTP://")) {
368 ast_log(LOG_WARNING
, "%s is not a valid directory\n", class->dir
);
373 if (!ast_test_flag(class, MOH_CUSTOM
)) {
374 argv
[argc
++] = "mpg123";
377 argv
[argc
++] = "--mono";
379 argv
[argc
++] = "8000";
381 if (!ast_test_flag(class, MOH_SINGLE
)) {
383 argv
[argc
++] = "2048";
388 if (ast_test_flag(class, MOH_QUIET
))
389 argv
[argc
++] = "4096";
391 argv
[argc
++] = "8192";
393 /* Look for extra arguments and add them to the list */
394 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
396 while (!ast_strlen_zero(argptr
)) {
397 argv
[argc
++] = argptr
;
398 strsep(&argptr
, ",");
401 /* Format arguments for argv vector */
402 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
404 while (!ast_strlen_zero(argptr
)) {
405 argv
[argc
++] = argptr
;
406 strsep(&argptr
, " ");
411 if (strstr(class->dir
,"http://") || strstr(class->dir
,"HTTP://")) {
412 ast_copy_string(fns
[files
], class->dir
, sizeof(fns
[files
]));
413 argv
[argc
++] = fns
[files
];
416 while ((de
= readdir(dir
)) && (files
< MAX_MP3S
)) {
417 if ((strlen(de
->d_name
) > 3) &&
418 ((ast_test_flag(class, MOH_CUSTOM
) &&
419 (!strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".raw") ||
420 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".sln"))) ||
421 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".mp3"))) {
422 ast_copy_string(fns
[files
], de
->d_name
, sizeof(fns
[files
]));
423 argv
[argc
++] = fns
[files
];
433 ast_log(LOG_WARNING
, "Pipe failed\n");
437 ast_log(LOG_WARNING
, "Found no files in '%s'\n", class->dir
);
442 if (time(NULL
) - class->start
< respawn_time
) {
443 sleep(respawn_time
- (time(NULL
) - class->start
));
446 /* Block signals during the fork() */
447 sigfillset(&signal_set
);
448 pthread_sigmask(SIG_BLOCK
, &signal_set
, &old_set
);
452 if (class->pid
< 0) {
455 ast_log(LOG_WARNING
, "Fork failed: %s\n", strerror(errno
));
461 if (ast_opt_high_priority
)
464 /* Reset ignored signals back to default */
465 signal(SIGPIPE
, SIG_DFL
);
466 pthread_sigmask(SIG_UNBLOCK
, &signal_set
, NULL
);
469 /* Stdout goes to pipe */
470 dup2(fds
[1], STDOUT_FILENO
);
471 /* Close unused file descriptors */
472 for (x
=3;x
<8192;x
++) {
473 if (-1 != fcntl(x
, F_GETFL
)) {
479 if (ast_test_flag(class, MOH_CUSTOM
)) {
480 execv(argv
[0], argv
);
482 /* Default install is /usr/local/bin */
483 execv(LOCAL_MPG_123
, argv
);
484 /* Many places have it in /usr/bin */
485 execv(MPG_123
, argv
);
486 /* Check PATH as a last-ditch effort */
487 execvp("mpg123", argv
);
489 ast_log(LOG_WARNING
, "Exec failed: %s\n", strerror(errno
));
494 pthread_sigmask(SIG_SETMASK
, &old_set
, NULL
);
500 static void *monmp3thread(void *data
)
502 #define MOH_MS_INTERVAL 100
504 struct mohclass
*class = data
;
510 struct timeval tv
, tv_tmp
;
515 pthread_testcancel();
516 /* Spawn mp3 player if it's not there */
517 if (class->srcfd
< 0) {
518 if ((class->srcfd
= spawn_mp3(class)) < 0) {
519 ast_log(LOG_WARNING
, "Unable to spawn mp3player\n");
520 /* Try again later */
522 pthread_testcancel();
525 if (class->pseudofd
> -1) {
529 /* Pause some amount of time */
530 res
= read(class->pseudofd
, buf
, sizeof(buf
));
531 pthread_testcancel();
535 tv_tmp
= ast_tvnow();
538 delta
= ast_tvdiff_ms(tv_tmp
, tv
);
539 if (delta
< MOH_MS_INTERVAL
) { /* too early */
540 tv
= ast_tvadd(tv
, ast_samp2tv(MOH_MS_INTERVAL
, 1000)); /* next deadline */
541 usleep(1000 * (MOH_MS_INTERVAL
- delta
));
542 pthread_testcancel();
544 ast_log(LOG_NOTICE
, "Request to schedule in the past?!?!\n");
547 res
= 8 * MOH_MS_INTERVAL
; /* 8 samples per millisecond */
549 if (AST_LIST_EMPTY(&class->members
))
552 len
= ast_codec_get_len(class->format
, res
);
554 if ((res2
= read(class->srcfd
, sbuf
, len
)) != len
) {
558 pthread_testcancel();
559 if (class->pid
> 1) {
560 kill(class->pid
, SIGHUP
);
562 kill(class->pid
, SIGTERM
);
564 kill(class->pid
, SIGKILL
);
568 ast_log(LOG_DEBUG
, "Read %d bytes of audio while expecting %d\n", res2
, len
);
571 pthread_testcancel();
572 AST_LIST_LOCK(&mohclasses
);
573 AST_LIST_TRAVERSE(&class->members
, moh
, list
) {
575 if ((res
= write(moh
->pipe
[1], sbuf
, res2
)) != res2
) {
577 ast_log(LOG_DEBUG
, "Only wrote %d of %d bytes to pipe\n", res
, res2
);
580 AST_LIST_UNLOCK(&mohclasses
);
585 static int moh0_exec(struct ast_channel
*chan
, void *data
)
587 if (ast_moh_start(chan
, data
, NULL
)) {
588 ast_log(LOG_WARNING
, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data
, chan
->name
);
591 while (!ast_safe_sleep(chan
, 10000));
596 static int moh1_exec(struct ast_channel
*chan
, void *data
)
599 if (!data
|| !atoi(data
)) {
600 ast_log(LOG_WARNING
, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
603 if (ast_moh_start(chan
, NULL
, NULL
)) {
604 ast_log(LOG_WARNING
, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data
), chan
->name
);
607 res
= ast_safe_sleep(chan
, atoi(data
) * 1000);
612 static int moh2_exec(struct ast_channel
*chan
, void *data
)
614 if (ast_strlen_zero(data
)) {
615 ast_log(LOG_WARNING
, "SetMusicOnHold requires an argument (class)\n");
618 ast_string_field_set(chan
, musicclass
, data
);
622 static int moh3_exec(struct ast_channel
*chan
, void *data
)
625 if (data
&& strlen(data
))
627 if (ast_moh_start(chan
, class, NULL
))
628 ast_log(LOG_NOTICE
, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan
->name
);
633 static int moh4_exec(struct ast_channel
*chan
, void *data
)
640 /*! \note This function should be called with the mohclasses list locked */
641 static struct mohclass
*get_mohbyname(const char *name
, int warn
)
643 struct mohclass
*moh
= NULL
;
645 AST_LIST_TRAVERSE(&mohclasses
, moh
, list
) {
646 if (!strcasecmp(name
, moh
->name
))
651 ast_log(LOG_WARNING
, "Music on Hold class '%s' not found\n", name
);
656 static struct mohdata
*mohalloc(struct mohclass
*cl
)
661 if (!(moh
= ast_calloc(1, sizeof(*moh
))))
664 if (pipe(moh
->pipe
)) {
665 ast_log(LOG_WARNING
, "Failed to create pipe: %s\n", strerror(errno
));
670 /* Make entirely non-blocking */
671 flags
= fcntl(moh
->pipe
[0], F_GETFL
);
672 fcntl(moh
->pipe
[0], F_SETFL
, flags
| O_NONBLOCK
);
673 flags
= fcntl(moh
->pipe
[1], F_GETFL
);
674 fcntl(moh
->pipe
[1], F_SETFL
, flags
| O_NONBLOCK
);
676 moh
->f
.frametype
= AST_FRAME_VOICE
;
677 moh
->f
.subclass
= cl
->format
;
678 moh
->f
.offset
= AST_FRIENDLY_OFFSET
;
682 AST_LIST_LOCK(&mohclasses
);
683 AST_LIST_INSERT_HEAD(&cl
->members
, moh
, list
);
684 AST_LIST_UNLOCK(&mohclasses
);
689 static void moh_release(struct ast_channel
*chan
, void *data
)
691 struct mohdata
*moh
= data
;
694 AST_LIST_LOCK(&mohclasses
);
695 AST_LIST_REMOVE(&moh
->parent
->members
, moh
, list
);
696 AST_LIST_UNLOCK(&mohclasses
);
700 oldwfmt
= moh
->origwfmt
;
701 if (moh
->parent
->delete && ast_atomic_dec_and_test(&moh
->parent
->inuse
))
702 ast_moh_destroy_one(moh
->parent
);
705 if (oldwfmt
&& ast_set_write_format(chan
, oldwfmt
))
706 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format %s\n", chan
->name
, ast_getformatname(oldwfmt
));
707 if (option_verbose
> 2)
708 ast_verbose(VERBOSE_PREFIX_3
"Stopped music on hold on %s\n", chan
->name
);
712 static void *moh_alloc(struct ast_channel
*chan
, void *params
)
715 struct mohclass
*class = params
;
717 if ((res
= mohalloc(class))) {
718 res
->origwfmt
= chan
->writeformat
;
719 if (ast_set_write_format(chan
, class->format
)) {
720 ast_log(LOG_WARNING
, "Unable to set channel '%s' to format '%s'\n", chan
->name
, ast_codec2str(class->format
));
721 moh_release(NULL
, res
);
724 if (option_verbose
> 2)
725 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on channel '%s'\n", class->name
, chan
->name
);
730 static int moh_generate(struct ast_channel
*chan
, void *data
, int len
, int samples
)
732 struct mohdata
*moh
= data
;
733 short buf
[1280 + AST_FRIENDLY_OFFSET
/ 2];
736 if (!moh
->parent
->pid
)
739 len
= ast_codec_get_len(moh
->parent
->format
, samples
);
741 if (len
> sizeof(buf
) - AST_FRIENDLY_OFFSET
) {
742 ast_log(LOG_WARNING
, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf
), len
, chan
->name
);
743 len
= sizeof(buf
) - AST_FRIENDLY_OFFSET
;
745 res
= read(moh
->pipe
[0], buf
+ AST_FRIENDLY_OFFSET
/2, len
);
749 moh
->f
.datalen
= res
;
750 moh
->f
.data
= buf
+ AST_FRIENDLY_OFFSET
/ 2;
751 moh
->f
.samples
= ast_codec_get_samples(&moh
->f
);
753 if (ast_write(chan
, &moh
->f
) < 0) {
754 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
761 static struct ast_generator mohgen
=
764 release
: moh_release
,
765 generate
: moh_generate
,
768 static int moh_add_file(struct mohclass
*class, const char *filepath
)
770 if (!class->allowed_files
) {
771 if (!(class->filearray
= ast_calloc(1, INITIAL_NUM_FILES
* sizeof(*class->filearray
))))
773 class->allowed_files
= INITIAL_NUM_FILES
;
774 } else if (class->total_files
== class->allowed_files
) {
775 if (!(class->filearray
= ast_realloc(class->filearray
, class->allowed_files
* sizeof(*class->filearray
) * 2))) {
776 class->allowed_files
= 0;
777 class->total_files
= 0;
780 class->allowed_files
*= 2;
783 if (!(class->filearray
[class->total_files
] = ast_strdup(filepath
)))
786 class->total_files
++;
791 static int moh_scan_files(struct mohclass
*class) {
794 struct dirent
*files_dirent
;
796 char filepath
[PATH_MAX
];
802 files_DIR
= opendir(class->dir
);
804 ast_log(LOG_WARNING
, "Cannot open dir %s or dir does not exist\n", class->dir
);
808 for (i
= 0; i
< class->total_files
; i
++)
809 free(class->filearray
[i
]);
811 class->total_files
= 0;
812 dirnamelen
= strlen(class->dir
) + 2;
813 getcwd(path
, sizeof(path
));
815 while ((files_dirent
= readdir(files_DIR
))) {
816 /* The file name must be at least long enough to have the file type extension */
817 if ((strlen(files_dirent
->d_name
) < 4))
820 /* Skip files that starts with a dot */
821 if (files_dirent
->d_name
[0] == '.')
824 /* Skip files without extensions... they are not audio */
825 if (!strchr(files_dirent
->d_name
, '.'))
828 snprintf(filepath
, sizeof(filepath
), "%s/%s", class->dir
, files_dirent
->d_name
);
830 if (stat(filepath
, &statbuf
))
833 if (!S_ISREG(statbuf
.st_mode
))
836 if ((ext
= strrchr(filepath
, '.'))) {
841 /* if the file is present in multiple formats, ensure we only put it into the list once */
842 for (i
= 0; i
< class->total_files
; i
++)
843 if (!strcmp(filepath
, class->filearray
[i
]))
846 if (i
== class->total_files
) {
847 if (moh_add_file(class, filepath
))
854 return class->total_files
;
857 static int moh_register(struct mohclass
*moh
, int reload
)
862 struct mohclass
*mohclass
= NULL
;
864 AST_LIST_LOCK(&mohclasses
);
865 if ((mohclass
= get_mohbyname(moh
->name
, 0))) {
866 mohclass
->delete = 0;
868 ast_log(LOG_DEBUG
, "Music on Hold class '%s' left alone from initial load.\n", moh
->name
);
870 ast_log(LOG_WARNING
, "Music on Hold class '%s' already exists\n", moh
->name
);
873 AST_LIST_UNLOCK(&mohclasses
);
876 AST_LIST_UNLOCK(&mohclasses
);
879 moh
->start
-= respawn_time
;
881 if (!strcasecmp(moh
->mode
, "files")) {
882 if (!moh_scan_files(moh
)) {
883 ast_moh_free_class(&moh
);
886 if (strchr(moh
->args
, 'r'))
887 ast_set_flag(moh
, MOH_RANDOMIZE
);
888 } 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")) {
890 if (!strcasecmp(moh
->mode
, "custom"))
891 ast_set_flag(moh
, MOH_CUSTOM
);
892 else if (!strcasecmp(moh
->mode
, "mp3nb"))
893 ast_set_flag(moh
, MOH_SINGLE
);
894 else if (!strcasecmp(moh
->mode
, "quietmp3nb"))
895 ast_set_flag(moh
, MOH_SINGLE
| MOH_QUIET
);
896 else if (!strcasecmp(moh
->mode
, "quietmp3"))
897 ast_set_flag(moh
, MOH_QUIET
);
901 /* Open /dev/zap/pseudo for timing... Is
902 there a better, yet reliable way to do this? */
903 moh
->pseudofd
= open("/dev/zap/pseudo", O_RDONLY
);
904 if (moh
->pseudofd
< 0) {
905 ast_log(LOG_WARNING
, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
908 ioctl(moh
->pseudofd
, ZT_SET_BLOCKSIZE
, &x
);
913 if (ast_pthread_create_background(&moh
->thread
, NULL
, monmp3thread
, moh
)) {
914 ast_log(LOG_WARNING
, "Unable to create moh...\n");
915 if (moh
->pseudofd
> -1)
916 close(moh
->pseudofd
);
917 ast_moh_free_class(&moh
);
921 ast_log(LOG_WARNING
, "Don't know how to do a mode '%s' music on hold\n", moh
->mode
);
922 ast_moh_free_class(&moh
);
926 AST_LIST_LOCK(&mohclasses
);
927 AST_LIST_INSERT_HEAD(&mohclasses
, moh
, list
);
928 AST_LIST_UNLOCK(&mohclasses
);
933 static void local_ast_moh_cleanup(struct ast_channel
*chan
)
935 if (chan
->music_state
) {
936 free(chan
->music_state
);
937 chan
->music_state
= NULL
;
941 static int local_ast_moh_start(struct ast_channel
*chan
, const char *mclass
, const char *interpclass
)
943 struct mohclass
*mohclass
= NULL
;
945 /* The following is the order of preference for which class to use:
946 * 1) The channels explicitly set musicclass, which should *only* be
947 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
948 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
949 * result of receiving a HOLD control frame, this should be the
950 * payload that came with the frame.
951 * 3) The interpclass argument. This would be from the mohinterpret
952 * option from channel drivers. This is the same as the old musicclass
954 * 4) The default class.
956 AST_LIST_LOCK(&mohclasses
);
957 if (!ast_strlen_zero(chan
->musicclass
))
958 mohclass
= get_mohbyname(chan
->musicclass
, 1);
959 if (!mohclass
&& !ast_strlen_zero(mclass
))
960 mohclass
= get_mohbyname(mclass
, 1);
961 if (!mohclass
&& !ast_strlen_zero(interpclass
))
962 mohclass
= get_mohbyname(interpclass
, 1);
964 mohclass
= get_mohbyname("default", 1);
966 ast_atomic_fetchadd_int(&mohclass
->inuse
, +1);
967 AST_LIST_UNLOCK(&mohclasses
);
972 ast_set_flag(chan
, AST_FLAG_MOH
);
973 if (mohclass
->total_files
) {
974 return ast_activate_generator(chan
, &moh_file_stream
, mohclass
);
976 return ast_activate_generator(chan
, &mohgen
, mohclass
);
979 static void local_ast_moh_stop(struct ast_channel
*chan
)
981 ast_clear_flag(chan
, AST_FLAG_MOH
);
982 ast_deactivate_generator(chan
);
984 if (chan
->music_state
) {
986 ast_closestream(chan
->stream
);
992 static struct mohclass
*moh_class_malloc(void)
994 struct mohclass
*class;
996 if ((class = ast_calloc(1, sizeof(*class))))
997 class->format
= AST_FORMAT_SLINEAR
;
1002 static int load_moh_classes(int reload
)
1004 struct ast_config
*cfg
;
1005 struct ast_variable
*var
;
1006 struct mohclass
*class;
1011 static int dep_warning
= 0;
1013 cfg
= ast_config_load("musiconhold.conf");
1019 AST_LIST_LOCK(&mohclasses
);
1020 AST_LIST_TRAVERSE(&mohclasses
, class, list
)
1022 AST_LIST_UNLOCK(&mohclasses
);
1025 cat
= ast_category_browse(cfg
, NULL
);
1026 for (; cat
; cat
= ast_category_browse(cfg
, cat
)) {
1027 if (strcasecmp(cat
, "classes") && strcasecmp(cat
, "moh_files")) {
1028 if (!(class = moh_class_malloc())) {
1031 ast_copy_string(class->name
, cat
, sizeof(class->name
));
1032 var
= ast_variable_browse(cfg
, cat
);
1034 if (!strcasecmp(var
->name
, "mode"))
1035 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1036 else if (!strcasecmp(var
->name
, "directory"))
1037 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1038 else if (!strcasecmp(var
->name
, "application"))
1039 ast_copy_string(class->args
, var
->value
, sizeof(class->args
));
1040 else if (!strcasecmp(var
->name
, "random"))
1041 ast_set2_flag(class, ast_true(var
->value
), MOH_RANDOMIZE
);
1042 else if (!strcasecmp(var
->name
, "format")) {
1043 class->format
= ast_getformatbyname(var
->value
);
1044 if (!class->format
) {
1045 ast_log(LOG_WARNING
, "Unknown format '%s' -- defaulting to SLIN\n", var
->value
);
1046 class->format
= AST_FORMAT_SLINEAR
;
1052 if (ast_strlen_zero(class->dir
)) {
1053 if (!strcasecmp(class->mode
, "custom")) {
1054 strcpy(class->dir
, "nodir");
1056 ast_log(LOG_WARNING
, "A directory must be specified for class '%s'!\n", class->name
);
1061 if (ast_strlen_zero(class->mode
)) {
1062 ast_log(LOG_WARNING
, "A mode must be specified for class '%s'!\n", class->name
);
1066 if (ast_strlen_zero(class->args
) && !strcasecmp(class->mode
, "custom")) {
1067 ast_log(LOG_WARNING
, "An application must be specified for class '%s'!\n", class->name
);
1072 /* Don't leak a class when it's already registered */
1073 moh_register(class, reload
);
1080 /* Deprecated Old-School Configuration */
1081 var
= ast_variable_browse(cfg
, "classes");
1084 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");
1087 data
= strchr(var
->value
, ':');
1090 args
= strchr(data
, ',');
1093 if (!(get_mohbyname(var
->name
, 0))) {
1094 if (!(class = moh_class_malloc())) {
1098 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1099 ast_copy_string(class->dir
, data
, sizeof(class->dir
));
1100 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1102 ast_copy_string(class->args
, args
, sizeof(class->args
));
1104 moh_register(class, reload
);
1110 var
= ast_variable_browse(cfg
, "moh_files");
1113 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");
1116 if (!(get_mohbyname(var
->name
, 0))) {
1117 args
= strchr(var
->value
, ',');
1120 if (!(class = moh_class_malloc())) {
1124 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1125 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1126 strcpy(class->mode
, "files");
1128 ast_copy_string(class->args
, args
, sizeof(class->args
));
1130 moh_register(class, reload
);
1136 ast_config_destroy(cfg
);
1141 static int ast_moh_destroy_one(struct mohclass
*moh
)
1144 int bytes
, tbytes
= 0, stime
= 0, pid
= 0;
1148 ast_log(LOG_DEBUG
, "killing %d!\n", moh
->pid
);
1149 stime
= time(NULL
) + 2;
1152 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1153 * to give the process a reason and time enough to kill off its
1160 while ((ast_wait_for_input(moh
->srcfd
, 100) > 0) && (bytes
= read(moh
->srcfd
, buff
, 8192)) && time(NULL
) < stime
)
1161 tbytes
= tbytes
+ bytes
;
1162 ast_log(LOG_DEBUG
, "mpg123 pid %d and child died after %d bytes read\n", pid
, tbytes
);
1165 ast_moh_free_class(&moh
);
1171 static void ast_moh_destroy(void)
1173 struct mohclass
*moh
;
1175 if (option_verbose
> 1)
1176 ast_verbose(VERBOSE_PREFIX_2
"Destroying musiconhold processes\n");
1178 AST_LIST_LOCK(&mohclasses
);
1179 while ((moh
= AST_LIST_REMOVE_HEAD(&mohclasses
, list
))) {
1180 ast_moh_destroy_one(moh
);
1182 AST_LIST_UNLOCK(&mohclasses
);
1185 static int moh_cli(int fd
, int argc
, char *argv
[])
1191 static int cli_files_show(int fd
, int argc
, char *argv
[])
1194 struct mohclass
*class;
1196 AST_LIST_LOCK(&mohclasses
);
1197 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1198 if (!class->total_files
)
1201 ast_cli(fd
, "Class: %s\n", class->name
);
1202 for (i
= 0; i
< class->total_files
; i
++)
1203 ast_cli(fd
, "\tFile: %s\n", class->filearray
[i
]);
1205 AST_LIST_UNLOCK(&mohclasses
);
1210 static int moh_classes_show(int fd
, int argc
, char *argv
[])
1212 struct mohclass
*class;
1214 AST_LIST_LOCK(&mohclasses
);
1215 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1216 ast_cli(fd
, "Class: %s\n", class->name
);
1217 ast_cli(fd
, "\tMode: %s\n", S_OR(class->mode
, "<none>"));
1218 ast_cli(fd
, "\tDirectory: %s\n", S_OR(class->dir
, "<none>"));
1219 ast_cli(fd
, "\tUse Count: %d\n", class->inuse
);
1220 if (ast_test_flag(class, MOH_CUSTOM
))
1221 ast_cli(fd
, "\tApplication: %s\n", S_OR(class->args
, "<none>"));
1222 if (strcasecmp(class->mode
, "files"))
1223 ast_cli(fd
, "\tFormat: %s\n", ast_getformatname(class->format
));
1225 AST_LIST_UNLOCK(&mohclasses
);
1230 static struct ast_cli_entry cli_moh_classes_show_deprecated
= {
1231 { "moh", "classes", "show"},
1232 moh_classes_show
, NULL
,
1235 static struct ast_cli_entry cli_moh_files_show_deprecated
= {
1236 { "moh", "files", "show"},
1237 cli_files_show
, NULL
,
1240 static struct ast_cli_entry cli_moh
[] = {
1241 { { "moh", "reload"},
1242 moh_cli
, "Music On Hold",
1245 { { "moh", "show", "classes"},
1246 moh_classes_show
, "List MOH classes",
1247 "Lists all MOH classes", NULL
, &cli_moh_classes_show_deprecated
},
1249 { { "moh", "show", "files"},
1250 cli_files_show
, "List MOH file-based classes",
1251 "Lists all loaded file-based MOH classes and their files", NULL
, &cli_moh_files_show_deprecated
},
1254 static int init_classes(int reload
)
1256 struct mohclass
*moh
;
1258 if (!load_moh_classes(reload
)) /* Load classes from config */
1259 return 0; /* Return if nothing is found */
1261 AST_LIST_LOCK(&mohclasses
);
1262 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses
, moh
, list
) {
1263 if (reload
&& moh
->delete) {
1264 AST_LIST_REMOVE_CURRENT(&mohclasses
, list
);
1266 ast_moh_destroy_one(moh
);
1267 } else if (moh
->total_files
) {
1268 if (moh_scan_files(moh
) <= 0) {
1269 ast_log(LOG_WARNING
, "No files found for class '%s'\n", moh
->name
);
1271 AST_LIST_REMOVE_CURRENT(&mohclasses
, list
);
1273 ast_moh_destroy_one(moh
);
1277 AST_LIST_TRAVERSE_SAFE_END
1278 AST_LIST_UNLOCK(&mohclasses
);
1283 static int load_module(void)
1287 res
= ast_register_application(app0
, moh0_exec
, synopsis0
, descrip0
);
1288 ast_register_atexit(ast_moh_destroy
);
1289 ast_cli_register_multiple(cli_moh
, sizeof(cli_moh
) / sizeof(struct ast_cli_entry
));
1291 res
= ast_register_application(app1
, moh1_exec
, synopsis1
, descrip1
);
1293 res
= ast_register_application(app2
, moh2_exec
, synopsis2
, descrip2
);
1295 res
= ast_register_application(app3
, moh3_exec
, synopsis3
, descrip3
);
1297 res
= ast_register_application(app4
, moh4_exec
, synopsis4
, descrip4
);
1299 if (!init_classes(0)) { /* No music classes configured, so skip it */
1300 ast_log(LOG_WARNING
, "No music on hold classes configured, disabling music on hold.\n");
1302 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1308 static int reload(void)
1310 if (init_classes(1))
1311 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1316 static int unload_module(void)
1321 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Music On Hold Resource",
1322 .load
= load_module
,
1323 .unload
= unload_module
,