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;
216 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
217 #define MPG_123 "/usr/bin/mpg123"
220 static int reload(void);
222 #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 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
228 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
230 struct mohclass *dup;
231 if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
232 if (_ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
233 FILE *ref = fopen("/tmp/refs", "a");
235 fprintf(ref, "%p =1 %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
238 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",
239 class, class->name, file, line, funcname);
244 ao2_t_ref(class, -1, (char *) tag);
250 static void moh_files_release(struct ast_channel *chan, void *data)
252 struct moh_files_state *state;
254 if (!chan || !chan->music_state) {
258 state = chan->music_state;
261 ast_closestream(chan->stream);
265 if (option_verbose > 2) {
266 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
269 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
270 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
273 state->save_pos = state->pos;
275 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
278 static int ast_moh_files_next(struct ast_channel *chan)
280 struct moh_files_state *state = chan->music_state;
283 /* Discontinue a stream if it is running already */
285 ast_closestream(chan->stream);
289 if (!state->class->total_files) {
290 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
294 /* If a specific file has been saved confirm it still exists and that it is still valid */
295 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
296 state->pos = state->save_pos;
297 state->save_pos = -1;
298 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
299 /* Get a random file and ensure we can open it */
300 for (tries = 0; tries < 20; tries++) {
301 state->pos = ast_random() % state->class->total_files;
302 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
305 state->save_pos = -1;
308 /* This is easy, just increment our position and make sure we don't exceed the total file count */
310 state->pos %= state->class->total_files;
311 state->save_pos = -1;
315 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
316 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
318 state->pos %= state->class->total_files;
322 /* Record the pointer to the filename for position resuming later */
323 state->save_pos_filename = state->class->filearray[state->pos];
325 ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
328 ast_seekstream(chan->stream, state->samples, SEEK_SET);
333 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
335 struct ast_frame *f = NULL;
337 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
338 if (!ast_moh_files_next(chan))
339 f = ast_readframe(chan->stream);
345 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
347 struct moh_files_state *state = chan->music_state;
348 struct ast_frame *f = NULL;
351 state->sample_queue += samples;
353 while (state->sample_queue > 0) {
354 if ((f = moh_files_readframe(chan))) {
355 state->samples += f->samples;
356 state->sample_queue -= f->samples;
357 res = ast_write(chan, f);
360 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
369 static void *moh_files_alloc(struct ast_channel *chan, void *params)
371 struct moh_files_state *state;
372 struct mohclass *class = params;
374 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
375 chan->music_state = state;
376 ast_module_ref(ast_module_info->self);
378 state = chan->music_state;
385 /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
386 * malloc may allocate a different class to the same memory block. This
387 * might only happen when two reloads are generated in a short period of
388 * time, but it's still important to protect against.
389 * PROG: Compare the quick operation first, to save CPU. */
390 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
391 memset(state, 0, sizeof(*state));
392 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
393 state->pos = ast_random() % class->total_files;
397 state->class = mohclass_ref(class, "Reffing music class for channel");
398 state->origwfmt = chan->writeformat;
399 /* For comparison on restart of MOH (see above) */
400 ast_copy_string(state->name, class->name, sizeof(state->name));
401 state->save_total = class->total_files;
403 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
405 return chan->music_state;
408 static int moh_digit_match(void *obj, void *arg, int flags)
411 struct mohclass *class = obj;
413 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
416 /*! \note This function should be called with the mohclasses list locked */
417 static struct mohclass *get_mohbydigit(char digit)
419 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
422 static void moh_handle_digit(struct ast_channel *chan, char digit)
424 struct mohclass *class;
425 const char *classname = NULL;
427 if ((class = get_mohbydigit(digit))) {
428 classname = ast_strdupa(class->name);
429 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
430 ast_string_field_set(chan,musicclass,classname);
432 ast_moh_start(chan, classname, NULL);
436 static struct ast_generator moh_file_stream =
438 .alloc = moh_files_alloc,
439 .release = moh_files_release,
440 .generate = moh_files_generator,
441 .digit = moh_handle_digit,
444 static int spawn_mp3(struct mohclass *class)
448 char fns[MAX_MP3S][80];
449 char *argv[MAX_MP3S + 50];
457 if (!strcasecmp(class->dir, "nodir")) {
460 dir = opendir(class->dir);
461 if (!dir && strncasecmp(class->dir, "http://", 7)) {
462 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
467 if (!ast_test_flag(class, MOH_CUSTOM)) {
468 argv[argc++] = "mpg123";
471 argv[argc++] = "--mono";
473 argv[argc++] = "8000";
475 if (!ast_test_flag(class, MOH_SINGLE)) {
477 argv[argc++] = "2048";
482 if (ast_test_flag(class, MOH_QUIET))
483 argv[argc++] = "4096";
485 argv[argc++] = "8192";
487 /* Look for extra arguments and add them to the list */
488 ast_copy_string(xargs, class->args, sizeof(xargs));
490 while (!ast_strlen_zero(argptr)) {
491 argv[argc++] = argptr;
492 strsep(&argptr, ",");
495 /* Format arguments for argv vector */
496 ast_copy_string(xargs, class->args, sizeof(xargs));
498 while (!ast_strlen_zero(argptr)) {
499 argv[argc++] = argptr;
500 strsep(&argptr, " ");
504 if (!strncasecmp(class->dir, "http://", 7)) {
505 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
506 argv[argc++] = fns[files];
509 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
510 if ((strlen(de->d_name) > 3) &&
511 ((ast_test_flag(class, MOH_CUSTOM) &&
512 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
513 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
514 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
515 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
516 argv[argc++] = fns[files];
526 ast_log(LOG_WARNING, "Pipe failed\n");
530 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
535 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
536 sleep(respawn_time - (time(NULL) - class->start));
540 class->pid = ast_safe_fork(0);
541 if (class->pid < 0) {
544 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
548 if (ast_opt_high_priority)
552 /* Stdout goes to pipe */
553 dup2(fds[1], STDOUT_FILENO);
555 /* Close everything else */
556 ast_close_fds_above_n(STDERR_FILENO);
559 if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
560 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
563 setpgid(0, getpid());
564 if (ast_test_flag(class, MOH_CUSTOM)) {
565 execv(argv[0], argv);
567 /* Default install is /usr/local/bin */
568 execv(LOCAL_MPG_123, argv);
569 /* Many places have it in /usr/bin */
570 execv(MPG_123, argv);
571 /* Check PATH as a last-ditch effort */
572 execvp("mpg123", argv);
574 /* Can't use logger, since log FDs are closed */
575 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
585 static void *monmp3thread(void *data)
587 #define MOH_MS_INTERVAL 100
589 struct mohclass *class = data;
595 struct timeval deadline, tv_tmp;
598 deadline.tv_usec = 0;
600 pthread_testcancel();
601 /* Spawn mp3 player if it's not there */
602 if (class->srcfd < 0) {
603 if ((class->srcfd = spawn_mp3(class)) < 0) {
604 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
605 /* Try again later */
607 pthread_testcancel();
610 if (class->pseudofd > -1) {
614 /* Pause some amount of time */
615 res = read(class->pseudofd, buf, sizeof(buf));
616 pthread_testcancel();
620 tv_tmp = ast_tvnow();
621 if (ast_tvzero(deadline))
623 delta = ast_tvdiff_ms(tv_tmp, deadline);
624 if (delta < MOH_MS_INTERVAL) { /* too early */
625 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
626 usleep(1000 * (MOH_MS_INTERVAL - delta));
627 pthread_testcancel();
629 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
632 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
634 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
637 len = ast_codec_get_len(class->format, res);
639 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
643 pthread_testcancel();
644 if (class->pid > 1) {
646 if (killpg(class->pid, SIGHUP) < 0) {
647 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
650 if (killpg(class->pid, SIGTERM) < 0) {
651 if (errno == ESRCH) {
654 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
657 if (killpg(class->pid, SIGKILL) < 0) {
658 if (errno == ESRCH) {
661 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
667 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
672 pthread_testcancel();
675 AST_LIST_TRAVERSE(&class->members, moh, list) {
677 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
678 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
686 static int play_moh_exec(struct ast_channel *chan, const char *data)
692 AST_DECLARE_APP_ARGS(args,
694 AST_APP_ARG(duration);
697 parse = ast_strdupa(data);
699 AST_STANDARD_APP_ARGS(args, parse);
701 if (!ast_strlen_zero(args.duration)) {
702 if (sscanf(args.duration, "%30d", &timeout) == 1) {
705 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
709 class = S_OR(args.class, NULL);
710 if (ast_moh_start(chan, class, NULL)) {
711 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
716 res = ast_safe_sleep(chan, timeout);
718 while (!(res = ast_safe_sleep(chan, 10000)));
726 static int wait_moh_exec(struct ast_channel *chan, const char *data)
728 static int deprecation_warning = 0;
731 if (!deprecation_warning) {
732 deprecation_warning = 1;
733 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
736 if (!data || !atoi(data)) {
737 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
740 if (ast_moh_start(chan, NULL, NULL)) {
741 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
744 res = ast_safe_sleep(chan, atoi(data) * 1000);
749 static int set_moh_exec(struct ast_channel *chan, const char *data)
751 static int deprecation_warning = 0;
753 if (!deprecation_warning) {
754 deprecation_warning = 1;
755 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
758 if (ast_strlen_zero(data)) {
759 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
762 ast_string_field_set(chan, musicclass, data);
766 static int start_moh_exec(struct ast_channel *chan, const char *data)
770 AST_DECLARE_APP_ARGS(args,
774 parse = ast_strdupa(data);
776 AST_STANDARD_APP_ARGS(args, parse);
778 class = S_OR(args.class, NULL);
779 if (ast_moh_start(chan, class, NULL))
780 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
785 static int stop_moh_exec(struct ast_channel *chan, const char *data)
792 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
794 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
796 struct mohclass *moh = NULL;
797 struct mohclass tmp_class = {
801 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
804 moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
806 moh = __ao2_find(mohclasses, &tmp_class, flags);
810 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
816 static struct mohdata *mohalloc(struct mohclass *cl)
821 if (!(moh = ast_calloc(1, sizeof(*moh))))
824 if (pipe(moh->pipe)) {
825 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
830 /* Make entirely non-blocking */
831 flags = fcntl(moh->pipe[0], F_GETFL);
832 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
833 flags = fcntl(moh->pipe[1], F_GETFL);
834 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
836 moh->f.frametype = AST_FRAME_VOICE;
837 moh->f.subclass.codec = cl->format;
838 moh->f.offset = AST_FRIENDLY_OFFSET;
840 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
843 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
849 static void moh_release(struct ast_channel *chan, void *data)
851 struct mohdata *moh = data;
852 struct mohclass *class = moh->parent;
856 AST_LIST_REMOVE(&moh->parent->members, moh, list);
862 oldwfmt = moh->origwfmt;
864 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
869 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
870 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
871 chan->name, ast_getformatname(oldwfmt));
874 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
878 static void *moh_alloc(struct ast_channel *chan, void *params)
881 struct mohclass *class = params;
882 struct moh_files_state *state;
884 /* Initiating music_state for current channel. Channel should know name of moh class */
885 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
886 chan->music_state = state;
887 state->class = mohclass_ref(class, "Copying reference into state container");
888 ast_module_ref(ast_module_info->self);
890 state = chan->music_state;
891 if (state && state->class != class) {
892 memset(state, 0, sizeof(*state));
893 state->class = class;
896 if ((res = mohalloc(class))) {
897 res->origwfmt = chan->writeformat;
898 if (ast_set_write_format(chan, class->format)) {
899 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
900 moh_release(NULL, res);
903 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
908 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
910 struct mohdata *moh = data;
911 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
914 len = ast_codec_get_len(moh->parent->format, samples);
916 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
917 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
918 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
920 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
924 moh->f.datalen = res;
925 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
926 moh->f.samples = ast_codec_get_samples(&moh->f);
928 if (ast_write(chan, &moh->f) < 0) {
929 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
936 static struct ast_generator mohgen = {
938 .release = moh_release,
939 .generate = moh_generate,
940 .digit = moh_handle_digit,
943 static int moh_add_file(struct mohclass *class, const char *filepath)
945 if (!class->allowed_files) {
946 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
948 class->allowed_files = INITIAL_NUM_FILES;
949 } else if (class->total_files == class->allowed_files) {
950 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
951 class->allowed_files = 0;
952 class->total_files = 0;
955 class->allowed_files *= 2;
958 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
961 class->total_files++;
966 static int moh_sort_compare(const void *i1, const void *i2)
970 s1 = ((char **)i1)[0];
971 s2 = ((char **)i2)[0];
973 return strcasecmp(s1, s2);
976 static int moh_scan_files(struct mohclass *class) {
979 struct dirent *files_dirent;
980 char dir_path[PATH_MAX];
982 char filepath[PATH_MAX];
988 if (class->dir[0] != '/') {
989 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
990 strncat(dir_path, "/", sizeof(dir_path) - 1);
991 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
993 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
995 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
996 files_DIR = opendir(dir_path);
998 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1002 for (i = 0; i < class->total_files; i++)
1003 ast_free(class->filearray[i]);
1005 class->total_files = 0;
1006 dirnamelen = strlen(dir_path) + 2;
1007 if (!getcwd(path, sizeof(path))) {
1008 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1011 if (chdir(dir_path) < 0) {
1012 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1015 while ((files_dirent = readdir(files_DIR))) {
1016 /* The file name must be at least long enough to have the file type extension */
1017 if ((strlen(files_dirent->d_name) < 4))
1020 /* Skip files that starts with a dot */
1021 if (files_dirent->d_name[0] == '.')
1024 /* Skip files without extensions... they are not audio */
1025 if (!strchr(files_dirent->d_name, '.'))
1028 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1030 if (stat(filepath, &statbuf))
1033 if (!S_ISREG(statbuf.st_mode))
1036 if ((ext = strrchr(filepath, '.')))
1039 /* if the file is present in multiple formats, ensure we only put it into the list once */
1040 for (i = 0; i < class->total_files; i++)
1041 if (!strcmp(filepath, class->filearray[i]))
1044 if (i == class->total_files) {
1045 if (moh_add_file(class, filepath))
1050 closedir(files_DIR);
1051 if (chdir(path) < 0) {
1052 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1055 if (ast_test_flag(class, MOH_SORTALPHA))
1056 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1057 return class->total_files;
1060 static int init_files_class(struct mohclass *class)
1064 res = moh_scan_files(class);
1071 if (option_verbose > 2) {
1072 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
1073 class->dir, class->name);
1078 if (strchr(class->args, 'r')) {
1079 ast_set_flag(class, MOH_RANDOMIZE);
1086 static int moh_diff(struct mohclass *old, struct mohclass *new)
1092 if (strcmp(old->dir, new->dir)) {
1094 } else if (strcmp(old->mode, new->mode)) {
1096 } else if (strcmp(old->args, new->args)) {
1098 } else if (old->flags != new->flags) {
1105 static int init_app_class(struct mohclass *class)
1111 if (!strcasecmp(class->mode, "custom")) {
1112 ast_set_flag(class, MOH_CUSTOM);
1113 } else if (!strcasecmp(class->mode, "mp3nb")) {
1114 ast_set_flag(class, MOH_SINGLE);
1115 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1116 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1117 } else if (!strcasecmp(class->mode, "quietmp3")) {
1118 ast_set_flag(class, MOH_QUIET);
1122 class->pseudofd = -1;
1125 /* Open /dev/zap/pseudo for timing... Is
1126 there a better, yet reliable way to do this? */
1127 class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1128 if (class->pseudofd < 0) {
1129 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1132 ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1136 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1137 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1138 if (class->pseudofd > -1) {
1139 close(class->pseudofd);
1140 class->pseudofd = -1;
1149 * \note This function owns the reference it gets to moh if unref is true
1151 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1152 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1154 struct mohclass *mohclass = NULL;
1156 if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
1157 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1158 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1160 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1163 } else if (mohclass) {
1164 /* Found a class, but it's different from the one being registered */
1165 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1169 moh->start -= respawn_time;
1171 if (!strcasecmp(moh->mode, "files")) {
1172 if (init_files_class(moh)) {
1174 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1178 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1179 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1180 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1181 if (init_app_class(moh)) {
1183 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1188 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1190 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1195 ao2_t_link(mohclasses, moh, "Adding class to container");
1198 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1204 static void local_ast_moh_cleanup(struct ast_channel *chan)
1206 struct moh_files_state *state = chan->music_state;
1210 state->class = mohclass_unref(state->class, "Channel MOH state destruction");
1212 ast_free(chan->music_state);
1213 chan->music_state = NULL;
1214 /* Only held a module reference if we had a music state */
1215 ast_module_unref(ast_module_info->self);
1219 static void moh_class_destructor(void *obj);
1221 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1223 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1225 struct mohclass *class;
1229 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
1230 #elif defined(__AST_DEBUG_MALLOC)
1231 __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
1233 ao2_alloc(sizeof(*class), moh_class_destructor)
1236 class->format = AST_FORMAT_SLINEAR;
1242 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1244 struct mohclass *mohclass = NULL;
1245 struct moh_files_state *state = chan->music_state;
1246 struct ast_variable *var = NULL;
1248 int realtime_possible = ast_check_realtime("musiconhold");
1250 /* The following is the order of preference for which class to use:
1251 * 1) The channels explicitly set musicclass, which should *only* be
1252 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1253 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1254 * result of receiving a HOLD control frame, this should be the
1255 * payload that came with the frame.
1256 * 3) The interpclass argument. This would be from the mohinterpret
1257 * option from channel drivers. This is the same as the old musicclass
1259 * 4) The default class.
1261 if (!ast_strlen_zero(chan->musicclass)) {
1262 mohclass = get_mohbyname(chan->musicclass, 1, 0);
1263 if (!mohclass && realtime_possible) {
1264 var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
1267 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1268 mohclass = get_mohbyname(mclass, 1, 0);
1269 if (!mohclass && realtime_possible) {
1270 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1273 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1274 mohclass = get_mohbyname(interpclass, 1, 0);
1275 if (!mohclass && realtime_possible) {
1276 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1280 if (!mohclass && !var) {
1281 mohclass = get_mohbyname("default", 1, 0);
1282 if (!mohclass && realtime_possible) {
1283 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1287 /* If no moh class found in memory, then check RT. Note that the logic used
1288 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1291 struct ast_variable *tmp = NULL;
1293 if ((mohclass = moh_class_malloc())) {
1294 mohclass->realtime = 1;
1295 for (tmp = var; tmp; tmp = tmp->next) {
1296 if (!strcasecmp(tmp->name, "name"))
1297 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1298 else if (!strcasecmp(tmp->name, "mode"))
1299 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1300 else if (!strcasecmp(tmp->name, "directory"))
1301 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1302 else if (!strcasecmp(tmp->name, "application"))
1303 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1304 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1305 mohclass->digit = *tmp->value;
1306 else if (!strcasecmp(tmp->name, "random"))
1307 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1308 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1309 ast_set_flag(mohclass, MOH_RANDOMIZE);
1310 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1311 ast_set_flag(mohclass, MOH_SORTALPHA);
1312 else if (!strcasecmp(tmp->name, "format")) {
1313 mohclass->format = ast_getformatbyname(tmp->value);
1314 if (!mohclass->format) {
1315 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1316 mohclass->format = AST_FORMAT_SLINEAR;
1320 ast_variables_destroy(var);
1321 if (ast_strlen_zero(mohclass->dir)) {
1322 if (!strcasecmp(mohclass->mode, "custom")) {
1323 strcpy(mohclass->dir, "nodir");
1325 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1326 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1330 if (ast_strlen_zero(mohclass->mode)) {
1331 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1332 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1335 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1336 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1337 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1341 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1342 /* CACHERTCLASSES enabled, let's add this class to default tree */
1343 if (state && state->class) {
1344 /* Class already exist for this channel */
1345 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1346 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1347 /* we found RT class with the same name, seems like we should continue playing existing one */
1348 /* XXX This code is impossible to reach */
1349 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1350 mohclass = state->class;
1353 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1354 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1355 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1356 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1359 moh_register(mohclass, 0, DONT_UNREF);
1361 /* We don't register RT moh class, so let's init it manualy */
1363 time(&mohclass->start);
1364 mohclass->start -= respawn_time;
1366 if (!strcasecmp(mohclass->mode, "files")) {
1367 if (!moh_scan_files(mohclass)) {
1368 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1371 if (strchr(mohclass->args, 'r'))
1372 ast_set_flag(mohclass, MOH_RANDOMIZE);
1373 } 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")) {
1375 if (!strcasecmp(mohclass->mode, "custom"))
1376 ast_set_flag(mohclass, MOH_CUSTOM);
1377 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1378 ast_set_flag(mohclass, MOH_SINGLE);
1379 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1380 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1381 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1382 ast_set_flag(mohclass, MOH_QUIET);
1384 mohclass->srcfd = -1;
1386 /* Open /dev/dahdi/pseudo for timing... Is
1387 there a better, yet reliable way to do this? */
1388 mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1389 if (mohclass->pseudofd < 0) {
1390 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
1393 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1396 mohclass->pseudofd = -1;
1398 /* Let's check if this channel already had a moh class before */
1399 if (state && state->class) {
1400 /* Class already exist for this channel */
1401 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1402 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1403 /* we found RT class with the same name, seems like we should continue playing existing one */
1404 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1405 mohclass = state->class;
1408 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1409 ast_log(LOG_WARNING, "Unable to create moh...\n");
1410 if (mohclass->pseudofd > -1) {
1411 close(mohclass->pseudofd);
1412 mohclass->pseudofd = -1;
1414 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1419 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1420 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1425 ast_variables_destroy(var);
1433 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1437 chan->name, chan->uniqueid);
1439 ast_set_flag(chan, AST_FLAG_MOH);
1441 if (mohclass->total_files) {
1442 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1444 res = ast_activate_generator(chan, &mohgen, mohclass);
1447 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1452 static void local_ast_moh_stop(struct ast_channel *chan)
1454 struct moh_files_state *state = chan->music_state;
1455 ast_clear_flag(chan, AST_FLAG_MOH);
1456 ast_deactivate_generator(chan);
1460 ast_closestream(chan->stream);
1461 chan->stream = NULL;
1465 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1469 chan->name, chan->uniqueid);
1472 static void moh_class_destructor(void *obj)
1474 struct mohclass *class = obj;
1475 struct mohdata *member;
1478 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1480 /* Kill the thread first, so it cannot restart the child process while the
1481 * class is being destroyed */
1482 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1483 tid = class->thread;
1484 class->thread = AST_PTHREADT_NULL;
1485 pthread_cancel(tid);
1486 /* We'll collect the exit status later, after we ensure all the readers
1490 if (class->pid > 1) {
1492 int bytes, tbytes = 0, stime = 0, pid = 0;
1494 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
1496 stime = time(NULL) + 2;
1500 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1501 * to give the process a reason and time enough to kill off its
1504 if (killpg(pid, SIGHUP) < 0) {
1505 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1508 if (killpg(pid, SIGTERM) < 0) {
1509 if (errno == ESRCH) {
1512 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1515 if (killpg(pid, SIGKILL) < 0) {
1516 if (errno == ESRCH) {
1519 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1523 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1524 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1525 tbytes = tbytes + bytes;
1528 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1530 close(class->srcfd);
1533 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1537 if (class->filearray) {
1539 for (i = 0; i < class->total_files; i++) {
1540 free(class->filearray[i]);
1542 free(class->filearray);
1543 class->filearray = NULL;
1546 /* Finally, collect the exit status of the monitor thread */
1548 pthread_join(tid, NULL);
1552 static int moh_class_mark(void *obj, void *arg, int flags)
1554 struct mohclass *class = obj;
1561 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1563 struct mohclass *class = obj;
1565 return class->delete ? CMP_MATCH : 0;
1568 static int load_moh_classes(int reload)
1570 struct ast_config *cfg;
1571 struct ast_variable *var;
1572 struct mohclass *class;
1575 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1577 cfg = ast_config_load("musiconhold.conf", config_flags);
1579 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1584 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1587 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1589 cat = ast_category_browse(cfg, NULL);
1590 for (; cat; cat = ast_category_browse(cfg, cat)) {
1591 /* Setup common options from [general] section */
1592 if (!strcasecmp(cat, "general")) {
1593 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1594 if (!strcasecmp(var->name, "cachertclasses")) {
1595 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1597 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1601 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1602 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
1603 !strcasecmp(cat, "general")) {
1607 if (!(class = moh_class_malloc())) {
1611 ast_copy_string(class->name, cat, sizeof(class->name));
1612 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1613 if (!strcasecmp(var->name, "mode"))
1614 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1615 else if (!strcasecmp(var->name, "directory"))
1616 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1617 else if (!strcasecmp(var->name, "application"))
1618 ast_copy_string(class->args, var->value, sizeof(class->args));
1619 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1620 class->digit = *var->value;
1621 else if (!strcasecmp(var->name, "random"))
1622 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1623 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1624 ast_set_flag(class, MOH_RANDOMIZE);
1625 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
1626 ast_set_flag(class, MOH_SORTALPHA);
1627 else if (!strcasecmp(var->name, "format")) {
1628 class->format = ast_getformatbyname(var->value);
1629 if (!class->format) {
1630 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1631 class->format = AST_FORMAT_SLINEAR;
1636 if (ast_strlen_zero(class->dir)) {
1637 if (!strcasecmp(class->mode, "custom")) {
1638 strcpy(class->dir, "nodir");
1640 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1641 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1645 if (ast_strlen_zero(class->mode)) {
1646 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1647 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1650 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1651 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1652 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1656 /* Don't leak a class when it's already registered */
1657 if (!moh_register(class, reload, HANDLE_REF)) {
1662 ast_config_destroy(cfg);
1664 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
1665 moh_classes_delete_marked, NULL, "Purge marked classes");
1670 static void ast_moh_destroy(void)
1672 ast_verb(2, "Destroying musiconhold processes\n");
1673 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1676 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1680 e->command = "moh reload";
1682 "Usage: moh reload\n"
1683 " Reloads the MusicOnHold module.\n"
1684 " Alias for 'module reload res_musiconhold.so'\n";
1690 if (a->argc != e->args)
1691 return CLI_SHOWUSAGE;
1698 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1700 struct mohclass *class;
1701 struct ao2_iterator i;
1705 e->command = "moh show files";
1707 "Usage: moh show files\n"
1708 " Lists all loaded file-based MusicOnHold classes and their\n"
1715 if (a->argc != e->args)
1716 return CLI_SHOWUSAGE;
1718 i = ao2_iterator_init(mohclasses, 0);
1719 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1722 if (!class->total_files) {
1726 ast_cli(a->fd, "Class: %s\n", class->name);
1727 for (x = 0; x < class->total_files; x++) {
1728 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1731 ao2_iterator_destroy(&i);
1736 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1738 struct mohclass *class;
1739 struct ao2_iterator i;
1743 e->command = "moh show classes";
1745 "Usage: moh show classes\n"
1746 " Lists all MusicOnHold classes.\n";
1752 if (a->argc != e->args)
1753 return CLI_SHOWUSAGE;
1755 i = ao2_iterator_init(mohclasses, 0);
1756 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1757 ast_cli(a->fd, "Class: %s\n", class->name);
1758 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1759 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1760 if (ast_test_flag(class, MOH_CUSTOM)) {
1761 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1763 if (strcasecmp(class->mode, "files")) {
1764 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1767 ao2_iterator_destroy(&i);
1772 static struct ast_cli_entry cli_moh[] = {
1773 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1774 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1775 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1778 static int moh_class_hash(const void *obj, const int flags)
1780 const struct mohclass *class = obj;
1782 return ast_str_case_hash(class->name);
1785 static int moh_class_cmp(void *obj, void *arg, int flags)
1787 struct mohclass *class = obj, *class2 = arg;
1789 return strcasecmp(class->name, class2->name) ? 0 :
1790 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1791 CMP_MATCH | CMP_STOP;
1794 static int load_module(void)
1798 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1799 return AST_MODULE_LOAD_DECLINE;
1802 if (!load_moh_classes(0)) { /* No music classes configured, so skip it */
1803 ast_log(LOG_WARNING, "No music on hold classes configured, "
1804 "disabling music on hold.\n");
1806 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1807 local_ast_moh_cleanup);
1810 res = ast_register_application_xml(play_moh, play_moh_exec);
1811 ast_register_atexit(ast_moh_destroy);
1812 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1814 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1816 res = ast_register_application_xml(set_moh, set_moh_exec);
1818 res = ast_register_application_xml(start_moh, start_moh_exec);
1820 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1822 return AST_MODULE_LOAD_SUCCESS;
1825 static int reload(void)
1827 if (load_moh_classes(1)) {
1828 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1829 local_ast_moh_cleanup);
1832 return AST_MODULE_LOAD_SUCCESS;
1835 static int moh_class_inuse(void *obj, void *arg, int flags)
1837 struct mohclass *class = obj;
1839 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1842 static int unload_module(void)
1845 struct mohclass *class = NULL;
1847 /* XXX This check shouldn't be required if module ref counting was being used
1849 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
1850 class = mohclass_unref(class, "unref of class from module unload callback");
1855 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1859 ast_uninstall_music_functions();
1862 res = ast_unregister_application(play_moh);
1863 res |= ast_unregister_application(wait_moh);
1864 res |= ast_unregister_application(set_moh);
1865 res |= ast_unregister_application(start_moh);
1866 res |= ast_unregister_application(stop_moh);
1867 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
1868 ast_unregister_atexit(ast_moh_destroy);
1873 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1874 .load = load_module,
1875 .unload = unload_module,