2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2010, 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 * \author Mark Spencer <markster@digium.com>
26 /*! \li \ref res_musiconhold.c uses the configuration file \ref musiconhold.conf
27 * \addtogroup configuration_file Configuration Files
31 * \page musiconhold.conf musiconhold.conf
32 * \verbinclude musiconhold.conf.sample
36 <conflict>win32</conflict>
37 <support_level>core</support_level>
46 #include <netinet/in.h>
54 #include "asterisk/lock.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/app.h"
59 #include "asterisk/module.h"
60 #include "asterisk/translate.h"
61 #include "asterisk/say.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/config.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/cli.h"
66 #include "asterisk/stringfields.h"
67 #include "asterisk/linkedlists.h"
68 #include "asterisk/stasis.h"
69 #include "asterisk/stasis_channels.h"
70 #include "asterisk/paths.h"
71 #include "asterisk/astobj2.h"
72 #include "asterisk/timing.h"
73 #include "asterisk/time.h"
74 #include "asterisk/poll-compat.h"
76 #define INITIAL_NUM_FILES 8
81 <application name="MusicOnHold" language="en_US">
83 Play Music On Hold indefinitely.
86 <parameter name="class" required="true" />
87 <parameter name="duration" />
90 <para>Plays hold music specified by class. If omitted, the default music
91 source for the channel will be used. Change the default class with
92 Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
93 specified number of seconds. If duration is ommited, music plays indefinitely.
94 Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
95 <para>This application does not automatically answer and should be preceeded by
96 an application such as Answer() or Progress().</para>
99 <application name="StartMusicOnHold" language="en_US">
104 <parameter name="class" required="true" />
107 <para>Starts playing music on hold, uses default music class for channel.
108 Starts playing music specified by class. If omitted, the default music
109 source for the channel will be used. Always returns <literal>0</literal>.</para>
112 <application name="StopMusicOnHold" language="en_US">
114 Stop playing Music On Hold.
118 <para>Stops playing music on hold.</para>
123 static const char play_moh[] = "MusicOnHold";
124 static const char start_moh[] = "StartMusicOnHold";
125 static const char stop_moh[] = "StopMusicOnHold";
127 static int respawn_time = 20;
129 struct moh_files_state {
130 /*! Holds a reference to the MOH class. */
131 struct mohclass *class;
132 struct ast_format *origwfmt;
133 struct ast_format *mohwfmt;
140 char name[MAX_MUSICCLASS];
141 char save_pos_filename[PATH_MAX];
144 #define MOH_QUIET (1 << 0)
145 #define MOH_SINGLE (1 << 1)
146 #define MOH_CUSTOM (1 << 2)
147 #define MOH_RANDOMIZE (1 << 3)
148 #define MOH_SORTALPHA (1 << 4)
149 #define MOH_RANDSTART (MOH_RANDOMIZE | MOH_SORTALPHA) /*!< Sorted but start at random position */
150 #define MOH_SORTMODE (3 << 3)
152 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
153 #define MOH_ANNOUNCEMENT (1 << 6) /*!< Do we play announcement files between songs on this channel? */
154 #define MOH_PREFERCHANNELCLASS (1 << 7) /*!< Should queue moh override channel moh */
156 /* Custom astobj2 flag */
157 #define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
158 #define MOH_REALTIME (1 << 31) /*!< Find only records that are realtime */
160 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
163 KILL_METHOD_PROCESS_GROUP = 0,
168 char name[MAX_MUSICCLASS];
171 char announcement[256];
174 /*! An immutable vector of filenames in "files" mode */
175 struct ast_vector_string *files;
177 /*! The format from the MOH source, not applicable to "files" mode */
178 struct ast_format *format;
179 /*! The pid of the external application delivering MOH */
183 /*! Millisecond delay between kill attempts */
186 enum kill_methods kill_method;
187 /*! Source of audio */
190 struct ast_timer *timer;
191 /*! Created on the fly, from RT engine */
192 unsigned int realtime:1;
193 unsigned int delete:1;
194 AST_LIST_HEAD_NOLOCK(, mohdata) members;
195 AST_LIST_ENTRY(mohclass) list;
200 struct ast_format *origwfmt;
201 struct mohclass *parent;
203 AST_LIST_ENTRY(mohdata) list;
206 static struct ao2_container *mohclasses;
208 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
209 #define MPG_123 "/usr/bin/mpg123"
212 static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass);
213 static int reload(void);
215 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
218 #define mohclass_unref(class,string) ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })
220 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
221 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
223 struct mohclass *dup = ao2_callback(mohclasses, OBJ_POINTER, ao2_match_by_addr, class);
226 if (__ao2_ref(dup, -1, tag, file, line, funcname) == 2) {
227 ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
228 class, class->name, file, line, funcname);
233 __ao2_ref(class, -1, tag, file, line, funcname);
239 static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
241 struct stasis_message *message;
242 struct ast_json *json_object;
244 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
245 moh_class_name, ast_channel_name(chan));
247 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
252 message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
253 ast_channel_moh_start_type(), json_object);
255 /* A channel snapshot must have been in the cache. */
256 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
258 stasis_publish(ast_channel_topic(chan), message);
260 ao2_cleanup(message);
261 ast_json_unref(json_object);
264 static void moh_post_stop(struct ast_channel *chan)
266 struct stasis_message *message;
268 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
270 message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
271 ast_channel_moh_stop_type(), NULL);
273 /* A channel snapshot must have been in the cache. */
274 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
276 stasis_publish(ast_channel_topic(chan), message);
278 ao2_cleanup(message);
281 static void moh_files_release(struct ast_channel *chan, void *data)
283 struct moh_files_state *state;
285 if (!chan || !ast_channel_music_state(chan)) {
289 state = ast_channel_music_state(chan);
291 if (ast_channel_stream(chan)) {
292 ast_closestream(ast_channel_stream(chan));
293 ast_channel_stream_set(chan, NULL);
298 ao2_ref(state->mohwfmt, -1);
299 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
300 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
301 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
302 ast_format_get_name(state->origwfmt));
304 ao2_cleanup(state->origwfmt);
305 state->origwfmt = NULL;
307 state->save_pos = state->pos;
308 state->announcement = 0;
310 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
313 static int ast_moh_files_next(struct ast_channel *chan)
315 struct moh_files_state *state = ast_channel_music_state(chan);
316 struct ast_vector_string *files;
320 /* Discontinue a stream if it is running already */
321 if (ast_channel_stream(chan)) {
322 ast_closestream(ast_channel_stream(chan));
323 ast_channel_stream_set(chan, NULL);
326 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
327 state->announcement = 1;
328 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
329 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
333 state->announcement = 0;
336 ao2_lock(state->class);
337 files = ao2_bump(state->class->files);
338 ao2_unlock(state->class);
340 file_count = AST_VECTOR_SIZE(files);
342 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
347 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
348 /* First time so lets play the file. */
349 state->save_pos = -1;
350 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
351 /* If a specific file has been saved confirm it still exists and that it is still valid */
352 state->pos = state->save_pos;
353 state->save_pos = -1;
354 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
355 /* Get a random file and ensure we can open it */
356 for (tries = 0; tries < 20; tries++) {
357 state->pos = ast_random() % file_count;
358 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
362 state->save_pos = -1;
365 /* This is easy, just increment our position and make sure we don't exceed the total file count */
367 state->pos %= file_count;
368 state->save_pos = -1;
372 for (tries = 0; tries < file_count; ++tries) {
373 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
377 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
379 state->pos %= file_count;
382 if (tries == file_count) {
387 /* Record the pointer to the filename for position resuming later */
388 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
390 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
392 if (state->samples) {
394 /* seek *SHOULD* be good since it's from a known location */
395 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
396 /* if the seek failed then recover because if there is not a valid read,
397 * moh_files_generate will return -1 and MOH will stop */
398 loc = ast_tellstream(ast_channel_stream(chan));
399 if (state->samples > loc && loc) {
400 /* seek one sample from the end for one guaranteed valid read */
401 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
409 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
413 f = ast_readframe(ast_channel_stream(chan));
415 /* Either there was no file stream setup or we reached EOF. */
416 if (!ast_moh_files_next(chan)) {
418 * Either we resetup the previously saved file stream position
419 * or we started a new file stream.
421 f = ast_readframe(ast_channel_stream(chan));
424 * We can get here if we were very unlucky because the
425 * resetup file stream was saved at EOF when MOH was
426 * previously stopped.
428 if (!ast_moh_files_next(chan)) {
429 f = ast_readframe(ast_channel_stream(chan));
438 static void moh_files_write_format_change(struct ast_channel *chan, void *data)
440 struct moh_files_state *state = ast_channel_music_state(chan);
442 /* In order to prevent a recursive call to this function as a result
443 * of setting the moh write format back on the channel. Clear
444 * the moh write format before setting the write format on the channel.*/
445 if (state->origwfmt) {
446 struct ast_format *tmp;
448 tmp = ao2_bump(ast_channel_writeformat(chan));
449 ao2_replace(state->origwfmt, NULL);
450 if (state->mohwfmt) {
451 ast_set_write_format(chan, state->mohwfmt);
453 state->origwfmt = tmp;
457 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
459 struct moh_files_state *state = ast_channel_music_state(chan);
460 struct ast_frame *f = NULL;
463 state->sample_queue += samples;
465 while (state->sample_queue > 0) {
466 ast_channel_lock(chan);
467 f = moh_files_readframe(chan);
469 /* We need to be sure that we unlock
470 * the channel prior to calling
471 * ast_write. Otherwise, the recursive locking
472 * that occurs can cause deadlocks when using
473 * indirect channels, like local channels
475 ast_channel_unlock(chan);
480 /* Only track our offset within the current file if we are not in the
481 * the middle of an announcement */
482 if (!state->announcement) {
483 state->samples += f->samples;
486 state->sample_queue -= f->samples;
487 if (ast_format_cmp(f->subclass.format, state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
488 ao2_replace(state->mohwfmt, f->subclass.format);
490 res = ast_write(chan, f);
493 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
500 static void *moh_files_alloc(struct ast_channel *chan, void *params)
502 struct moh_files_state *state;
503 struct mohclass *class = params;
506 state = ast_channel_music_state(chan);
507 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
508 ast_channel_music_state_set(chan, state);
509 ast_module_ref(ast_module_info->self);
515 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
516 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
521 file_count = AST_VECTOR_SIZE(class->files);
524 /* Resume MOH from where we left off last time or start from scratch? */
525 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
526 /* Start MOH from scratch. */
527 ao2_cleanup(state->origwfmt);
528 ao2_cleanup(state->mohwfmt);
529 memset(state, 0, sizeof(*state));
530 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
531 state->pos = ast_random() % file_count;
535 state->class = mohclass_ref(class, "Reffing music class for channel");
536 /* it's possible state is not a new allocation, don't leak old refs */
537 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
538 ao2_replace(state->mohwfmt, ast_channel_writeformat(chan));
539 /* For comparison on restart of MOH (see above) */
540 ast_copy_string(state->name, class->name, sizeof(state->name));
541 state->save_total = file_count;
543 moh_post_start(chan, class->name);
548 static int moh_digit_match(void *obj, void *arg, int flags)
551 struct mohclass *class = obj;
553 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
556 /*! \note This function should be called with the mohclasses list locked */
557 static struct mohclass *get_mohbydigit(char digit)
559 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
562 static void moh_handle_digit(struct ast_channel *chan, char digit)
564 struct mohclass *class;
565 const char *classname = NULL;
567 if ((class = get_mohbydigit(digit))) {
568 classname = ast_strdupa(class->name);
569 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
570 ast_channel_musicclass_set(chan, classname);
572 ast_moh_start(chan, classname, NULL);
576 static struct ast_generator moh_file_stream = {
577 .alloc = moh_files_alloc,
578 .release = moh_files_release,
579 .generate = moh_files_generator,
580 .digit = moh_handle_digit,
581 .write_format_change = moh_files_write_format_change,
584 static int spawn_mp3(struct mohclass *class)
588 char fns[MAX_MP3S][80];
589 char *argv[MAX_MP3S + 50];
597 if (!strcasecmp(class->dir, "nodir")) {
600 dir = opendir(class->dir);
601 if (!dir && strncasecmp(class->dir, "http://", 7)) {
602 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
607 if (!ast_test_flag(class, MOH_CUSTOM)) {
608 argv[argc++] = "mpg123";
611 argv[argc++] = "--mono";
613 argv[argc++] = "8000";
615 if (!ast_test_flag(class, MOH_SINGLE)) {
617 argv[argc++] = "2048";
622 if (ast_test_flag(class, MOH_QUIET))
623 argv[argc++] = "4096";
625 argv[argc++] = "8192";
627 /* Look for extra arguments and add them to the list */
628 ast_copy_string(xargs, class->args, sizeof(xargs));
630 while (!ast_strlen_zero(argptr)) {
631 argv[argc++] = argptr;
632 strsep(&argptr, ",");
635 /* Format arguments for argv vector */
636 ast_copy_string(xargs, class->args, sizeof(xargs));
638 while (!ast_strlen_zero(argptr)) {
639 argv[argc++] = argptr;
640 strsep(&argptr, " ");
644 if (!strncasecmp(class->dir, "http://", 7)) {
645 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
646 argv[argc++] = fns[files];
649 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
650 if ((strlen(de->d_name) > 3) &&
651 ((ast_test_flag(class, MOH_CUSTOM) &&
652 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
653 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
654 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
655 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
656 argv[argc++] = fns[files];
666 ast_log(LOG_WARNING, "Pipe failed\n");
670 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
675 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
676 sleep(respawn_time - (time(NULL) - class->start));
680 class->pid = ast_safe_fork(0);
681 if (class->pid < 0) {
684 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
688 if (ast_opt_high_priority)
692 /* Stdout goes to pipe */
693 dup2(fds[1], STDOUT_FILENO);
695 /* Close everything else */
696 ast_close_fds_above_n(STDERR_FILENO);
699 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
700 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
703 setpgid(0, getpid());
704 if (ast_test_flag(class, MOH_CUSTOM)) {
705 execv(argv[0], argv);
707 /* Default install is /usr/local/bin */
708 execv(LOCAL_MPG_123, argv);
709 /* Many places have it in /usr/bin */
710 execv(MPG_123, argv);
711 /* Check PATH as a last-ditch effort */
712 execvp("mpg123", argv);
714 /* Can't use logger, since log FDs are closed */
715 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
725 static int killer(pid_t pid, int signum, enum kill_methods kill_method)
727 switch (kill_method) {
728 case KILL_METHOD_PROCESS_GROUP:
729 return killpg(pid, signum);
730 case KILL_METHOD_PROCESS:
731 return kill(pid, signum);
737 static void killpid(int pid, size_t delay, enum kill_methods kill_method)
739 if (killer(pid, SIGHUP, kill_method) < 0) {
740 if (errno == ESRCH) {
743 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
745 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
746 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
749 if (killer(pid, SIGTERM, kill_method) < 0) {
750 if (errno == ESRCH) {
753 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
755 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
756 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
759 if (killer(pid, SIGKILL, kill_method) < 0) {
760 if (errno == ESRCH) {
763 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
765 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
766 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
770 static void *monmp3thread(void *data)
772 #define MOH_MS_INTERVAL 100
774 struct mohclass *class = data;
779 struct timeval deadline, tv_tmp;
782 deadline.tv_usec = 0;
784 pthread_testcancel();
785 /* Spawn mp3 player if it's not there */
786 if (class->srcfd < 0) {
787 if ((class->srcfd = spawn_mp3(class)) < 0) {
788 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
789 /* Try again later */
795 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
800 /* Pause some amount of time */
801 if (ast_poll(&pfd, 1, -1) > 0) {
802 if (ast_timer_ack(class->timer, 1) < 0) {
803 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
806 /* 25 samples per second => 40ms framerate => 320 samples */
807 res = 320; /* 320/40 = 8 samples/ms */
809 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
812 pthread_testcancel();
816 tv_tmp = ast_tvnow();
817 if (ast_tvzero(deadline))
819 delta = ast_tvdiff_ms(tv_tmp, deadline);
820 if (delta < MOH_MS_INTERVAL) { /* too early */
821 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
822 usleep(1000 * (MOH_MS_INTERVAL - delta));
823 pthread_testcancel();
825 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
828 /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
829 res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
831 /* For non-8000Hz formats, we need to alter the resolution */
832 res = res * ast_format_get_sample_rate(class->format) / 8000;
834 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
837 len = ast_format_determine_length(class->format, res);
839 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
843 pthread_testcancel();
844 if (class->pid > 1) {
845 killpid(class->pid, class->kill_delay, class->kill_method);
849 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
854 pthread_testcancel();
857 AST_LIST_TRAVERSE(&class->members, moh, list) {
859 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
860 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
868 static int play_moh_exec(struct ast_channel *chan, const char *data)
874 AST_DECLARE_APP_ARGS(args,
876 AST_APP_ARG(duration);
879 parse = ast_strdupa(data);
881 AST_STANDARD_APP_ARGS(args, parse);
883 if (!ast_strlen_zero(args.duration)) {
884 if (sscanf(args.duration, "%30d", &timeout) == 1) {
887 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
891 class = S_OR(args.class, NULL);
892 if (ast_moh_start(chan, class, NULL)) {
893 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
898 res = ast_safe_sleep(chan, timeout);
900 while (!(res = ast_safe_sleep(chan, 10000)));
908 static int start_moh_exec(struct ast_channel *chan, const char *data)
912 AST_DECLARE_APP_ARGS(args,
916 parse = ast_strdupa(data);
918 AST_STANDARD_APP_ARGS(args, parse);
920 class = S_OR(args.class, NULL);
921 if (ast_moh_start(chan, class, NULL))
922 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
927 static int stop_moh_exec(struct ast_channel *chan, const char *data)
934 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
936 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
938 struct mohclass *moh = NULL;
939 struct mohclass tmp_class = {
943 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
945 moh = __ao2_find(mohclasses, &tmp_class, flags,
946 "get_mohbyname", file, lineno, funcname);
949 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
955 static struct mohdata *mohalloc(struct mohclass *cl)
959 if (!(moh = ast_calloc(1, sizeof(*moh))))
962 if (ast_pipe_nonblock(moh->pipe)) {
963 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
968 moh->f.frametype = AST_FRAME_VOICE;
969 moh->f.subclass.format = cl->format;
970 moh->f.offset = AST_FRIENDLY_OFFSET;
972 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
975 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
981 static void moh_release(struct ast_channel *chan, void *data)
983 struct mohdata *moh = data;
984 struct mohclass *class = moh->parent;
985 struct ast_format *oldwfmt;
988 AST_LIST_REMOVE(&moh->parent->members, moh, list);
994 oldwfmt = moh->origwfmt;
996 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1001 struct moh_files_state *state;
1003 state = ast_channel_music_state(chan);
1004 if (state && state->class) {
1005 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1007 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1008 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1009 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1012 moh_post_stop(chan);
1015 ao2_cleanup(oldwfmt);
1018 static void *moh_alloc(struct ast_channel *chan, void *params)
1020 struct mohdata *res;
1021 struct mohclass *class = params;
1022 struct moh_files_state *state;
1024 /* Initiating music_state for current channel. Channel should know name of moh class */
1025 state = ast_channel_music_state(chan);
1026 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1027 ast_channel_music_state_set(chan, state);
1028 ast_module_ref(ast_module_info->self);
1034 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1035 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1037 ao2_cleanup(state->origwfmt);
1038 ao2_cleanup(state->mohwfmt);
1039 memset(state, 0, sizeof(*state));
1042 if ((res = mohalloc(class))) {
1043 res->origwfmt = ao2_bump(ast_channel_writeformat(chan));
1044 if (ast_set_write_format(chan, class->format)) {
1045 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1046 ast_format_get_name(class->format));
1047 moh_release(NULL, res);
1050 state->class = mohclass_ref(class, "Placing reference into state container");
1051 moh_post_start(chan, class->name);
1057 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1059 struct mohdata *moh = data;
1060 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1063 len = ast_format_determine_length(moh->parent->format, samples);
1065 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1066 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1067 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1069 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1073 moh->f.datalen = res;
1074 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1075 moh->f.samples = ast_codec_samples_count(&moh->f);
1077 if (ast_write(chan, &moh->f) < 0) {
1078 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1085 static struct ast_generator mohgen = {
1087 .release = moh_release,
1088 .generate = moh_generate,
1089 .digit = moh_handle_digit,
1092 static void moh_file_vector_destructor(void *obj)
1094 struct ast_vector_string *files = obj;
1095 AST_VECTOR_RESET(files, ast_free);
1096 AST_VECTOR_FREE(files);
1099 static struct ast_vector_string *moh_file_vector_alloc(int initial_capacity)
1101 struct ast_vector_string *files = ao2_alloc_options(
1102 sizeof(struct ast_vector_string),
1103 moh_file_vector_destructor,
1104 AO2_ALLOC_OPT_LOCK_NOLOCK);
1106 AST_VECTOR_INIT(files, initial_capacity);
1111 static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
1113 struct ast_vector_string *playlist_entries = NULL;
1115 for (; var; var = var->next) {
1116 if (!strcasecmp(var->name, "name")) {
1117 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1118 } else if (!strcasecmp(var->name, "mode")) {
1119 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1120 } else if (!strcasecmp(var->name, "entry")) {
1121 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1124 if (!playlist_entries) {
1125 playlist_entries = moh_file_vector_alloc(16);
1126 if (!playlist_entries) {
1131 dup = ast_strdup(var->value);
1136 if (ast_begins_with(dup, "/")) {
1137 char *last_pos_dot = strrchr(dup, '.');
1138 char *last_pos_slash = strrchr(dup, '/');
1139 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1140 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1145 AST_VECTOR_APPEND(playlist_entries, dup);
1147 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1149 } else if (!strcasecmp(var->name, "directory")) {
1150 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1151 } else if (!strcasecmp(var->name, "application")) {
1152 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1153 } else if (!strcasecmp(var->name, "announcement")) {
1154 ast_copy_string(mohclass->announcement, var->value, sizeof(mohclass->announcement));
1155 ast_set_flag(mohclass, MOH_ANNOUNCEMENT);
1156 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1157 mohclass->digit = *var->value;
1158 } else if (!strcasecmp(var->name, "random")) {
1159 static int deprecation_warning = 0;
1160 if (!deprecation_warning) {
1161 ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1162 deprecation_warning = 1;
1164 ast_set2_flag(mohclass, ast_true(var->value), MOH_RANDOMIZE);
1165 } else if (!strcasecmp(var->name, "sort")) {
1166 if (!strcasecmp(var->value, "random")) {
1167 ast_set_flag(mohclass, MOH_RANDOMIZE);
1168 } else if (!strcasecmp(var->value, "alpha")) {
1169 ast_set_flag(mohclass, MOH_SORTALPHA);
1170 } else if (!strcasecmp(var->value, "randstart")) {
1171 ast_set_flag(mohclass, MOH_RANDSTART);
1173 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1174 ao2_cleanup(mohclass->format);
1175 mohclass->format = ast_format_cache_get(var->value);
1176 if (!mohclass->format) {
1177 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1178 mohclass->format = ao2_bump(ast_format_slin);
1180 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1181 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1182 mohclass->kill_delay *= 1000;
1184 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1185 mohclass->kill_delay = 100000;
1187 } else if (!strcasecmp(var->name, "kill_method")) {
1188 if (!strcasecmp(var->value, "process")) {
1189 mohclass->kill_method = KILL_METHOD_PROCESS;
1190 } else if (!strcasecmp(var->value, "process_group")) {
1191 mohclass->kill_method = KILL_METHOD_PROCESS_GROUP;
1193 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1194 mohclass->kill_method = KILL_METHOD_PROCESS_GROUP;
1199 if (playlist_entries) {
1200 /* If we aren't in playlist mode, drop any list we may have already built */
1201 if (strcasecmp(mohclass->mode, "playlist")) {
1202 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1204 ao2_ref(playlist_entries, -1);
1208 AST_VECTOR_COMPACT(playlist_entries);
1210 /* We don't need to lock here because we are the thread that
1211 * created this mohclass and we haven't published it yet */
1212 ao2_ref(mohclass->files, -1);
1213 mohclass->files = playlist_entries;
1217 static int on_moh_file(const char *directory, const char *filename, void *obj)
1219 struct ast_vector_string *files = obj;
1223 /* Skip files that starts with a dot */
1224 if (*filename == '.') {
1225 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1226 directory, filename);
1230 /* We can't do anything with files that don't have an extension,
1231 * so check that first and punt if we can't find something */
1232 extension = strrchr(filename, '.');
1234 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1235 directory, filename);
1239 /* The extension needs at least two characters (after the .) to be useful */
1240 if (strlen(extension) < 3) {
1241 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1242 "character extension\n", directory, filename);
1246 /* Build the full path (excluding the extension) */
1247 if (ast_asprintf(&full_path, "%s/%.*s",
1249 (int) (extension - filename), filename) < 0) {
1250 /* If we don't have enough memory to build this path, there is no
1251 * point in continuing */
1255 /* If the file is present in multiple formats, ensure we only put it
1256 * into the list once. Pretty sure this is O(n^2). */
1257 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1258 ast_free(full_path);
1262 if (AST_VECTOR_APPEND(files, full_path)) {
1263 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1264 * we stop iterating */
1265 ast_free(full_path);
1272 static int moh_filename_strcasecmp(const void *a, const void *b)
1274 const char **s1 = (const char **) a;
1275 const char **s2 = (const char **) b;
1276 return strcasecmp(*s1, *s2);
1279 static int moh_scan_files(struct mohclass *class) {
1281 char dir_path[PATH_MAX - sizeof(class->dir)];
1282 struct ast_vector_string *files;
1284 if (class->dir[0] != '/') {
1285 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1287 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1290 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1292 /* 16 seems like a reasonable default */
1293 files = moh_file_vector_alloc(16);
1298 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1303 if (ast_test_flag(class, MOH_SORTALPHA)) {
1304 AST_VECTOR_SORT(files, moh_filename_strcasecmp);
1307 AST_VECTOR_COMPACT(files);
1310 ao2_ref(class->files, -1);
1311 class->files = files;
1314 return AST_VECTOR_SIZE(files);
1317 static int init_files_class(struct mohclass *class)
1321 res = moh_scan_files(class);
1328 ast_verb(3, "Files not found in %s for moh class:%s\n",
1329 class->dir, class->name);
1336 static void moh_rescan_files(void) {
1337 struct ao2_iterator i;
1340 i = ao2_iterator_init(mohclasses, 0);
1342 while ((c = ao2_iterator_next(&i))) {
1343 if (!strcasecmp(c->mode, "files")) {
1349 ao2_iterator_destroy(&i);
1352 static int moh_diff(struct mohclass *old, struct mohclass *new)
1358 if (strcmp(old->dir, new->dir)) {
1360 } else if (strcmp(old->mode, new->mode)) {
1362 } else if (strcmp(old->args, new->args)) {
1364 } else if (old->flags != new->flags) {
1371 static int init_app_class(struct mohclass *class)
1373 if (!strcasecmp(class->mode, "custom")) {
1374 ast_set_flag(class, MOH_CUSTOM);
1375 } else if (!strcasecmp(class->mode, "mp3nb")) {
1376 ast_set_flag(class, MOH_SINGLE);
1377 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1378 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1379 } else if (!strcasecmp(class->mode, "quietmp3")) {
1380 ast_set_flag(class, MOH_QUIET);
1385 if (!(class->timer = ast_timer_open())) {
1386 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1389 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1390 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1391 ast_timer_close(class->timer);
1392 class->timer = NULL;
1395 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1396 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1398 ast_timer_close(class->timer);
1399 class->timer = NULL;
1408 * \note This function owns the reference it gets to moh if unref is true
1410 #define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1411 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1413 struct mohclass *mohclass = NULL;
1415 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1417 if (mohclass && !moh_diff(mohclass, moh)) {
1418 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1419 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1421 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1424 } else if (mohclass) {
1425 /* Found a class, but it's different from the one being registered */
1426 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1430 moh->start -= respawn_time;
1432 if (!strcasecmp(moh->mode, "files")) {
1433 if (init_files_class(moh)) {
1435 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1439 } else if (!strcasecmp(moh->mode, "playlist")) {
1443 file_count = AST_VECTOR_SIZE(moh->files);
1448 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1452 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1453 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1454 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1455 if (init_app_class(moh)) {
1457 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1462 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1464 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1469 ao2_t_link(mohclasses, moh, "Adding class to container");
1472 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1478 #define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1479 static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
1481 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1485 static void local_ast_moh_cleanup(struct ast_channel *chan)
1487 struct moh_files_state *state = ast_channel_music_state(chan);
1490 ast_channel_music_state_set(chan, NULL);
1492 /* This should never happen. We likely just leaked some resource. */
1494 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1495 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1497 ao2_cleanup(state->origwfmt);
1498 ao2_cleanup(state->mohwfmt);
1500 /* Only held a module reference if we had a music state */
1501 ast_module_unref(ast_module_info->self);
1505 /*! \brief Support routing for 'moh unregister class' CLI
1506 * This is in charge of generating all strings that match a prefix in the
1507 * given position. As many functions of this kind, each invokation has
1508 * O(state) time complexity so be careful in using it.
1510 static char *complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
1513 struct mohclass *cur;
1515 int wordlen = strlen(word);
1516 struct ao2_iterator i;
1522 i = ao2_iterator_init(mohclasses, 0);
1523 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1524 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1525 c = ast_strdup(cur->name);
1526 mohclass_unref(cur, "drop ref in iterator loop break");
1529 mohclass_unref(cur, "drop ref in iterator loop");
1531 ao2_iterator_destroy(&i);
1536 static char *handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1538 struct mohclass *cur;
1541 struct ao2_iterator i;
1545 e->command = "moh unregister class";
1547 "Usage: moh unregister class <class>\n"
1548 " Unregisters a realtime moh class.\n";
1551 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1555 return CLI_SHOWUSAGE;
1557 len = strlen(a->argv[3]);
1559 i = ao2_iterator_init(mohclasses, 0);
1560 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1561 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1565 mohclass_unref(cur, "drop ref in iterator loop");
1567 ao2_iterator_destroy(&i);
1570 moh_unregister(cur);
1571 mohclass_unref(cur, "drop ref after unregister");
1573 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1581 static void moh_class_destructor(void *obj);
1583 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1585 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1587 struct mohclass *class;
1589 class = __ao2_alloc(sizeof(*class), moh_class_destructor, AO2_ALLOC_OPT_LOCK_MUTEX,
1590 "Allocating new moh class", file, line, funcname);
1592 class->format = ao2_bump(ast_format_slin);
1594 class->kill_delay = 100000;
1596 /* We create an empty one by default */
1597 class->files = moh_file_vector_alloc(0);
1598 if (!class->files) {
1607 static struct ast_variable *load_realtime_musiconhold(const char *name)
1609 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1612 const char *mode = ast_variable_find_in_list(var, "mode");
1613 if (ast_strings_equal(mode, "playlist")) {
1614 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1615 char *category = NULL;
1616 size_t entry_count = 0;
1618 /* entries is NULL if there are no results */
1620 while ((category = ast_category_browse(entries, category))) {
1621 const char *entry = ast_variable_retrieve(entries, category, "entry");
1624 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1627 ast_variable_list_append(&var, dup);
1631 ast_config_destroy(entries);
1634 if (entry_count == 0) {
1635 /* Behave as though this class doesn't exist */
1636 ast_variables_destroy(var);
1643 ast_log(LOG_WARNING,
1644 "Music on Hold class '%s' not found in memory/database. "
1645 "Verify your configuration.\n",
1651 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1653 struct mohclass *mohclass = NULL;
1654 struct moh_files_state *state = ast_channel_music_state(chan);
1655 struct ast_variable *var = NULL;
1658 int realtime_possible = ast_check_realtime("musiconhold");
1659 int warn_if_not_in_memory = !realtime_possible;
1660 const char *classes[] = {NULL, NULL, interpclass, "default"};
1662 if (ast_test_flag(global_flags, MOH_PREFERCHANNELCLASS)) {
1663 classes[0] = ast_channel_musicclass(chan);
1664 classes[1] = mclass;
1666 classes[0] = mclass;
1667 classes[1] = ast_channel_musicclass(chan);
1670 /* The following is the order of preference for which class to use:
1671 * 1) The channels explicitly set musicclass, which should *only* be
1672 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1673 * Unless preferchannelclass in musiconhold.conf is false
1674 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1675 * result of receiving a HOLD control frame, this should be the
1676 * payload that came with the frame.
1677 * 3) The channels explicitly set musicclass, which should *only* be
1678 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1679 * 4) The interpclass argument. This would be from the mohinterpret
1680 * option from channel drivers. This is the same as the old musicclass
1682 * 5) The default class.
1685 for (i = 0; i < ARRAY_LEN(classes); ++i) {
1686 if (!ast_strlen_zero(classes[i])) {
1687 mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1688 if (!mohclass && realtime_possible) {
1689 var = load_realtime_musiconhold(classes[i]);
1691 if (mohclass || var) {
1697 /* If no moh class found in memory, then check RT. Note that the logic used
1698 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1701 if ((mohclass = moh_class_malloc())) {
1702 mohclass->realtime = 1;
1704 moh_parse_options(var, mohclass);
1705 ast_variables_destroy(var);
1707 if (ast_strlen_zero(mohclass->dir)) {
1708 if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1709 strcpy(mohclass->dir, "nodir");
1711 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1712 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1716 if (ast_strlen_zero(mohclass->mode)) {
1717 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1718 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1721 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1722 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1723 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1727 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1728 /* CACHERTCLASSES enabled, let's add this class to default tree */
1729 if (state && state->class) {
1730 /* Class already exist for this channel */
1731 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1733 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1734 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1735 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1736 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1739 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1740 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1744 /* We don't register RT moh class, so let's init it manualy */
1746 time(&mohclass->start);
1747 mohclass->start -= respawn_time;
1749 if (!strcasecmp(mohclass->mode, "files")) {
1751 * XXX moh_scan_files returns -1 if it is unable to open the
1752 * configured directory or there is a memory allocation
1753 * failure. Otherwise it returns the number of files for this music
1754 * class. This check is only checking if the number of files is zero
1755 * and it ignores the -1 case. To avoid a behavior change we keep this
1756 * as-is, but we should address what the 'correct' behavior should be.
1758 if (!moh_scan_files(mohclass)) {
1759 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1762 if (strchr(mohclass->args, 'r')) {
1763 static int deprecation_warning = 0;
1764 if (!deprecation_warning) {
1765 ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1766 deprecation_warning = 1;
1768 ast_set_flag(mohclass, MOH_RANDOMIZE);
1770 } else if (!strcasecmp(mohclass->mode, "playlist")) {
1774 file_count = AST_VECTOR_SIZE(mohclass->files);
1775 ao2_unlock(mohclass);
1778 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1781 } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
1783 if (!strcasecmp(mohclass->mode, "custom"))
1784 ast_set_flag(mohclass, MOH_CUSTOM);
1785 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1786 ast_set_flag(mohclass, MOH_SINGLE);
1787 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1788 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1789 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1790 ast_set_flag(mohclass, MOH_QUIET);
1792 mohclass->srcfd = -1;
1793 if (!(mohclass->timer = ast_timer_open())) {
1794 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1796 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1797 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1798 ast_timer_close(mohclass->timer);
1799 mohclass->timer = NULL;
1802 /* Let's check if this channel already had a moh class before */
1803 if (state && state->class) {
1804 /* Class already exist for this channel */
1805 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1806 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1807 /* we found RT class with the same name, seems like we should continue playing existing one */
1808 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1809 mohclass = mohclass_ref(state->class, "using existing class from state");
1812 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1813 ast_log(LOG_WARNING, "Unable to create moh...\n");
1814 if (mohclass->timer) {
1815 ast_timer_close(mohclass->timer);
1816 mohclass->timer = NULL;
1818 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1823 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1824 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1829 ast_variables_destroy(var);
1838 /* If we are using a cached realtime class with files, re-scan the files */
1839 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1841 * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1842 * or there is a memory allocation failure. Otherwise it returns the number of
1843 * files for this music class. This check is only checking if the number of files
1844 * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1845 * as-is, but we should address what the 'correct' behavior should be.
1847 if (!moh_scan_files(mohclass)) {
1848 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1853 if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1857 file_count = AST_VECTOR_SIZE(mohclass->files);
1858 ao2_unlock(mohclass);
1861 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1863 res = ast_activate_generator(chan, &mohgen, mohclass);
1867 ast_channel_lock(chan);
1868 ast_channel_latest_musicclass_set(chan, mohclass->name);
1869 ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1870 ast_channel_unlock(chan);
1873 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1878 static void local_ast_moh_stop(struct ast_channel *chan)
1880 ast_deactivate_generator(chan);
1882 ast_channel_lock(chan);
1883 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1884 if (ast_channel_music_state(chan)) {
1885 if (ast_channel_stream(chan)) {
1886 ast_closestream(ast_channel_stream(chan));
1887 ast_channel_stream_set(chan, NULL);
1890 ast_channel_unlock(chan);
1893 static void moh_class_destructor(void *obj)
1895 struct mohclass *class = obj;
1896 struct mohdata *member;
1899 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1902 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1905 ao2_cleanup(class->files);
1908 /* Kill the thread first, so it cannot restart the child process while the
1909 * class is being destroyed */
1910 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1911 tid = class->thread;
1912 class->thread = AST_PTHREADT_NULL;
1913 pthread_cancel(tid);
1914 /* We'll collect the exit status later, after we ensure all the readers
1918 if (class->pid > 1) {
1920 int bytes, tbytes = 0, stime = 0;
1922 ast_debug(1, "killing %d!\n", class->pid);
1924 stime = time(NULL) + 2;
1925 killpid(class->pid, class->kill_delay, class->kill_method);
1927 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1928 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1929 tbytes = tbytes + bytes;
1932 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1933 class->pid, tbytes);
1936 close(class->srcfd);
1941 ast_timer_close(class->timer);
1942 class->timer = NULL;
1945 ao2_cleanup(class->format);
1947 /* Finally, collect the exit status of the monitor thread */
1949 pthread_join(tid, NULL);
1954 static int moh_class_mark(void *obj, void *arg, int flags)
1956 struct mohclass *class = obj;
1958 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1965 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1967 struct mohclass *class = obj;
1969 return class->delete ? CMP_MATCH : 0;
1972 static int load_moh_classes(int reload)
1974 struct ast_config *cfg;
1975 struct ast_variable *var;
1976 struct mohclass *class;
1979 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1981 cfg = ast_config_load("musiconhold.conf", config_flags);
1983 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1984 if (ast_check_realtime("musiconhold") && reload) {
1985 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
1986 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
1992 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1993 if (ast_check_realtime("musiconhold") && reload) {
1994 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1995 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
2001 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2004 ast_clear_flag(global_flags, AST_FLAGS_ALL);
2005 ast_set2_flag(global_flags, 1, MOH_PREFERCHANNELCLASS);
2007 cat = ast_category_browse(cfg, NULL);
2008 for (; cat; cat = ast_category_browse(cfg, cat)) {
2009 /* Setup common options from [general] section */
2010 if (!strcasecmp(cat, "general")) {
2011 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2012 if (!strcasecmp(var->name, "cachertclasses")) {
2013 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
2014 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2015 ast_set2_flag(global_flags, ast_true(var->value), MOH_PREFERCHANNELCLASS);
2017 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2023 if (!(class = moh_class_malloc())) {
2027 moh_parse_options(ast_variable_browse(cfg, cat), class);
2028 /* For compatibility with the past, we overwrite any name=name
2029 * with the context [name]. */
2030 ast_copy_string(class->name, cat, sizeof(class->name));
2032 if (ast_strlen_zero(class->dir)) {
2033 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2034 strcpy(class->dir, "nodir");
2036 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2037 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2041 if (ast_strlen_zero(class->mode)) {
2042 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2043 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2046 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2047 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2048 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2052 /* Don't leak a class when it's already registered */
2053 if (!moh_register(class, reload, HANDLE_REF)) {
2058 ast_config_destroy(cfg);
2060 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
2061 moh_classes_delete_marked, NULL, "Purge marked classes");
2066 static void ast_moh_destroy(void)
2068 ast_verb(2, "Destroying musiconhold processes\n");
2070 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
2071 ao2_ref(mohclasses, -1);
2076 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2080 e->command = "moh reload";
2082 "Usage: moh reload\n"
2083 " Reloads the MusicOnHold module.\n"
2084 " Alias for 'module reload res_musiconhold.so'\n";
2090 if (a->argc != e->args)
2091 return CLI_SHOWUSAGE;
2093 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2094 ast_module_reload("res_musiconhold");
2099 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2101 struct mohclass *class;
2102 struct ao2_iterator i;
2106 e->command = "moh show files";
2108 "Usage: moh show files\n"
2109 " Lists all loaded file-based MusicOnHold classes and their\n"
2116 if (a->argc != e->args)
2117 return CLI_SHOWUSAGE;
2119 i = ao2_iterator_init(mohclasses, 0);
2120 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2121 struct ast_vector_string *files;
2124 files = ao2_bump(class->files);
2127 if (AST_VECTOR_SIZE(files)) {
2129 ast_cli(a->fd, "Class: %s\n", class->name);
2130 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2131 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2137 ao2_iterator_destroy(&i);
2142 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2144 struct mohclass *class;
2145 struct ao2_iterator i;
2149 e->command = "moh show classes";
2151 "Usage: moh show classes\n"
2152 " Lists all MusicOnHold classes.\n";
2158 if (a->argc != e->args)
2159 return CLI_SHOWUSAGE;
2161 i = ao2_iterator_init(mohclasses, 0);
2162 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2163 ast_cli(a->fd, "Class: %s\n", class->name);
2164 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2165 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2166 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2167 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2169 if (ast_test_flag(class, MOH_CUSTOM)) {
2170 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2171 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2172 ast_cli(a->fd, "\tKill Method: %s\n",
2173 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2175 if (strcasecmp(class->mode, "files")) {
2176 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2179 ao2_iterator_destroy(&i);
2184 static struct ast_cli_entry cli_moh[] = {
2185 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2186 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2187 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2188 AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2191 static int moh_class_hash(const void *obj, const int flags)
2193 const struct mohclass *class = obj;
2195 return ast_str_case_hash(class->name);
2198 static int moh_class_cmp(void *obj, void *arg, int flags)
2200 struct mohclass *class = obj, *class2 = arg;
2202 return strcasecmp(class->name, class2->name) ? 0 :
2203 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2204 CMP_MATCH | CMP_STOP;
2208 * \brief Load the module
2210 * Module loading including tests for configuration or dependencies.
2211 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2212 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2213 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2214 * configuration file or other non-critical problem return
2215 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2217 static int load_module(void)
2221 mohclasses = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 53,
2222 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2224 return AST_MODULE_LOAD_DECLINE;
2227 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2228 ast_log(LOG_WARNING, "No music on hold classes configured, "
2229 "disabling music on hold.\n");
2231 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2232 local_ast_moh_cleanup);
2235 res = ast_register_application_xml(play_moh, play_moh_exec);
2236 ast_register_atexit(ast_moh_destroy);
2237 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
2239 res = ast_register_application_xml(start_moh, start_moh_exec);
2241 res = ast_register_application_xml(stop_moh, stop_moh_exec);
2243 return AST_MODULE_LOAD_SUCCESS;
2246 static int reload(void)
2248 if (load_moh_classes(1)) {
2249 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2250 local_ast_moh_cleanup);
2253 return AST_MODULE_LOAD_SUCCESS;
2256 static int moh_class_inuse(void *obj, void *arg, int flags)
2258 struct mohclass *class = obj;
2260 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2263 static int unload_module(void)
2266 struct mohclass *class = NULL;
2268 /* XXX This check shouldn't be required if module ref counting was being used
2270 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2271 class = mohclass_unref(class, "unref of class from module unload callback");
2276 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2280 ast_uninstall_music_functions();
2283 res = ast_unregister_application(play_moh);
2284 res |= ast_unregister_application(start_moh);
2285 res |= ast_unregister_application(stop_moh);
2286 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
2287 ast_unregister_atexit(ast_moh_destroy);
2292 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
2293 .support_level = AST_MODULE_SUPPORT_CORE,
2294 .load = load_module,
2295 .unload = unload_module,
2297 .load_pri = AST_MODPRI_CHANNEL_DEPEND,