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$")
40 #include <sys/signal.h>
41 #include <netinet/in.h>
44 #include <sys/ioctl.h>
51 #include <dahdi/user.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/manager.h"
69 #include "asterisk/paths.h"
70 #include "asterisk/astobj2.h"
72 #define INITIAL_NUM_FILES 8
77 <application name="MusicOnHold" language="en_US">
79 Play Music On Hold indefinitely.
82 <parameter name="class" required="true" />
83 <parameter name="duration" />
86 <para>Plays hold music specified by class. If omitted, the default music
87 source for the channel will be used. Change the default class with
88 Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
89 specified number of seconds. If duration is ommited, music plays indefinitely.
90 Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
93 <application name="WaitMusicOnHold" language="en_US">
95 Wait, playing Music On Hold.
98 <parameter name="delay" required="true" />
101 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
102 <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
103 or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
104 with no sound.</para>
105 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
108 <application name="SetMusicOnHold" language="en_US">
110 Set default Music On Hold class.
113 <parameter name="class" required="yes" />
116 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
117 <para>Sets the default class for music on hold for a given channel.
118 When music on hold is activated, this class will be used to select which
119 music is played.</para>
120 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
123 <application name="StartMusicOnHold" language="en_US">
128 <parameter name="class" required="true" />
131 <para>Starts playing music on hold, uses default music class for channel.
132 Starts playing music specified by class. If omitted, the default music
133 source for the channel will be used. Always returns <literal>0</literal>.</para>
136 <application name="StopMusicOnHold" language="en_US">
138 Stop playing Music On Hold.
142 <para>Stops playing music on hold.</para>
147 static const char play_moh[] = "MusicOnHold";
148 static const char wait_moh[] = "WaitMusicOnHold";
149 static const char set_moh[] = "SetMusicOnHold";
150 static const char start_moh[] = "StartMusicOnHold";
151 static const char stop_moh[] = "StopMusicOnHold";
153 static int respawn_time = 20;
155 struct moh_files_state {
156 struct mohclass *class;
157 char name[MAX_MUSICCLASS];
164 char *save_pos_filename;
167 #define MOH_QUIET (1 << 0)
168 #define MOH_SINGLE (1 << 1)
169 #define MOH_CUSTOM (1 << 2)
170 #define MOH_RANDOMIZE (1 << 3)
171 #define MOH_SORTALPHA (1 << 4)
173 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
175 /* Custom astobj2 flag */
176 #define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
178 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
181 char name[MAX_MUSICCLASS];
186 /*! A dynamically sized array to hold the list of filenames in "files" mode */
188 /*! The current size of the filearray */
190 /*! The current number of files loaded into the filearray */
193 /*! The format from the MOH source, not applicable to "files" mode */
195 /*! The pid of the external application delivering MOH */
199 /*! Source of audio */
201 /*! FD for timing source */
203 /*! Created on the fly, from RT engine */
205 unsigned int delete:1;
206 AST_LIST_HEAD_NOLOCK(, mohdata) members;
207 AST_LIST_ENTRY(mohclass) list;
213 struct mohclass *parent;
215 AST_LIST_ENTRY(mohdata) list;
218 static struct ao2_container *mohclasses;
220 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
221 #define MPG_123 "/usr/bin/mpg123"
224 static int reload(void);
226 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
229 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
231 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
232 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
234 struct mohclass *dup;
235 if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
236 if (_ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
237 FILE *ref = fopen("/tmp/refs", "a");
239 fprintf(ref, "%p =1 %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
242 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",
243 class, class->name, file, line, funcname);
248 ao2_t_ref(class, -1, (char *) tag);
254 static void moh_files_release(struct ast_channel *chan, void *data)
256 struct moh_files_state *state;
258 if (!chan || !chan->music_state) {
262 state = chan->music_state;
265 ast_closestream(chan->stream);
269 if (option_verbose > 2) {
270 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
273 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
274 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
277 state->save_pos = state->pos;
279 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
282 static int ast_moh_files_next(struct ast_channel *chan)
284 struct moh_files_state *state = chan->music_state;
287 /* Discontinue a stream if it is running already */
289 ast_closestream(chan->stream);
293 if (!state->class->total_files) {
294 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
298 /* If a specific file has been saved confirm it still exists and that it is still valid */
299 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
300 state->pos = state->save_pos;
301 state->save_pos = -1;
302 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
303 /* Get a random file and ensure we can open it */
304 for (tries = 0; tries < 20; tries++) {
305 state->pos = ast_random() % state->class->total_files;
306 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
309 state->save_pos = -1;
312 /* This is easy, just increment our position and make sure we don't exceed the total file count */
314 state->pos %= state->class->total_files;
315 state->save_pos = -1;
319 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
320 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
322 state->pos %= state->class->total_files;
326 /* Record the pointer to the filename for position resuming later */
327 state->save_pos_filename = state->class->filearray[state->pos];
329 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
332 ast_seekstream(chan->stream, state->samples, SEEK_SET);
337 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
339 struct ast_frame *f = NULL;
341 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
342 if (!ast_moh_files_next(chan))
343 f = ast_readframe(chan->stream);
349 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
351 struct moh_files_state *state = chan->music_state;
352 struct ast_frame *f = NULL;
355 state->sample_queue += samples;
357 while (state->sample_queue > 0) {
358 ast_channel_lock(chan);
359 if ((f = moh_files_readframe(chan))) {
360 /* We need to be sure that we unlock
361 * the channel prior to calling
362 * ast_write. Otherwise, the recursive locking
363 * that occurs can cause deadlocks when using
364 * indirect channels, like local channels
366 ast_channel_unlock(chan);
367 state->samples += f->samples;
368 state->sample_queue -= f->samples;
369 res = ast_write(chan, f);
372 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
376 ast_channel_unlock(chan);
383 static void *moh_files_alloc(struct ast_channel *chan, void *params)
385 struct moh_files_state *state;
386 struct mohclass *class = params;
388 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
389 chan->music_state = state;
390 ast_module_ref(ast_module_info->self);
392 state = chan->music_state;
399 /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
400 * malloc may allocate a different class to the same memory block. This
401 * might only happen when two reloads are generated in a short period of
402 * time, but it's still important to protect against.
403 * PROG: Compare the quick operation first, to save CPU. */
404 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
405 memset(state, 0, sizeof(*state));
406 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
407 state->pos = ast_random() % class->total_files;
411 state->class = mohclass_ref(class, "Reffing music class for channel");
412 state->origwfmt = chan->writeformat;
413 /* For comparison on restart of MOH (see above) */
414 ast_copy_string(state->name, class->name, sizeof(state->name));
415 state->save_total = class->total_files;
417 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
419 return chan->music_state;
422 static int moh_digit_match(void *obj, void *arg, int flags)
425 struct mohclass *class = obj;
427 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
430 /*! \note This function should be called with the mohclasses list locked */
431 static struct mohclass *get_mohbydigit(char digit)
433 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
436 static void moh_handle_digit(struct ast_channel *chan, char digit)
438 struct mohclass *class;
439 const char *classname = NULL;
441 if ((class = get_mohbydigit(digit))) {
442 classname = ast_strdupa(class->name);
443 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
444 ast_string_field_set(chan,musicclass,classname);
446 ast_moh_start(chan, classname, NULL);
450 static struct ast_generator moh_file_stream =
452 .alloc = moh_files_alloc,
453 .release = moh_files_release,
454 .generate = moh_files_generator,
455 .digit = moh_handle_digit,
458 static int spawn_mp3(struct mohclass *class)
462 char fns[MAX_MP3S][80];
463 char *argv[MAX_MP3S + 50];
471 if (!strcasecmp(class->dir, "nodir")) {
474 dir = opendir(class->dir);
475 if (!dir && strncasecmp(class->dir, "http://", 7)) {
476 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
481 if (!ast_test_flag(class, MOH_CUSTOM)) {
482 argv[argc++] = "mpg123";
485 argv[argc++] = "--mono";
487 argv[argc++] = "8000";
489 if (!ast_test_flag(class, MOH_SINGLE)) {
491 argv[argc++] = "2048";
496 if (ast_test_flag(class, MOH_QUIET))
497 argv[argc++] = "4096";
499 argv[argc++] = "8192";
501 /* Look for extra arguments and add them to the list */
502 ast_copy_string(xargs, class->args, sizeof(xargs));
504 while (!ast_strlen_zero(argptr)) {
505 argv[argc++] = argptr;
506 strsep(&argptr, ",");
509 /* Format arguments for argv vector */
510 ast_copy_string(xargs, class->args, sizeof(xargs));
512 while (!ast_strlen_zero(argptr)) {
513 argv[argc++] = argptr;
514 strsep(&argptr, " ");
518 if (!strncasecmp(class->dir, "http://", 7)) {
519 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
520 argv[argc++] = fns[files];
523 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
524 if ((strlen(de->d_name) > 3) &&
525 ((ast_test_flag(class, MOH_CUSTOM) &&
526 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
527 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
528 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
529 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
530 argv[argc++] = fns[files];
540 ast_log(LOG_WARNING, "Pipe failed\n");
544 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
549 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
550 sleep(respawn_time - (time(NULL) - class->start));
554 class->pid = ast_safe_fork(0);
555 if (class->pid < 0) {
558 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
562 if (ast_opt_high_priority)
566 /* Stdout goes to pipe */
567 dup2(fds[1], STDOUT_FILENO);
569 /* Close everything else */
570 ast_close_fds_above_n(STDERR_FILENO);
573 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
574 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
577 setpgid(0, getpid());
578 if (ast_test_flag(class, MOH_CUSTOM)) {
579 execv(argv[0], argv);
581 /* Default install is /usr/local/bin */
582 execv(LOCAL_MPG_123, argv);
583 /* Many places have it in /usr/bin */
584 execv(MPG_123, argv);
585 /* Check PATH as a last-ditch effort */
586 execvp("mpg123", argv);
588 /* Can't use logger, since log FDs are closed */
589 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
599 static void *monmp3thread(void *data)
601 #define MOH_MS_INTERVAL 100
603 struct mohclass *class = data;
609 struct timeval deadline, tv_tmp;
612 deadline.tv_usec = 0;
614 pthread_testcancel();
615 /* Spawn mp3 player if it's not there */
616 if (class->srcfd < 0) {
617 if ((class->srcfd = spawn_mp3(class)) < 0) {
618 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
619 /* Try again later */
621 pthread_testcancel();
624 if (class->pseudofd > -1) {
628 /* Pause some amount of time */
629 res = read(class->pseudofd, buf, sizeof(buf));
630 pthread_testcancel();
634 tv_tmp = ast_tvnow();
635 if (ast_tvzero(deadline))
637 delta = ast_tvdiff_ms(tv_tmp, deadline);
638 if (delta < MOH_MS_INTERVAL) { /* too early */
639 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
640 usleep(1000 * (MOH_MS_INTERVAL - delta));
641 pthread_testcancel();
643 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
646 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
648 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
651 len = ast_codec_get_len(class->format, res);
653 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
657 pthread_testcancel();
658 if (class->pid > 1) {
660 if (killpg(class->pid, SIGHUP) < 0) {
661 if (errno == ESRCH) {
664 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
667 if (killpg(class->pid, SIGTERM) < 0) {
668 if (errno == ESRCH) {
671 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
674 if (killpg(class->pid, SIGKILL) < 0) {
675 if (errno == ESRCH) {
678 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
684 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
689 pthread_testcancel();
692 AST_LIST_TRAVERSE(&class->members, moh, list) {
694 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
695 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
703 static int play_moh_exec(struct ast_channel *chan, const char *data)
709 AST_DECLARE_APP_ARGS(args,
711 AST_APP_ARG(duration);
714 parse = ast_strdupa(data);
716 AST_STANDARD_APP_ARGS(args, parse);
718 if (!ast_strlen_zero(args.duration)) {
719 if (sscanf(args.duration, "%30d", &timeout) == 1) {
722 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
726 class = S_OR(args.class, NULL);
727 if (ast_moh_start(chan, class, NULL)) {
728 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
733 res = ast_safe_sleep(chan, timeout);
735 while (!(res = ast_safe_sleep(chan, 10000)));
743 static int wait_moh_exec(struct ast_channel *chan, const char *data)
745 static int deprecation_warning = 0;
748 if (!deprecation_warning) {
749 deprecation_warning = 1;
750 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
753 if (!data || !atoi(data)) {
754 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
757 if (ast_moh_start(chan, NULL, NULL)) {
758 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
761 res = ast_safe_sleep(chan, atoi(data) * 1000);
766 static int set_moh_exec(struct ast_channel *chan, const char *data)
768 static int deprecation_warning = 0;
770 if (!deprecation_warning) {
771 deprecation_warning = 1;
772 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
775 if (ast_strlen_zero(data)) {
776 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
779 ast_string_field_set(chan, musicclass, data);
783 static int start_moh_exec(struct ast_channel *chan, const char *data)
787 AST_DECLARE_APP_ARGS(args,
791 parse = ast_strdupa(data);
793 AST_STANDARD_APP_ARGS(args, parse);
795 class = S_OR(args.class, NULL);
796 if (ast_moh_start(chan, class, NULL))
797 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
802 static int stop_moh_exec(struct ast_channel *chan, const char *data)
809 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
811 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
813 struct mohclass *moh = NULL;
814 struct mohclass tmp_class = {
818 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
821 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
823 moh = __ao2_find(mohclasses, &tmp_class, flags);
827 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
833 static struct mohdata *mohalloc(struct mohclass *cl)
838 if (!(moh = ast_calloc(1, sizeof(*moh))))
841 if (pipe(moh->pipe)) {
842 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
847 /* Make entirely non-blocking */
848 flags = fcntl(moh->pipe[0], F_GETFL);
849 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
850 flags = fcntl(moh->pipe[1], F_GETFL);
851 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
853 moh->f.frametype = AST_FRAME_VOICE;
854 moh->f.subclass.codec = cl->format;
855 moh->f.offset = AST_FRIENDLY_OFFSET;
857 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
860 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
866 static void moh_release(struct ast_channel *chan, void *data)
868 struct mohdata *moh = data;
869 struct mohclass *class = moh->parent;
873 AST_LIST_REMOVE(&moh->parent->members, moh, list);
879 oldwfmt = moh->origwfmt;
881 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
886 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
887 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
888 chan->name, ast_getformatname(oldwfmt));
891 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
895 static void *moh_alloc(struct ast_channel *chan, void *params)
898 struct mohclass *class = params;
899 struct moh_files_state *state;
901 /* Initiating music_state for current channel. Channel should know name of moh class */
902 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
903 chan->music_state = state;
904 state->class = mohclass_ref(class, "Copying reference into state container");
905 ast_module_ref(ast_module_info->self);
907 state = chan->music_state;
908 if (state && state->class != class) {
909 memset(state, 0, sizeof(*state));
910 state->class = class;
913 if ((res = mohalloc(class))) {
914 res->origwfmt = chan->writeformat;
915 if (ast_set_write_format(chan, class->format)) {
916 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
917 moh_release(NULL, res);
920 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
925 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
927 struct mohdata *moh = data;
928 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
931 len = ast_codec_get_len(moh->parent->format, samples);
933 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
934 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
935 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
937 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
941 moh->f.datalen = res;
942 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
943 moh->f.samples = ast_codec_get_samples(&moh->f);
945 if (ast_write(chan, &moh->f) < 0) {
946 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
953 static struct ast_generator mohgen = {
955 .release = moh_release,
956 .generate = moh_generate,
957 .digit = moh_handle_digit,
960 static int moh_add_file(struct mohclass *class, const char *filepath)
962 if (!class->allowed_files) {
963 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
965 class->allowed_files = INITIAL_NUM_FILES;
966 } else if (class->total_files == class->allowed_files) {
967 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
968 class->allowed_files = 0;
969 class->total_files = 0;
972 class->allowed_files *= 2;
975 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
978 class->total_files++;
983 static int moh_sort_compare(const void *i1, const void *i2)
987 s1 = ((char **)i1)[0];
988 s2 = ((char **)i2)[0];
990 return strcasecmp(s1, s2);
993 static int moh_scan_files(struct mohclass *class) {
996 struct dirent *files_dirent;
997 char dir_path[PATH_MAX];
999 char filepath[PATH_MAX];
1001 struct stat statbuf;
1005 if (class->dir[0] != '/') {
1006 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
1007 strncat(dir_path, "/", sizeof(dir_path) - 1);
1008 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
1010 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1012 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1013 files_DIR = opendir(dir_path);
1015 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1019 for (i = 0; i < class->total_files; i++)
1020 ast_free(class->filearray[i]);
1022 class->total_files = 0;
1023 dirnamelen = strlen(dir_path) + 2;
1024 if (!getcwd(path, sizeof(path))) {
1025 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1028 if (chdir(dir_path) < 0) {
1029 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1032 while ((files_dirent = readdir(files_DIR))) {
1033 /* The file name must be at least long enough to have the file type extension */
1034 if ((strlen(files_dirent->d_name) < 4))
1037 /* Skip files that starts with a dot */
1038 if (files_dirent->d_name[0] == '.')
1041 /* Skip files without extensions... they are not audio */
1042 if (!strchr(files_dirent->d_name, '.'))
1045 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1047 if (stat(filepath, &statbuf))
1050 if (!S_ISREG(statbuf.st_mode))
1053 if ((ext = strrchr(filepath, '.')))
1056 /* if the file is present in multiple formats, ensure we only put it into the list once */
1057 for (i = 0; i < class->total_files; i++)
1058 if (!strcmp(filepath, class->filearray[i]))
1061 if (i == class->total_files) {
1062 if (moh_add_file(class, filepath))
1067 closedir(files_DIR);
1068 if (chdir(path) < 0) {
1069 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1072 if (ast_test_flag(class, MOH_SORTALPHA))
1073 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1074 return class->total_files;
1077 static int init_files_class(struct mohclass *class)
1081 res = moh_scan_files(class);
1088 if (option_verbose > 2) {
1089 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
1090 class->dir, class->name);
1095 if (strchr(class->args, 'r')) {
1096 ast_set_flag(class, MOH_RANDOMIZE);
1103 static int moh_diff(struct mohclass *old, struct mohclass *new)
1109 if (strcmp(old->dir, new->dir)) {
1111 } else if (strcmp(old->mode, new->mode)) {
1113 } else if (strcmp(old->args, new->args)) {
1115 } else if (old->flags != new->flags) {
1122 static int init_app_class(struct mohclass *class)
1128 if (!strcasecmp(class->mode, "custom")) {
1129 ast_set_flag(class, MOH_CUSTOM);
1130 } else if (!strcasecmp(class->mode, "mp3nb")) {
1131 ast_set_flag(class, MOH_SINGLE);
1132 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1133 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1134 } else if (!strcasecmp(class->mode, "quietmp3")) {
1135 ast_set_flag(class, MOH_QUIET);
1139 class->pseudofd = -1;
1142 /* Open /dev/zap/pseudo for timing... Is
1143 there a better, yet reliable way to do this? */
1144 class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1145 if (class->pseudofd < 0) {
1146 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1149 ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1153 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1154 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1155 if (class->pseudofd > -1) {
1156 close(class->pseudofd);
1157 class->pseudofd = -1;
1166 * \note This function owns the reference it gets to moh if unref is true
1168 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1169 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1171 struct mohclass *mohclass = NULL;
1173 if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
1174 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1175 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1177 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1180 } else if (mohclass) {
1181 /* Found a class, but it's different from the one being registered */
1182 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1186 moh->start -= respawn_time;
1188 if (!strcasecmp(moh->mode, "files")) {
1189 if (init_files_class(moh)) {
1191 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1195 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1196 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1197 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1198 if (init_app_class(moh)) {
1200 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1205 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1207 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1212 ao2_t_link(mohclasses, moh, "Adding class to container");
1215 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1221 static void local_ast_moh_cleanup(struct ast_channel *chan)
1223 struct moh_files_state *state = chan->music_state;
1227 state->class = mohclass_unref(state->class, "Channel MOH state destruction");
1229 ast_free(chan->music_state);
1230 chan->music_state = NULL;
1231 /* Only held a module reference if we had a music state */
1232 ast_module_unref(ast_module_info->self);
1236 static void moh_class_destructor(void *obj);
1238 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1240 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1242 struct mohclass *class;
1246 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
1247 #elif defined(__AST_DEBUG_MALLOC)
1248 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
1250 ao2_alloc(sizeof(*class), moh_class_destructor)
1253 class->format = AST_FORMAT_SLINEAR;
1259 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1261 struct mohclass *mohclass = NULL;
1262 struct moh_files_state *state = chan->music_state;
1263 struct ast_variable *var = NULL;
1265 int realtime_possible = ast_check_realtime("musiconhold");
1267 /* The following is the order of preference for which class to use:
1268 * 1) The channels explicitly set musicclass, which should *only* be
1269 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1270 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1271 * result of receiving a HOLD control frame, this should be the
1272 * payload that came with the frame.
1273 * 3) The interpclass argument. This would be from the mohinterpret
1274 * option from channel drivers. This is the same as the old musicclass
1276 * 4) The default class.
1278 if (!ast_strlen_zero(chan->musicclass)) {
1279 mohclass = get_mohbyname(chan->musicclass, 1, 0);
1280 if (!mohclass && realtime_possible) {
1281 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
1284 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1285 mohclass = get_mohbyname(mclass, 1, 0);
1286 if (!mohclass && realtime_possible) {
1287 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1290 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1291 mohclass = get_mohbyname(interpclass, 1, 0);
1292 if (!mohclass && realtime_possible) {
1293 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1297 if (!mohclass && !var) {
1298 mohclass = get_mohbyname("default", 1, 0);
1299 if (!mohclass && realtime_possible) {
1300 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1304 /* If no moh class found in memory, then check RT. Note that the logic used
1305 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1308 struct ast_variable *tmp = NULL;
1310 if ((mohclass = moh_class_malloc())) {
1311 mohclass->realtime = 1;
1312 for (tmp = var; tmp; tmp = tmp->next) {
1313 if (!strcasecmp(tmp->name, "name"))
1314 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1315 else if (!strcasecmp(tmp->name, "mode"))
1316 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1317 else if (!strcasecmp(tmp->name, "directory"))
1318 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1319 else if (!strcasecmp(tmp->name, "application"))
1320 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1321 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1322 mohclass->digit = *tmp->value;
1323 else if (!strcasecmp(tmp->name, "random"))
1324 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1325 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1326 ast_set_flag(mohclass, MOH_RANDOMIZE);
1327 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1328 ast_set_flag(mohclass, MOH_SORTALPHA);
1329 else if (!strcasecmp(tmp->name, "format")) {
1330 mohclass->format = ast_getformatbyname(tmp->value);
1331 if (!mohclass->format) {
1332 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1333 mohclass->format = AST_FORMAT_SLINEAR;
1337 ast_variables_destroy(var);
1338 if (ast_strlen_zero(mohclass->dir)) {
1339 if (!strcasecmp(mohclass->mode, "custom")) {
1340 strcpy(mohclass->dir, "nodir");
1342 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1343 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1347 if (ast_strlen_zero(mohclass->mode)) {
1348 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1349 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1352 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1353 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1354 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1358 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1359 /* CACHERTCLASSES enabled, let's add this class to default tree */
1360 if (state && state->class) {
1361 /* Class already exist for this channel */
1362 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1363 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1364 /* we found RT class with the same name, seems like we should continue playing existing one */
1365 /* XXX This code is impossible to reach */
1366 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1367 mohclass = state->class;
1370 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1371 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1372 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1373 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1376 moh_register(mohclass, 0, DONT_UNREF);
1378 /* We don't register RT moh class, so let's init it manualy */
1380 time(&mohclass->start);
1381 mohclass->start -= respawn_time;
1383 if (!strcasecmp(mohclass->mode, "files")) {
1384 if (!moh_scan_files(mohclass)) {
1385 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1388 if (strchr(mohclass->args, 'r'))
1389 ast_set_flag(mohclass, MOH_RANDOMIZE);
1390 } 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")) {
1392 if (!strcasecmp(mohclass->mode, "custom"))
1393 ast_set_flag(mohclass, MOH_CUSTOM);
1394 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1395 ast_set_flag(mohclass, MOH_SINGLE);
1396 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1397 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1398 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1399 ast_set_flag(mohclass, MOH_QUIET);
1401 mohclass->srcfd = -1;
1403 /* Open /dev/dahdi/pseudo for timing... Is
1404 there a better, yet reliable way to do this? */
1405 mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1406 if (mohclass->pseudofd < 0) {
1407 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1410 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1413 mohclass->pseudofd = -1;
1415 /* Let's check if this channel already had a moh class before */
1416 if (state && state->class) {
1417 /* Class already exist for this channel */
1418 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1419 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1420 /* we found RT class with the same name, seems like we should continue playing existing one */
1421 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1422 mohclass = state->class;
1425 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1426 ast_log(LOG_WARNING, "Unable to create moh...\n");
1427 if (mohclass->pseudofd > -1) {
1428 close(mohclass->pseudofd);
1429 mohclass->pseudofd = -1;
1431 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1436 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1437 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1442 ast_variables_destroy(var);
1450 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1455 chan->name, chan->uniqueid,
1458 ast_set_flag(chan, AST_FLAG_MOH);
1460 if (mohclass->total_files) {
1461 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1463 res = ast_activate_generator(chan, &mohgen, mohclass);
1466 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1471 static void local_ast_moh_stop(struct ast_channel *chan)
1473 ast_clear_flag(chan, AST_FLAG_MOH);
1474 ast_deactivate_generator(chan);
1476 ast_channel_lock(chan);
1477 if (chan->music_state) {
1479 ast_closestream(chan->stream);
1480 chan->stream = NULL;
1484 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1488 chan->name, chan->uniqueid);
1489 ast_channel_unlock(chan);
1492 static void moh_class_destructor(void *obj)
1494 struct mohclass *class = obj;
1495 struct mohdata *member;
1498 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1500 /* Kill the thread first, so it cannot restart the child process while the
1501 * class is being destroyed */
1502 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1503 tid = class->thread;
1504 class->thread = AST_PTHREADT_NULL;
1505 pthread_cancel(tid);
1506 /* We'll collect the exit status later, after we ensure all the readers
1510 if (class->pid > 1) {
1512 int bytes, tbytes = 0, stime = 0, pid = 0;
1514 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
1516 stime = time(NULL) + 2;
1520 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1521 * to give the process a reason and time enough to kill off its
1524 if (killpg(pid, SIGHUP) < 0) {
1525 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1528 if (killpg(pid, SIGTERM) < 0) {
1529 if (errno == ESRCH) {
1532 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1535 if (killpg(pid, SIGKILL) < 0) {
1536 if (errno == ESRCH) {
1539 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1543 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1544 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1545 tbytes = tbytes + bytes;
1548 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1550 close(class->srcfd);
1553 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1557 if (class->filearray) {
1559 for (i = 0; i < class->total_files; i++) {
1560 free(class->filearray[i]);
1562 free(class->filearray);
1563 class->filearray = NULL;
1566 /* Finally, collect the exit status of the monitor thread */
1568 pthread_join(tid, NULL);
1572 static int moh_class_mark(void *obj, void *arg, int flags)
1574 struct mohclass *class = obj;
1581 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1583 struct mohclass *class = obj;
1585 return class->delete ? CMP_MATCH : 0;
1588 static int load_moh_classes(int reload)
1590 struct ast_config *cfg;
1591 struct ast_variable *var;
1592 struct mohclass *class;
1595 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1597 cfg = ast_config_load("musiconhold.conf", config_flags);
1599 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1604 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1607 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1609 cat = ast_category_browse(cfg, NULL);
1610 for (; cat; cat = ast_category_browse(cfg, cat)) {
1611 /* Setup common options from [general] section */
1612 if (!strcasecmp(cat, "general")) {
1613 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1614 if (!strcasecmp(var->name, "cachertclasses")) {
1615 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1617 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1621 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1622 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
1623 !strcasecmp(cat, "general")) {
1627 if (!(class = moh_class_malloc())) {
1631 ast_copy_string(class->name, cat, sizeof(class->name));
1632 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1633 if (!strcasecmp(var->name, "mode"))
1634 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1635 else if (!strcasecmp(var->name, "directory"))
1636 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1637 else if (!strcasecmp(var->name, "application"))
1638 ast_copy_string(class->args, var->value, sizeof(class->args));
1639 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1640 class->digit = *var->value;
1641 else if (!strcasecmp(var->name, "random"))
1642 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1643 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1644 ast_set_flag(class, MOH_RANDOMIZE);
1645 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1646 ast_set_flag(class, MOH_SORTALPHA);
1647 else if (!strcasecmp(var->name, "format")) {
1648 class->format = ast_getformatbyname(var->value);
1649 if (!class->format) {
1650 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1651 class->format = AST_FORMAT_SLINEAR;
1656 if (ast_strlen_zero(class->dir)) {
1657 if (!strcasecmp(class->mode, "custom")) {
1658 strcpy(class->dir, "nodir");
1660 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1661 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1665 if (ast_strlen_zero(class->mode)) {
1666 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1667 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1670 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1671 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1672 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1676 /* Don't leak a class when it's already registered */
1677 if (!moh_register(class, reload, HANDLE_REF)) {
1682 ast_config_destroy(cfg);
1684 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
1685 moh_classes_delete_marked, NULL, "Purge marked classes");
1690 static void ast_moh_destroy(void)
1692 ast_verb(2, "Destroying musiconhold processes\n");
1693 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1696 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1700 e->command = "moh reload";
1702 "Usage: moh reload\n"
1703 " Reloads the MusicOnHold module.\n"
1704 " Alias for 'module reload res_musiconhold.so'\n";
1710 if (a->argc != e->args)
1711 return CLI_SHOWUSAGE;
1718 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1720 struct mohclass *class;
1721 struct ao2_iterator i;
1725 e->command = "moh show files";
1727 "Usage: moh show files\n"
1728 " Lists all loaded file-based MusicOnHold classes and their\n"
1735 if (a->argc != e->args)
1736 return CLI_SHOWUSAGE;
1738 i = ao2_iterator_init(mohclasses, 0);
1739 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1742 if (!class->total_files) {
1746 ast_cli(a->fd, "Class: %s\n", class->name);
1747 for (x = 0; x < class->total_files; x++) {
1748 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1751 ao2_iterator_destroy(&i);
1756 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1758 struct mohclass *class;
1759 struct ao2_iterator i;
1763 e->command = "moh show classes";
1765 "Usage: moh show classes\n"
1766 " Lists all MusicOnHold classes.\n";
1772 if (a->argc != e->args)
1773 return CLI_SHOWUSAGE;
1775 i = ao2_iterator_init(mohclasses, 0);
1776 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1777 ast_cli(a->fd, "Class: %s\n", class->name);
1778 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1779 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1780 if (ast_test_flag(class, MOH_CUSTOM)) {
1781 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1783 if (strcasecmp(class->mode, "files")) {
1784 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1787 ao2_iterator_destroy(&i);
1792 static struct ast_cli_entry cli_moh[] = {
1793 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1794 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1795 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1798 static int moh_class_hash(const void *obj, const int flags)
1800 const struct mohclass *class = obj;
1802 return ast_str_case_hash(class->name);
1805 static int moh_class_cmp(void *obj, void *arg, int flags)
1807 struct mohclass *class = obj, *class2 = arg;
1809 return strcasecmp(class->name, class2->name) ? 0 :
1810 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1811 CMP_MATCH | CMP_STOP;
1814 static int load_module(void)
1818 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1819 return AST_MODULE_LOAD_DECLINE;
1822 if (!load_moh_classes(0)) { /* No music classes configured, so skip it */
1823 ast_log(LOG_WARNING, "No music on hold classes configured, "
1824 "disabling music on hold.\n");
1826 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1827 local_ast_moh_cleanup);
1830 res = ast_register_application_xml(play_moh, play_moh_exec);
1831 ast_register_atexit(ast_moh_destroy);
1832 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1834 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1836 res = ast_register_application_xml(set_moh, set_moh_exec);
1838 res = ast_register_application_xml(start_moh, start_moh_exec);
1840 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1842 return AST_MODULE_LOAD_SUCCESS;
1845 static int reload(void)
1847 if (load_moh_classes(1)) {
1848 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1849 local_ast_moh_cleanup);
1852 return AST_MODULE_LOAD_SUCCESS;
1855 static int moh_class_inuse(void *obj, void *arg, int flags)
1857 struct mohclass *class = obj;
1859 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1862 static int unload_module(void)
1865 struct mohclass *class = NULL;
1867 /* XXX This check shouldn't be required if module ref counting was being used
1869 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
1870 class = mohclass_unref(class, "unref of class from module unload callback");
1875 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1879 ast_uninstall_music_functions();
1882 res = ast_unregister_application(play_moh);
1883 res |= ast_unregister_application(wait_moh);
1884 res |= ast_unregister_application(set_moh);
1885 res |= ast_unregister_application(start_moh);
1886 res |= ast_unregister_application(stop_moh);
1887 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
1888 ast_unregister_atexit(ast_moh_destroy);
1893 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1894 .load = load_module,
1895 .unload = unload_module,