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>
47 #include <dahdi/user.h>
50 #include "asterisk/lock.h"
51 #include "asterisk/file.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/app.h"
55 #include "asterisk/module.h"
56 #include "asterisk/translate.h"
57 #include "asterisk/say.h"
58 #include "asterisk/musiconhold.h"
59 #include "asterisk/config.h"
60 #include "asterisk/utils.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/linkedlists.h"
64 #include "asterisk/manager.h"
65 #include "asterisk/paths.h"
66 #include "asterisk/astobj2.h"
68 #define INITIAL_NUM_FILES 8
73 <application name="MusicOnHold" language="en_US">
75 Play Music On Hold indefinitely.
78 <parameter name="class" required="true" />
79 <parameter name="duration" />
82 <para>Plays hold music specified by class. If omitted, the default music
83 source for the channel will be used. Change the default class with
84 Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
85 specified number of seconds. If duration is ommited, music plays indefinitely.
86 Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
89 <application name="WaitMusicOnHold" language="en_US">
91 Wait, playing Music On Hold.
94 <parameter name="delay" required="true" />
97 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
98 <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
99 or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
100 with no sound.</para>
101 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
104 <application name="SetMusicOnHold" language="en_US">
106 Set default Music On Hold class.
109 <parameter name="class" required="yes" />
112 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
113 <para>Sets the default class for music on hold for a given channel.
114 When music on hold is activated, this class will be used to select which
115 music is played.</para>
116 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
119 <application name="StartMusicOnHold" language="en_US">
124 <parameter name="class" required="true" />
127 <para>Starts playing music on hold, uses default music class for channel.
128 Starts playing music specified by class. If omitted, the default music
129 source for the channel will be used. Always returns <literal>0</literal>.</para>
132 <application name="StopMusicOnHold" language="en_US">
134 Stop playing Music On Hold.
138 <para>Stops playing music on hold.</para>
143 static const char play_moh[] = "MusicOnHold";
144 static const char wait_moh[] = "WaitMusicOnHold";
145 static const char set_moh[] = "SetMusicOnHold";
146 static const char start_moh[] = "StartMusicOnHold";
147 static const char stop_moh[] = "StopMusicOnHold";
149 static int respawn_time = 20;
151 struct moh_files_state {
152 struct mohclass *class;
153 char name[MAX_MUSICCLASS];
160 char *save_pos_filename;
163 #define MOH_QUIET (1 << 0)
164 #define MOH_SINGLE (1 << 1)
165 #define MOH_CUSTOM (1 << 2)
166 #define MOH_RANDOMIZE (1 << 3)
167 #define MOH_SORTALPHA (1 << 4)
169 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
171 /* Custom astobj2 flag */
172 #define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
174 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
177 char name[MAX_MUSICCLASS];
182 /*! A dynamically sized array to hold the list of filenames in "files" mode */
184 /*! The current size of the filearray */
186 /*! The current number of files loaded into the filearray */
189 /*! The format from the MOH source, not applicable to "files" mode */
191 /*! The pid of the external application delivering MOH */
195 /*! Source of audio */
197 /*! FD for timing source */
199 /*! Created on the fly, from RT engine */
201 unsigned int delete:1;
202 AST_LIST_HEAD_NOLOCK(, mohdata) members;
203 AST_LIST_ENTRY(mohclass) list;
209 struct mohclass *parent;
211 AST_LIST_ENTRY(mohdata) list;
214 static struct ao2_container *mohclasses;
215 static struct ao2_container *deleted_classes;
216 static pthread_t deleted_thread;
218 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
219 #define MPG_123 "/usr/bin/mpg123"
222 static int reload(void);
224 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
225 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
227 static void moh_files_release(struct ast_channel *chan, void *data)
229 struct moh_files_state *state;
231 if (!chan || !chan->music_state) {
235 state = chan->music_state;
238 ast_closestream(chan->stream);
242 if (option_verbose > 2) {
243 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
246 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
247 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
250 state->save_pos = state->pos;
252 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
255 static int ast_moh_files_next(struct ast_channel *chan)
257 struct moh_files_state *state = chan->music_state;
260 /* Discontinue a stream if it is running already */
262 ast_closestream(chan->stream);
266 if (!state->class->total_files) {
267 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
271 /* If a specific file has been saved confirm it still exists and that it is still valid */
272 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
273 state->pos = state->save_pos;
274 state->save_pos = -1;
275 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
276 /* Get a random file and ensure we can open it */
277 for (tries = 0; tries < 20; tries++) {
278 state->pos = ast_random() % state->class->total_files;
279 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
282 state->save_pos = -1;
285 /* This is easy, just increment our position and make sure we don't exceed the total file count */
287 state->pos %= state->class->total_files;
288 state->save_pos = -1;
292 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
293 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
295 state->pos %= state->class->total_files;
299 /* Record the pointer to the filename for position resuming later */
300 state->save_pos_filename = state->class->filearray[state->pos];
302 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
305 ast_seekstream(chan->stream, state->samples, SEEK_SET);
310 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
312 struct ast_frame *f = NULL;
314 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
315 if (!ast_moh_files_next(chan))
316 f = ast_readframe(chan->stream);
322 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
324 struct moh_files_state *state = chan->music_state;
325 struct ast_frame *f = NULL;
328 state->sample_queue += samples;
330 while (state->sample_queue > 0) {
331 if ((f = moh_files_readframe(chan))) {
332 state->samples += f->samples;
333 state->sample_queue -= f->samples;
334 res = ast_write(chan, f);
337 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
346 static void *moh_files_alloc(struct ast_channel *chan, void *params)
348 struct moh_files_state *state;
349 struct mohclass *class = params;
351 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
352 chan->music_state = state;
354 state = chan->music_state;
361 /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
362 * malloc may allocate a different class to the same memory block. This
363 * might only happen when two reloads are generated in a short period of
364 * time, but it's still important to protect against.
365 * PROG: Compare the quick operation first, to save CPU. */
366 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
367 memset(state, 0, sizeof(*state));
368 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
369 state->pos = ast_random() % class->total_files;
373 state->class = mohclass_ref(class, "Reffing music class for channel");
374 state->origwfmt = chan->writeformat;
375 /* For comparison on restart of MOH (see above) */
376 ast_copy_string(state->name, class->name, sizeof(state->name));
377 state->save_total = class->total_files;
379 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
381 return chan->music_state;
384 static int moh_digit_match(void *obj, void *arg, int flags)
387 struct mohclass *class = obj;
389 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
392 /*! \note This function should be called with the mohclasses list locked */
393 static struct mohclass *get_mohbydigit(char digit)
395 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
398 static void moh_handle_digit(struct ast_channel *chan, char digit)
400 struct mohclass *class;
401 const char *classname = NULL;
403 if ((class = get_mohbydigit(digit))) {
404 classname = ast_strdupa(class->name);
405 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
406 ast_string_field_set(chan,musicclass,classname);
408 ast_moh_start(chan, classname, NULL);
412 static struct ast_generator moh_file_stream =
414 .alloc = moh_files_alloc,
415 .release = moh_files_release,
416 .generate = moh_files_generator,
417 .digit = moh_handle_digit,
420 static int spawn_mp3(struct mohclass *class)
424 char fns[MAX_MP3S][80];
425 char *argv[MAX_MP3S + 50];
433 if (!strcasecmp(class->dir, "nodir")) {
436 dir = opendir(class->dir);
437 if (!dir && strncasecmp(class->dir, "http://", 7)) {
438 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
443 if (!ast_test_flag(class, MOH_CUSTOM)) {
444 argv[argc++] = "mpg123";
447 argv[argc++] = "--mono";
449 argv[argc++] = "8000";
451 if (!ast_test_flag(class, MOH_SINGLE)) {
453 argv[argc++] = "2048";
458 if (ast_test_flag(class, MOH_QUIET))
459 argv[argc++] = "4096";
461 argv[argc++] = "8192";
463 /* Look for extra arguments and add them to the list */
464 ast_copy_string(xargs, class->args, sizeof(xargs));
466 while (!ast_strlen_zero(argptr)) {
467 argv[argc++] = argptr;
468 strsep(&argptr, ",");
471 /* Format arguments for argv vector */
472 ast_copy_string(xargs, class->args, sizeof(xargs));
474 while (!ast_strlen_zero(argptr)) {
475 argv[argc++] = argptr;
476 strsep(&argptr, " ");
480 if (!strncasecmp(class->dir, "http://", 7)) {
481 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
482 argv[argc++] = fns[files];
485 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
486 if ((strlen(de->d_name) > 3) &&
487 ((ast_test_flag(class, MOH_CUSTOM) &&
488 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
489 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
490 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
491 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
492 argv[argc++] = fns[files];
502 ast_log(LOG_WARNING, "Pipe failed\n");
506 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
511 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
512 sleep(respawn_time - (time(NULL) - class->start));
516 class->pid = ast_safe_fork(0);
517 if (class->pid < 0) {
520 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
524 if (ast_opt_high_priority)
528 /* Stdout goes to pipe */
529 dup2(fds[1], STDOUT_FILENO);
531 /* Close everything else */
532 ast_close_fds_above_n(STDERR_FILENO);
535 if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
536 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
539 setpgid(0, getpid());
540 if (ast_test_flag(class, MOH_CUSTOM)) {
541 execv(argv[0], argv);
543 /* Default install is /usr/local/bin */
544 execv(LOCAL_MPG_123, argv);
545 /* Many places have it in /usr/bin */
546 execv(MPG_123, argv);
547 /* Check PATH as a last-ditch effort */
548 execvp("mpg123", argv);
550 /* Can't use logger, since log FDs are closed */
551 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
561 static void *deleted_monitor(void *data)
563 struct ao2_iterator iter;
564 struct mohclass *class;
565 struct ast_module *mod = NULL;
568 pthread_testcancel();
569 if (ao2_container_count(deleted_classes) == 0) {
571 ast_module_unref(mod);
576 /* While deleted classes are still in use, prohibit unloading */
577 mod = ast_module_ref(ast_module_info->self);
579 pthread_testcancel();
580 iter = ao2_iterator_init(deleted_classes, 0);
581 while ((class = ao2_iterator_next(&iter))) {
582 if (ao2_ref(class, 0) == 2) {
583 ao2_unlink(deleted_classes, class);
587 ao2_iterator_destroy(&iter);
588 if (ao2_container_count(deleted_classes) == 0 && mod) {
589 ast_module_unref(mod);
592 pthread_testcancel();
598 static void *monmp3thread(void *data)
600 #define MOH_MS_INTERVAL 100
602 struct mohclass *class = data;
608 struct timeval deadline, tv_tmp;
611 deadline.tv_usec = 0;
613 pthread_testcancel();
614 /* Spawn mp3 player if it's not there */
615 if (class->srcfd < 0) {
616 if ((class->srcfd = spawn_mp3(class)) < 0) {
617 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
618 /* Try again later */
620 pthread_testcancel();
623 if (class->pseudofd > -1) {
627 /* Pause some amount of time */
628 res = read(class->pseudofd, buf, sizeof(buf));
629 pthread_testcancel();
633 tv_tmp = ast_tvnow();
634 if (ast_tvzero(deadline))
636 delta = ast_tvdiff_ms(tv_tmp, deadline);
637 if (delta < MOH_MS_INTERVAL) { /* too early */
638 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
639 usleep(1000 * (MOH_MS_INTERVAL - delta));
640 pthread_testcancel();
642 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
645 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
647 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
650 len = ast_codec_get_len(class->format, res);
652 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
656 pthread_testcancel();
657 if (class->pid > 1) {
659 if (killpg(class->pid, SIGHUP) < 0) {
660 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
663 if (killpg(class->pid, SIGTERM) < 0) {
664 if (errno == ESRCH) {
667 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
670 if (killpg(class->pid, SIGKILL) < 0) {
671 if (errno == ESRCH) {
674 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
680 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
685 pthread_testcancel();
688 AST_LIST_TRAVERSE(&class->members, moh, list) {
690 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
691 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
699 static int play_moh_exec(struct ast_channel *chan, const char *data)
705 AST_DECLARE_APP_ARGS(args,
707 AST_APP_ARG(duration);
710 parse = ast_strdupa(data);
712 AST_STANDARD_APP_ARGS(args, parse);
714 if (!ast_strlen_zero(args.duration)) {
715 if (sscanf(args.duration, "%30d", &timeout) == 1) {
718 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
722 class = S_OR(args.class, NULL);
723 if (ast_moh_start(chan, class, NULL)) {
724 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
729 res = ast_safe_sleep(chan, timeout);
731 while (!(res = ast_safe_sleep(chan, 10000)));
739 static int wait_moh_exec(struct ast_channel *chan, const char *data)
741 static int deprecation_warning = 0;
744 if (!deprecation_warning) {
745 deprecation_warning = 1;
746 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
749 if (!data || !atoi(data)) {
750 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
753 if (ast_moh_start(chan, NULL, NULL)) {
754 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
757 res = ast_safe_sleep(chan, atoi(data) * 1000);
762 static int set_moh_exec(struct ast_channel *chan, const char *data)
764 static int deprecation_warning = 0;
766 if (!deprecation_warning) {
767 deprecation_warning = 1;
768 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
771 if (ast_strlen_zero(data)) {
772 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
775 ast_string_field_set(chan, musicclass, data);
779 static int start_moh_exec(struct ast_channel *chan, const char *data)
783 AST_DECLARE_APP_ARGS(args,
787 parse = ast_strdupa(data);
789 AST_STANDARD_APP_ARGS(args, parse);
791 class = S_OR(args.class, NULL);
792 if (ast_moh_start(chan, class, NULL))
793 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
798 static int stop_moh_exec(struct ast_channel *chan, const char *data)
805 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
807 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
809 struct mohclass *moh = NULL;
810 struct mohclass tmp_class = {
814 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
817 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
819 moh = __ao2_find(mohclasses, &tmp_class, flags);
823 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
829 static struct mohdata *mohalloc(struct mohclass *cl)
834 if (!(moh = ast_calloc(1, sizeof(*moh))))
837 if (pipe(moh->pipe)) {
838 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
843 /* Make entirely non-blocking */
844 flags = fcntl(moh->pipe[0], F_GETFL);
845 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
846 flags = fcntl(moh->pipe[1], F_GETFL);
847 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
849 moh->f.frametype = AST_FRAME_VOICE;
850 moh->f.subclass.codec = cl->format;
851 moh->f.offset = AST_FRIENDLY_OFFSET;
853 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
856 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
862 static void moh_release(struct ast_channel *chan, void *data)
864 struct mohdata *moh = data;
865 struct mohclass *class = moh->parent;
869 AST_LIST_REMOVE(&moh->parent->members, moh, list);
875 oldwfmt = moh->origwfmt;
877 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
882 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
883 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
884 chan->name, ast_getformatname(oldwfmt));
887 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
891 static void *moh_alloc(struct ast_channel *chan, void *params)
894 struct mohclass *class = params;
895 struct moh_files_state *state;
897 /* Initiating music_state for current channel. Channel should know name of moh class */
898 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
899 chan->music_state = state;
900 state->class = class;
902 state = chan->music_state;
903 if (state && state->class != class) {
904 memset(state, 0, sizeof(*state));
905 state->class = class;
908 if ((res = mohalloc(class))) {
909 res->origwfmt = chan->writeformat;
910 if (ast_set_write_format(chan, class->format)) {
911 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
912 moh_release(NULL, res);
915 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
920 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
922 struct mohdata *moh = data;
923 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
926 len = ast_codec_get_len(moh->parent->format, samples);
928 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
929 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
930 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
932 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
936 moh->f.datalen = res;
937 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
938 moh->f.samples = ast_codec_get_samples(&moh->f);
940 if (ast_write(chan, &moh->f) < 0) {
941 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
948 static struct ast_generator mohgen = {
950 .release = moh_release,
951 .generate = moh_generate,
952 .digit = moh_handle_digit,
955 static int moh_add_file(struct mohclass *class, const char *filepath)
957 if (!class->allowed_files) {
958 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
960 class->allowed_files = INITIAL_NUM_FILES;
961 } else if (class->total_files == class->allowed_files) {
962 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
963 class->allowed_files = 0;
964 class->total_files = 0;
967 class->allowed_files *= 2;
970 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
973 class->total_files++;
978 static int moh_sort_compare(const void *i1, const void *i2)
982 s1 = ((char **)i1)[0];
983 s2 = ((char **)i2)[0];
985 return strcasecmp(s1, s2);
988 static int moh_scan_files(struct mohclass *class) {
991 struct dirent *files_dirent;
992 char dir_path[PATH_MAX];
994 char filepath[PATH_MAX];
1000 if (class->dir[0] != '/') {
1001 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
1002 strncat(dir_path, "/", sizeof(dir_path) - 1);
1003 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
1005 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1007 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1008 files_DIR = opendir(dir_path);
1010 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1014 for (i = 0; i < class->total_files; i++)
1015 ast_free(class->filearray[i]);
1017 class->total_files = 0;
1018 dirnamelen = strlen(dir_path) + 2;
1019 if (!getcwd(path, sizeof(path))) {
1020 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1023 if (chdir(dir_path) < 0) {
1024 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1027 while ((files_dirent = readdir(files_DIR))) {
1028 /* The file name must be at least long enough to have the file type extension */
1029 if ((strlen(files_dirent->d_name) < 4))
1032 /* Skip files that starts with a dot */
1033 if (files_dirent->d_name[0] == '.')
1036 /* Skip files without extensions... they are not audio */
1037 if (!strchr(files_dirent->d_name, '.'))
1040 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1042 if (stat(filepath, &statbuf))
1045 if (!S_ISREG(statbuf.st_mode))
1048 if ((ext = strrchr(filepath, '.')))
1051 /* if the file is present in multiple formats, ensure we only put it into the list once */
1052 for (i = 0; i < class->total_files; i++)
1053 if (!strcmp(filepath, class->filearray[i]))
1056 if (i == class->total_files) {
1057 if (moh_add_file(class, filepath))
1062 closedir(files_DIR);
1063 if (chdir(path) < 0) {
1064 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1067 if (ast_test_flag(class, MOH_SORTALPHA))
1068 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1069 return class->total_files;
1072 static int init_files_class(struct mohclass *class)
1076 res = moh_scan_files(class);
1083 if (option_verbose > 2) {
1084 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
1085 class->dir, class->name);
1090 if (strchr(class->args, 'r')) {
1091 ast_set_flag(class, MOH_RANDOMIZE);
1098 static int moh_diff(struct mohclass *old, struct mohclass *new)
1104 if (strcmp(old->dir, new->dir)) {
1106 } else if (strcmp(old->mode, new->mode)) {
1108 } else if (strcmp(old->args, new->args)) {
1110 } else if (old->flags != new->flags) {
1117 static int init_app_class(struct mohclass *class)
1123 if (!strcasecmp(class->mode, "custom")) {
1124 ast_set_flag(class, MOH_CUSTOM);
1125 } else if (!strcasecmp(class->mode, "mp3nb")) {
1126 ast_set_flag(class, MOH_SINGLE);
1127 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1128 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1129 } else if (!strcasecmp(class->mode, "quietmp3")) {
1130 ast_set_flag(class, MOH_QUIET);
1134 class->pseudofd = -1;
1137 /* Open /dev/zap/pseudo for timing... Is
1138 there a better, yet reliable way to do this? */
1139 class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1140 if (class->pseudofd < 0) {
1141 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1144 ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1148 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1149 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1150 if (class->pseudofd > -1) {
1151 close(class->pseudofd);
1152 class->pseudofd = -1;
1161 * \note This function owns the reference it gets to moh if unref is true
1163 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1164 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1166 struct mohclass *mohclass = NULL;
1168 if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
1169 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1170 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1172 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1175 } else if (mohclass) {
1176 /* Found a class, but it's different from the one being registered */
1177 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1181 moh->start -= respawn_time;
1183 if (!strcasecmp(moh->mode, "files")) {
1184 if (init_files_class(moh)) {
1185 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1188 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1189 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1190 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1191 if (init_app_class(moh)) {
1192 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1196 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1197 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1201 ao2_t_link(mohclasses, moh, "Adding class to container");
1204 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1210 static void local_ast_moh_cleanup(struct ast_channel *chan)
1212 struct moh_files_state *state = chan->music_state;
1216 state->class = mohclass_unref(state->class, "Channel MOH state destruction");
1218 ast_free(chan->music_state);
1219 chan->music_state = NULL;
1223 static void moh_class_destructor(void *obj);
1225 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1227 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1229 struct mohclass *class;
1233 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
1234 #elif defined(__AST_DEBUG_MALLOC)
1235 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
1237 ao2_alloc(sizeof(*class), moh_class_destructor)
1240 class->format = AST_FORMAT_SLINEAR;
1246 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1248 struct mohclass *mohclass = NULL;
1249 struct moh_files_state *state = chan->music_state;
1250 struct ast_variable *var = NULL;
1252 int realtime_possible = ast_check_realtime("musiconhold");
1254 /* The following is the order of preference for which class to use:
1255 * 1) The channels explicitly set musicclass, which should *only* be
1256 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1257 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1258 * result of receiving a HOLD control frame, this should be the
1259 * payload that came with the frame.
1260 * 3) The interpclass argument. This would be from the mohinterpret
1261 * option from channel drivers. This is the same as the old musicclass
1263 * 4) The default class.
1265 if (!ast_strlen_zero(chan->musicclass)) {
1266 mohclass = get_mohbyname(chan->musicclass, 1, 0);
1267 if (!mohclass && realtime_possible) {
1268 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
1271 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1272 mohclass = get_mohbyname(mclass, 1, 0);
1273 if (!mohclass && realtime_possible) {
1274 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1277 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1278 mohclass = get_mohbyname(interpclass, 1, 0);
1279 if (!mohclass && realtime_possible) {
1280 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1284 if (!mohclass && !var) {
1285 mohclass = get_mohbyname("default", 1, 0);
1286 if (!mohclass && realtime_possible) {
1287 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1291 /* If no moh class found in memory, then check RT. Note that the logic used
1292 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1295 struct ast_variable *tmp = NULL;
1297 if ((mohclass = moh_class_malloc())) {
1298 mohclass->realtime = 1;
1299 for (tmp = var; tmp; tmp = tmp->next) {
1300 if (!strcasecmp(tmp->name, "name"))
1301 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1302 else if (!strcasecmp(tmp->name, "mode"))
1303 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1304 else if (!strcasecmp(tmp->name, "directory"))
1305 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1306 else if (!strcasecmp(tmp->name, "application"))
1307 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1308 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1309 mohclass->digit = *tmp->value;
1310 else if (!strcasecmp(tmp->name, "random"))
1311 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1312 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1313 ast_set_flag(mohclass, MOH_RANDOMIZE);
1314 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1315 ast_set_flag(mohclass, MOH_SORTALPHA);
1316 else if (!strcasecmp(tmp->name, "format")) {
1317 mohclass->format = ast_getformatbyname(tmp->value);
1318 if (!mohclass->format) {
1319 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1320 mohclass->format = AST_FORMAT_SLINEAR;
1324 ast_variables_destroy(var);
1325 if (ast_strlen_zero(mohclass->dir)) {
1326 if (!strcasecmp(mohclass->mode, "custom")) {
1327 strcpy(mohclass->dir, "nodir");
1329 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1330 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1334 if (ast_strlen_zero(mohclass->mode)) {
1335 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1336 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1339 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1340 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1341 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1345 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1346 /* CACHERTCLASSES enabled, let's add this class to default tree */
1347 if (state && state->class) {
1348 /* Class already exist for this channel */
1349 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1350 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1351 /* we found RT class with the same name, seems like we should continue playing existing one */
1352 /* XXX This code is impossible to reach */
1353 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1354 mohclass = state->class;
1357 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1358 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1359 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1360 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1363 moh_register(mohclass, 0, DONT_UNREF);
1365 /* We don't register RT moh class, so let's init it manualy */
1367 time(&mohclass->start);
1368 mohclass->start -= respawn_time;
1370 if (!strcasecmp(mohclass->mode, "files")) {
1371 if (!moh_scan_files(mohclass)) {
1372 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1375 if (strchr(mohclass->args, 'r'))
1376 ast_set_flag(mohclass, MOH_RANDOMIZE);
1377 } 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")) {
1379 if (!strcasecmp(mohclass->mode, "custom"))
1380 ast_set_flag(mohclass, MOH_CUSTOM);
1381 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1382 ast_set_flag(mohclass, MOH_SINGLE);
1383 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1384 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1385 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1386 ast_set_flag(mohclass, MOH_QUIET);
1388 mohclass->srcfd = -1;
1390 /* Open /dev/dahdi/pseudo for timing... Is
1391 there a better, yet reliable way to do this? */
1392 mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1393 if (mohclass->pseudofd < 0) {
1394 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1397 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1400 mohclass->pseudofd = -1;
1402 /* Let's check if this channel already had a moh class before */
1403 if (state && state->class) {
1404 /* Class already exist for this channel */
1405 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1406 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1407 /* we found RT class with the same name, seems like we should continue playing existing one */
1408 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1409 mohclass = state->class;
1412 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1413 ast_log(LOG_WARNING, "Unable to create moh...\n");
1414 if (mohclass->pseudofd > -1) {
1415 close(mohclass->pseudofd);
1416 mohclass->pseudofd = -1;
1418 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1423 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1424 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1429 ast_variables_destroy(var);
1437 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1441 chan->name, chan->uniqueid);
1443 ast_set_flag(chan, AST_FLAG_MOH);
1445 if (mohclass->total_files) {
1446 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1448 res = ast_activate_generator(chan, &mohgen, mohclass);
1451 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1456 static void local_ast_moh_stop(struct ast_channel *chan)
1458 struct moh_files_state *state = chan->music_state;
1459 ast_clear_flag(chan, AST_FLAG_MOH);
1460 ast_deactivate_generator(chan);
1464 ast_closestream(chan->stream);
1465 chan->stream = NULL;
1469 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1473 chan->name, chan->uniqueid);
1476 static void moh_class_destructor(void *obj)
1478 struct mohclass *class = obj;
1479 struct mohdata *member;
1482 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1484 /* Kill the thread first, so it cannot restart the child process while the
1485 * class is being destroyed */
1486 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1487 tid = class->thread;
1488 class->thread = AST_PTHREADT_NULL;
1489 pthread_cancel(tid);
1490 /* We'll collect the exit status later, after we ensure all the readers
1494 if (class->pid > 1) {
1496 int bytes, tbytes = 0, stime = 0, pid = 0;
1498 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
1500 stime = time(NULL) + 2;
1504 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1505 * to give the process a reason and time enough to kill off its
1508 if (killpg(pid, SIGHUP) < 0) {
1509 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1512 if (killpg(pid, SIGTERM) < 0) {
1513 if (errno == ESRCH) {
1516 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1519 if (killpg(pid, SIGKILL) < 0) {
1520 if (errno == ESRCH) {
1523 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1527 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1528 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1529 tbytes = tbytes + bytes;
1532 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1534 close(class->srcfd);
1537 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1541 if (class->filearray) {
1543 for (i = 0; i < class->total_files; i++) {
1544 free(class->filearray[i]);
1546 free(class->filearray);
1547 class->filearray = NULL;
1550 /* Finally, collect the exit status of the monitor thread */
1552 pthread_join(tid, NULL);
1556 static int moh_class_mark(void *obj, void *arg, int flags)
1558 struct mohclass *class = obj;
1565 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1567 struct mohclass *class = obj;
1569 if (class->delete) {
1570 ao2_link(deleted_classes, obj);
1571 pthread_kill(deleted_thread, SIGURG);
1577 static int load_moh_classes(int reload)
1579 struct ast_config *cfg;
1580 struct ast_variable *var;
1581 struct mohclass *class;
1584 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1586 cfg = ast_config_load("musiconhold.conf", config_flags);
1588 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1593 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1596 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1598 cat = ast_category_browse(cfg, NULL);
1599 for (; cat; cat = ast_category_browse(cfg, cat)) {
1600 /* Setup common options from [general] section */
1601 if (!strcasecmp(cat, "general")) {
1602 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1603 if (!strcasecmp(var->name, "cachertclasses")) {
1604 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1606 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1610 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1611 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
1612 !strcasecmp(cat, "general")) {
1616 if (!(class = moh_class_malloc())) {
1620 ast_copy_string(class->name, cat, sizeof(class->name));
1621 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1622 if (!strcasecmp(var->name, "mode"))
1623 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1624 else if (!strcasecmp(var->name, "directory"))
1625 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1626 else if (!strcasecmp(var->name, "application"))
1627 ast_copy_string(class->args, var->value, sizeof(class->args));
1628 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1629 class->digit = *var->value;
1630 else if (!strcasecmp(var->name, "random"))
1631 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1632 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1633 ast_set_flag(class, MOH_RANDOMIZE);
1634 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1635 ast_set_flag(class, MOH_SORTALPHA);
1636 else if (!strcasecmp(var->name, "format")) {
1637 class->format = ast_getformatbyname(var->value);
1638 if (!class->format) {
1639 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1640 class->format = AST_FORMAT_SLINEAR;
1645 if (ast_strlen_zero(class->dir)) {
1646 if (!strcasecmp(class->mode, "custom")) {
1647 strcpy(class->dir, "nodir");
1649 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1650 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1654 if (ast_strlen_zero(class->mode)) {
1655 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1656 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1659 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1660 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1661 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1665 /* Don't leak a class when it's already registered */
1666 if (!moh_register(class, reload, HANDLE_REF)) {
1671 ast_config_destroy(cfg);
1673 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
1674 moh_classes_delete_marked, NULL, "Purge marked classes");
1679 static void ast_moh_destroy(void)
1681 ast_verb(2, "Destroying musiconhold processes\n");
1682 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1685 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1689 e->command = "moh reload";
1691 "Usage: moh reload\n"
1692 " Reloads the MusicOnHold module.\n"
1693 " Alias for 'module reload res_musiconhold.so'\n";
1699 if (a->argc != e->args)
1700 return CLI_SHOWUSAGE;
1707 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1709 struct mohclass *class;
1710 struct ao2_iterator i;
1714 e->command = "moh show files";
1716 "Usage: moh show files\n"
1717 " Lists all loaded file-based MusicOnHold classes and their\n"
1724 if (a->argc != e->args)
1725 return CLI_SHOWUSAGE;
1727 i = ao2_iterator_init(mohclasses, 0);
1728 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1731 if (!class->total_files) {
1735 ast_cli(a->fd, "Class: %s\n", class->name);
1736 for (x = 0; x < class->total_files; x++) {
1737 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1740 ao2_iterator_destroy(&i);
1745 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1747 struct mohclass *class;
1748 struct ao2_iterator i;
1752 e->command = "moh show classes";
1754 "Usage: moh show classes\n"
1755 " Lists all MusicOnHold classes.\n";
1761 if (a->argc != e->args)
1762 return CLI_SHOWUSAGE;
1764 i = ao2_iterator_init(mohclasses, 0);
1765 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1766 ast_cli(a->fd, "Class: %s\n", class->name);
1767 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1768 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1769 if (ast_test_flag(class, MOH_CUSTOM)) {
1770 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1772 if (strcasecmp(class->mode, "files")) {
1773 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1776 ao2_iterator_destroy(&i);
1777 i = ao2_iterator_init(deleted_classes, 0);
1778 for (; (class = ao2_t_iterator_next(&i, "Show deleted classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1779 ast_cli(a->fd, "(Deleted) Class: %s (%d)\n", class->name, ao2_ref(class, 0) - 2);
1780 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1781 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1782 ast_cli(a->fd, "\tRealtime: %s\n", class->realtime ? "yes" : "no");
1783 if (ast_test_flag(class, MOH_CUSTOM)) {
1784 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1786 if (strcasecmp(class->mode, "files")) {
1787 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1790 ao2_iterator_destroy(&i);
1795 static struct ast_cli_entry cli_moh[] = {
1796 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1797 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1798 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1801 static int moh_class_hash(const void *obj, const int flags)
1803 const struct mohclass *class = obj;
1805 return ast_str_case_hash(class->name);
1808 static int moh_class_cmp(void *obj, void *arg, int flags)
1810 struct mohclass *class = obj, *class2 = arg;
1812 return strcasecmp(class->name, class2->name) ? 0 :
1813 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1814 CMP_MATCH | CMP_STOP;
1817 static int load_module(void)
1821 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1822 return AST_MODULE_LOAD_DECLINE;
1825 if (!(deleted_classes = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh deleted class container"))) {
1826 return AST_MODULE_LOAD_DECLINE;
1829 if (ast_pthread_create_background(&deleted_thread, NULL, deleted_monitor, NULL)) {
1830 return AST_MODULE_LOAD_DECLINE;
1833 if (!load_moh_classes(0)) { /* No music classes configured, so skip it */
1834 ast_log(LOG_WARNING, "No music on hold classes configured, "
1835 "disabling music on hold.\n");
1837 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1838 local_ast_moh_cleanup);
1841 res = ast_register_application_xml(play_moh, play_moh_exec);
1842 ast_register_atexit(ast_moh_destroy);
1843 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1845 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1847 res = ast_register_application_xml(set_moh, set_moh_exec);
1849 res = ast_register_application_xml(start_moh, start_moh_exec);
1851 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1853 return AST_MODULE_LOAD_SUCCESS;
1856 static int reload(void)
1858 if (load_moh_classes(1)) {
1859 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1860 local_ast_moh_cleanup);
1863 return AST_MODULE_LOAD_SUCCESS;
1866 static int moh_class_inuse(void *obj, void *arg, int flags)
1868 struct mohclass *class = obj;
1870 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1873 static int unload_module(void)
1876 struct mohclass *class = NULL;
1878 /* XXX This check shouldn't be required if module ref counting was being used
1880 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
1881 class = mohclass_unref(class, "unref of class from module unload callback");
1886 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1890 ast_uninstall_music_functions();
1893 res = ast_unregister_application(play_moh);
1894 res |= ast_unregister_application(wait_moh);
1895 res |= ast_unregister_application(set_moh);
1896 res |= ast_unregister_application(start_moh);
1897 res |= ast_unregister_application(stop_moh);
1898 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
1899 ast_unregister_atexit(ast_moh_destroy);
1901 pthread_cancel(deleted_thread);
1902 pthread_kill(deleted_thread, SIGURG);
1903 pthread_join(deleted_thread, NULL);
1908 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1909 .load = load_module,
1910 .unload = unload_module,