2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2010, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Routines implementing music on hold
23 * \arg See also \ref Config_moh
25 * \author Mark Spencer <markster@digium.com>
29 <conflict>win32</conflict>
30 <support_level>core</support_level>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <sys/signal.h>
41 #include <netinet/in.h>
49 #include "asterisk/lock.h"
50 #include "asterisk/file.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/app.h"
54 #include "asterisk/module.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/say.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/config.h"
59 #include "asterisk/utils.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/stringfields.h"
62 #include "asterisk/linkedlists.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/paths.h"
65 #include "asterisk/astobj2.h"
66 #include "asterisk/timing.h"
67 #include "asterisk/time.h"
68 #include "asterisk/poll-compat.h"
70 #define INITIAL_NUM_FILES 8
75 <application name="MusicOnHold" language="en_US">
77 Play Music On Hold indefinitely.
80 <parameter name="class" required="true" />
81 <parameter name="duration" />
84 <para>Plays hold music specified by class. If omitted, the default music
85 source for the channel will be used. Change the default class with
86 Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
87 specified number of seconds. If duration is ommited, music plays indefinitely.
88 Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
89 <para>This application does not automatically answer and should be preceeded by
90 an application such as Answer() or Progress().</para>
93 <application name="WaitMusicOnHold" language="en_US">
95 Wait, playing Music On Hold.
98 <parameter name="delay" required="true" />
101 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
102 <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
103 or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
104 with no sound.</para>
105 <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
108 <application name="SetMusicOnHold" language="en_US">
110 Set default Music On Hold class.
113 <parameter name="class" required="yes" />
116 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
117 <para>Sets the default class for music on hold for a given channel.
118 When music on hold is activated, this class will be used to select which
119 music is played.</para>
120 <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
123 <application name="StartMusicOnHold" language="en_US">
128 <parameter name="class" required="true" />
131 <para>Starts playing music on hold, uses default music class for channel.
132 Starts playing music specified by class. If omitted, the default music
133 source for the channel will be used. Always returns <literal>0</literal>.</para>
136 <application name="StopMusicOnHold" language="en_US">
138 Stop playing Music On Hold.
142 <para>Stops playing music on hold.</para>
147 static const char play_moh[] = "MusicOnHold";
148 static const char wait_moh[] = "WaitMusicOnHold";
149 static const char set_moh[] = "SetMusicOnHold";
150 static const char start_moh[] = "StartMusicOnHold";
151 static const char stop_moh[] = "StopMusicOnHold";
153 static int respawn_time = 20;
155 struct moh_files_state {
156 /*! Holds a reference to the MOH class. */
157 struct mohclass *class;
158 char name[MAX_MUSICCLASS];
159 struct ast_format origwfmt;
160 struct ast_format mohwfmt;
167 char save_pos_filename[PATH_MAX];
170 #define MOH_QUIET (1 << 0)
171 #define MOH_SINGLE (1 << 1)
172 #define MOH_CUSTOM (1 << 2)
173 #define MOH_RANDOMIZE (1 << 3)
174 #define MOH_SORTALPHA (1 << 4)
176 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
177 #define MOH_ANNOUNCEMENT (1 << 6) /*!< Do we play announcement files between songs on this channel? */
179 /* Custom astobj2 flag */
180 #define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
182 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
185 char name[MAX_MUSICCLASS];
188 char announcement[256];
191 /*! A dynamically sized array to hold the list of filenames in "files" mode */
193 /*! The current size of the filearray */
195 /*! The current number of files loaded into the filearray */
198 /*! The format from the MOH source, not applicable to "files" mode */
199 struct ast_format format;
200 /*! The pid of the external application delivering MOH */
204 /*! Source of audio */
207 struct ast_timer *timer;
208 /*! Created on the fly, from RT engine */
209 unsigned int realtime:1;
210 unsigned int delete:1;
211 AST_LIST_HEAD_NOLOCK(, mohdata) members;
212 AST_LIST_ENTRY(mohclass) list;
217 struct ast_format origwfmt;
218 struct mohclass *parent;
220 AST_LIST_ENTRY(mohdata) list;
223 static struct ao2_container *mohclasses;
225 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
226 #define MPG_123 "/usr/bin/mpg123"
229 static int reload(void);
231 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
234 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
236 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
237 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
239 struct mohclass *dup;
240 if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
241 if (__ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
242 FILE *ref = fopen("/tmp/refs", "a");
244 fprintf(ref, "%p =1 %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
247 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",
248 class, class->name, file, line, funcname);
253 ao2_t_ref(class, -1, (char *) tag);
259 static void moh_files_release(struct ast_channel *chan, void *data)
261 struct moh_files_state *state;
263 if (!chan || !ast_channel_music_state(chan)) {
267 state = ast_channel_music_state(chan);
269 if (ast_channel_stream(chan)) {
270 ast_closestream(ast_channel_stream(chan));
271 ast_channel_stream_set(chan, NULL);
274 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
276 ast_format_clear(&state->mohwfmt); /* make sure to clear this format before restoring the original format. */
277 if (state->origwfmt.id && ast_set_write_format(chan, &state->origwfmt)) {
278 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan), ast_getformatname(&state->origwfmt));
281 state->save_pos = state->pos;
282 state->announcement = 0;
284 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
287 static int ast_moh_files_next(struct ast_channel *chan)
289 struct moh_files_state *state = ast_channel_music_state(chan);
292 /* Discontinue a stream if it is running already */
293 if (ast_channel_stream(chan)) {
294 ast_closestream(ast_channel_stream(chan));
295 ast_channel_stream_set(chan, NULL);
298 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
299 state->announcement = 1;
300 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
301 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
305 state->announcement = 0;
308 if (!state->class->total_files) {
309 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
313 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
314 /* First time so lets play the file. */
315 state->save_pos = -1;
316 } else if (state->save_pos >= 0 && state->save_pos < state->class->total_files && !strcmp(state->class->filearray[state->save_pos], state->save_pos_filename)) {
317 /* If a specific file has been saved confirm it still exists and that it is still valid */
318 state->pos = state->save_pos;
319 state->save_pos = -1;
320 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
321 /* Get a random file and ensure we can open it */
322 for (tries = 0; tries < 20; tries++) {
323 state->pos = ast_random() % state->class->total_files;
324 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0) {
328 state->save_pos = -1;
331 /* This is easy, just increment our position and make sure we don't exceed the total file count */
333 state->pos %= state->class->total_files;
334 state->save_pos = -1;
338 for (tries = 0; tries < state->class->total_files; ++tries) {
339 if (ast_openstream_full(chan, state->class->filearray[state->pos], ast_channel_language(chan), 1)) {
343 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
345 state->pos %= state->class->total_files;
348 if (tries == state->class->total_files) {
352 /* Record the pointer to the filename for position resuming later */
353 ast_copy_string(state->save_pos_filename, state->class->filearray[state->pos], sizeof(state->save_pos_filename));
355 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->class->filearray[state->pos]);
357 if (state->samples) {
359 /* seek *SHOULD* be good since it's from a known location */
360 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
361 /* if the seek failed then recover because if there is not a valid read,
362 * moh_files_generate will return -1 and MOH will stop */
363 loc = ast_tellstream(ast_channel_stream(chan));
364 if (state->samples > loc && loc) {
365 /* seek one sample from the end for one guaranteed valid read */
366 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
373 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
375 struct ast_frame *f = NULL;
377 if (!(ast_channel_stream(chan) && (f = ast_readframe(ast_channel_stream(chan))))) {
378 if (!ast_moh_files_next(chan))
379 f = ast_readframe(ast_channel_stream(chan));
385 static void moh_files_write_format_change(struct ast_channel *chan, void *data)
387 struct moh_files_state *state = ast_channel_music_state(chan);
389 /* In order to prevent a recursive call to this function as a result
390 * of setting the moh write format back on the channel. Clear
391 * the moh write format before setting the write format on the channel.*/
392 if (&state->origwfmt.id) {
393 struct ast_format tmp;
395 ast_format_copy(&tmp, ast_channel_writeformat(chan));
396 if (state->mohwfmt.id) {
397 ast_format_clear(&state->origwfmt);
398 ast_set_write_format(chan, &state->mohwfmt);
400 ast_format_copy(&state->origwfmt, &tmp);
404 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
406 struct moh_files_state *state = ast_channel_music_state(chan);
407 struct ast_frame *f = NULL;
410 state->sample_queue += samples;
412 while (state->sample_queue > 0) {
413 ast_channel_lock(chan);
414 if ((f = moh_files_readframe(chan))) {
415 /* We need to be sure that we unlock
416 * the channel prior to calling
417 * ast_write. Otherwise, the recursive locking
418 * that occurs can cause deadlocks when using
419 * indirect channels, like local channels
421 ast_channel_unlock(chan);
422 state->samples += f->samples;
423 state->sample_queue -= f->samples;
424 if (ast_format_cmp(&f->subclass.format, &state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
425 ast_format_copy(&state->mohwfmt, &f->subclass.format);
427 res = ast_write(chan, f);
430 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
434 ast_channel_unlock(chan);
441 static void *moh_files_alloc(struct ast_channel *chan, void *params)
443 struct moh_files_state *state;
444 struct mohclass *class = params;
446 if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
447 ast_channel_music_state_set(chan, state);
448 ast_module_ref(ast_module_info->self);
450 state = ast_channel_music_state(chan);
455 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
456 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
460 /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
461 * malloc may allocate a different class to the same memory block. This
462 * might only happen when two reloads are generated in a short period of
463 * time, but it's still important to protect against.
464 * PROG: Compare the quick operation first, to save CPU. */
465 if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
466 memset(state, 0, sizeof(*state));
467 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
468 state->pos = ast_random() % class->total_files;
472 state->class = mohclass_ref(class, "Reffing music class for channel");
473 ast_format_copy(&state->origwfmt, ast_channel_writeformat(chan));
474 ast_format_copy(&state->mohwfmt, ast_channel_writeformat(chan));
476 /* For comparison on restart of MOH (see above) */
477 ast_copy_string(state->name, class->name, sizeof(state->name));
478 state->save_total = class->total_files;
480 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, ast_channel_name(chan));
482 return ast_channel_music_state(chan);
485 static int moh_digit_match(void *obj, void *arg, int flags)
488 struct mohclass *class = obj;
490 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
493 /*! \note This function should be called with the mohclasses list locked */
494 static struct mohclass *get_mohbydigit(char digit)
496 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
499 static void moh_handle_digit(struct ast_channel *chan, char digit)
501 struct mohclass *class;
502 const char *classname = NULL;
504 if ((class = get_mohbydigit(digit))) {
505 classname = ast_strdupa(class->name);
506 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
507 ast_channel_musicclass_set(chan, classname);
509 ast_moh_start(chan, classname, NULL);
513 static struct ast_generator moh_file_stream =
515 .alloc = moh_files_alloc,
516 .release = moh_files_release,
517 .generate = moh_files_generator,
518 .digit = moh_handle_digit,
519 .write_format_change = moh_files_write_format_change,
522 static int spawn_mp3(struct mohclass *class)
526 char fns[MAX_MP3S][80];
527 char *argv[MAX_MP3S + 50];
535 if (!strcasecmp(class->dir, "nodir")) {
538 dir = opendir(class->dir);
539 if (!dir && strncasecmp(class->dir, "http://", 7)) {
540 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
545 if (!ast_test_flag(class, MOH_CUSTOM)) {
546 argv[argc++] = "mpg123";
549 argv[argc++] = "--mono";
551 argv[argc++] = "8000";
553 if (!ast_test_flag(class, MOH_SINGLE)) {
555 argv[argc++] = "2048";
560 if (ast_test_flag(class, MOH_QUIET))
561 argv[argc++] = "4096";
563 argv[argc++] = "8192";
565 /* Look for extra arguments and add them to the list */
566 ast_copy_string(xargs, class->args, sizeof(xargs));
568 while (!ast_strlen_zero(argptr)) {
569 argv[argc++] = argptr;
570 strsep(&argptr, ",");
573 /* Format arguments for argv vector */
574 ast_copy_string(xargs, class->args, sizeof(xargs));
576 while (!ast_strlen_zero(argptr)) {
577 argv[argc++] = argptr;
578 strsep(&argptr, " ");
582 if (!strncasecmp(class->dir, "http://", 7)) {
583 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
584 argv[argc++] = fns[files];
587 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
588 if ((strlen(de->d_name) > 3) &&
589 ((ast_test_flag(class, MOH_CUSTOM) &&
590 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
591 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
592 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
593 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
594 argv[argc++] = fns[files];
604 ast_log(LOG_WARNING, "Pipe failed\n");
608 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
613 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
614 sleep(respawn_time - (time(NULL) - class->start));
618 class->pid = ast_safe_fork(0);
619 if (class->pid < 0) {
622 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
626 if (ast_opt_high_priority)
630 /* Stdout goes to pipe */
631 dup2(fds[1], STDOUT_FILENO);
633 /* Close everything else */
634 ast_close_fds_above_n(STDERR_FILENO);
637 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
638 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
641 setpgid(0, getpid());
642 if (ast_test_flag(class, MOH_CUSTOM)) {
643 execv(argv[0], argv);
645 /* Default install is /usr/local/bin */
646 execv(LOCAL_MPG_123, argv);
647 /* Many places have it in /usr/bin */
648 execv(MPG_123, argv);
649 /* Check PATH as a last-ditch effort */
650 execvp("mpg123", argv);
652 /* Can't use logger, since log FDs are closed */
653 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
663 static void *monmp3thread(void *data)
665 #define MOH_MS_INTERVAL 100
667 struct mohclass *class = data;
672 struct timeval deadline, tv_tmp;
675 deadline.tv_usec = 0;
677 pthread_testcancel();
678 /* Spawn mp3 player if it's not there */
679 if (class->srcfd < 0) {
680 if ((class->srcfd = spawn_mp3(class)) < 0) {
681 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
682 /* Try again later */
684 pthread_testcancel();
688 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
693 /* Pause some amount of time */
694 if (ast_poll(&pfd, 1, -1) > 0) {
695 ast_timer_ack(class->timer, 1);
698 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
701 pthread_testcancel();
705 tv_tmp = ast_tvnow();
706 if (ast_tvzero(deadline))
708 delta = ast_tvdiff_ms(tv_tmp, deadline);
709 if (delta < MOH_MS_INTERVAL) { /* too early */
710 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
711 usleep(1000 * (MOH_MS_INTERVAL - delta));
712 pthread_testcancel();
714 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
717 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
719 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
722 len = ast_codec_get_len(&class->format, res);
724 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
728 pthread_testcancel();
729 if (class->pid > 1) {
731 if (killpg(class->pid, SIGHUP) < 0) {
732 if (errno == ESRCH) {
735 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
738 if (killpg(class->pid, SIGTERM) < 0) {
739 if (errno == ESRCH) {
742 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
745 if (killpg(class->pid, SIGKILL) < 0) {
746 if (errno == ESRCH) {
749 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
755 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
760 pthread_testcancel();
763 AST_LIST_TRAVERSE(&class->members, moh, list) {
765 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
766 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
774 static int play_moh_exec(struct ast_channel *chan, const char *data)
780 AST_DECLARE_APP_ARGS(args,
782 AST_APP_ARG(duration);
785 parse = ast_strdupa(data);
787 AST_STANDARD_APP_ARGS(args, parse);
789 if (!ast_strlen_zero(args.duration)) {
790 if (sscanf(args.duration, "%30d", &timeout) == 1) {
793 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
797 class = S_OR(args.class, NULL);
798 if (ast_moh_start(chan, class, NULL)) {
799 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
804 res = ast_safe_sleep(chan, timeout);
806 while (!(res = ast_safe_sleep(chan, 10000)));
814 static int wait_moh_exec(struct ast_channel *chan, const char *data)
816 static int deprecation_warning = 0;
819 if (!deprecation_warning) {
820 deprecation_warning = 1;
821 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
824 if (!data || !atoi(data)) {
825 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
828 if (ast_moh_start(chan, NULL, NULL)) {
829 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), ast_channel_name(chan));
832 res = ast_safe_sleep(chan, atoi(data) * 1000);
837 static int set_moh_exec(struct ast_channel *chan, const char *data)
839 static int deprecation_warning = 0;
841 if (!deprecation_warning) {
842 deprecation_warning = 1;
843 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
846 if (ast_strlen_zero(data)) {
847 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
850 ast_channel_musicclass_set(chan, data);
854 static int start_moh_exec(struct ast_channel *chan, const char *data)
858 AST_DECLARE_APP_ARGS(args,
862 parse = ast_strdupa(data);
864 AST_STANDARD_APP_ARGS(args, parse);
866 class = S_OR(args.class, NULL);
867 if (ast_moh_start(chan, class, NULL))
868 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
873 static int stop_moh_exec(struct ast_channel *chan, const char *data)
880 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
882 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
884 struct mohclass *moh = NULL;
885 struct mohclass tmp_class = {
889 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
892 moh = __ao2_find_debug(mohclasses, &tmp_class, flags,
893 "get_mohbyname", file, lineno, funcname);
895 moh = __ao2_find(mohclasses, &tmp_class, flags);
899 ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
905 static struct mohdata *mohalloc(struct mohclass *cl)
910 if (!(moh = ast_calloc(1, sizeof(*moh))))
913 if (pipe(moh->pipe)) {
914 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
919 /* Make entirely non-blocking */
920 flags = fcntl(moh->pipe[0], F_GETFL);
921 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
922 flags = fcntl(moh->pipe[1], F_GETFL);
923 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
925 moh->f.frametype = AST_FRAME_VOICE;
926 ast_format_copy(&moh->f.subclass.format, &cl->format);
927 moh->f.offset = AST_FRIENDLY_OFFSET;
929 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
932 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
938 static void moh_release(struct ast_channel *chan, void *data)
940 struct mohdata *moh = data;
941 struct mohclass *class = moh->parent;
942 struct ast_format oldwfmt;
945 AST_LIST_REMOVE(&moh->parent->members, moh, list);
951 ast_format_copy(&oldwfmt, &moh->origwfmt);
953 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
958 struct moh_files_state *state;
960 state = ast_channel_music_state(chan);
961 if (state && state->class) {
962 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
964 if (oldwfmt.id && ast_set_write_format(chan, &oldwfmt)) {
965 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
966 ast_channel_name(chan), ast_getformatname(&oldwfmt));
969 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
973 static void *moh_alloc(struct ast_channel *chan, void *params)
976 struct mohclass *class = params;
977 struct moh_files_state *state;
979 /* Initiating music_state for current channel. Channel should know name of moh class */
980 if (!ast_channel_music_state(chan) && (state = ast_calloc(1, sizeof(*state)))) {
981 ast_channel_music_state_set(chan, state);
982 ast_module_ref(ast_module_info->self);
984 state = ast_channel_music_state(chan);
989 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
990 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
992 memset(state, 0, sizeof(*state));
995 if ((res = mohalloc(class))) {
996 ast_format_copy(&res->origwfmt, ast_channel_writeformat(chan));
997 if (ast_set_write_format(chan, &class->format)) {
998 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan), ast_codec2str(&class->format));
999 moh_release(NULL, res);
1002 state->class = mohclass_ref(class, "Placing reference into state container");
1004 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, ast_channel_name(chan));
1009 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1011 struct mohdata *moh = data;
1012 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1015 len = ast_codec_get_len(&moh->parent->format, samples);
1017 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1018 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1019 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1021 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1025 moh->f.datalen = res;
1026 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1027 moh->f.samples = ast_codec_get_samples(&moh->f);
1029 if (ast_write(chan, &moh->f) < 0) {
1030 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1037 static struct ast_generator mohgen = {
1039 .release = moh_release,
1040 .generate = moh_generate,
1041 .digit = moh_handle_digit,
1044 static int moh_add_file(struct mohclass *class, const char *filepath)
1046 if (!class->allowed_files) {
1047 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
1049 class->allowed_files = INITIAL_NUM_FILES;
1050 } else if (class->total_files == class->allowed_files) {
1051 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
1052 class->allowed_files = 0;
1053 class->total_files = 0;
1056 class->allowed_files *= 2;
1059 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
1062 class->total_files++;
1067 static int moh_sort_compare(const void *i1, const void *i2)
1071 s1 = ((char **)i1)[0];
1072 s2 = ((char **)i2)[0];
1074 return strcasecmp(s1, s2);
1077 static int moh_scan_files(struct mohclass *class) {
1080 struct dirent *files_dirent;
1081 char dir_path[PATH_MAX];
1082 char path[PATH_MAX];
1083 char filepath[PATH_MAX];
1085 struct stat statbuf;
1088 if (class->dir[0] != '/') {
1089 ast_copy_string(dir_path, ast_config_AST_DATA_DIR, sizeof(dir_path));
1090 strncat(dir_path, "/", sizeof(dir_path) - 1);
1091 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
1093 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1095 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1096 files_DIR = opendir(dir_path);
1098 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1102 for (i = 0; i < class->total_files; i++)
1103 ast_free(class->filearray[i]);
1105 class->total_files = 0;
1106 if (!getcwd(path, sizeof(path))) {
1107 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1110 if (chdir(dir_path) < 0) {
1111 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1114 while ((files_dirent = readdir(files_DIR))) {
1115 /* The file name must be at least long enough to have the file type extension */
1116 if ((strlen(files_dirent->d_name) < 4))
1119 /* Skip files that starts with a dot */
1120 if (files_dirent->d_name[0] == '.')
1123 /* Skip files without extensions... they are not audio */
1124 if (!strchr(files_dirent->d_name, '.'))
1127 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1129 if (stat(filepath, &statbuf))
1132 if (!S_ISREG(statbuf.st_mode))
1135 if ((ext = strrchr(filepath, '.')))
1138 /* if the file is present in multiple formats, ensure we only put it into the list once */
1139 for (i = 0; i < class->total_files; i++)
1140 if (!strcmp(filepath, class->filearray[i]))
1143 if (i == class->total_files) {
1144 if (moh_add_file(class, filepath))
1149 closedir(files_DIR);
1150 if (chdir(path) < 0) {
1151 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1154 if (ast_test_flag(class, MOH_SORTALPHA))
1155 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1156 return class->total_files;
1159 static int init_files_class(struct mohclass *class)
1163 res = moh_scan_files(class);
1170 ast_verb(3, "Files not found in %s for moh class:%s\n",
1171 class->dir, class->name);
1176 /* XXX This isn't correct. Args is an application for custom mode. XXX */
1177 if (strchr(class->args, 'r')) {
1178 ast_set_flag(class, MOH_RANDOMIZE);
1185 static void moh_rescan_files(void) {
1186 struct ao2_iterator i;
1189 i = ao2_iterator_init(mohclasses, 0);
1191 while ((c = ao2_iterator_next(&i))) {
1192 if (!strcasecmp(c->mode, "files")) {
1198 ao2_iterator_destroy(&i);
1201 static int moh_diff(struct mohclass *old, struct mohclass *new)
1207 if (strcmp(old->dir, new->dir)) {
1209 } else if (strcmp(old->mode, new->mode)) {
1211 } else if (strcmp(old->args, new->args)) {
1213 } else if (old->flags != new->flags) {
1220 static int init_app_class(struct mohclass *class)
1222 if (!strcasecmp(class->mode, "custom")) {
1223 ast_set_flag(class, MOH_CUSTOM);
1224 } else if (!strcasecmp(class->mode, "mp3nb")) {
1225 ast_set_flag(class, MOH_SINGLE);
1226 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1227 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1228 } else if (!strcasecmp(class->mode, "quietmp3")) {
1229 ast_set_flag(class, MOH_QUIET);
1234 if (!(class->timer = ast_timer_open())) {
1235 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1238 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1239 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1240 ast_timer_close(class->timer);
1241 class->timer = NULL;
1244 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1245 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1247 ast_timer_close(class->timer);
1248 class->timer = NULL;
1257 * \note This function owns the reference it gets to moh if unref is true
1259 #define moh_register(a,b,c) _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1260 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1262 struct mohclass *mohclass = NULL;
1264 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1266 if (mohclass && !moh_diff(mohclass, moh)) {
1267 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1268 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1270 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1273 } else if (mohclass) {
1274 /* Found a class, but it's different from the one being registered */
1275 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1279 moh->start -= respawn_time;
1281 if (!strcasecmp(moh->mode, "files")) {
1282 if (init_files_class(moh)) {
1284 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1288 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1289 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1290 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1291 if (init_app_class(moh)) {
1293 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1298 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1300 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1305 ao2_t_link(mohclasses, moh, "Adding class to container");
1308 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1314 static void local_ast_moh_cleanup(struct ast_channel *chan)
1316 struct moh_files_state *state = ast_channel_music_state(chan);
1320 /* This should never happen. We likely just leaked some resource. */
1322 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1323 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1325 ast_free(ast_channel_music_state(chan));
1326 ast_channel_music_state_set(chan, NULL);
1327 /* Only held a module reference if we had a music state */
1328 ast_module_unref(ast_module_info->self);
1332 static void moh_class_destructor(void *obj);
1334 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1336 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1338 struct mohclass *class;
1342 __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
1343 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 1)
1344 #elif defined(__AST_DEBUG_MALLOC)
1345 __ao2_alloc_debug(sizeof(*class), moh_class_destructor,
1346 AO2_ALLOC_OPT_LOCK_MUTEX, "Allocating new moh class", file, line, funcname, 0)
1348 ao2_alloc(sizeof(*class), moh_class_destructor)
1351 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
1358 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1360 struct mohclass *mohclass = NULL;
1361 struct moh_files_state *state = ast_channel_music_state(chan);
1362 struct ast_variable *var = NULL;
1364 int realtime_possible = ast_check_realtime("musiconhold");
1366 /* The following is the order of preference for which class to use:
1367 * 1) The channels explicitly set musicclass, which should *only* be
1368 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1369 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1370 * result of receiving a HOLD control frame, this should be the
1371 * payload that came with the frame.
1372 * 3) The interpclass argument. This would be from the mohinterpret
1373 * option from channel drivers. This is the same as the old musicclass
1375 * 4) The default class.
1377 if (!ast_strlen_zero(ast_channel_musicclass(chan))) {
1378 mohclass = get_mohbyname(ast_channel_musicclass(chan), 1, 0);
1379 if (!mohclass && realtime_possible) {
1380 var = ast_load_realtime("musiconhold", "name", ast_channel_musicclass(chan), SENTINEL);
1383 if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1384 mohclass = get_mohbyname(mclass, 1, 0);
1385 if (!mohclass && realtime_possible) {
1386 var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1389 if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1390 mohclass = get_mohbyname(interpclass, 1, 0);
1391 if (!mohclass && realtime_possible) {
1392 var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1396 if (!mohclass && !var) {
1397 mohclass = get_mohbyname("default", 1, 0);
1398 if (!mohclass && realtime_possible) {
1399 var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1403 /* If no moh class found in memory, then check RT. Note that the logic used
1404 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1407 struct ast_variable *tmp = NULL;
1409 if ((mohclass = moh_class_malloc())) {
1410 mohclass->realtime = 1;
1411 for (tmp = var; tmp; tmp = tmp->next) {
1412 if (!strcasecmp(tmp->name, "name"))
1413 ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1414 else if (!strcasecmp(tmp->name, "mode"))
1415 ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
1416 else if (!strcasecmp(tmp->name, "directory"))
1417 ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1418 else if (!strcasecmp(tmp->name, "application"))
1419 ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1420 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1421 mohclass->digit = *tmp->value;
1422 else if (!strcasecmp(tmp->name, "random"))
1423 ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1424 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1425 ast_set_flag(mohclass, MOH_RANDOMIZE);
1426 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
1427 ast_set_flag(mohclass, MOH_SORTALPHA);
1428 else if (!strcasecmp(tmp->name, "format")) {
1429 ast_getformatbyname(tmp->value, &mohclass->format);
1430 if (!mohclass->format.id) {
1431 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1432 ast_format_set(&mohclass->format, AST_FORMAT_SLINEAR, 0);
1436 ast_variables_destroy(var);
1437 if (ast_strlen_zero(mohclass->dir)) {
1438 if (!strcasecmp(mohclass->mode, "custom")) {
1439 strcpy(mohclass->dir, "nodir");
1441 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1442 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1446 if (ast_strlen_zero(mohclass->mode)) {
1447 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1448 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1451 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1452 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1453 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1457 if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1458 /* CACHERTCLASSES enabled, let's add this class to default tree */
1459 if (state && state->class) {
1460 /* Class already exist for this channel */
1461 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1462 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1463 /* we found RT class with the same name, seems like we should continue playing existing one */
1464 /* XXX This code is impossible to reach */
1465 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1466 mohclass = state->class;
1469 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1470 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1471 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1472 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1475 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1476 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1480 /* We don't register RT moh class, so let's init it manualy */
1482 time(&mohclass->start);
1483 mohclass->start -= respawn_time;
1485 if (!strcasecmp(mohclass->mode, "files")) {
1486 if (!moh_scan_files(mohclass)) {
1487 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1490 if (strchr(mohclass->args, 'r'))
1491 ast_set_flag(mohclass, MOH_RANDOMIZE);
1492 } 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")) {
1494 if (!strcasecmp(mohclass->mode, "custom"))
1495 ast_set_flag(mohclass, MOH_CUSTOM);
1496 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1497 ast_set_flag(mohclass, MOH_SINGLE);
1498 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1499 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1500 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1501 ast_set_flag(mohclass, MOH_QUIET);
1503 mohclass->srcfd = -1;
1504 if (!(mohclass->timer = ast_timer_open())) {
1505 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1507 if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1508 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1509 ast_timer_close(mohclass->timer);
1510 mohclass->timer = NULL;
1513 /* Let's check if this channel already had a moh class before */
1514 if (state && state->class) {
1515 /* Class already exist for this channel */
1516 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1517 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1518 /* we found RT class with the same name, seems like we should continue playing existing one */
1519 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1520 mohclass = state->class;
1523 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1524 ast_log(LOG_WARNING, "Unable to create moh...\n");
1525 if (mohclass->timer) {
1526 ast_timer_close(mohclass->timer);
1527 mohclass->timer = NULL;
1529 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1534 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1535 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1540 ast_variables_destroy(var);
1549 /* If we are using a cached realtime class with files, re-scan the files */
1550 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1551 if (!moh_scan_files(mohclass)) {
1552 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1557 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1562 ast_channel_name(chan), ast_channel_uniqueid(chan),
1565 ast_set_flag(chan, AST_FLAG_MOH);
1567 if (mohclass->total_files) {
1568 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1570 res = ast_activate_generator(chan, &mohgen, mohclass);
1573 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1578 static void local_ast_moh_stop(struct ast_channel *chan)
1580 ast_clear_flag(chan, AST_FLAG_MOH);
1581 ast_deactivate_generator(chan);
1583 ast_channel_lock(chan);
1584 if (ast_channel_music_state(chan)) {
1585 if (ast_channel_stream(chan)) {
1586 ast_closestream(ast_channel_stream(chan));
1587 ast_channel_stream_set(chan, NULL);
1591 ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1595 ast_channel_name(chan), ast_channel_uniqueid(chan));
1596 ast_channel_unlock(chan);
1599 static void moh_class_destructor(void *obj)
1601 struct mohclass *class = obj;
1602 struct mohdata *member;
1605 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1608 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1613 /* Kill the thread first, so it cannot restart the child process while the
1614 * class is being destroyed */
1615 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1616 tid = class->thread;
1617 class->thread = AST_PTHREADT_NULL;
1618 pthread_cancel(tid);
1619 /* We'll collect the exit status later, after we ensure all the readers
1623 if (class->pid > 1) {
1625 int bytes, tbytes = 0, stime = 0, pid = 0;
1627 ast_debug(1, "killing %d!\n", class->pid);
1629 stime = time(NULL) + 2;
1633 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1634 * to give the process a reason and time enough to kill off its
1637 if (killpg(pid, SIGHUP) < 0) {
1638 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1641 if (killpg(pid, SIGTERM) < 0) {
1642 if (errno == ESRCH) {
1645 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1648 if (killpg(pid, SIGKILL) < 0) {
1649 if (errno == ESRCH) {
1652 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1656 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1657 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1658 tbytes = tbytes + bytes;
1661 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1663 close(class->srcfd);
1667 if (class->filearray) {
1669 for (i = 0; i < class->total_files; i++) {
1670 free(class->filearray[i]);
1672 free(class->filearray);
1673 class->filearray = NULL;
1677 ast_timer_close(class->timer);
1678 class->timer = NULL;
1681 /* Finally, collect the exit status of the monitor thread */
1683 pthread_join(tid, NULL);
1688 static int moh_class_mark(void *obj, void *arg, int flags)
1690 struct mohclass *class = obj;
1697 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1699 struct mohclass *class = obj;
1701 return class->delete ? CMP_MATCH : 0;
1704 static int load_moh_classes(int reload)
1706 struct ast_config *cfg;
1707 struct ast_variable *var;
1708 struct mohclass *class;
1711 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1713 cfg = ast_config_load("musiconhold.conf", config_flags);
1715 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1716 if (ast_check_realtime("musiconhold") && reload) {
1717 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1718 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
1722 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1728 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1731 ast_clear_flag(global_flags, AST_FLAGS_ALL);
1733 cat = ast_category_browse(cfg, NULL);
1734 for (; cat; cat = ast_category_browse(cfg, cat)) {
1735 /* Setup common options from [general] section */
1736 if (!strcasecmp(cat, "general")) {
1737 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1738 if (!strcasecmp(var->name, "cachertclasses")) {
1739 ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1741 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1745 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1746 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") ||
1747 !strcasecmp(cat, "general")) {
1751 if (!(class = moh_class_malloc())) {
1755 ast_copy_string(class->name, cat, sizeof(class->name));
1756 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1757 if (!strcasecmp(var->name, "mode")) {
1758 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1759 } else if (!strcasecmp(var->name, "directory")) {
1760 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1761 } else if (!strcasecmp(var->name, "application")) {
1762 ast_copy_string(class->args, var->value, sizeof(class->args));
1763 } else if (!strcasecmp(var->name, "announcement")) {
1764 ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
1765 ast_set_flag(class, MOH_ANNOUNCEMENT);
1766 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1767 class->digit = *var->value;
1768 } else if (!strcasecmp(var->name, "random")) {
1769 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1770 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
1771 ast_set_flag(class, MOH_RANDOMIZE);
1772 } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
1773 ast_set_flag(class, MOH_SORTALPHA);
1774 } else if (!strcasecmp(var->name, "format")) {
1775 ast_getformatbyname(var->value, &class->format);
1776 if (!class->format.id) {
1777 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1778 ast_format_set(&class->format, AST_FORMAT_SLINEAR, 0);
1783 if (ast_strlen_zero(class->dir)) {
1784 if (!strcasecmp(class->mode, "custom")) {
1785 strcpy(class->dir, "nodir");
1787 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1788 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1792 if (ast_strlen_zero(class->mode)) {
1793 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1794 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1797 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1798 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1799 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1803 /* Don't leak a class when it's already registered */
1804 if (!moh_register(class, reload, HANDLE_REF)) {
1809 ast_config_destroy(cfg);
1811 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
1812 moh_classes_delete_marked, NULL, "Purge marked classes");
1817 static void ast_moh_destroy(void)
1819 ast_verb(2, "Destroying musiconhold processes\n");
1820 ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1823 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1827 e->command = "moh reload";
1829 "Usage: moh reload\n"
1830 " Reloads the MusicOnHold module.\n"
1831 " Alias for 'module reload res_musiconhold.so'\n";
1837 if (a->argc != e->args)
1838 return CLI_SHOWUSAGE;
1845 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1847 struct mohclass *class;
1848 struct ao2_iterator i;
1852 e->command = "moh show files";
1854 "Usage: moh show files\n"
1855 " Lists all loaded file-based MusicOnHold classes and their\n"
1862 if (a->argc != e->args)
1863 return CLI_SHOWUSAGE;
1865 i = ao2_iterator_init(mohclasses, 0);
1866 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1869 if (!class->total_files) {
1873 ast_cli(a->fd, "Class: %s\n", class->name);
1874 for (x = 0; x < class->total_files; x++) {
1875 ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1878 ao2_iterator_destroy(&i);
1883 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1885 struct mohclass *class;
1886 struct ao2_iterator i;
1890 e->command = "moh show classes";
1892 "Usage: moh show classes\n"
1893 " Lists all MusicOnHold classes.\n";
1899 if (a->argc != e->args)
1900 return CLI_SHOWUSAGE;
1902 i = ao2_iterator_init(mohclasses, 0);
1903 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1904 ast_cli(a->fd, "Class: %s\n", class->name);
1905 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1906 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1907 if (ast_test_flag(class, MOH_CUSTOM)) {
1908 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1910 if (strcasecmp(class->mode, "files")) {
1911 ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(&class->format));
1914 ao2_iterator_destroy(&i);
1919 static struct ast_cli_entry cli_moh[] = {
1920 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
1921 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1922 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
1925 static int moh_class_hash(const void *obj, const int flags)
1927 const struct mohclass *class = obj;
1929 return ast_str_case_hash(class->name);
1932 static int moh_class_cmp(void *obj, void *arg, int flags)
1934 struct mohclass *class = obj, *class2 = arg;
1936 return strcasecmp(class->name, class2->name) ? 0 :
1937 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1938 CMP_MATCH | CMP_STOP;
1941 static int load_module(void)
1945 if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1946 return AST_MODULE_LOAD_DECLINE;
1949 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
1950 ast_log(LOG_WARNING, "No music on hold classes configured, "
1951 "disabling music on hold.\n");
1953 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1954 local_ast_moh_cleanup);
1957 res = ast_register_application_xml(play_moh, play_moh_exec);
1958 ast_register_atexit(ast_moh_destroy);
1959 ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1961 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1963 res = ast_register_application_xml(set_moh, set_moh_exec);
1965 res = ast_register_application_xml(start_moh, start_moh_exec);
1967 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1969 return AST_MODULE_LOAD_SUCCESS;
1972 static int reload(void)
1974 if (load_moh_classes(1)) {
1975 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1976 local_ast_moh_cleanup);
1979 return AST_MODULE_LOAD_SUCCESS;
1982 static int moh_class_inuse(void *obj, void *arg, int flags)
1984 struct mohclass *class = obj;
1986 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1989 static int unload_module(void)
1992 struct mohclass *class = NULL;
1994 /* XXX This check shouldn't be required if module ref counting was being used
1996 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
1997 class = mohclass_unref(class, "unref of class from module unload callback");
2002 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2006 ast_uninstall_music_functions();
2009 res = ast_unregister_application(play_moh);
2010 res |= ast_unregister_application(wait_moh);
2011 res |= ast_unregister_application(set_moh);
2012 res |= ast_unregister_application(start_moh);
2013 res |= ast_unregister_application(stop_moh);
2014 ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
2015 ast_unregister_atexit(ast_moh_destroy);
2020 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
2021 .load = load_module,
2022 .unload = unload_module,
2024 .load_pri = AST_MODPRI_CHANNEL_DEPEND,