Asterisk - The Open Source Telephony Project GIT-master-5963e62
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_musiconhold.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2010, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
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.
13 *
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.
17 */
18
19/*! \file
20 *
21 * \brief Routines implementing music on hold
22 *
23 * \author Mark Spencer <markster@digium.com>
24 */
25
26/*! \li \ref res_musiconhold.c uses the configuration file \ref musiconhold.conf
27 * \addtogroup configuration_file Configuration Files
28 */
29
30/*!
31 * \page musiconhold.conf musiconhold.conf
32 * \verbinclude musiconhold.conf.sample
33 */
34
35/*** MODULEINFO
36 <conflict>win32</conflict>
37 <support_level>core</support_level>
38 ***/
39
40#include "asterisk.h"
41
42#include <ctype.h>
43#include <signal.h>
44#include <sys/time.h>
45#include <signal.h>
46#include <netinet/in.h>
47#include <sys/stat.h>
48#include <dirent.h>
49
50#ifdef SOLARIS
51#include <thread.h>
52#endif
53
54#include "asterisk/lock.h"
55#include "asterisk/file.h"
56#include "asterisk/channel.h"
57#include "asterisk/pbx.h"
58#include "asterisk/app.h"
59#include "asterisk/module.h"
60#include "asterisk/translate.h"
61#include "asterisk/say.h"
63#include "asterisk/config.h"
64#include "asterisk/utils.h"
65#include "asterisk/cli.h"
68#include "asterisk/stasis.h"
70#include "asterisk/paths.h"
71#include "asterisk/astobj2.h"
72#include "asterisk/timing.h"
73#include "asterisk/time.h"
75
76#define INITIAL_NUM_FILES 8
77#define HANDLE_REF 1
78#define DONT_UNREF 0
79
80/*** DOCUMENTATION
81 <application name="MusicOnHold" language="en_US">
82 <since>
83 <version>0.1.12</version>
84 </since>
85 <synopsis>
86 Play Music On Hold indefinitely.
87 </synopsis>
88 <syntax>
89 <parameter name="class" required="true" />
90 <parameter name="duration" />
91 </syntax>
92 <description>
93 <para>Plays hold music specified by class. If omitted, the default music
94 source for the channel will be used. Change the default class with
95 Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
96 specified number of seconds. If duration is omitted, music plays indefinitely.
97 Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
98 <para>This application does not automatically answer and should be preceeded by
99 an application such as Answer() or Progress().</para>
100 </description>
101 </application>
102 <application name="StartMusicOnHold" language="en_US">
103 <since>
104 <version>1.2.0</version>
105 </since>
106 <synopsis>
107 Play Music On Hold.
108 </synopsis>
109 <syntax>
110 <parameter name="class" required="true" />
111 </syntax>
112 <description>
113 <para>Starts playing music on hold, uses default music class for channel.
114 Starts playing music specified by class. If omitted, the default music
115 source for the channel will be used. Always returns <literal>0</literal>.</para>
116 </description>
117 </application>
118 <application name="StopMusicOnHold" language="en_US">
119 <since>
120 <version>1.2.0</version>
121 </since>
122 <synopsis>
123 Stop playing Music On Hold.
124 </synopsis>
125 <syntax />
126 <description>
127 <para>Stops playing music on hold.</para>
128 </description>
129 </application>
130 ***/
131
132static const char play_moh[] = "MusicOnHold";
133static const char start_moh[] = "StartMusicOnHold";
134static const char stop_moh[] = "StopMusicOnHold";
135
136static int respawn_time = 20;
137
139 /*! Holds a reference to the MOH class. */
140 struct mohclass *class;
146 int pos;
151};
152
153#define MOH_QUIET (1 << 0)
154#define MOH_SINGLE (1 << 1)
155#define MOH_CUSTOM (1 << 2)
156#define MOH_RANDOMIZE (1 << 3)
157#define MOH_SORTALPHA (1 << 4)
158#define MOH_RANDSTART (MOH_RANDOMIZE | MOH_SORTALPHA) /*!< Sorted but start at random position */
159#define MOH_SORTMODE (3 << 3)
160
161#define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
162#define MOH_ANNOUNCEMENT (1 << 6) /*!< Do we play announcement files between songs on this channel? */
163#define MOH_PREFERCHANNELCLASS (1 << 7) /*!< Should queue moh override channel moh */
164
165#define MOH_LOOPLAST (1 << 8) /*!< Whether to loop the last file in the music class when we reach the end, rather than starting over */
166
167/* Custom astobj2 flag */
168#define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
169#define MOH_REALTIME (1 << 31) /*!< Find only records that are realtime */
170
171static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
172
177
178struct mohclass {
180 char dir[256];
181 char args[256];
182 char announcement[256];
183 char mode[80];
184 char digit;
185 /*! An immutable vector of filenames in "files" mode */
187 unsigned int flags;
188 /*! The format from the MOH source, not applicable to "files" mode */
190 /*! The pid of the external application delivering MOH */
191 int pid;
192 time_t start;
193 pthread_t thread;
194 /*! Millisecond delay between kill attempts */
196 /*! Kill method */
198 /*! Source of audio */
199 int srcfd;
200 /*! Generic timer */
202 /*! Created on the fly, from RT engine */
203 unsigned int realtime:1;
204 unsigned int delete:1;
207 /*!< Play the moh if the channel answered */
209};
210
211struct mohdata {
212 int pipe[2];
215 struct ast_frame f;
217};
218
220
221#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
222#define MPG_123 "/usr/bin/mpg123"
223#define MAX_MP3S 256
224
225static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass);
226static int reload(void);
227
228#define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
229
230#ifndef AST_DEVMODE
231#define mohclass_unref(class,string) ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })
232#else
233#define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
234static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
235{
237
238 if (dup) {
239 if (__ao2_ref(dup, -1, tag, file, line, funcname) == 2) {
240 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",
241 class, class->name, file, line, funcname);
242 } else {
243 ao2_ref(class, -1);
244 }
245 } else {
246 __ao2_ref(class, -1, tag, file, line, funcname);
247 }
248 return NULL;
249}
250#endif
251
252static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
253{
254 struct stasis_message *message;
255 struct ast_json *json_object;
256
257 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
258 moh_class_name, ast_channel_name(chan));
259
260 json_object = ast_json_pack("{s: s}", "class", moh_class_name);
261 if (!json_object) {
262 return;
263 }
264
266 ast_channel_moh_start_type(), json_object);
267 if (message) {
268 /* A channel snapshot must have been in the cache. */
269 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
270
272 }
274 ast_json_unref(json_object);
275}
276
277static void moh_post_stop(struct ast_channel *chan)
278{
279 struct stasis_message *message;
280
281 ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
282
285 if (message) {
286 /* A channel snapshot must have been in the cache. */
287 ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
288
290 }
292}
293
294/*
295 * Called with `chan` locked.
296 */
297static void moh_files_release(struct ast_channel *chan, void *data)
298{
299 struct moh_files_state *state;
300
301 if (!chan || !ast_channel_music_state(chan)) {
302 return;
303 }
304
306
307 if (ast_channel_stream(chan)) {
310 }
311
312 moh_post_stop(chan);
313
314 ao2_ref(state->mohwfmt, -1);
315 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
316 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
317 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
318 ast_format_get_name(state->origwfmt));
319 }
320 ao2_cleanup(state->origwfmt);
321 state->origwfmt = NULL;
322
323 state->save_pos = state->pos;
324 state->announcement = 0;
325
326 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
327}
328
329/*
330 * Called with `chan` locked.
331 */
332static int moh_files_next(struct ast_channel *chan)
333{
335 struct ast_vector_string *files;
336 int tries;
337 size_t file_count;
338
339 /* Discontinue a stream if it is running already */
340 if (ast_channel_stream(chan)) {
343 }
344
345 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
346 state->announcement = 1;
347 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
348 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
349 return 0;
350 }
351 } else {
352 state->announcement = 0;
353 }
354
355 ao2_lock(state->class);
356 files = ao2_bump(state->class->files);
357 ao2_unlock(state->class);
358
359 file_count = AST_VECTOR_SIZE(files);
360 if (!file_count) {
361 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
362 ao2_ref(files, -1);
363 return -1;
364 }
365
366 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
367 /* First time so lets play the file. */
368 state->save_pos = -1;
369 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
370 /* If a specific file has been saved confirm it still exists and that it is still valid */
371 state->pos = state->save_pos;
372 state->save_pos = -1;
373 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
374 /* Get a random file and ensure we can open it */
375 for (tries = 0; tries < 20; tries++) {
376 state->pos = ast_random() % file_count;
377 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
378 break;
379 }
380 }
381 state->save_pos = -1;
382 state->samples = 0;
383 } else {
384 /* This is easy, just increment our position and make sure we don't exceed the total file count */
385 state->pos++;
386 if (ast_test_flag(state->class, MOH_LOOPLAST)) {
387 state->pos = MIN(file_count - 1, state->pos);
388 } else {
389 state->pos %= file_count;
390 }
391 state->save_pos = -1;
392 state->samples = 0;
393 }
394
395 for (tries = 0; tries < file_count; ++tries) {
396 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
397 break;
398 }
399
400 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
401 state->pos++;
402 state->pos %= file_count;
403 }
404
405 if (tries == file_count) {
406 ao2_ref(files, -1);
407 return -1;
408 }
409
410 /* Record the pointer to the filename for position resuming later */
411 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
412
413 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
414
415 if (state->samples) {
416 size_t loc;
417 /* seek *SHOULD* be good since it's from a known location */
418 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
419 /* if the seek failed then recover because if there is not a valid read,
420 * moh_files_generate will return -1 and MOH will stop */
422 if (state->samples > loc && loc) {
423 /* seek one sample from the end for one guaranteed valid read */
424 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
425 }
426 }
427
428 ao2_ref(files, -1);
429 return 0;
430}
431
432/*
433 * Called with `chan` locked.
434 */
435static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
436{
437 struct ast_frame *f;
438
440 if (!f) {
441 /* Either there was no file stream setup or we reached EOF. */
442 if (!moh_files_next(chan)) {
443 /*
444 * Either we resetup the previously saved file stream position
445 * or we started a new file stream.
446 */
448 if (!f) {
449 /*
450 * We can get here if we were very unlucky because the
451 * resetup file stream was saved at EOF when MOH was
452 * previously stopped.
453 */
454 if (!moh_files_next(chan)) {
456 }
457 }
458 }
459 }
460
461 return f;
462}
463
464/*
465 * Called with `chan` locked.
466 */
467static void moh_files_write_format_change(struct ast_channel *chan, void *data)
468{
470
471 /* In order to prevent a recursive call to this function as a result
472 * of setting the moh write format back on the channel. Clear
473 * the moh write format before setting the write format on the channel.*/
474 if (state->origwfmt) {
475 struct ast_format *tmp;
476
478 ao2_replace(state->origwfmt, NULL);
479 if (state->mohwfmt) {
480 ast_set_write_format(chan, state->mohwfmt);
481 }
482 state->origwfmt = tmp;
483 }
484}
485
486static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
487{
488 struct moh_files_state *state;
489 struct ast_frame *f = NULL;
490 int res = 0, sample_queue = 0;
491
492 ast_channel_lock(chan);
494 state->sample_queue += samples;
495 /* save the sample queue value for un-locked access */
496 sample_queue = state->sample_queue;
497 ast_channel_unlock(chan);
498
499 while (sample_queue > 0) {
500 ast_channel_lock(chan);
501 f = moh_files_readframe(chan);
502 if (!f) {
503 ast_channel_unlock(chan);
504 return -1;
505 }
506
507 /* Only track our offset within the current file if we are not in the
508 * the middle of an announcement */
509 if (!state->announcement) {
510 state->samples += f->samples;
511 }
512
513 state->sample_queue -= f->samples;
515 ao2_replace(state->mohwfmt, f->subclass.format);
516 }
517
518 /* We need to be sure that we unlock
519 * the channel prior to calling
520 * ast_write, but after our references to state
521 * as it refers to chan->music_state. Update
522 * sample_queue for our loop
523 * Otherwise, the recursive locking that occurs
524 * can cause deadlocks when using indirect
525 * channels, like local channels
526 */
527 sample_queue = state->sample_queue;
528 ast_channel_unlock(chan);
529
530 res = ast_write(chan, f);
531 ast_frfree(f);
532 if (res < 0) {
533 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
534 return -1;
535 }
536 }
537 return res;
538}
539
540/*
541 * Called with `chan` locked.
542 */
543static void *moh_files_alloc(struct ast_channel *chan, void *params)
544{
545 struct moh_files_state *state;
546 struct mohclass *class = params;
547 size_t file_count;
548
550 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
553 } else {
554 if (!state) {
555 return NULL;
556 }
557 if (state->class) {
558 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
559 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
560 }
561 }
562
563 ao2_lock(class);
564 file_count = AST_VECTOR_SIZE(class->files);
565 ao2_unlock(class);
566
567 /* Resume MOH from where we left off last time or start from scratch? */
568 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
569 /* Start MOH from scratch. */
570 ao2_cleanup(state->origwfmt);
571 ao2_cleanup(state->mohwfmt);
572 memset(state, 0, sizeof(*state));
573 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
574 state->pos = ast_random() % file_count;
575 }
576 }
577
578 state->class = mohclass_ref(class, "Reffing music class for channel");
579 /* it's possible state is not a new allocation, don't leak old refs */
580 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
582 /* For comparison on restart of MOH (see above) */
583 ast_copy_string(state->name, class->name, sizeof(state->name));
584 state->save_total = file_count;
585
586 moh_post_start(chan, class->name);
587
588 return state;
589}
590
591static int moh_digit_match(void *obj, void *arg, int flags)
592{
593 char *digit = arg;
594 struct mohclass *class = obj;
595
596 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
597}
598
599/*! \note This function should be called with the mohclasses list locked */
600static struct mohclass *get_mohbydigit(char digit)
601{
602 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
603}
604
605static void moh_handle_digit(struct ast_channel *chan, char digit)
606{
607 struct mohclass *class;
608 const char *classname = NULL;
609
610 if ((class = get_mohbydigit(digit))) {
611 classname = ast_strdupa(class->name);
612 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
613 ast_channel_musicclass_set(chan, classname);
614 ast_moh_stop(chan);
615 ast_moh_start(chan, classname, NULL);
616 }
617}
618
621 .release = moh_files_release,
622 .generate = moh_files_generator,
623 .digit = moh_handle_digit,
624 .write_format_change = moh_files_write_format_change,
625};
626
627static int spawn_mp3(struct mohclass *class)
628{
629 int fds[2];
630 int files = 0;
631 char fns[MAX_MP3S][80];
632 char *argv[MAX_MP3S + 50];
633 char xargs[256];
634 char *argptr;
635 int argc = 0;
636 DIR *dir = NULL;
637 struct dirent *de;
638
639
640 if (!strcasecmp(class->dir, "nodir")) {
641 files = 1;
642 } else {
643 dir = opendir(class->dir);
644 if (!dir && strncasecmp(class->dir, "http://", 7)) {
645 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
646 return -1;
647 }
648 }
649
650 if (!ast_test_flag(class, MOH_CUSTOM)) {
651 argv[argc++] = "mpg123";
652 argv[argc++] = "-q";
653 argv[argc++] = "-s";
654 argv[argc++] = "--mono";
655 argv[argc++] = "-r";
656 argv[argc++] = "8000";
657
658 if (!ast_test_flag(class, MOH_SINGLE)) {
659 argv[argc++] = "-b";
660 argv[argc++] = "2048";
661 }
662
663 argv[argc++] = "-f";
664
665 if (ast_test_flag(class, MOH_QUIET))
666 argv[argc++] = "4096";
667 else
668 argv[argc++] = "8192";
669
670 /* Look for extra arguments and add them to the list */
671 ast_copy_string(xargs, class->args, sizeof(xargs));
672 argptr = xargs;
673 while (!ast_strlen_zero(argptr)) {
674 argv[argc++] = argptr;
675 strsep(&argptr, ",");
676 }
677 } else {
678 /* Format arguments for argv vector */
679 ast_copy_string(xargs, class->args, sizeof(xargs));
680 argptr = xargs;
681 while (!ast_strlen_zero(argptr)) {
682 argv[argc++] = argptr;
683 strsep(&argptr, " ");
684 }
685 }
686
687 if (!strncasecmp(class->dir, "http://", 7)) {
688 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
689 argv[argc++] = fns[files];
690 files++;
691 } else if (dir) {
692 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
693 if ((strlen(de->d_name) > 3) &&
694 ((ast_test_flag(class, MOH_CUSTOM) &&
695 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
696 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
697 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
698 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
699 argv[argc++] = fns[files];
700 files++;
701 }
702 }
703 }
704 argv[argc] = NULL;
705 if (dir) {
706 closedir(dir);
707 }
708 if (pipe(fds)) {
709 ast_log(LOG_WARNING, "Pipe failed\n");
710 return -1;
711 }
712 if (!files) {
713 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
714 close(fds[0]);
715 close(fds[1]);
716 return -1;
717 }
718 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
719 sleep(respawn_time - (time(NULL) - class->start));
720 }
721
722 time(&class->start);
723 class->pid = ast_safe_fork(0);
724 if (class->pid < 0) {
725 close(fds[0]);
726 close(fds[1]);
727 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
728 return -1;
729 }
730 if (!class->pid) {
733
734 close(fds[0]);
735 /* Stdout goes to pipe */
736 dup2(fds[1], STDOUT_FILENO);
737
738 /* Close everything else */
739 ast_close_fds_above_n(STDERR_FILENO);
740
741 /* Child */
742 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
743 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
744 _exit(1);
745 }
746 setpgid(0, getpid());
747 if (ast_test_flag(class, MOH_CUSTOM)) {
748 execv(argv[0], argv);
749 } else {
750 /* Default install is /usr/local/bin */
751 execv(LOCAL_MPG_123, argv);
752 /* Many places have it in /usr/bin */
753 execv(MPG_123, argv);
754 /* Check PATH as a last-ditch effort */
755 execvp("mpg123", argv);
756 }
757 /* Can't use logger, since log FDs are closed */
758 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
759 close(fds[1]);
760 _exit(1);
761 } else {
762 /* Parent */
763 close(fds[1]);
764 }
765 return fds[0];
766}
767
768static int killer(pid_t pid, int signum, enum kill_methods kill_method)
769{
770 switch (kill_method) {
772 return killpg(pid, signum);
774 return kill(pid, signum);
775 }
776
777 return -1;
778}
779
780static void killpid(int pid, size_t delay, enum kill_methods kill_method)
781{
782 if (killer(pid, SIGHUP, kill_method) < 0) {
783 if (errno == ESRCH) {
784 return;
785 }
786 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
787 } else {
788 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
789 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
790 }
791 usleep(delay);
792 if (killer(pid, SIGTERM, kill_method) < 0) {
793 if (errno == ESRCH) {
794 return;
795 }
796 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
797 } else {
798 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
799 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
800 }
801 usleep(delay);
802 if (killer(pid, SIGKILL, kill_method) < 0) {
803 if (errno == ESRCH) {
804 return;
805 }
806 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
807 } else {
808 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
809 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
810 }
811}
812
813static void *monmp3thread(void *data)
814{
815#define MOH_MS_INTERVAL 100
816
817 struct mohclass *class = data;
818 struct mohdata *moh;
819 short sbuf[8192];
820 int res = 0, res2;
821 int len;
822 struct timeval deadline, tv_tmp;
823
824 deadline.tv_sec = 0;
825 deadline.tv_usec = 0;
826 for(;/* ever */;) {
827 pthread_testcancel();
828 /* Spawn mp3 player if it's not there */
829 if (class->srcfd < 0) {
830 if ((class->srcfd = spawn_mp3(class)) < 0) {
831 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
832 /* Try again later */
833 sleep(500);
834 continue;
835 }
836 }
837 if (class->timer) {
838 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
839
840#ifdef SOLARIS
841 thr_yield();
842#endif
843 /* Pause some amount of time */
844 if (ast_poll(&pfd, 1, -1) > 0) {
845 if (ast_timer_ack(class->timer, 1) < 0) {
846 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
847 return NULL;
848 }
849 /* 25 samples per second => 40ms framerate => 320 samples */
850 res = 320; /* 320/40 = 8 samples/ms */
851 } else {
852 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
853 res = 0;
854 }
855 pthread_testcancel();
856 } else {
857 long delta;
858 /* Reliable sleep */
859 tv_tmp = ast_tvnow();
860 if (ast_tvzero(deadline))
861 deadline = tv_tmp;
862 delta = ast_tvdiff_ms(tv_tmp, deadline);
863 if (delta < MOH_MS_INTERVAL) { /* too early */
864 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
865 usleep(1000 * (MOH_MS_INTERVAL - delta));
866 pthread_testcancel();
867 } else {
868 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
869 deadline = tv_tmp;
870 }
871 /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
872 res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
873 }
874 /* For non-8000Hz formats, we need to alter the resolution */
875 res = res * ast_format_get_sample_rate(class->format) / 8000;
876
877 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
878 continue;
879 /* Read mp3 audio */
880 len = ast_format_determine_length(class->format, res);
881
882 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
883 if (!res2) {
884 close(class->srcfd);
885 class->srcfd = -1;
886 pthread_testcancel();
887 if (class->pid > 1) {
888 killpid(class->pid, class->kill_delay, class->kill_method);
889 class->pid = 0;
890 }
891 } else {
892 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
893 }
894 continue;
895 }
896
897 pthread_testcancel();
898
899 ao2_lock(class);
900 AST_LIST_TRAVERSE(&class->members, moh, list) {
901 /* Write data */
902 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
903 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
904 }
905 }
906 ao2_unlock(class);
907 }
908 return NULL;
909}
910
911static int play_moh_exec(struct ast_channel *chan, const char *data)
912{
913 char *parse;
914 char *class;
915 int timeout = -1;
916 int res;
918 AST_APP_ARG(class);
919 AST_APP_ARG(duration);
920 );
921
922 parse = ast_strdupa(data);
923
925
926 if (!ast_strlen_zero(args.duration)) {
927 if (sscanf(args.duration, "%30d", &timeout) == 1) {
928 timeout *= 1000;
929 } else {
930 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
931 }
932 }
933
934 class = S_OR(args.class, NULL);
935 if (ast_moh_start(chan, class, NULL)) {
936 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
937 return 0;
938 }
939
940 if (timeout > 0)
941 res = ast_safe_sleep(chan, timeout);
942 else {
943 while (!(res = ast_safe_sleep(chan, 10000)));
944 }
945
946 ast_moh_stop(chan);
947
948 return res;
949}
950
951static int start_moh_exec(struct ast_channel *chan, const char *data)
952{
953 char *parse;
954 char *class;
956 AST_APP_ARG(class);
957 );
958
959 parse = ast_strdupa(data);
960
962
963 class = S_OR(args.class, NULL);
964 if (ast_moh_start(chan, class, NULL))
965 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
966
967 return 0;
968}
969
970static int stop_moh_exec(struct ast_channel *chan, const char *data)
971{
972 ast_moh_stop(chan);
973
974 return 0;
975}
976
977#define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
978
979static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
980{
981 struct mohclass *moh = NULL;
982 struct mohclass tmp_class = {
983 .flags = 0,
984 };
985
986 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
987
988 moh = __ao2_find(mohclasses, &tmp_class, flags,
989 "get_mohbyname", file, lineno, funcname);
990
991 if (!moh && warn) {
992 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
993 }
994
995 return moh;
996}
997
998static struct mohdata *mohalloc(struct mohclass *cl)
999{
1000 struct mohdata *moh;
1001
1002 if (!(moh = ast_calloc(1, sizeof(*moh))))
1003 return NULL;
1004
1005 if (ast_pipe_nonblock(moh->pipe)) {
1006 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
1007 ast_free(moh);
1008 return NULL;
1009 }
1010
1012 moh->f.subclass.format = cl->format;
1014
1015 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
1016
1017 ao2_lock(cl);
1018 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
1019 ao2_unlock(cl);
1020
1021 return moh;
1022}
1023
1024/*
1025 * Called with `chan` locked.
1026 */
1027static void moh_release(struct ast_channel *chan, void *data)
1028{
1029 struct mohdata *moh = data;
1030 struct mohclass *class = moh->parent;
1031 struct ast_format *oldwfmt;
1032
1033 ao2_lock(class);
1034 AST_LIST_REMOVE(&moh->parent->members, moh, list);
1035 ao2_unlock(class);
1036
1037 close(moh->pipe[0]);
1038 close(moh->pipe[1]);
1039
1040 oldwfmt = moh->origwfmt;
1041
1042 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1043
1044 ast_free(moh);
1045
1046 if (chan) {
1047 struct moh_files_state *state;
1048
1050 if (state && state->class) {
1051 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1052 }
1053 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1054 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1055 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1056 }
1057
1058 moh_post_stop(chan);
1059 }
1060
1061 ao2_cleanup(oldwfmt);
1062}
1063
1064/*
1065 * Called with `chan` locked.
1066 */
1067static void *moh_alloc(struct ast_channel *chan, void *params)
1068{
1069 struct mohdata *res;
1070 struct mohclass *class = params;
1071 struct moh_files_state *state;
1072
1073 /* Initiating music_state for current channel. Channel should know name of moh class */
1075 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1078 } else {
1079 if (!state) {
1080 return NULL;
1081 }
1082 if (state->class) {
1083 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1084 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1085 }
1086 ao2_cleanup(state->origwfmt);
1087 ao2_cleanup(state->mohwfmt);
1088 memset(state, 0, sizeof(*state));
1089 }
1090
1091 if ((res = mohalloc(class))) {
1093 if (ast_set_write_format(chan, class->format)) {
1094 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1096 moh_release(NULL, res);
1097 res = NULL;
1098 } else {
1099 state->class = mohclass_ref(class, "Placing reference into state container");
1100 moh_post_start(chan, class->name);
1101 }
1102 }
1103 return res;
1104}
1105
1106static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1107{
1108 struct mohdata *moh = data;
1109 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1110 int res;
1111
1112 len = ast_format_determine_length(moh->parent->format, samples);
1113
1114 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1115 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1116 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1117 }
1118 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1119 if (res <= 0)
1120 return 0;
1121
1122 moh->f.datalen = res;
1123 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1124 moh->f.samples = ast_codec_samples_count(&moh->f);
1125
1126 if (ast_write(chan, &moh->f) < 0) {
1127 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1128 return -1;
1129 }
1130
1131 return 0;
1132}
1133
1134static struct ast_generator mohgen = {
1135 .alloc = moh_alloc,
1136 .release = moh_release,
1137 .generate = moh_generate,
1138 .digit = moh_handle_digit,
1139};
1140
1141static void moh_file_vector_destructor(void *obj)
1142{
1143 struct ast_vector_string *files = obj;
1144 AST_VECTOR_RESET(files, ast_free);
1145 AST_VECTOR_FREE(files);
1146}
1147
1148static struct ast_vector_string *moh_file_vector_alloc(int initial_capacity)
1149{
1150 struct ast_vector_string *files = ao2_alloc_options(
1151 sizeof(struct ast_vector_string),
1154 if (files) {
1155 AST_VECTOR_INIT(files, initial_capacity);
1156 }
1157 return files;
1158}
1159
1161{
1162 struct ast_vector_string *playlist_entries = NULL;
1163
1164 for (; var; var = var->next) {
1165 if (!strcasecmp(var->name, "name")) {
1166 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1167 } else if (!strcasecmp(var->name, "mode")) {
1168 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1169 } else if (!strcasecmp(var->name, "entry")) {
1170 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1171 char *dup;
1172
1173 if (!playlist_entries) {
1174 playlist_entries = moh_file_vector_alloc(16);
1175 if (!playlist_entries) {
1176 continue;
1177 }
1178 }
1179
1180 dup = ast_strdup(var->value);
1181 if (!dup) {
1182 continue;
1183 }
1184
1185 if (ast_begins_with(dup, "/")) {
1186 char *last_pos_dot = strrchr(dup, '.');
1187 char *last_pos_slash = strrchr(dup, '/');
1188 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1189 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1190 dup);
1191 }
1192 }
1193
1194 AST_VECTOR_APPEND(playlist_entries, dup);
1195 } else {
1196 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1197 }
1198 } else if (!strcasecmp(var->name, "directory")) {
1199 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1200 } else if (!strcasecmp(var->name, "application")) {
1201 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1202 } else if (!strcasecmp(var->name, "announcement")) {
1205 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1206 mohclass->digit = *var->value;
1207 } else if (!strcasecmp(var->name, "random")) {
1208 static int deprecation_warning = 0;
1209 if (!deprecation_warning) {
1210 ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1211 deprecation_warning = 1;
1212 }
1214 } else if (!strcasecmp(var->name, "sort")) {
1215 if (!strcasecmp(var->value, "random")) {
1217 } else if (!strcasecmp(var->value, "alpha")) {
1219 } else if (!strcasecmp(var->value, "randstart")) {
1221 }
1222 } else if (!strcasecmp(var->name, "loop_last")) {
1223 if (ast_true(var->value)) {
1225 } else {
1227 }
1228 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1231 if (!mohclass->format) {
1232 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1234 }
1235 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1236 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1237 mohclass->kill_delay *= 1000;
1238 } else {
1239 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1240 mohclass->kill_delay = 100000;
1241 }
1242 } else if (!strcasecmp(var->name, "kill_method")) {
1243 if (!strcasecmp(var->value, "process")) {
1245 } else if (!strcasecmp(var->value, "process_group")) {
1247 } else {
1248 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1250 }
1251 } else if (!strcasecmp(var->name, "answeredonly")) {
1252 mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1253 }
1254 }
1255
1256 if (playlist_entries) {
1257 /* If we aren't in playlist mode, drop any list we may have already built */
1258 if (strcasecmp(mohclass->mode, "playlist")) {
1259 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1260 mohclass->mode);
1261 ao2_ref(playlist_entries, -1);
1262 return;
1263 }
1264
1265 AST_VECTOR_COMPACT(playlist_entries);
1266
1267 /* We don't need to lock here because we are the thread that
1268 * created this mohclass and we haven't published it yet */
1269 ao2_ref(mohclass->files, -1);
1270 mohclass->files = playlist_entries;
1271 }
1272}
1273
1274static int on_moh_file(const char *directory, const char *filename, void *obj)
1275{
1276 struct ast_vector_string *files = obj;
1277 char *full_path;
1278 char *extension;
1279
1280 /* Skip files that starts with a dot */
1281 if (*filename == '.') {
1282 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1283 directory, filename);
1284 return 0;
1285 }
1286
1287 /* We can't do anything with files that don't have an extension,
1288 * so check that first and punt if we can't find something */
1289 extension = strrchr(filename, '.');
1290 if (!extension) {
1291 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1292 directory, filename);
1293 return 0;
1294 }
1295
1296 /* The extension needs at least two characters (after the .) to be useful */
1297 if (strlen(extension) < 3) {
1298 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1299 "character extension\n", directory, filename);
1300 return 0;
1301 }
1302
1303 /* Build the full path (excluding the extension) */
1304 if (ast_asprintf(&full_path, "%s/%.*s",
1305 directory,
1306 (int) (extension - filename), filename) < 0) {
1307 /* If we don't have enough memory to build this path, there is no
1308 * point in continuing */
1309 return 1;
1310 }
1311
1312 /* If the file is present in multiple formats, ensure we only put it
1313 * into the list once. Pretty sure this is O(n^2). */
1314 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1315 ast_free(full_path);
1316 return 0;
1317 }
1318
1319 if (AST_VECTOR_APPEND(files, full_path)) {
1320 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1321 * we stop iterating */
1322 ast_free(full_path);
1323 return 1;
1324 }
1325
1326 return 0;
1327}
1328
1329static int moh_filename_strcasecmp(const void *a, const void *b)
1330{
1331 const char **s1 = (const char **) a;
1332 const char **s2 = (const char **) b;
1333 return strcasecmp(*s1, *s2);
1334}
1335
1336static int moh_scan_files(struct mohclass *class) {
1337
1338 char dir_path[PATH_MAX - sizeof(class->dir)];
1339 struct ast_vector_string *files;
1340
1341 if (class->dir[0] != '/') {
1342 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1343 } else {
1344 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1345 }
1346
1347 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1348
1349 /* 16 seems like a reasonable default */
1350 files = moh_file_vector_alloc(16);
1351 if (!files) {
1352 return -1;
1353 }
1354
1355 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1356 ao2_ref(files, -1);
1357 return -1;
1358 }
1359
1360 if (ast_test_flag(class, MOH_SORTALPHA)) {
1362 }
1363
1364 AST_VECTOR_COMPACT(files);
1365
1366 ao2_lock(class);
1367 ao2_ref(class->files, -1);
1368 class->files = files;
1369 ao2_unlock(class);
1370
1371 return AST_VECTOR_SIZE(files);
1372}
1373
1374static int init_files_class(struct mohclass *class)
1375{
1376 int res;
1377
1378 res = moh_scan_files(class);
1379
1380 if (res < 0) {
1381 return -1;
1382 }
1383
1384 if (!res) {
1385 ast_verb(3, "Files not found in %s for moh class:%s\n",
1386 class->dir, class->name);
1387 return -1;
1388 }
1389
1390 return 0;
1391}
1392
1393static void moh_rescan_files(void) {
1394 struct ao2_iterator i;
1395 struct mohclass *c;
1396
1398
1399 while ((c = ao2_iterator_next(&i))) {
1400 if (!strcasecmp(c->mode, "files")) {
1402 }
1403 ao2_ref(c, -1);
1404 }
1405
1407}
1408
1409static int moh_diff(struct mohclass *old, struct mohclass *new)
1410{
1411 if (!old || !new) {
1412 return -1;
1413 }
1414
1415 if (strcmp(old->dir, new->dir)) {
1416 return -1;
1417 } else if (strcmp(old->mode, new->mode)) {
1418 return -1;
1419 } else if (strcmp(old->args, new->args)) {
1420 return -1;
1421 } else if (old->flags != new->flags) {
1422 return -1;
1423 }
1424
1425 return 0;
1426}
1427
1428static int init_app_class(struct mohclass *class)
1429{
1430 if (!strcasecmp(class->mode, "custom")) {
1431 ast_set_flag(class, MOH_CUSTOM);
1432 } else if (!strcasecmp(class->mode, "mp3nb")) {
1433 ast_set_flag(class, MOH_SINGLE);
1434 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1436 } else if (!strcasecmp(class->mode, "quietmp3")) {
1437 ast_set_flag(class, MOH_QUIET);
1438 }
1439
1440 class->srcfd = -1;
1441
1442 if (!(class->timer = ast_timer_open())) {
1443 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1444 return -1;
1445 }
1446 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1447 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1448 ast_timer_close(class->timer);
1449 class->timer = NULL;
1450 }
1451
1452 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1453 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1454 if (class->timer) {
1455 ast_timer_close(class->timer);
1456 class->timer = NULL;
1457 }
1458 return -1;
1459 }
1460
1461 return 0;
1462}
1463
1464/*!
1465 * \note This function owns the reference it gets to moh if unref is true
1466 */
1467#define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1468static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1469{
1470 struct mohclass *mohclass = NULL;
1471
1472 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1473
1474 if (mohclass && !moh_diff(mohclass, moh)) {
1475 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1476 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1477 if (unref) {
1478 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1479 }
1480 return -1;
1481 } else if (mohclass) {
1482 /* Found a class, but it's different from the one being registered */
1483 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1484 }
1485
1486 time(&moh->start);
1487 moh->start -= respawn_time;
1488
1489 if (!strcasecmp(moh->mode, "files")) {
1490 if (init_files_class(moh)) {
1491 if (unref) {
1492 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1493 }
1494 return -1;
1495 }
1496 } else if (!strcasecmp(moh->mode, "playlist")) {
1497 size_t file_count;
1498
1499 ao2_lock(moh);
1500 file_count = AST_VECTOR_SIZE(moh->files);
1501 ao2_unlock(moh);
1502
1503 if (!file_count) {
1504 if (unref) {
1505 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1506 }
1507 return -1;
1508 }
1509 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1510 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1511 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1512 if (init_app_class(moh)) {
1513 if (unref) {
1514 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1515 }
1516 return -1;
1517 }
1518 } else {
1519 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1520 if (unref) {
1521 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1522 }
1523 return -1;
1524 }
1525
1526 ao2_t_link(mohclasses, moh, "Adding class to container");
1527
1528 if (unref) {
1529 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1530 }
1531
1532 return 0;
1533}
1534
1535#define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1536static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
1537{
1538 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1539 return 0;
1540}
1541
1542/*
1543 * Called with `chan` UNLOCKED.
1544 */
1545static void local_ast_moh_cleanup(struct ast_channel *chan)
1546{
1547 struct moh_files_state *state;
1548
1549 ast_channel_lock(chan);
1551 if (state) {
1553 if (state->class) {
1554 /* This should never happen. We likely just leaked some resource. */
1555 state->class =
1556 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1557 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1558 }
1559 ao2_cleanup(state->origwfmt);
1560 ao2_cleanup(state->mohwfmt);
1561 ast_free(state);
1562 /* Only held a module reference if we had a music state */
1564 }
1565 ast_channel_unlock(chan);
1566}
1567
1568/*! \brief Support routing for 'moh unregister class' CLI
1569 * This is in charge of generating all strings that match a prefix in the
1570 * given position. As many functions of this kind, each invokation has
1571 * O(state) time complexity so be careful in using it.
1572 */
1573static char *complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
1574{
1575 int which=0;
1576 struct mohclass *cur;
1577 char *c = NULL;
1578 int wordlen = strlen(word);
1579 struct ao2_iterator i;
1580
1581 if (pos != 3) {
1582 return NULL;
1583 }
1584
1586 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1587 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1588 c = ast_strdup(cur->name);
1589 mohclass_unref(cur, "drop ref in iterator loop break");
1590 break;
1591 }
1592 mohclass_unref(cur, "drop ref in iterator loop");
1593 }
1595
1596 return c;
1597}
1598
1599static char *handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1600{
1601 struct mohclass *cur;
1602 int len;
1603 int found = 0;
1604 struct ao2_iterator i;
1605
1606 switch (cmd) {
1607 case CLI_INIT:
1608 e->command = "moh unregister class";
1609 e->usage =
1610 "Usage: moh unregister class <class>\n"
1611 " Unregisters a realtime moh class.\n";
1612 return NULL;
1613 case CLI_GENERATE:
1614 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1615 }
1616
1617 if (a->argc != 4)
1618 return CLI_SHOWUSAGE;
1619
1620 len = strlen(a->argv[3]);
1621
1623 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1624 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1625 found = 1;
1626 break;
1627 }
1628 mohclass_unref(cur, "drop ref in iterator loop");
1629 }
1631
1632 if (found) {
1633 moh_unregister(cur);
1634 mohclass_unref(cur, "drop ref after unregister");
1635 } else {
1636 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1637 }
1638
1639 return CLI_SUCCESS;
1640}
1641
1642
1643
1644static void moh_class_destructor(void *obj);
1645
1646#define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1647
1648static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1649{
1650 struct mohclass *class;
1651
1653 "Allocating new moh class", file, line, funcname);
1654 if (class) {
1655 class->format = ao2_bump(ast_format_slin);
1656 class->srcfd = -1;
1657 class->kill_delay = 100000;
1658
1659 /* We create an empty one by default */
1660 class->files = moh_file_vector_alloc(0);
1661 if (!class->files) {
1662 ao2_ref(class, -1);
1663 return NULL;
1664 }
1665 }
1666
1667 return class;
1668}
1669
1671{
1672 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1673
1674 if (var) {
1675 const char *mode = ast_variable_find_in_list(var, "mode");
1676 if (ast_strings_equal(mode, "playlist")) {
1677 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1678 char *category = NULL;
1679 size_t entry_count = 0;
1680
1681 /* entries is NULL if there are no results */
1682 if (entries) {
1683 while ((category = ast_category_browse(entries, category))) {
1684 const char *entry = ast_variable_retrieve(entries, category, "entry");
1685
1686 if (entry) {
1687 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1688 if (dup) {
1689 entry_count++;
1691 }
1692 }
1693 }
1694 ast_config_destroy(entries);
1695 }
1696
1697 if (entry_count == 0) {
1698 /* Behave as though this class doesn't exist */
1700 var = NULL;
1701 }
1702 }
1703 }
1704
1705 if (!var) {
1707 "Music on Hold class '%s' not found in memory/database. "
1708 "Verify your configuration.\n",
1709 name);
1710 }
1711 return var;
1712}
1713
1714/*
1715 * Called with `chan` UNLOCKED.
1716 */
1717static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1718{
1719 struct mohclass *mohclass = NULL;
1720 struct moh_files_state *state;
1721 struct ast_variable *var = NULL;
1722 int res = 0;
1723 int i;
1724 int realtime_possible = ast_check_realtime("musiconhold");
1725 int warn_if_not_in_memory = !realtime_possible;
1726 const char *classes[] = {NULL, NULL, interpclass, "default"};
1727
1729 classes[0] = ast_channel_musicclass(chan);
1730 classes[1] = mclass;
1731 } else {
1732 classes[0] = mclass;
1733 classes[1] = ast_channel_musicclass(chan);
1734 }
1735
1736 /* The following is the order of preference for which class to use:
1737 * 1) The channels explicitly set musicclass, which should *only* be
1738 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1739 * Unless preferchannelclass in musiconhold.conf is false
1740 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1741 * result of receiving a HOLD control frame, this should be the
1742 * payload that came with the frame.
1743 * 3) The channels explicitly set musicclass, which should *only* be
1744 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1745 * 4) The interpclass argument. This would be from the mohinterpret
1746 * option from channel drivers. This is the same as the old musicclass
1747 * option.
1748 * 5) The default class.
1749 */
1750
1751 for (i = 0; i < ARRAY_LEN(classes); ++i) {
1752 if (!ast_strlen_zero(classes[i])) {
1753 mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1754 if (!mohclass && realtime_possible) {
1755 var = load_realtime_musiconhold(classes[i]);
1756 }
1757 if (mohclass || var) {
1758 break;
1759 }
1760 }
1761 }
1762
1763 /* If no moh class found in memory, then check RT. Note that the logic used
1764 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1765 */
1766 if (var) {
1767 if ((mohclass = moh_class_malloc())) {
1768 mohclass->realtime = 1;
1769
1772
1774 if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1775 strcpy(mohclass->dir, "nodir");
1776 } else {
1777 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1778 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1779 return -1;
1780 }
1781 }
1783 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1784 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1785 return -1;
1786 }
1787 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1788 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1789 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1790 return -1;
1791 }
1792
1794 /* CACHERTCLASSES enabled, let's add this class to default tree */
1795 ast_channel_lock(chan);
1797 if (state && state->class) {
1798 /* Class already exist for this channel */
1799 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1800 }
1801 ast_channel_unlock(chan);
1802 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1803 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1804 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1805 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1806 * invalid memory.
1807 */
1808 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1809 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1810 return -1;
1811 }
1812 } else {
1813 /* We don't register RT moh class, so let's init it manually */
1814
1815 time(&mohclass->start);
1817
1818 if (!strcasecmp(mohclass->mode, "files")) {
1819 /*
1820 * XXX moh_scan_files returns -1 if it is unable to open the
1821 * configured directory or there is a memory allocation
1822 * failure. Otherwise it returns the number of files for this music
1823 * class. This check is only checking if the number of files is zero
1824 * and it ignores the -1 case. To avoid a behavior change we keep this
1825 * as-is, but we should address what the 'correct' behavior should be.
1826 */
1827 if (!moh_scan_files(mohclass)) {
1828 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1829 return -1;
1830 }
1831 if (strchr(mohclass->args, 'r')) {
1832 static int deprecation_warning = 0;
1833 if (!deprecation_warning) {
1834 ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1835 deprecation_warning = 1;
1836 }
1838 }
1839 } else if (!strcasecmp(mohclass->mode, "playlist")) {
1840 size_t file_count;
1841
1843 file_count = AST_VECTOR_SIZE(mohclass->files);
1845
1846 if (!file_count) {
1847 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1848 return -1;
1849 }
1850 } 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")) {
1851
1852 if (!strcasecmp(mohclass->mode, "custom"))
1854 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1856 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1858 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1860
1861 mohclass->srcfd = -1;
1862 if (!(mohclass->timer = ast_timer_open())) {
1863 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1864 }
1866 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1868 mohclass->timer = NULL;
1869 }
1870
1871 /* Let's check if this channel already had a moh class before */
1872 ast_channel_lock(chan);
1874 if (state && state->class) {
1875 /* Class already exist for this channel */
1876 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1877 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1878 /* we found RT class with the same name, seems like we should continue playing existing one */
1879 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1880 mohclass = mohclass_ref(state->class, "using existing class from state");
1881 }
1882 ast_channel_unlock(chan);
1883 } else {
1884 ast_channel_unlock(chan);
1886 ast_log(LOG_WARNING, "Unable to create moh...\n");
1887 if (mohclass->timer) {
1889 mohclass->timer = NULL;
1890 }
1891 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1892 return -1;
1893 }
1894 }
1895 } else {
1896 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1897 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1898 return -1;
1899 }
1900 }
1901 } else {
1903 var = NULL;
1904 }
1905 }
1906
1907 if (!mohclass) {
1908 return -1;
1909 }
1910
1912 ast_verb(3, "The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1913 return -1;
1914 }
1915
1916 /* If we are using a cached realtime class with files, re-scan the files */
1917 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1918 /*
1919 * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1920 * or there is a memory allocation failure. Otherwise it returns the number of
1921 * files for this music class. This check is only checking if the number of files
1922 * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1923 * as-is, but we should address what the 'correct' behavior should be.
1924 */
1925 if (!moh_scan_files(mohclass)) {
1926 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1927 return -1;
1928 }
1929 }
1930
1931 ast_channel_lock(chan);
1933 if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1934 size_t file_count;
1935
1937 file_count = AST_VECTOR_SIZE(mohclass->files);
1939
1940 if (file_count) {
1942 } else {
1943 res = ast_activate_generator(chan, &mohgen, mohclass);
1944 }
1945 }
1946 ast_channel_unlock(chan);
1947 if (!res) {
1948 ast_channel_lock(chan);
1949 ast_channel_latest_musicclass_set(chan, mohclass->name);
1951 ast_channel_unlock(chan);
1952 }
1953
1954 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1955
1956 return res;
1957}
1958
1959/*
1960 * Called with `chan` UNLOCKED.
1961 */
1962static void local_ast_moh_stop(struct ast_channel *chan)
1963{
1965
1966 ast_channel_lock(chan);
1968 if (ast_channel_music_state(chan)) {
1969 if (ast_channel_stream(chan)) {
1972 }
1973 }
1974 ast_channel_unlock(chan);
1975}
1976
1977static void moh_class_destructor(void *obj)
1978{
1979 struct mohclass *class = obj;
1980 struct mohdata *member;
1981 pthread_t tid = 0;
1982
1983 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1984
1985 ao2_lock(class);
1986 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1988 }
1989 ao2_cleanup(class->files);
1990 ao2_unlock(class);
1991
1992 /* Kill the thread first, so it cannot restart the child process while the
1993 * class is being destroyed */
1994 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1995 tid = class->thread;
1996 class->thread = AST_PTHREADT_NULL;
1997 pthread_cancel(tid);
1998 /* We'll collect the exit status later, after we ensure all the readers
1999 * are dead. */
2000 }
2001
2002 if (class->pid > 1) {
2003 char buff[8192];
2004 int bytes, tbytes = 0, stime = 0;
2005
2006 ast_debug(1, "killing %d!\n", class->pid);
2007
2008 stime = time(NULL) + 2;
2009 killpid(class->pid, class->kill_delay, class->kill_method);
2010
2011 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
2012 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
2013 tbytes = tbytes + bytes;
2014 }
2015
2016 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
2017 class->pid, tbytes);
2018
2019 class->pid = 0;
2020 close(class->srcfd);
2021 class->srcfd = -1;
2022 }
2023
2024 if (class->timer) {
2025 ast_timer_close(class->timer);
2026 class->timer = NULL;
2027 }
2028
2029 ao2_cleanup(class->format);
2030
2031 /* Finally, collect the exit status of the monitor thread */
2032 if (tid > 0) {
2033 pthread_join(tid, NULL);
2034 }
2035
2036}
2037
2038static int moh_class_mark(void *obj, void *arg, int flags)
2039{
2040 struct mohclass *class = obj;
2041
2042 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
2043 class->delete = 1;
2044 }
2045
2046 return 0;
2047}
2048
2049static int moh_classes_delete_marked(void *obj, void *arg, int flags)
2050{
2051 struct mohclass *class = obj;
2052
2053 return class->delete ? CMP_MATCH : 0;
2054}
2055
2057{
2058 struct ast_config *cfg;
2059 struct ast_variable *var;
2060 struct mohclass *class;
2061 char *cat;
2062 int numclasses = 0;
2063 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2064
2065 cfg = ast_config_load("musiconhold.conf", config_flags);
2066
2067 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2068 if (ast_check_realtime("musiconhold") && reload) {
2069 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2071 }
2073 return 0;
2074 }
2075
2077 if (ast_check_realtime("musiconhold") && reload) {
2078 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2080 }
2081 return 0;
2082 }
2083
2084 if (reload) {
2085 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2086 }
2087
2090
2091 cat = ast_category_browse(cfg, NULL);
2092 for (; cat; cat = ast_category_browse(cfg, cat)) {
2093 /* Setup common options from [general] section */
2094 if (!strcasecmp(cat, "general")) {
2095 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2096 if (!strcasecmp(var->name, "cachertclasses")) {
2098 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2100 } else {
2101 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2102 }
2103 }
2104 continue;
2105 }
2106
2107 if (!(class = moh_class_malloc())) {
2108 break;
2109 }
2110
2111 moh_parse_options(ast_variable_browse(cfg, cat), class);
2112 /* For compatibility with the past, we overwrite any name=name
2113 * with the context [name]. */
2114 ast_copy_string(class->name, cat, sizeof(class->name));
2115
2116 if (ast_strlen_zero(class->dir)) {
2117 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2118 strcpy(class->dir, "nodir");
2119 } else {
2120 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2121 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2122 continue;
2123 }
2124 }
2125 if (ast_strlen_zero(class->mode)) {
2126 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2127 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2128 continue;
2129 }
2130 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2131 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2132 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2133 continue;
2134 }
2135
2136 /* Don't leak a class when it's already registered */
2137 if (!moh_register(class, reload, HANDLE_REF)) {
2138 numclasses++;
2139 }
2140 }
2141
2142 ast_config_destroy(cfg);
2143
2145 moh_classes_delete_marked, NULL, "Purge marked classes");
2146
2147 return numclasses;
2148}
2149
2150static void ast_moh_destroy(void)
2151{
2152 ast_verb(2, "Destroying musiconhold processes\n");
2153 if (mohclasses) {
2155 ao2_ref(mohclasses, -1);
2156 mohclasses = NULL;
2157 }
2158}
2159
2160static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2161{
2162 switch (cmd) {
2163 case CLI_INIT:
2164 e->command = "moh reload";
2165 e->usage =
2166 "Usage: moh reload\n"
2167 " Reloads the MusicOnHold module.\n"
2168 " Alias for 'module reload res_musiconhold.so'\n";
2169 return NULL;
2170 case CLI_GENERATE:
2171 return NULL;
2172 }
2173
2174 if (a->argc != e->args)
2175 return CLI_SHOWUSAGE;
2176
2177 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2178 ast_module_reload("res_musiconhold");
2179
2180 return CLI_SUCCESS;
2181}
2182
2183static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2184{
2185 struct mohclass *class;
2186 struct ao2_iterator i;
2187
2188 switch (cmd) {
2189 case CLI_INIT:
2190 e->command = "moh show files";
2191 e->usage =
2192 "Usage: moh show files\n"
2193 " Lists all loaded file-based MusicOnHold classes and their\n"
2194 " files.\n";
2195 return NULL;
2196 case CLI_GENERATE:
2197 return NULL;
2198 }
2199
2200 if (a->argc != e->args)
2201 return CLI_SHOWUSAGE;
2202
2204 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2205 struct ast_vector_string *files;
2206
2207 ao2_lock(class);
2208 files = ao2_bump(class->files);
2209 ao2_unlock(class);
2210
2211 if (AST_VECTOR_SIZE(files)) {
2212 int x;
2213 ast_cli(a->fd, "Class: %s\n", class->name);
2214 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2215 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2216 }
2217 }
2218
2219 ao2_ref(files, -1);
2220 }
2222
2223 return CLI_SUCCESS;
2224}
2225
2226static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2227{
2228 struct mohclass *class;
2229 struct ao2_iterator i;
2230
2231 switch (cmd) {
2232 case CLI_INIT:
2233 e->command = "moh show classes";
2234 e->usage =
2235 "Usage: moh show classes\n"
2236 " Lists all MusicOnHold classes.\n";
2237 return NULL;
2238 case CLI_GENERATE:
2239 return NULL;
2240 }
2241
2242 if (a->argc != e->args)
2243 return CLI_SHOWUSAGE;
2244
2246 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2247 ast_cli(a->fd, "Class: %s\n", class->name);
2248 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2249 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2250 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2251 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2252 }
2253 if (ast_test_flag(class, MOH_CUSTOM)) {
2254 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2255 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2256 ast_cli(a->fd, "\tKill Method: %s\n",
2257 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2258 }
2259 if (strcasecmp(class->mode, "files")) {
2260 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2261 }
2262 }
2264
2265 return CLI_SUCCESS;
2266}
2267
2268static struct ast_cli_entry cli_moh[] = {
2269 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2270 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2271 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2272 AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2273};
2274
2275static int moh_class_hash(const void *obj, const int flags)
2276{
2277 const struct mohclass *class = obj;
2278
2279 return ast_str_case_hash(class->name);
2280}
2281
2282static int moh_class_cmp(void *obj, void *arg, int flags)
2283{
2284 struct mohclass *class = obj, *class2 = arg;
2285
2286 return strcasecmp(class->name, class2->name) ? 0 :
2287 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2289}
2290
2291/*!
2292 * \brief Load the module
2293 *
2294 * Module loading including tests for configuration or dependencies.
2295 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2296 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2297 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2298 * configuration file or other non-critical problem return
2299 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2300 */
2301static int load_module(void)
2302{
2303 int res;
2304
2306 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2307 if (!mohclasses) {
2309 }
2310
2311 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2312 ast_log(LOG_WARNING, "No music on hold classes configured, "
2313 "disabling music on hold.\n");
2314 } else {
2317 }
2318
2322 if (!res)
2324 if (!res)
2326
2328}
2329
2330static int reload(void)
2331{
2332 if (load_moh_classes(1)) {
2335 }
2336
2338}
2339
2340static int moh_class_inuse(void *obj, void *arg, int flags)
2341{
2342 struct mohclass *class = obj;
2343
2344 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2345}
2346
2347static int unload_module(void)
2348{
2349 int res = 0;
2350 struct mohclass *class = NULL;
2351
2352 /* XXX This check shouldn't be required if module ref counting was being used
2353 * properly ... */
2354 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2355 class = mohclass_unref(class, "unref of class from module unload callback");
2356 res = -1;
2357 }
2358
2359 if (res < 0) {
2360 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2361 return res;
2362 }
2363
2365
2372
2373 return res;
2374}
2375
2377 .support_level = AST_MODULE_SUPPORT_CORE,
2378 .load = load_module,
2379 .unload = unload_module,
2380 .reload = reload,
2381 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
static const char app[]
Definition: app_adsiprog.c:56
char digit
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1072
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
int ast_set_priority(int)
We set ourselves to a high priority, that we might pre-empt everything else. If your PBX has heavy ac...
Definition: asterisk.c:1853
#define PATH_MAX
Definition: asterisk.h:40
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_t_iterator_next(iter, tag)
Definition: astobj2.h:1909
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_t_link(container, obj, tag)
Definition: astobj2.h:1534
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
void * __ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition: astobj2.h:501
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
#define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag)
Definition: astobj2.h:1306
#define ao2_t_unlink(container, obj, tag)
Definition: astobj2.h:1580
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int __ao2_ref(void *o, int delta, const char *tag, const char *file, int line, const char *func)
Definition: astobj2.c:498
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
void * __ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result
Definition: astobj2.c:768
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
Definition: astobj2.h:1696
enum cc_state state
Definition: ccss.c:399
static unsigned char * buff
Definition: chan_unistim.c:259
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2919
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
void * ast_channel_music_state(const struct ast_channel *chan)
const char * ast_channel_musicclass(const struct ast_channel *chan)
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
#define ast_channel_lock(chan)
Definition: channel.h:2972
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
@ AST_FLAG_MOH
Definition: channel.h:1011
const char * ast_channel_uniqueid(const struct ast_channel *chan)
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2861
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:5099
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5758
const char * ast_channel_language(const struct ast_channel *chan)
void ast_channel_music_state_set(struct ast_channel *chan, void *value)
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1541
#define ast_channel_unlock(chan)
Definition: channel.h:2973
#define MAX_MUSICCLASS
Definition: channel.h:175
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:379
short word
#define SENTINEL
Definition: compiler.h:87
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1093
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition: file.h:203
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition: file.c:850
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:944
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1083
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1119
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1137
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition: format.h:38
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples)
Get the length (in milliseconds) for the format with a given number of samples.
Definition: format.c:384
#define ast_format_cache_get(name)
Retrieve a named format from the cache.
Definition: format_cache.h:278
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child.
Definition: main/app.c:3207
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: main/app.c:3202
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define CONFIG_STATUS_FILEMISSING
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3842
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:1013
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3750
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:869
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3726
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
@ CONFIG_FLAG_FILEUNCHANGED
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define ast_frfree(fr)
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
@ AST_FRAME_VOICE
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
Asterisk locking-related definitions:
#define AST_PTHREADT_NULL
Definition: lock.h:73
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:457
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1730
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Music on hold handling.
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7735
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7745
void ast_install_music_functions(int(*start_ptr)(struct ast_channel *, const char *, const char *), void(*stop_ptr)(struct ast_channel *), void(*cleanup_ptr)(struct ast_channel *))
Definition: channel.c:7719
void ast_uninstall_music_functions(void)
Definition: channel.c:7728
#define ast_opt_high_priority
Definition: options.h:113
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:159
Core PBX routines and definitions.
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
static struct ast_frame * moh_files_readframe(struct ast_channel *chan)
#define MPG_123
static char * complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match...
static int moh_filename_strcasecmp(const void *a, const void *b)
static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
static void moh_release(struct ast_channel *chan, void *data)
static void moh_handle_digit(struct ast_channel *chan, char digit)
#define MOH_LOOPLAST
static struct ast_generator mohgen
static void moh_file_vector_destructor(void *obj)
#define MOH_SORTMODE
static struct ast_cli_entry cli_moh[]
static struct ast_variable * load_realtime_musiconhold(const char *name)
static void * monmp3thread(void *data)
static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
#define MOH_CUSTOM
static void ast_moh_destroy(void)
#define MOH_QUIET
#define HANDLE_REF
static int play_moh_exec(struct ast_channel *chan, const char *data)
#define get_mohbyname(a, b, c)
static void local_ast_moh_stop(struct ast_channel *chan)
static int moh_class_mark(void *obj, void *arg, int flags)
static void moh_rescan_files(void)
#define MOH_RANDOMIZE
static struct ao2_container * mohclasses
static void moh_post_stop(struct ast_channel *chan)
static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
static int moh_files_next(struct ast_channel *chan)
#define MOH_MS_INTERVAL
#define moh_register(moh, reload, unref)
static int moh_scan_files(struct mohclass *class)
static void killpid(int pid, size_t delay, enum kill_methods kill_method)
static int load_moh_classes(int reload)
static char * handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define MOH_ANNOUNCEMENT
static int init_app_class(struct mohclass *class)
static int init_files_class(struct mohclass *class)
static int stop_moh_exec(struct ast_channel *chan, const char *data)
kill_methods
@ KILL_METHOD_PROCESS_GROUP
@ KILL_METHOD_PROCESS
#define moh_class_malloc()
static int moh_diff(struct mohclass *old, struct mohclass *new)
#define MOH_PREFERCHANNELCLASS
static void * moh_files_alloc(struct ast_channel *chan, void *params)
#define MOH_RANDSTART
static struct mohdata * mohalloc(struct mohclass *cl)
static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
static int start_moh_exec(struct ast_channel *chan, const char *data)
static char * handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define MOH_SORTALPHA
#define LOCAL_MPG_123
#define mohclass_ref(class, string)
static struct ast_generator moh_file_stream
#define MOH_CACHERTCLASSES
static void moh_files_release(struct ast_channel *chan, void *data)
#define MOH_REALTIME
static int moh_class_inuse(void *obj, void *arg, int flags)
static int moh_digit_match(void *obj, void *arg, int flags)
static int moh_class_cmp(void *obj, void *arg, int flags)
static int killer(pid_t pid, int signum, enum kill_methods kill_method)
#define MAX_MP3S
static void moh_files_write_format_change(struct ast_channel *chan, void *data)
static int on_moh_file(const char *directory, const char *filename, void *obj)
static int spawn_mp3(struct mohclass *class)
static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
static void moh_class_destructor(void *obj)
static char * handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
static int load_module(void)
Load the module.
static const char play_moh[]
#define MOH_SINGLE
#define moh_unregister(a)
static int unload_module(void)
static int reload(void)
static int moh_class_hash(const void *obj, const int flags)
static const char start_moh[]
#define MOH_NOTDELETED
static const char stop_moh[]
static void local_ast_moh_cleanup(struct ast_channel *chan)
static struct mohclass * _get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
static int respawn_time
#define mohclass_unref(class, string)
static struct mohclass * get_mohbydigit(char digit)
static struct mohclass * _moh_class_malloc(const char *file, int line, const char *funcname)
static struct ast_vector_string * moh_file_vector_alloc(int initial_capacity)
static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
static struct ast_flags global_flags[1]
#define DONT_UNREF
static char * handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int moh_classes_delete_marked(void *obj, void *arg, int flags)
static void * moh_alloc(struct ast_channel *chan, void *params)
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1538
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Blob of data associated with a channel.
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Structure used to handle boolean flags.
Definition: utils.h:199
Definition of a media format.
Definition: format.c:43
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@231 data
void *(* alloc)(struct ast_channel *chan, void *params)
Definition: channel.h:228
Abstract JSON element (object, array, string, int, ...).
struct ast_module * self
Definition: module.h:356
Structure for variables, used for configurations and for channel variables.
String vector definitions.
Definition: vector.h:55
structure to hold extensions
char name[MAX_MUSICCLASS]
struct ast_format * mohwfmt
struct mohclass * class
struct ast_format * origwfmt
char save_pos_filename[PATH_MAX]
char announcement[256]
pthread_t thread
char name[MAX_MUSICCLASS]
char args[256]
struct mohclass::@445 list
struct ast_vector_string * files
char dir[256]
unsigned int realtime
struct mohclass::@444 members
unsigned int delete
struct ast_timer * timer
enum kill_methods kill_method
struct ast_format * format
unsigned int flags
char mode[80]
time_t start
size_t kill_delay
struct mohclass * parent
int pipe[2]
struct ast_format * origwfmt
struct ast_frame f
struct mohdata::@446 list
char name[TZ_STRLEN_MAX+1]
Definition: localtime.c:160
const char * name
const char * args
static struct test_val b
static struct test_val a
static struct test_val c
Time-related functions and macros.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Timing source management.
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition: timing.c:171
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition: timing.c:166
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition: timing.c:161
Support for translation of data formats. translate.c.
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define ast_assert(a)
Definition: utils.h:745
#define MIN(a, b)
Definition: utils.h:231
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:598
int ast_wait_for_input(int fd, int ms)
Definition: utils.c:1698
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_clear_flag(p, flag)
Definition: utils.h:77
long int ast_random(void)
Definition: utils.c:2312
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition: utils.h:1096
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:672
#define AST_FLAGS_ALL
Definition: utils.h:196
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:636
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:620
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:407
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:185
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:742
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:267
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:649
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:691