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;
123 char *save_pos_filename
;
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 /*! A dynamically sized array to hold the list of filenames in "files" mode */
138 /*! The current size of the filearray */
140 /*! The current number of files loaded into the filearray */
143 /*! The format from the MOH source, not applicable to "files" mode */
145 /*! The pid of the external application delivering MOH */
149 /*! Source of audio */
151 /*! FD for timing source */
153 /*! Number of users */
155 unsigned int delete:1;
156 AST_LIST_HEAD_NOLOCK(, mohdata
) members
;
157 AST_LIST_ENTRY(mohclass
) list
;
163 struct mohclass
*parent
;
165 AST_LIST_ENTRY(mohdata
) list
;
168 AST_LIST_HEAD_STATIC(mohclasses
, mohclass
);
170 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
171 #define MPG_123 "/usr/bin/mpg123"
174 static int ast_moh_destroy_one(struct mohclass
*moh
);
175 static int reload(void);
177 static void ast_moh_free_class(struct mohclass
**mohclass
)
179 struct mohdata
*member
;
180 struct mohclass
*class = *mohclass
;
183 while ((member
= AST_LIST_REMOVE_HEAD(&class->members
, list
)))
187 pthread_cancel(class->thread
);
191 if (class->filearray
) {
192 for (i
= 0; i
< class->total_files
; i
++)
193 free(class->filearray
[i
]);
194 free(class->filearray
);
202 static void moh_files_release(struct ast_channel
*chan
, void *data
)
204 struct moh_files_state
*state
;
207 if ((state
= chan
->music_state
)) {
209 ast_closestream(chan
->stream
);
212 if (option_verbose
> 2)
213 ast_verbose(VERBOSE_PREFIX_3
"Stopped music on hold on %s\n", chan
->name
);
215 if (state
->origwfmt
&& ast_set_write_format(chan
, state
->origwfmt
)) {
216 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format '%d'\n", chan
->name
, state
->origwfmt
);
218 state
->save_pos
= state
->pos
;
220 if (ast_atomic_dec_and_test(&state
->class->inuse
) && state
->class->delete)
221 ast_moh_destroy_one(state
->class);
227 static int ast_moh_files_next(struct ast_channel
*chan
)
229 struct moh_files_state
*state
= chan
->music_state
;
232 /* Discontinue a stream if it is running already */
234 ast_closestream(chan
->stream
);
238 if (!state
->class->total_files
) {
239 ast_log(LOG_WARNING
, "No files available for class '%s'\n", state
->class->name
);
243 /* If a specific file has been saved confirm it still exists and that it is still valid */
244 if (state
->save_pos
>= 0 && state
->save_pos
< state
->class->total_files
&& state
->class->filearray
[state
->save_pos
] == state
->save_pos_filename
) {
245 state
->pos
= state
->save_pos
;
246 state
->save_pos
= -1;
247 } else if (ast_test_flag(state
->class, MOH_RANDOMIZE
)) {
248 /* Get a random file and ensure we can open it */
249 for (tries
= 0; tries
< 20; tries
++) {
250 state
->pos
= ast_random() % state
->class->total_files
;
251 if (ast_fileexists(state
->class->filearray
[state
->pos
], NULL
, NULL
) > 0)
254 state
->save_pos
= -1;
257 /* This is easy, just increment our position and make sure we don't exceed the total file count */
259 state
->pos
%= state
->class->total_files
;
260 state
->save_pos
= -1;
264 if (!ast_openstream_full(chan
, state
->class->filearray
[state
->pos
], chan
->language
, 1)) {
265 ast_log(LOG_WARNING
, "Unable to open file '%s': %s\n", state
->class->filearray
[state
->pos
], strerror(errno
));
267 state
->pos
%= state
->class->total_files
;
271 /* Record the pointer to the filename for position resuming later */
272 state
->save_pos_filename
= state
->class->filearray
[state
->pos
];
275 ast_log(LOG_DEBUG
, "%s Opened file %d '%s'\n", chan
->name
, state
->pos
, state
->class->filearray
[state
->pos
]);
278 ast_seekstream(chan
->stream
, state
->samples
, SEEK_SET
);
284 static struct ast_frame
*moh_files_readframe(struct ast_channel
*chan
)
286 struct ast_frame
*f
= NULL
;
288 if (!(chan
->stream
&& (f
= ast_readframe(chan
->stream
)))) {
289 if (!ast_moh_files_next(chan
))
290 f
= ast_readframe(chan
->stream
);
296 static int moh_files_generator(struct ast_channel
*chan
, void *data
, int len
, int samples
)
298 struct moh_files_state
*state
= chan
->music_state
;
299 struct ast_frame
*f
= NULL
;
302 state
->sample_queue
+= samples
;
304 while (state
->sample_queue
> 0) {
305 if ((f
= moh_files_readframe(chan
))) {
306 state
->samples
+= f
->samples
;
307 state
->sample_queue
-= f
->samples
;
308 res
= ast_write(chan
, f
);
311 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
321 static void *moh_files_alloc(struct ast_channel
*chan
, void *params
)
323 struct moh_files_state
*state
;
324 struct mohclass
*class = params
;
326 if (!chan
->music_state
&& (state
= ast_calloc(1, sizeof(*state
)))) {
327 chan
->music_state
= state
;
328 state
->class = class;
329 state
->save_pos
= -1;
331 state
= chan
->music_state
;
334 if (state
->class != class) {
336 memset(state
, 0, sizeof(*state
));
337 state
->class = class;
338 if (ast_test_flag(state
->class, MOH_RANDOMIZE
) && class->total_files
)
339 state
->pos
= ast_random() % class->total_files
;
342 state
->origwfmt
= chan
->writeformat
;
344 if (option_verbose
> 2)
345 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on %s\n", class->name
, chan
->name
);
348 return chan
->music_state
;
351 static struct ast_generator moh_file_stream
=
353 alloc
: moh_files_alloc
,
354 release
: moh_files_release
,
355 generate
: moh_files_generator
,
358 static int spawn_mp3(struct mohclass
*class)
362 char fns
[MAX_MP3S
][80];
363 char *argv
[MAX_MP3S
+ 50];
369 sigset_t signal_set
, old_set
;
372 if (!strcasecmp(class->dir
, "nodir")) {
375 dir
= opendir(class->dir
);
376 if (!dir
&& !strstr(class->dir
,"http://") && !strstr(class->dir
,"HTTP://")) {
377 ast_log(LOG_WARNING
, "%s is not a valid directory\n", class->dir
);
382 if (!ast_test_flag(class, MOH_CUSTOM
)) {
383 argv
[argc
++] = "mpg123";
386 argv
[argc
++] = "--mono";
388 argv
[argc
++] = "8000";
390 if (!ast_test_flag(class, MOH_SINGLE
)) {
392 argv
[argc
++] = "2048";
397 if (ast_test_flag(class, MOH_QUIET
))
398 argv
[argc
++] = "4096";
400 argv
[argc
++] = "8192";
402 /* Look for extra arguments and add them to the list */
403 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
405 while (!ast_strlen_zero(argptr
)) {
406 argv
[argc
++] = argptr
;
407 strsep(&argptr
, ",");
410 /* Format arguments for argv vector */
411 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
413 while (!ast_strlen_zero(argptr
)) {
414 argv
[argc
++] = argptr
;
415 strsep(&argptr
, " ");
420 if (strstr(class->dir
,"http://") || strstr(class->dir
,"HTTP://")) {
421 ast_copy_string(fns
[files
], class->dir
, sizeof(fns
[files
]));
422 argv
[argc
++] = fns
[files
];
425 while ((de
= readdir(dir
)) && (files
< MAX_MP3S
)) {
426 if ((strlen(de
->d_name
) > 3) &&
427 ((ast_test_flag(class, MOH_CUSTOM
) &&
428 (!strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".raw") ||
429 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".sln"))) ||
430 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".mp3"))) {
431 ast_copy_string(fns
[files
], de
->d_name
, sizeof(fns
[files
]));
432 argv
[argc
++] = fns
[files
];
442 ast_log(LOG_WARNING
, "Pipe failed\n");
446 ast_log(LOG_WARNING
, "Found no files in '%s'\n", class->dir
);
451 if (time(NULL
) - class->start
< respawn_time
) {
452 sleep(respawn_time
- (time(NULL
) - class->start
));
455 /* Block signals during the fork() */
456 sigfillset(&signal_set
);
457 pthread_sigmask(SIG_BLOCK
, &signal_set
, &old_set
);
461 if (class->pid
< 0) {
464 ast_log(LOG_WARNING
, "Fork failed: %s\n", strerror(errno
));
470 if (ast_opt_high_priority
)
473 /* Reset ignored signals back to default */
474 signal(SIGPIPE
, SIG_DFL
);
475 pthread_sigmask(SIG_UNBLOCK
, &signal_set
, NULL
);
478 /* Stdout goes to pipe */
479 dup2(fds
[1], STDOUT_FILENO
);
480 /* Close unused file descriptors */
481 for (x
=3;x
<8192;x
++) {
482 if (-1 != fcntl(x
, F_GETFL
)) {
488 if (ast_test_flag(class, MOH_CUSTOM
)) {
489 execv(argv
[0], argv
);
491 /* Default install is /usr/local/bin */
492 execv(LOCAL_MPG_123
, argv
);
493 /* Many places have it in /usr/bin */
494 execv(MPG_123
, argv
);
495 /* Check PATH as a last-ditch effort */
496 execvp("mpg123", argv
);
498 ast_log(LOG_WARNING
, "Exec failed: %s\n", strerror(errno
));
503 pthread_sigmask(SIG_SETMASK
, &old_set
, NULL
);
509 static void *monmp3thread(void *data
)
511 #define MOH_MS_INTERVAL 100
513 struct mohclass
*class = data
;
519 struct timeval tv
, tv_tmp
;
524 pthread_testcancel();
525 /* Spawn mp3 player if it's not there */
526 if (class->srcfd
< 0) {
527 if ((class->srcfd
= spawn_mp3(class)) < 0) {
528 ast_log(LOG_WARNING
, "Unable to spawn mp3player\n");
529 /* Try again later */
531 pthread_testcancel();
534 if (class->pseudofd
> -1) {
538 /* Pause some amount of time */
539 res
= read(class->pseudofd
, buf
, sizeof(buf
));
540 pthread_testcancel();
544 tv_tmp
= ast_tvnow();
547 delta
= ast_tvdiff_ms(tv_tmp
, tv
);
548 if (delta
< MOH_MS_INTERVAL
) { /* too early */
549 tv
= ast_tvadd(tv
, ast_samp2tv(MOH_MS_INTERVAL
, 1000)); /* next deadline */
550 usleep(1000 * (MOH_MS_INTERVAL
- delta
));
551 pthread_testcancel();
553 ast_log(LOG_NOTICE
, "Request to schedule in the past?!?!\n");
556 res
= 8 * MOH_MS_INTERVAL
; /* 8 samples per millisecond */
558 if (AST_LIST_EMPTY(&class->members
))
561 len
= ast_codec_get_len(class->format
, res
);
563 if ((res2
= read(class->srcfd
, sbuf
, len
)) != len
) {
567 pthread_testcancel();
568 if (class->pid
> 1) {
569 kill(class->pid
, SIGHUP
);
571 kill(class->pid
, SIGTERM
);
573 kill(class->pid
, SIGKILL
);
577 ast_log(LOG_DEBUG
, "Read %d bytes of audio while expecting %d\n", res2
, len
);
580 pthread_testcancel();
581 AST_LIST_LOCK(&mohclasses
);
582 AST_LIST_TRAVERSE(&class->members
, moh
, list
) {
584 if ((res
= write(moh
->pipe
[1], sbuf
, res2
)) != res2
) {
586 ast_log(LOG_DEBUG
, "Only wrote %d of %d bytes to pipe\n", res
, res2
);
589 AST_LIST_UNLOCK(&mohclasses
);
594 static int moh0_exec(struct ast_channel
*chan
, void *data
)
596 if (ast_moh_start(chan
, data
, NULL
)) {
597 ast_log(LOG_WARNING
, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data
, chan
->name
);
600 while (!ast_safe_sleep(chan
, 10000));
605 static int moh1_exec(struct ast_channel
*chan
, void *data
)
608 if (!data
|| !atoi(data
)) {
609 ast_log(LOG_WARNING
, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
612 if (ast_moh_start(chan
, NULL
, NULL
)) {
613 ast_log(LOG_WARNING
, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data
), chan
->name
);
616 res
= ast_safe_sleep(chan
, atoi(data
) * 1000);
621 static int moh2_exec(struct ast_channel
*chan
, void *data
)
623 if (ast_strlen_zero(data
)) {
624 ast_log(LOG_WARNING
, "SetMusicOnHold requires an argument (class)\n");
627 ast_string_field_set(chan
, musicclass
, data
);
631 static int moh3_exec(struct ast_channel
*chan
, void *data
)
634 if (data
&& strlen(data
))
636 if (ast_moh_start(chan
, class, NULL
))
637 ast_log(LOG_NOTICE
, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan
->name
);
642 static int moh4_exec(struct ast_channel
*chan
, void *data
)
649 /*! \note This function should be called with the mohclasses list locked */
650 static struct mohclass
*get_mohbyname(const char *name
, int warn
)
652 struct mohclass
*moh
= NULL
;
654 AST_LIST_TRAVERSE(&mohclasses
, moh
, list
) {
655 if (!strcasecmp(name
, moh
->name
))
660 ast_log(LOG_WARNING
, "Music on Hold class '%s' not found\n", name
);
665 static struct mohdata
*mohalloc(struct mohclass
*cl
)
670 if (!(moh
= ast_calloc(1, sizeof(*moh
))))
673 if (pipe(moh
->pipe
)) {
674 ast_log(LOG_WARNING
, "Failed to create pipe: %s\n", strerror(errno
));
679 /* Make entirely non-blocking */
680 flags
= fcntl(moh
->pipe
[0], F_GETFL
);
681 fcntl(moh
->pipe
[0], F_SETFL
, flags
| O_NONBLOCK
);
682 flags
= fcntl(moh
->pipe
[1], F_GETFL
);
683 fcntl(moh
->pipe
[1], F_SETFL
, flags
| O_NONBLOCK
);
685 moh
->f
.frametype
= AST_FRAME_VOICE
;
686 moh
->f
.subclass
= cl
->format
;
687 moh
->f
.offset
= AST_FRIENDLY_OFFSET
;
691 AST_LIST_LOCK(&mohclasses
);
692 AST_LIST_INSERT_HEAD(&cl
->members
, moh
, list
);
693 AST_LIST_UNLOCK(&mohclasses
);
698 static void moh_release(struct ast_channel
*chan
, void *data
)
700 struct mohdata
*moh
= data
;
703 AST_LIST_LOCK(&mohclasses
);
704 AST_LIST_REMOVE(&moh
->parent
->members
, moh
, list
);
705 AST_LIST_UNLOCK(&mohclasses
);
709 oldwfmt
= moh
->origwfmt
;
710 if (moh
->parent
->delete && ast_atomic_dec_and_test(&moh
->parent
->inuse
))
711 ast_moh_destroy_one(moh
->parent
);
714 if (oldwfmt
&& ast_set_write_format(chan
, oldwfmt
))
715 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format %s\n", chan
->name
, ast_getformatname(oldwfmt
));
716 if (option_verbose
> 2)
717 ast_verbose(VERBOSE_PREFIX_3
"Stopped music on hold on %s\n", chan
->name
);
721 static void *moh_alloc(struct ast_channel
*chan
, void *params
)
724 struct mohclass
*class = params
;
726 if ((res
= mohalloc(class))) {
727 res
->origwfmt
= chan
->writeformat
;
728 if (ast_set_write_format(chan
, class->format
)) {
729 ast_log(LOG_WARNING
, "Unable to set channel '%s' to format '%s'\n", chan
->name
, ast_codec2str(class->format
));
730 moh_release(NULL
, res
);
733 if (option_verbose
> 2)
734 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on channel '%s'\n", class->name
, chan
->name
);
739 static int moh_generate(struct ast_channel
*chan
, void *data
, int len
, int samples
)
741 struct mohdata
*moh
= data
;
742 short buf
[1280 + AST_FRIENDLY_OFFSET
/ 2];
745 if (!moh
->parent
->pid
)
748 len
= ast_codec_get_len(moh
->parent
->format
, samples
);
750 if (len
> sizeof(buf
) - AST_FRIENDLY_OFFSET
) {
751 ast_log(LOG_WARNING
, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf
), len
, chan
->name
);
752 len
= sizeof(buf
) - AST_FRIENDLY_OFFSET
;
754 res
= read(moh
->pipe
[0], buf
+ AST_FRIENDLY_OFFSET
/2, len
);
758 moh
->f
.datalen
= res
;
759 moh
->f
.data
= buf
+ AST_FRIENDLY_OFFSET
/ 2;
760 moh
->f
.samples
= ast_codec_get_samples(&moh
->f
);
762 if (ast_write(chan
, &moh
->f
) < 0) {
763 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
770 static struct ast_generator mohgen
=
773 release
: moh_release
,
774 generate
: moh_generate
,
777 static int moh_add_file(struct mohclass
*class, const char *filepath
)
779 if (!class->allowed_files
) {
780 if (!(class->filearray
= ast_calloc(1, INITIAL_NUM_FILES
* sizeof(*class->filearray
))))
782 class->allowed_files
= INITIAL_NUM_FILES
;
783 } else if (class->total_files
== class->allowed_files
) {
784 if (!(class->filearray
= ast_realloc(class->filearray
, class->allowed_files
* sizeof(*class->filearray
) * 2))) {
785 class->allowed_files
= 0;
786 class->total_files
= 0;
789 class->allowed_files
*= 2;
792 if (!(class->filearray
[class->total_files
] = ast_strdup(filepath
)))
795 class->total_files
++;
800 static int moh_scan_files(struct mohclass
*class) {
803 struct dirent
*files_dirent
;
805 char filepath
[PATH_MAX
];
811 files_DIR
= opendir(class->dir
);
813 ast_log(LOG_WARNING
, "Cannot open dir %s or dir does not exist\n", class->dir
);
817 for (i
= 0; i
< class->total_files
; i
++)
818 free(class->filearray
[i
]);
820 class->total_files
= 0;
821 dirnamelen
= strlen(class->dir
) + 2;
822 getcwd(path
, sizeof(path
));
824 while ((files_dirent
= readdir(files_DIR
))) {
825 /* The file name must be at least long enough to have the file type extension */
826 if ((strlen(files_dirent
->d_name
) < 4))
829 /* Skip files that starts with a dot */
830 if (files_dirent
->d_name
[0] == '.')
833 /* Skip files without extensions... they are not audio */
834 if (!strchr(files_dirent
->d_name
, '.'))
837 snprintf(filepath
, sizeof(filepath
), "%s/%s", class->dir
, files_dirent
->d_name
);
839 if (stat(filepath
, &statbuf
))
842 if (!S_ISREG(statbuf
.st_mode
))
845 if ((ext
= strrchr(filepath
, '.'))) {
850 /* if the file is present in multiple formats, ensure we only put it into the list once */
851 for (i
= 0; i
< class->total_files
; i
++)
852 if (!strcmp(filepath
, class->filearray
[i
]))
855 if (i
== class->total_files
) {
856 if (moh_add_file(class, filepath
))
863 return class->total_files
;
866 static int moh_register(struct mohclass
*moh
, int reload
)
871 struct mohclass
*mohclass
= NULL
;
874 AST_LIST_LOCK(&mohclasses
);
875 if ((mohclass
= get_mohbyname(moh
->name
, 0))) {
876 if (!mohclass
->delete) {
877 ast_log(LOG_WARNING
, "Music on Hold class '%s' already exists\n", moh
->name
);
879 AST_LIST_UNLOCK(&mohclasses
);
883 AST_LIST_UNLOCK(&mohclasses
);
886 moh
->start
-= respawn_time
;
888 if (!strcasecmp(moh
->mode
, "files")) {
889 res
= moh_scan_files(moh
);
892 if (option_verbose
> 2)
893 ast_verbose(VERBOSE_PREFIX_3
"Files not found in %s for moh class:%s\n", moh
->dir
, moh
->name
);
895 ast_moh_free_class(&moh
);
898 if (strchr(moh
->args
, 'r'))
899 ast_set_flag(moh
, MOH_RANDOMIZE
);
900 } 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")) {
902 if (!strcasecmp(moh
->mode
, "custom"))
903 ast_set_flag(moh
, MOH_CUSTOM
);
904 else if (!strcasecmp(moh
->mode
, "mp3nb"))
905 ast_set_flag(moh
, MOH_SINGLE
);
906 else if (!strcasecmp(moh
->mode
, "quietmp3nb"))
907 ast_set_flag(moh
, MOH_SINGLE
| MOH_QUIET
);
908 else if (!strcasecmp(moh
->mode
, "quietmp3"))
909 ast_set_flag(moh
, MOH_QUIET
);
913 /* Open /dev/zap/pseudo for timing... Is
914 there a better, yet reliable way to do this? */
915 moh
->pseudofd
= open("/dev/zap/pseudo", O_RDONLY
);
916 if (moh
->pseudofd
< 0) {
917 ast_log(LOG_WARNING
, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
920 ioctl(moh
->pseudofd
, ZT_SET_BLOCKSIZE
, &x
);
925 if (ast_pthread_create_background(&moh
->thread
, NULL
, monmp3thread
, moh
)) {
926 ast_log(LOG_WARNING
, "Unable to create moh...\n");
927 if (moh
->pseudofd
> -1)
928 close(moh
->pseudofd
);
929 ast_moh_free_class(&moh
);
933 ast_log(LOG_WARNING
, "Don't know how to do a mode '%s' music on hold\n", moh
->mode
);
934 ast_moh_free_class(&moh
);
938 AST_LIST_LOCK(&mohclasses
);
939 AST_LIST_INSERT_HEAD(&mohclasses
, moh
, list
);
940 AST_LIST_UNLOCK(&mohclasses
);
945 static void local_ast_moh_cleanup(struct ast_channel
*chan
)
947 if (chan
->music_state
) {
948 free(chan
->music_state
);
949 chan
->music_state
= NULL
;
953 static int local_ast_moh_start(struct ast_channel
*chan
, const char *mclass
, const char *interpclass
)
955 struct mohclass
*mohclass
= NULL
;
957 /* The following is the order of preference for which class to use:
958 * 1) The channels explicitly set musicclass, which should *only* be
959 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
960 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
961 * result of receiving a HOLD control frame, this should be the
962 * payload that came with the frame.
963 * 3) The interpclass argument. This would be from the mohinterpret
964 * option from channel drivers. This is the same as the old musicclass
966 * 4) The default class.
968 AST_LIST_LOCK(&mohclasses
);
969 if (!ast_strlen_zero(chan
->musicclass
))
970 mohclass
= get_mohbyname(chan
->musicclass
, 1);
971 if (!mohclass
&& !ast_strlen_zero(mclass
))
972 mohclass
= get_mohbyname(mclass
, 1);
973 if (!mohclass
&& !ast_strlen_zero(interpclass
))
974 mohclass
= get_mohbyname(interpclass
, 1);
976 mohclass
= get_mohbyname("default", 1);
978 ast_atomic_fetchadd_int(&mohclass
->inuse
, +1);
979 AST_LIST_UNLOCK(&mohclasses
);
984 ast_set_flag(chan
, AST_FLAG_MOH
);
985 if (mohclass
->total_files
) {
986 return ast_activate_generator(chan
, &moh_file_stream
, mohclass
);
988 return ast_activate_generator(chan
, &mohgen
, mohclass
);
991 static void local_ast_moh_stop(struct ast_channel
*chan
)
993 ast_clear_flag(chan
, AST_FLAG_MOH
);
994 ast_deactivate_generator(chan
);
996 if (chan
->music_state
) {
998 ast_closestream(chan
->stream
);
1004 static struct mohclass
*moh_class_malloc(void)
1006 struct mohclass
*class;
1008 if ((class = ast_calloc(1, sizeof(*class))))
1009 class->format
= AST_FORMAT_SLINEAR
;
1014 static int load_moh_classes(int reload
)
1016 struct ast_config
*cfg
;
1017 struct ast_variable
*var
;
1018 struct mohclass
*class;
1023 static int dep_warning
= 0;
1025 cfg
= ast_config_load("musiconhold.conf");
1031 AST_LIST_LOCK(&mohclasses
);
1032 AST_LIST_TRAVERSE(&mohclasses
, class, list
)
1034 AST_LIST_UNLOCK(&mohclasses
);
1037 cat
= ast_category_browse(cfg
, NULL
);
1038 for (; cat
; cat
= ast_category_browse(cfg
, cat
)) {
1039 if (strcasecmp(cat
, "classes") && strcasecmp(cat
, "moh_files")) {
1040 if (!(class = moh_class_malloc())) {
1043 ast_copy_string(class->name
, cat
, sizeof(class->name
));
1044 var
= ast_variable_browse(cfg
, cat
);
1046 if (!strcasecmp(var
->name
, "mode"))
1047 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1048 else if (!strcasecmp(var
->name
, "directory"))
1049 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1050 else if (!strcasecmp(var
->name
, "application"))
1051 ast_copy_string(class->args
, var
->value
, sizeof(class->args
));
1052 else if (!strcasecmp(var
->name
, "random"))
1053 ast_set2_flag(class, ast_true(var
->value
), MOH_RANDOMIZE
);
1054 else if (!strcasecmp(var
->name
, "format")) {
1055 class->format
= ast_getformatbyname(var
->value
);
1056 if (!class->format
) {
1057 ast_log(LOG_WARNING
, "Unknown format '%s' -- defaulting to SLIN\n", var
->value
);
1058 class->format
= AST_FORMAT_SLINEAR
;
1064 if (ast_strlen_zero(class->dir
)) {
1065 if (!strcasecmp(class->mode
, "custom")) {
1066 strcpy(class->dir
, "nodir");
1068 ast_log(LOG_WARNING
, "A directory must be specified for class '%s'!\n", class->name
);
1073 if (ast_strlen_zero(class->mode
)) {
1074 ast_log(LOG_WARNING
, "A mode must be specified for class '%s'!\n", class->name
);
1078 if (ast_strlen_zero(class->args
) && !strcasecmp(class->mode
, "custom")) {
1079 ast_log(LOG_WARNING
, "An application must be specified for class '%s'!\n", class->name
);
1084 /* Don't leak a class when it's already registered */
1085 moh_register(class, reload
);
1092 /* Deprecated Old-School Configuration */
1093 var
= ast_variable_browse(cfg
, "classes");
1096 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");
1099 data
= strchr(var
->value
, ':');
1102 args
= strchr(data
, ',');
1105 if (!(get_mohbyname(var
->name
, 0))) {
1106 if (!(class = moh_class_malloc())) {
1110 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1111 ast_copy_string(class->dir
, data
, sizeof(class->dir
));
1112 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1114 ast_copy_string(class->args
, args
, sizeof(class->args
));
1116 moh_register(class, reload
);
1122 var
= ast_variable_browse(cfg
, "moh_files");
1125 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");
1128 if (!(get_mohbyname(var
->name
, 0))) {
1129 args
= strchr(var
->value
, ',');
1132 if (!(class = moh_class_malloc())) {
1136 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1137 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1138 strcpy(class->mode
, "files");
1140 ast_copy_string(class->args
, args
, sizeof(class->args
));
1142 moh_register(class, reload
);
1148 ast_config_destroy(cfg
);
1153 static int ast_moh_destroy_one(struct mohclass
*moh
)
1156 int bytes
, tbytes
= 0, stime
= 0, pid
= 0;
1160 ast_log(LOG_DEBUG
, "killing %d!\n", moh
->pid
);
1161 stime
= time(NULL
) + 2;
1164 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1165 * to give the process a reason and time enough to kill off its
1172 while ((ast_wait_for_input(moh
->srcfd
, 100) > 0) && (bytes
= read(moh
->srcfd
, buff
, 8192)) && time(NULL
) < stime
)
1173 tbytes
= tbytes
+ bytes
;
1174 ast_log(LOG_DEBUG
, "mpg123 pid %d and child died after %d bytes read\n", pid
, tbytes
);
1177 ast_moh_free_class(&moh
);
1183 static void ast_moh_destroy(void)
1185 struct mohclass
*moh
;
1187 if (option_verbose
> 1)
1188 ast_verbose(VERBOSE_PREFIX_2
"Destroying musiconhold processes\n");
1190 AST_LIST_LOCK(&mohclasses
);
1191 while ((moh
= AST_LIST_REMOVE_HEAD(&mohclasses
, list
))) {
1192 ast_moh_destroy_one(moh
);
1194 AST_LIST_UNLOCK(&mohclasses
);
1197 static int moh_cli(int fd
, int argc
, char *argv
[])
1203 static int cli_files_show(int fd
, int argc
, char *argv
[])
1206 struct mohclass
*class;
1208 AST_LIST_LOCK(&mohclasses
);
1209 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1210 if (!class->total_files
)
1213 ast_cli(fd
, "Class: %s\n", class->name
);
1214 for (i
= 0; i
< class->total_files
; i
++)
1215 ast_cli(fd
, "\tFile: %s\n", class->filearray
[i
]);
1217 AST_LIST_UNLOCK(&mohclasses
);
1222 static int moh_classes_show(int fd
, int argc
, char *argv
[])
1224 struct mohclass
*class;
1226 AST_LIST_LOCK(&mohclasses
);
1227 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1228 ast_cli(fd
, "Class: %s\n", class->name
);
1229 ast_cli(fd
, "\tMode: %s\n", S_OR(class->mode
, "<none>"));
1230 ast_cli(fd
, "\tDirectory: %s\n", S_OR(class->dir
, "<none>"));
1231 ast_cli(fd
, "\tUse Count: %d\n", class->inuse
);
1232 if (ast_test_flag(class, MOH_CUSTOM
))
1233 ast_cli(fd
, "\tApplication: %s\n", S_OR(class->args
, "<none>"));
1234 if (strcasecmp(class->mode
, "files"))
1235 ast_cli(fd
, "\tFormat: %s\n", ast_getformatname(class->format
));
1237 AST_LIST_UNLOCK(&mohclasses
);
1242 static struct ast_cli_entry cli_moh_classes_show_deprecated
= {
1243 { "moh", "classes", "show"},
1244 moh_classes_show
, NULL
,
1247 static struct ast_cli_entry cli_moh_files_show_deprecated
= {
1248 { "moh", "files", "show"},
1249 cli_files_show
, NULL
,
1252 static struct ast_cli_entry cli_moh
[] = {
1253 { { "moh", "reload"},
1254 moh_cli
, "Music On Hold",
1255 "Usage: moh reload\n Rereads configuration\n" },
1257 { { "moh", "show", "classes"},
1258 moh_classes_show
, "List MOH classes",
1259 "Usage: moh show classes\n Lists all MOH classes\n", NULL
, &cli_moh_classes_show_deprecated
},
1261 { { "moh", "show", "files"},
1262 cli_files_show
, "List MOH file-based classes",
1263 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL
, &cli_moh_files_show_deprecated
},
1266 static int init_classes(int reload
)
1268 struct mohclass
*moh
;
1270 if (!load_moh_classes(reload
)) /* Load classes from config */
1271 return 0; /* Return if nothing is found */
1273 AST_LIST_LOCK(&mohclasses
);
1274 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses
, moh
, list
) {
1275 if (reload
&& moh
->delete) {
1276 AST_LIST_REMOVE_CURRENT(&mohclasses
, list
);
1278 ast_moh_destroy_one(moh
);
1281 AST_LIST_TRAVERSE_SAFE_END
1282 AST_LIST_UNLOCK(&mohclasses
);
1287 static int load_module(void)
1291 res
= ast_register_application(app0
, moh0_exec
, synopsis0
, descrip0
);
1292 ast_register_atexit(ast_moh_destroy
);
1293 ast_cli_register_multiple(cli_moh
, sizeof(cli_moh
) / sizeof(struct ast_cli_entry
));
1295 res
= ast_register_application(app1
, moh1_exec
, synopsis1
, descrip1
);
1297 res
= ast_register_application(app2
, moh2_exec
, synopsis2
, descrip2
);
1299 res
= ast_register_application(app3
, moh3_exec
, synopsis3
, descrip3
);
1301 res
= ast_register_application(app4
, moh4_exec
, synopsis4
, descrip4
);
1303 if (!init_classes(0)) { /* No music classes configured, so skip it */
1304 ast_log(LOG_WARNING
, "No music on hold classes configured, disabling music on hold.\n");
1306 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1312 static int reload(void)
1314 if (init_classes(1))
1315 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1320 static int unload_module(void)
1323 struct mohclass
*class = NULL
;
1325 AST_LIST_LOCK(&mohclasses
);
1326 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1327 if (class->inuse
> 0) {
1332 AST_LIST_UNLOCK(&mohclasses
);
1334 ast_log(LOG_WARNING
, "Unable to unload res_musiconhold due to active MOH channels\n");
1338 ast_uninstall_music_functions();
1340 res
= ast_unregister_application(app0
);
1341 res
|= ast_unregister_application(app1
);
1342 res
|= ast_unregister_application(app2
);
1343 res
|= ast_unregister_application(app3
);
1344 res
|= ast_unregister_application(app4
);
1345 ast_cli_unregister_multiple(cli_moh
, sizeof(cli_moh
) / sizeof(struct ast_cli_entry
));
1349 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Music On Hold Resource",
1350 .load
= load_module
,
1351 .unload
= unload_module
,