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>
55 #include "asterisk/lock.h"
56 #include "asterisk/file.h"
57 #include "asterisk/logger.h"
58 #include "asterisk/channel.h"
59 #include "asterisk/pbx.h"
60 #include "asterisk/options.h"
61 #include "asterisk/module.h"
62 #include "asterisk/translate.h"
63 #include "asterisk/say.h"
64 #include "asterisk/musiconhold.h"
65 #include "asterisk/config.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/cli.h"
68 #include "asterisk/stringfields.h"
69 #include "asterisk/linkedlists.h"
71 #include "asterisk/dahdi_compat.h"
73 #define INITIAL_NUM_FILES 8
75 static char *app0
= "MusicOnHold";
76 static char *app1
= "WaitMusicOnHold";
77 static char *app2
= "SetMusicOnHold";
78 static char *app3
= "StartMusicOnHold";
79 static char *app4
= "StopMusicOnHold";
81 static char *synopsis0
= "Play Music On Hold indefinitely";
82 static char *synopsis1
= "Wait, playing Music On Hold";
83 static char *synopsis2
= "Set default Music On Hold class";
84 static char *synopsis3
= "Play Music On Hold";
85 static char *synopsis4
= "Stop Playing Music On Hold";
87 static char *descrip0
= "MusicOnHold(class): "
88 "Plays hold music specified by class. If omitted, the default\n"
89 "music source for the channel will be used. Set the default \n"
90 "class with the SetMusicOnHold() application.\n"
91 "Returns -1 on hangup.\n"
92 "Never returns otherwise.\n";
94 static char *descrip1
= "WaitMusicOnHold(delay): "
95 "Plays hold music specified number of seconds. Returns 0 when\n"
96 "done, or -1 on hangup. If no hold music is available, the delay will\n"
97 "still occur with no sound.\n";
99 static char *descrip2
= "SetMusicOnHold(class): "
100 "Sets the default class for music on hold for a given channel. When\n"
101 "music on hold is activated, this class will be used to select which\n"
102 "music is played.\n";
104 static char *descrip3
= "StartMusicOnHold(class): "
105 "Starts playing music on hold, uses default music class for channel.\n"
106 "Starts playing music specified by class. If omitted, the default\n"
107 "music source for the channel will be used. Always returns 0.\n";
109 static char *descrip4
= "StopMusicOnHold: "
110 "Stops playing music on hold.\n";
112 static int respawn_time
= 20;
114 struct moh_files_state
{
115 struct mohclass
*class;
121 char *save_pos_filename
;
124 #define MOH_QUIET (1 << 0)
125 #define MOH_SINGLE (1 << 1)
126 #define MOH_CUSTOM (1 << 2)
127 #define MOH_RANDOMIZE (1 << 3)
130 char name
[MAX_MUSICCLASS
];
134 /*! A dynamically sized array to hold the list of filenames in "files" mode */
136 /*! The current size of the filearray */
138 /*! The current number of files loaded into the filearray */
141 /*! The format from the MOH source, not applicable to "files" mode */
143 /*! The pid of the external application delivering MOH */
147 /*! Source of audio */
149 /*! FD for timing source */
151 /*! Number of users */
153 unsigned int delete:1;
154 AST_LIST_HEAD_NOLOCK(, mohdata
) members
;
155 AST_LIST_ENTRY(mohclass
) list
;
161 struct mohclass
*parent
;
163 AST_LIST_ENTRY(mohdata
) list
;
166 AST_LIST_HEAD_STATIC(mohclasses
, mohclass
);
168 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
169 #define MPG_123 "/usr/bin/mpg123"
172 static int ast_moh_destroy_one(struct mohclass
*moh
);
173 static int reload(void);
175 static void ast_moh_free_class(struct mohclass
**mohclass
)
177 struct mohdata
*member
;
178 struct mohclass
*class = *mohclass
;
181 while ((member
= AST_LIST_REMOVE_HEAD(&class->members
, list
)))
185 pthread_cancel(class->thread
);
189 if (class->filearray
) {
190 for (i
= 0; i
< class->total_files
; i
++)
191 free(class->filearray
[i
]);
192 free(class->filearray
);
200 static void moh_files_release(struct ast_channel
*chan
, void *data
)
202 struct moh_files_state
*state
;
205 if ((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);
225 static int ast_moh_files_next(struct ast_channel
*chan
)
227 struct moh_files_state
*state
= chan
->music_state
;
230 /* Discontinue a stream if it is running already */
232 ast_closestream(chan
->stream
);
236 if (!state
->class->total_files
) {
237 ast_log(LOG_WARNING
, "No files available for class '%s'\n", state
->class->name
);
241 /* If a specific file has been saved confirm it still exists and that it is still valid */
242 if (state
->save_pos
>= 0 && state
->save_pos
< state
->class->total_files
&& state
->class->filearray
[state
->save_pos
] == state
->save_pos_filename
) {
243 state
->pos
= state
->save_pos
;
244 state
->save_pos
= -1;
245 } else if (ast_test_flag(state
->class, MOH_RANDOMIZE
)) {
246 /* Get a random file and ensure we can open it */
247 for (tries
= 0; tries
< 20; tries
++) {
248 state
->pos
= ast_random() % state
->class->total_files
;
249 if (ast_fileexists(state
->class->filearray
[state
->pos
], NULL
, NULL
) > 0)
252 state
->save_pos
= -1;
255 /* This is easy, just increment our position and make sure we don't exceed the total file count */
257 state
->pos
%= state
->class->total_files
;
258 state
->save_pos
= -1;
262 if (!ast_openstream_full(chan
, state
->class->filearray
[state
->pos
], chan
->language
, 1)) {
263 ast_log(LOG_WARNING
, "Unable to open file '%s': %s\n", state
->class->filearray
[state
->pos
], strerror(errno
));
265 state
->pos
%= state
->class->total_files
;
269 /* Record the pointer to the filename for position resuming later */
270 state
->save_pos_filename
= state
->class->filearray
[state
->pos
];
273 ast_log(LOG_DEBUG
, "%s Opened file %d '%s'\n", chan
->name
, state
->pos
, state
->class->filearray
[state
->pos
]);
276 ast_seekstream(chan
->stream
, state
->samples
, SEEK_SET
);
282 static struct ast_frame
*moh_files_readframe(struct ast_channel
*chan
)
284 struct ast_frame
*f
= NULL
;
286 if (!(chan
->stream
&& (f
= ast_readframe(chan
->stream
)))) {
287 if (!ast_moh_files_next(chan
))
288 f
= ast_readframe(chan
->stream
);
294 static int moh_files_generator(struct ast_channel
*chan
, void *data
, int len
, int samples
)
296 struct moh_files_state
*state
= chan
->music_state
;
297 struct ast_frame
*f
= NULL
;
300 state
->sample_queue
+= samples
;
302 while (state
->sample_queue
> 0) {
303 if ((f
= moh_files_readframe(chan
))) {
304 state
->samples
+= f
->samples
;
305 state
->sample_queue
-= f
->samples
;
306 res
= ast_write(chan
, f
);
309 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
319 static void *moh_files_alloc(struct ast_channel
*chan
, void *params
)
321 struct moh_files_state
*state
;
322 struct mohclass
*class = params
;
324 if (!chan
->music_state
&& (state
= ast_calloc(1, sizeof(*state
)))) {
325 chan
->music_state
= state
;
326 state
->class = class;
327 state
->save_pos
= -1;
329 state
= chan
->music_state
;
332 if (state
->class != class) {
334 memset(state
, 0, sizeof(*state
));
335 state
->class = class;
336 if (ast_test_flag(state
->class, MOH_RANDOMIZE
) && class->total_files
)
337 state
->pos
= ast_random() % class->total_files
;
340 state
->origwfmt
= chan
->writeformat
;
342 if (option_verbose
> 2)
343 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on %s\n", class->name
, chan
->name
);
346 return chan
->music_state
;
349 static struct ast_generator moh_file_stream
=
351 alloc
: moh_files_alloc
,
352 release
: moh_files_release
,
353 generate
: moh_files_generator
,
356 static int spawn_mp3(struct mohclass
*class)
360 char fns
[MAX_MP3S
][80];
361 char *argv
[MAX_MP3S
+ 50];
367 sigset_t signal_set
, old_set
;
370 if (!strcasecmp(class->dir
, "nodir")) {
373 dir
= opendir(class->dir
);
374 if (!dir
&& !strstr(class->dir
,"http://") && !strstr(class->dir
,"HTTP://")) {
375 ast_log(LOG_WARNING
, "%s is not a valid directory\n", class->dir
);
380 if (!ast_test_flag(class, MOH_CUSTOM
)) {
381 argv
[argc
++] = "mpg123";
384 argv
[argc
++] = "--mono";
386 argv
[argc
++] = "8000";
388 if (!ast_test_flag(class, MOH_SINGLE
)) {
390 argv
[argc
++] = "2048";
395 if (ast_test_flag(class, MOH_QUIET
))
396 argv
[argc
++] = "4096";
398 argv
[argc
++] = "8192";
400 /* Look for extra arguments and add them to the list */
401 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
403 while (!ast_strlen_zero(argptr
)) {
404 argv
[argc
++] = argptr
;
405 strsep(&argptr
, ",");
408 /* Format arguments for argv vector */
409 ast_copy_string(xargs
, class->args
, sizeof(xargs
));
411 while (!ast_strlen_zero(argptr
)) {
412 argv
[argc
++] = argptr
;
413 strsep(&argptr
, " ");
418 if (strstr(class->dir
,"http://") || strstr(class->dir
,"HTTP://")) {
419 ast_copy_string(fns
[files
], class->dir
, sizeof(fns
[files
]));
420 argv
[argc
++] = fns
[files
];
423 while ((de
= readdir(dir
)) && (files
< MAX_MP3S
)) {
424 if ((strlen(de
->d_name
) > 3) &&
425 ((ast_test_flag(class, MOH_CUSTOM
) &&
426 (!strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".raw") ||
427 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".sln"))) ||
428 !strcasecmp(de
->d_name
+ strlen(de
->d_name
) - 4, ".mp3"))) {
429 ast_copy_string(fns
[files
], de
->d_name
, sizeof(fns
[files
]));
430 argv
[argc
++] = fns
[files
];
440 ast_log(LOG_WARNING
, "Pipe failed\n");
444 ast_log(LOG_WARNING
, "Found no files in '%s'\n", class->dir
);
449 if (time(NULL
) - class->start
< respawn_time
) {
450 sleep(respawn_time
- (time(NULL
) - class->start
));
453 /* Block signals during the fork() */
454 sigfillset(&signal_set
);
455 pthread_sigmask(SIG_BLOCK
, &signal_set
, &old_set
);
459 if (class->pid
< 0) {
462 ast_log(LOG_WARNING
, "Fork failed: %s\n", strerror(errno
));
468 if (ast_opt_high_priority
)
471 /* Reset ignored signals back to default */
472 signal(SIGPIPE
, SIG_DFL
);
473 pthread_sigmask(SIG_UNBLOCK
, &signal_set
, NULL
);
476 /* Stdout goes to pipe */
477 dup2(fds
[1], STDOUT_FILENO
);
478 /* Close unused file descriptors */
479 for (x
=3;x
<8192;x
++) {
480 if (-1 != fcntl(x
, F_GETFL
)) {
486 if (ast_test_flag(class, MOH_CUSTOM
)) {
487 execv(argv
[0], argv
);
489 /* Default install is /usr/local/bin */
490 execv(LOCAL_MPG_123
, argv
);
491 /* Many places have it in /usr/bin */
492 execv(MPG_123
, argv
);
493 /* Check PATH as a last-ditch effort */
494 execvp("mpg123", argv
);
496 ast_log(LOG_WARNING
, "Exec failed: %s\n", strerror(errno
));
501 pthread_sigmask(SIG_SETMASK
, &old_set
, NULL
);
507 static void *monmp3thread(void *data
)
509 #define MOH_MS_INTERVAL 100
511 struct mohclass
*class = data
;
517 struct timeval tv
, tv_tmp
;
522 pthread_testcancel();
523 /* Spawn mp3 player if it's not there */
524 if (class->srcfd
< 0) {
525 if ((class->srcfd
= spawn_mp3(class)) < 0) {
526 ast_log(LOG_WARNING
, "Unable to spawn mp3player\n");
527 /* Try again later */
529 pthread_testcancel();
532 if (class->pseudofd
> -1) {
536 /* Pause some amount of time */
537 res
= read(class->pseudofd
, buf
, sizeof(buf
));
538 pthread_testcancel();
542 tv_tmp
= ast_tvnow();
545 delta
= ast_tvdiff_ms(tv_tmp
, tv
);
546 if (delta
< MOH_MS_INTERVAL
) { /* too early */
547 tv
= ast_tvadd(tv
, ast_samp2tv(MOH_MS_INTERVAL
, 1000)); /* next deadline */
548 usleep(1000 * (MOH_MS_INTERVAL
- delta
));
549 pthread_testcancel();
551 ast_log(LOG_NOTICE
, "Request to schedule in the past?!?!\n");
554 res
= 8 * MOH_MS_INTERVAL
; /* 8 samples per millisecond */
556 if (AST_LIST_EMPTY(&class->members
))
559 len
= ast_codec_get_len(class->format
, res
);
561 if ((res2
= read(class->srcfd
, sbuf
, len
)) != len
) {
565 pthread_testcancel();
566 if (class->pid
> 1) {
567 kill(class->pid
, SIGHUP
);
569 kill(class->pid
, SIGTERM
);
571 kill(class->pid
, SIGKILL
);
575 ast_log(LOG_DEBUG
, "Read %d bytes of audio while expecting %d\n", res2
, len
);
578 pthread_testcancel();
579 AST_LIST_LOCK(&mohclasses
);
580 AST_LIST_TRAVERSE(&class->members
, moh
, list
) {
582 if ((res
= write(moh
->pipe
[1], sbuf
, res2
)) != res2
) {
584 ast_log(LOG_DEBUG
, "Only wrote %d of %d bytes to pipe\n", res
, res2
);
587 AST_LIST_UNLOCK(&mohclasses
);
592 static int moh0_exec(struct ast_channel
*chan
, void *data
)
594 if (ast_moh_start(chan
, data
, NULL
)) {
595 ast_log(LOG_WARNING
, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data
, chan
->name
);
598 while (!ast_safe_sleep(chan
, 10000));
603 static int moh1_exec(struct ast_channel
*chan
, void *data
)
606 if (!data
|| !atoi(data
)) {
607 ast_log(LOG_WARNING
, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
610 if (ast_moh_start(chan
, NULL
, NULL
)) {
611 ast_log(LOG_WARNING
, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data
), chan
->name
);
614 res
= ast_safe_sleep(chan
, atoi(data
) * 1000);
619 static int moh2_exec(struct ast_channel
*chan
, void *data
)
621 if (ast_strlen_zero(data
)) {
622 ast_log(LOG_WARNING
, "SetMusicOnHold requires an argument (class)\n");
625 ast_string_field_set(chan
, musicclass
, data
);
629 static int moh3_exec(struct ast_channel
*chan
, void *data
)
632 if (data
&& strlen(data
))
634 if (ast_moh_start(chan
, class, NULL
))
635 ast_log(LOG_NOTICE
, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan
->name
);
640 static int moh4_exec(struct ast_channel
*chan
, void *data
)
647 /*! \note This function should be called with the mohclasses list locked */
648 static struct mohclass
*get_mohbyname(const char *name
, int warn
)
650 struct mohclass
*moh
= NULL
;
652 AST_LIST_TRAVERSE(&mohclasses
, moh
, list
) {
653 if (!strcasecmp(name
, moh
->name
))
658 ast_log(LOG_WARNING
, "Music on Hold class '%s' not found\n", name
);
663 static struct mohdata
*mohalloc(struct mohclass
*cl
)
668 if (!(moh
= ast_calloc(1, sizeof(*moh
))))
671 if (pipe(moh
->pipe
)) {
672 ast_log(LOG_WARNING
, "Failed to create pipe: %s\n", strerror(errno
));
677 /* Make entirely non-blocking */
678 flags
= fcntl(moh
->pipe
[0], F_GETFL
);
679 fcntl(moh
->pipe
[0], F_SETFL
, flags
| O_NONBLOCK
);
680 flags
= fcntl(moh
->pipe
[1], F_GETFL
);
681 fcntl(moh
->pipe
[1], F_SETFL
, flags
| O_NONBLOCK
);
683 moh
->f
.frametype
= AST_FRAME_VOICE
;
684 moh
->f
.subclass
= cl
->format
;
685 moh
->f
.offset
= AST_FRIENDLY_OFFSET
;
689 AST_LIST_LOCK(&mohclasses
);
690 AST_LIST_INSERT_HEAD(&cl
->members
, moh
, list
);
691 AST_LIST_UNLOCK(&mohclasses
);
696 static void moh_release(struct ast_channel
*chan
, void *data
)
698 struct mohdata
*moh
= data
;
701 AST_LIST_LOCK(&mohclasses
);
702 AST_LIST_REMOVE(&moh
->parent
->members
, moh
, list
);
703 AST_LIST_UNLOCK(&mohclasses
);
707 oldwfmt
= moh
->origwfmt
;
708 if (moh
->parent
->delete && ast_atomic_dec_and_test(&moh
->parent
->inuse
))
709 ast_moh_destroy_one(moh
->parent
);
712 if (oldwfmt
&& ast_set_write_format(chan
, oldwfmt
))
713 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format %s\n", chan
->name
, ast_getformatname(oldwfmt
));
714 if (option_verbose
> 2)
715 ast_verbose(VERBOSE_PREFIX_3
"Stopped music on hold on %s\n", chan
->name
);
719 static void *moh_alloc(struct ast_channel
*chan
, void *params
)
722 struct mohclass
*class = params
;
724 if ((res
= mohalloc(class))) {
725 res
->origwfmt
= chan
->writeformat
;
726 if (ast_set_write_format(chan
, class->format
)) {
727 ast_log(LOG_WARNING
, "Unable to set channel '%s' to format '%s'\n", chan
->name
, ast_codec2str(class->format
));
728 moh_release(NULL
, res
);
731 if (option_verbose
> 2)
732 ast_verbose(VERBOSE_PREFIX_3
"Started music on hold, class '%s', on channel '%s'\n", class->name
, chan
->name
);
737 static int moh_generate(struct ast_channel
*chan
, void *data
, int len
, int samples
)
739 struct mohdata
*moh
= data
;
740 short buf
[1280 + AST_FRIENDLY_OFFSET
/ 2];
743 if (!moh
->parent
->pid
)
746 len
= ast_codec_get_len(moh
->parent
->format
, samples
);
748 if (len
> sizeof(buf
) - AST_FRIENDLY_OFFSET
) {
749 ast_log(LOG_WARNING
, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf
), len
, chan
->name
);
750 len
= sizeof(buf
) - AST_FRIENDLY_OFFSET
;
752 res
= read(moh
->pipe
[0], buf
+ AST_FRIENDLY_OFFSET
/2, len
);
756 moh
->f
.datalen
= res
;
757 moh
->f
.data
= buf
+ AST_FRIENDLY_OFFSET
/ 2;
758 moh
->f
.samples
= ast_codec_get_samples(&moh
->f
);
760 if (ast_write(chan
, &moh
->f
) < 0) {
761 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
768 static struct ast_generator mohgen
=
771 release
: moh_release
,
772 generate
: moh_generate
,
775 static int moh_add_file(struct mohclass
*class, const char *filepath
)
777 if (!class->allowed_files
) {
778 if (!(class->filearray
= ast_calloc(1, INITIAL_NUM_FILES
* sizeof(*class->filearray
))))
780 class->allowed_files
= INITIAL_NUM_FILES
;
781 } else if (class->total_files
== class->allowed_files
) {
782 if (!(class->filearray
= ast_realloc(class->filearray
, class->allowed_files
* sizeof(*class->filearray
) * 2))) {
783 class->allowed_files
= 0;
784 class->total_files
= 0;
787 class->allowed_files
*= 2;
790 if (!(class->filearray
[class->total_files
] = ast_strdup(filepath
)))
793 class->total_files
++;
798 static int moh_scan_files(struct mohclass
*class) {
801 struct dirent
*files_dirent
;
803 char filepath
[PATH_MAX
];
809 files_DIR
= opendir(class->dir
);
811 ast_log(LOG_WARNING
, "Cannot open dir %s or dir does not exist\n", class->dir
);
815 for (i
= 0; i
< class->total_files
; i
++)
816 free(class->filearray
[i
]);
818 class->total_files
= 0;
819 dirnamelen
= strlen(class->dir
) + 2;
820 getcwd(path
, sizeof(path
));
822 while ((files_dirent
= readdir(files_DIR
))) {
823 /* The file name must be at least long enough to have the file type extension */
824 if ((strlen(files_dirent
->d_name
) < 4))
827 /* Skip files that starts with a dot */
828 if (files_dirent
->d_name
[0] == '.')
831 /* Skip files without extensions... they are not audio */
832 if (!strchr(files_dirent
->d_name
, '.'))
835 snprintf(filepath
, sizeof(filepath
), "%s/%s", class->dir
, files_dirent
->d_name
);
837 if (stat(filepath
, &statbuf
))
840 if (!S_ISREG(statbuf
.st_mode
))
843 if ((ext
= strrchr(filepath
, '.'))) {
848 /* if the file is present in multiple formats, ensure we only put it into the list once */
849 for (i
= 0; i
< class->total_files
; i
++)
850 if (!strcmp(filepath
, class->filearray
[i
]))
853 if (i
== class->total_files
) {
854 if (moh_add_file(class, filepath
))
861 return class->total_files
;
864 static int moh_register(struct mohclass
*moh
, int reload
)
869 struct mohclass
*mohclass
= NULL
;
872 AST_LIST_LOCK(&mohclasses
);
873 if ((mohclass
= get_mohbyname(moh
->name
, 0))) {
874 if (!mohclass
->delete) {
875 ast_log(LOG_WARNING
, "Music on Hold class '%s' already exists\n", moh
->name
);
877 AST_LIST_UNLOCK(&mohclasses
);
881 AST_LIST_UNLOCK(&mohclasses
);
884 moh
->start
-= respawn_time
;
886 if (!strcasecmp(moh
->mode
, "files")) {
887 res
= moh_scan_files(moh
);
890 if (option_verbose
> 2)
891 ast_verbose(VERBOSE_PREFIX_3
"Files not found in %s for moh class:%s\n", moh
->dir
, moh
->name
);
893 ast_moh_free_class(&moh
);
896 if (strchr(moh
->args
, 'r'))
897 ast_set_flag(moh
, MOH_RANDOMIZE
);
898 } 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")) {
900 if (!strcasecmp(moh
->mode
, "custom"))
901 ast_set_flag(moh
, MOH_CUSTOM
);
902 else if (!strcasecmp(moh
->mode
, "mp3nb"))
903 ast_set_flag(moh
, MOH_SINGLE
);
904 else if (!strcasecmp(moh
->mode
, "quietmp3nb"))
905 ast_set_flag(moh
, MOH_SINGLE
| MOH_QUIET
);
906 else if (!strcasecmp(moh
->mode
, "quietmp3"))
907 ast_set_flag(moh
, MOH_QUIET
);
911 /* Open /dev/zap/pseudo for timing... Is
912 there a better, yet reliable way to do this? */
914 moh
->pseudofd
= open("/dev/zap/pseudo", O_RDONLY
);
916 moh
->pseudofd
= open("/dev/dahdi/pseudo", O_RDONLY
);
918 if (moh
->pseudofd
< 0) {
919 ast_log(LOG_WARNING
, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
922 ioctl(moh
->pseudofd
, DAHDI_SET_BLOCKSIZE
, &x
);
927 if (ast_pthread_create_background(&moh
->thread
, NULL
, monmp3thread
, moh
)) {
928 ast_log(LOG_WARNING
, "Unable to create moh...\n");
929 if (moh
->pseudofd
> -1)
930 close(moh
->pseudofd
);
931 ast_moh_free_class(&moh
);
935 ast_log(LOG_WARNING
, "Don't know how to do a mode '%s' music on hold\n", moh
->mode
);
936 ast_moh_free_class(&moh
);
940 AST_LIST_LOCK(&mohclasses
);
941 AST_LIST_INSERT_HEAD(&mohclasses
, moh
, list
);
942 AST_LIST_UNLOCK(&mohclasses
);
947 static void local_ast_moh_cleanup(struct ast_channel
*chan
)
949 if (chan
->music_state
) {
950 free(chan
->music_state
);
951 chan
->music_state
= NULL
;
955 static int local_ast_moh_start(struct ast_channel
*chan
, const char *mclass
, const char *interpclass
)
957 struct mohclass
*mohclass
= NULL
;
959 /* The following is the order of preference for which class to use:
960 * 1) The channels explicitly set musicclass, which should *only* be
961 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
962 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
963 * result of receiving a HOLD control frame, this should be the
964 * payload that came with the frame.
965 * 3) The interpclass argument. This would be from the mohinterpret
966 * option from channel drivers. This is the same as the old musicclass
968 * 4) The default class.
970 AST_LIST_LOCK(&mohclasses
);
971 if (!ast_strlen_zero(chan
->musicclass
))
972 mohclass
= get_mohbyname(chan
->musicclass
, 1);
973 if (!mohclass
&& !ast_strlen_zero(mclass
))
974 mohclass
= get_mohbyname(mclass
, 1);
975 if (!mohclass
&& !ast_strlen_zero(interpclass
))
976 mohclass
= get_mohbyname(interpclass
, 1);
978 mohclass
= get_mohbyname("default", 1);
980 ast_atomic_fetchadd_int(&mohclass
->inuse
, +1);
981 AST_LIST_UNLOCK(&mohclasses
);
986 ast_set_flag(chan
, AST_FLAG_MOH
);
987 if (mohclass
->total_files
) {
988 return ast_activate_generator(chan
, &moh_file_stream
, mohclass
);
990 return ast_activate_generator(chan
, &mohgen
, mohclass
);
993 static void local_ast_moh_stop(struct ast_channel
*chan
)
995 ast_clear_flag(chan
, AST_FLAG_MOH
);
996 ast_deactivate_generator(chan
);
998 if (chan
->music_state
) {
1000 ast_closestream(chan
->stream
);
1001 chan
->stream
= NULL
;
1006 static struct mohclass
*moh_class_malloc(void)
1008 struct mohclass
*class;
1010 if ((class = ast_calloc(1, sizeof(*class))))
1011 class->format
= AST_FORMAT_SLINEAR
;
1016 static int load_moh_classes(int reload
)
1018 struct ast_config
*cfg
;
1019 struct ast_variable
*var
;
1020 struct mohclass
*class;
1025 static int dep_warning
= 0;
1027 cfg
= ast_config_load("musiconhold.conf");
1033 AST_LIST_LOCK(&mohclasses
);
1034 AST_LIST_TRAVERSE(&mohclasses
, class, list
)
1036 AST_LIST_UNLOCK(&mohclasses
);
1039 cat
= ast_category_browse(cfg
, NULL
);
1040 for (; cat
; cat
= ast_category_browse(cfg
, cat
)) {
1041 if (strcasecmp(cat
, "classes") && strcasecmp(cat
, "moh_files")) {
1042 if (!(class = moh_class_malloc())) {
1045 ast_copy_string(class->name
, cat
, sizeof(class->name
));
1046 var
= ast_variable_browse(cfg
, cat
);
1048 if (!strcasecmp(var
->name
, "mode"))
1049 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1050 else if (!strcasecmp(var
->name
, "directory"))
1051 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1052 else if (!strcasecmp(var
->name
, "application"))
1053 ast_copy_string(class->args
, var
->value
, sizeof(class->args
));
1054 else if (!strcasecmp(var
->name
, "random"))
1055 ast_set2_flag(class, ast_true(var
->value
), MOH_RANDOMIZE
);
1056 else if (!strcasecmp(var
->name
, "format")) {
1057 class->format
= ast_getformatbyname(var
->value
);
1058 if (!class->format
) {
1059 ast_log(LOG_WARNING
, "Unknown format '%s' -- defaulting to SLIN\n", var
->value
);
1060 class->format
= AST_FORMAT_SLINEAR
;
1066 if (ast_strlen_zero(class->dir
)) {
1067 if (!strcasecmp(class->mode
, "custom")) {
1068 strcpy(class->dir
, "nodir");
1070 ast_log(LOG_WARNING
, "A directory must be specified for class '%s'!\n", class->name
);
1075 if (ast_strlen_zero(class->mode
)) {
1076 ast_log(LOG_WARNING
, "A mode must be specified for class '%s'!\n", class->name
);
1080 if (ast_strlen_zero(class->args
) && !strcasecmp(class->mode
, "custom")) {
1081 ast_log(LOG_WARNING
, "An application must be specified for class '%s'!\n", class->name
);
1086 /* Don't leak a class when it's already registered */
1087 moh_register(class, reload
);
1094 /* Deprecated Old-School Configuration */
1095 var
= ast_variable_browse(cfg
, "classes");
1098 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");
1101 data
= strchr(var
->value
, ':');
1104 args
= strchr(data
, ',');
1107 if (!(get_mohbyname(var
->name
, 0))) {
1108 if (!(class = moh_class_malloc())) {
1112 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1113 ast_copy_string(class->dir
, data
, sizeof(class->dir
));
1114 ast_copy_string(class->mode
, var
->value
, sizeof(class->mode
));
1116 ast_copy_string(class->args
, args
, sizeof(class->args
));
1118 moh_register(class, reload
);
1124 var
= ast_variable_browse(cfg
, "moh_files");
1127 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");
1130 if (!(get_mohbyname(var
->name
, 0))) {
1131 args
= strchr(var
->value
, ',');
1134 if (!(class = moh_class_malloc())) {
1138 ast_copy_string(class->name
, var
->name
, sizeof(class->name
));
1139 ast_copy_string(class->dir
, var
->value
, sizeof(class->dir
));
1140 strcpy(class->mode
, "files");
1142 ast_copy_string(class->args
, args
, sizeof(class->args
));
1144 moh_register(class, reload
);
1150 ast_config_destroy(cfg
);
1155 static int ast_moh_destroy_one(struct mohclass
*moh
)
1158 int bytes
, tbytes
= 0, stime
= 0, pid
= 0;
1162 ast_log(LOG_DEBUG
, "killing %d!\n", moh
->pid
);
1163 stime
= time(NULL
) + 2;
1166 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1167 * to give the process a reason and time enough to kill off its
1174 while ((ast_wait_for_input(moh
->srcfd
, 100) > 0) && (bytes
= read(moh
->srcfd
, buff
, 8192)) && time(NULL
) < stime
)
1175 tbytes
= tbytes
+ bytes
;
1176 ast_log(LOG_DEBUG
, "mpg123 pid %d and child died after %d bytes read\n", pid
, tbytes
);
1179 ast_moh_free_class(&moh
);
1185 static void ast_moh_destroy(void)
1187 struct mohclass
*moh
;
1189 if (option_verbose
> 1)
1190 ast_verbose(VERBOSE_PREFIX_2
"Destroying musiconhold processes\n");
1192 AST_LIST_LOCK(&mohclasses
);
1193 while ((moh
= AST_LIST_REMOVE_HEAD(&mohclasses
, list
))) {
1194 ast_moh_destroy_one(moh
);
1196 AST_LIST_UNLOCK(&mohclasses
);
1199 static int moh_cli(int fd
, int argc
, char *argv
[])
1205 static int cli_files_show(int fd
, int argc
, char *argv
[])
1208 struct mohclass
*class;
1210 AST_LIST_LOCK(&mohclasses
);
1211 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1212 if (!class->total_files
)
1215 ast_cli(fd
, "Class: %s\n", class->name
);
1216 for (i
= 0; i
< class->total_files
; i
++)
1217 ast_cli(fd
, "\tFile: %s\n", class->filearray
[i
]);
1219 AST_LIST_UNLOCK(&mohclasses
);
1224 static int moh_classes_show(int fd
, int argc
, char *argv
[])
1226 struct mohclass
*class;
1228 AST_LIST_LOCK(&mohclasses
);
1229 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1230 ast_cli(fd
, "Class: %s\n", class->name
);
1231 ast_cli(fd
, "\tMode: %s\n", S_OR(class->mode
, "<none>"));
1232 ast_cli(fd
, "\tDirectory: %s\n", S_OR(class->dir
, "<none>"));
1233 ast_cli(fd
, "\tUse Count: %d\n", class->inuse
);
1234 if (ast_test_flag(class, MOH_CUSTOM
))
1235 ast_cli(fd
, "\tApplication: %s\n", S_OR(class->args
, "<none>"));
1236 if (strcasecmp(class->mode
, "files"))
1237 ast_cli(fd
, "\tFormat: %s\n", ast_getformatname(class->format
));
1239 AST_LIST_UNLOCK(&mohclasses
);
1244 static struct ast_cli_entry cli_moh_classes_show_deprecated
= {
1245 { "moh", "classes", "show"},
1246 moh_classes_show
, NULL
,
1249 static struct ast_cli_entry cli_moh_files_show_deprecated
= {
1250 { "moh", "files", "show"},
1251 cli_files_show
, NULL
,
1254 static struct ast_cli_entry cli_moh
[] = {
1255 { { "moh", "reload"},
1256 moh_cli
, "Music On Hold",
1257 "Usage: moh reload\n Rereads configuration\n" },
1259 { { "moh", "show", "classes"},
1260 moh_classes_show
, "List MOH classes",
1261 "Usage: moh show classes\n Lists all MOH classes\n", NULL
, &cli_moh_classes_show_deprecated
},
1263 { { "moh", "show", "files"},
1264 cli_files_show
, "List MOH file-based classes",
1265 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL
, &cli_moh_files_show_deprecated
},
1268 static int init_classes(int reload
)
1270 struct mohclass
*moh
;
1272 if (!load_moh_classes(reload
)) /* Load classes from config */
1273 return 0; /* Return if nothing is found */
1275 AST_LIST_LOCK(&mohclasses
);
1276 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses
, moh
, list
) {
1277 if (reload
&& moh
->delete) {
1278 AST_LIST_REMOVE_CURRENT(&mohclasses
, list
);
1280 ast_moh_destroy_one(moh
);
1283 AST_LIST_TRAVERSE_SAFE_END
1284 AST_LIST_UNLOCK(&mohclasses
);
1289 static int load_module(void)
1293 res
= ast_register_application(app0
, moh0_exec
, synopsis0
, descrip0
);
1294 ast_register_atexit(ast_moh_destroy
);
1295 ast_cli_register_multiple(cli_moh
, sizeof(cli_moh
) / sizeof(struct ast_cli_entry
));
1297 res
= ast_register_application(app1
, moh1_exec
, synopsis1
, descrip1
);
1299 res
= ast_register_application(app2
, moh2_exec
, synopsis2
, descrip2
);
1301 res
= ast_register_application(app3
, moh3_exec
, synopsis3
, descrip3
);
1303 res
= ast_register_application(app4
, moh4_exec
, synopsis4
, descrip4
);
1305 if (!init_classes(0)) { /* No music classes configured, so skip it */
1306 ast_log(LOG_WARNING
, "No music on hold classes configured, disabling music on hold.\n");
1308 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1314 static int reload(void)
1316 if (init_classes(1))
1317 ast_install_music_functions(local_ast_moh_start
, local_ast_moh_stop
, local_ast_moh_cleanup
);
1322 static int unload_module(void)
1325 struct mohclass
*class = NULL
;
1327 AST_LIST_LOCK(&mohclasses
);
1328 AST_LIST_TRAVERSE(&mohclasses
, class, list
) {
1329 if (class->inuse
> 0) {
1334 AST_LIST_UNLOCK(&mohclasses
);
1336 ast_log(LOG_WARNING
, "Unable to unload res_musiconhold due to active MOH channels\n");
1340 ast_uninstall_music_functions();
1342 res
= ast_unregister_application(app0
);
1343 res
|= ast_unregister_application(app1
);
1344 res
|= ast_unregister_application(app2
);
1345 res
|= ast_unregister_application(app3
);
1346 res
|= ast_unregister_application(app4
);
1347 ast_cli_unregister_multiple(cli_moh
, sizeof(cli_moh
) / sizeof(struct ast_cli_entry
));
1351 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Music On Hold Resource",
1352 .load
= load_module
,
1353 .unload
= unload_module
,