Asterisk - The Open Source Telephony Project GIT-master-66c01d8
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
294static void moh_files_release(struct ast_channel *chan, void *data)
295{
296 struct moh_files_state *state;
297
298 if (!chan || !ast_channel_music_state(chan)) {
299 return;
300 }
301
303
304 if (ast_channel_stream(chan)) {
307 }
308
309 moh_post_stop(chan);
310
311 ao2_ref(state->mohwfmt, -1);
312 state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
313 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
314 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
315 ast_format_get_name(state->origwfmt));
316 }
317 ao2_cleanup(state->origwfmt);
318 state->origwfmt = NULL;
319
320 state->save_pos = state->pos;
321 state->announcement = 0;
322
323 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
324}
325
326static int ast_moh_files_next(struct ast_channel *chan)
327{
329 struct ast_vector_string *files;
330 int tries;
331 size_t file_count;
332
333 /* Discontinue a stream if it is running already */
334 if (ast_channel_stream(chan)) {
337 }
338
339 if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
340 state->announcement = 1;
341 if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
342 ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
343 return 0;
344 }
345 } else {
346 state->announcement = 0;
347 }
348
349 ao2_lock(state->class);
350 files = ao2_bump(state->class->files);
351 ao2_unlock(state->class);
352
353 file_count = AST_VECTOR_SIZE(files);
354 if (!file_count) {
355 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
356 ao2_ref(files, -1);
357 return -1;
358 }
359
360 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
361 /* First time so lets play the file. */
362 state->save_pos = -1;
363 } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
364 /* If a specific file has been saved confirm it still exists and that it is still valid */
365 state->pos = state->save_pos;
366 state->save_pos = -1;
367 } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
368 /* Get a random file and ensure we can open it */
369 for (tries = 0; tries < 20; tries++) {
370 state->pos = ast_random() % file_count;
371 if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
372 break;
373 }
374 }
375 state->save_pos = -1;
376 state->samples = 0;
377 } else {
378 /* This is easy, just increment our position and make sure we don't exceed the total file count */
379 state->pos++;
380 if (ast_test_flag(state->class, MOH_LOOPLAST)) {
381 state->pos = MIN(file_count - 1, state->pos);
382 } else {
383 state->pos %= file_count;
384 }
385 state->save_pos = -1;
386 state->samples = 0;
387 }
388
389 for (tries = 0; tries < file_count; ++tries) {
390 if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
391 break;
392 }
393
394 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
395 state->pos++;
396 state->pos %= file_count;
397 }
398
399 if (tries == file_count) {
400 ao2_ref(files, -1);
401 return -1;
402 }
403
404 /* Record the pointer to the filename for position resuming later */
405 ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
406
407 ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
408
409 if (state->samples) {
410 size_t loc;
411 /* seek *SHOULD* be good since it's from a known location */
412 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
413 /* if the seek failed then recover because if there is not a valid read,
414 * moh_files_generate will return -1 and MOH will stop */
416 if (state->samples > loc && loc) {
417 /* seek one sample from the end for one guaranteed valid read */
418 ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
419 }
420 }
421
422 ao2_ref(files, -1);
423 return 0;
424}
425
426static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
427{
428 struct ast_frame *f;
429
431 if (!f) {
432 /* Either there was no file stream setup or we reached EOF. */
433 if (!ast_moh_files_next(chan)) {
434 /*
435 * Either we resetup the previously saved file stream position
436 * or we started a new file stream.
437 */
439 if (!f) {
440 /*
441 * We can get here if we were very unlucky because the
442 * resetup file stream was saved at EOF when MOH was
443 * previously stopped.
444 */
445 if (!ast_moh_files_next(chan)) {
447 }
448 }
449 }
450 }
451
452 return f;
453}
454
455static void moh_files_write_format_change(struct ast_channel *chan, void *data)
456{
458
459 /* In order to prevent a recursive call to this function as a result
460 * of setting the moh write format back on the channel. Clear
461 * the moh write format before setting the write format on the channel.*/
462 if (state->origwfmt) {
463 struct ast_format *tmp;
464
466 ao2_replace(state->origwfmt, NULL);
467 if (state->mohwfmt) {
468 ast_set_write_format(chan, state->mohwfmt);
469 }
470 state->origwfmt = tmp;
471 }
472}
473
474static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
475{
476 struct moh_files_state *state;
477 struct ast_frame *f = NULL;
478 int res = 0, sample_queue = 0;
479
480 ast_channel_lock(chan);
482 state->sample_queue += samples;
483 /* save the sample queue value for un-locked access */
484 sample_queue = state->sample_queue;
485 ast_channel_unlock(chan);
486
487 while (sample_queue > 0) {
488 ast_channel_lock(chan);
489 f = moh_files_readframe(chan);
490 if (!f) {
491 ast_channel_unlock(chan);
492 return -1;
493 }
494
495 /* Only track our offset within the current file if we are not in the
496 * the middle of an announcement */
497 if (!state->announcement) {
498 state->samples += f->samples;
499 }
500
501 state->sample_queue -= f->samples;
503 ao2_replace(state->mohwfmt, f->subclass.format);
504 }
505
506 /* We need to be sure that we unlock
507 * the channel prior to calling
508 * ast_write, but after our references to state
509 * as it refers to chan->music_state. Update
510 * sample_queue for our loop
511 * Otherwise, the recursive locking that occurs
512 * can cause deadlocks when using indirect
513 * channels, like local channels
514 */
515 sample_queue = state->sample_queue;
516 ast_channel_unlock(chan);
517
518 res = ast_write(chan, f);
519 ast_frfree(f);
520 if (res < 0) {
521 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
522 return -1;
523 }
524 }
525 return res;
526}
527
528static void *moh_files_alloc(struct ast_channel *chan, void *params)
529{
530 struct moh_files_state *state;
531 struct mohclass *class = params;
532 size_t file_count;
533
535 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
538 } else {
539 if (!state) {
540 return NULL;
541 }
542 if (state->class) {
543 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
544 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
545 }
546 }
547
548 ao2_lock(class);
549 file_count = AST_VECTOR_SIZE(class->files);
550 ao2_unlock(class);
551
552 /* Resume MOH from where we left off last time or start from scratch? */
553 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
554 /* Start MOH from scratch. */
555 ao2_cleanup(state->origwfmt);
556 ao2_cleanup(state->mohwfmt);
557 memset(state, 0, sizeof(*state));
558 if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
559 state->pos = ast_random() % file_count;
560 }
561 }
562
563 state->class = mohclass_ref(class, "Reffing music class for channel");
564 /* it's possible state is not a new allocation, don't leak old refs */
565 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
567 /* For comparison on restart of MOH (see above) */
568 ast_copy_string(state->name, class->name, sizeof(state->name));
569 state->save_total = file_count;
570
571 moh_post_start(chan, class->name);
572
573 return state;
574}
575
576static int moh_digit_match(void *obj, void *arg, int flags)
577{
578 char *digit = arg;
579 struct mohclass *class = obj;
580
581 return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
582}
583
584/*! \note This function should be called with the mohclasses list locked */
585static struct mohclass *get_mohbydigit(char digit)
586{
587 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
588}
589
590static void moh_handle_digit(struct ast_channel *chan, char digit)
591{
592 struct mohclass *class;
593 const char *classname = NULL;
594
595 if ((class = get_mohbydigit(digit))) {
596 classname = ast_strdupa(class->name);
597 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
598 ast_channel_musicclass_set(chan, classname);
599 ast_moh_stop(chan);
600 ast_moh_start(chan, classname, NULL);
601 }
602}
603
606 .release = moh_files_release,
607 .generate = moh_files_generator,
608 .digit = moh_handle_digit,
609 .write_format_change = moh_files_write_format_change,
610};
611
612static int spawn_mp3(struct mohclass *class)
613{
614 int fds[2];
615 int files = 0;
616 char fns[MAX_MP3S][80];
617 char *argv[MAX_MP3S + 50];
618 char xargs[256];
619 char *argptr;
620 int argc = 0;
621 DIR *dir = NULL;
622 struct dirent *de;
623
624
625 if (!strcasecmp(class->dir, "nodir")) {
626 files = 1;
627 } else {
628 dir = opendir(class->dir);
629 if (!dir && strncasecmp(class->dir, "http://", 7)) {
630 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
631 return -1;
632 }
633 }
634
635 if (!ast_test_flag(class, MOH_CUSTOM)) {
636 argv[argc++] = "mpg123";
637 argv[argc++] = "-q";
638 argv[argc++] = "-s";
639 argv[argc++] = "--mono";
640 argv[argc++] = "-r";
641 argv[argc++] = "8000";
642
643 if (!ast_test_flag(class, MOH_SINGLE)) {
644 argv[argc++] = "-b";
645 argv[argc++] = "2048";
646 }
647
648 argv[argc++] = "-f";
649
650 if (ast_test_flag(class, MOH_QUIET))
651 argv[argc++] = "4096";
652 else
653 argv[argc++] = "8192";
654
655 /* Look for extra arguments and add them to the list */
656 ast_copy_string(xargs, class->args, sizeof(xargs));
657 argptr = xargs;
658 while (!ast_strlen_zero(argptr)) {
659 argv[argc++] = argptr;
660 strsep(&argptr, ",");
661 }
662 } else {
663 /* Format arguments for argv vector */
664 ast_copy_string(xargs, class->args, sizeof(xargs));
665 argptr = xargs;
666 while (!ast_strlen_zero(argptr)) {
667 argv[argc++] = argptr;
668 strsep(&argptr, " ");
669 }
670 }
671
672 if (!strncasecmp(class->dir, "http://", 7)) {
673 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
674 argv[argc++] = fns[files];
675 files++;
676 } else if (dir) {
677 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
678 if ((strlen(de->d_name) > 3) &&
679 ((ast_test_flag(class, MOH_CUSTOM) &&
680 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
681 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
682 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
683 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
684 argv[argc++] = fns[files];
685 files++;
686 }
687 }
688 }
689 argv[argc] = NULL;
690 if (dir) {
691 closedir(dir);
692 }
693 if (pipe(fds)) {
694 ast_log(LOG_WARNING, "Pipe failed\n");
695 return -1;
696 }
697 if (!files) {
698 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
699 close(fds[0]);
700 close(fds[1]);
701 return -1;
702 }
703 if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
704 sleep(respawn_time - (time(NULL) - class->start));
705 }
706
707 time(&class->start);
708 class->pid = ast_safe_fork(0);
709 if (class->pid < 0) {
710 close(fds[0]);
711 close(fds[1]);
712 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
713 return -1;
714 }
715 if (!class->pid) {
718
719 close(fds[0]);
720 /* Stdout goes to pipe */
721 dup2(fds[1], STDOUT_FILENO);
722
723 /* Close everything else */
724 ast_close_fds_above_n(STDERR_FILENO);
725
726 /* Child */
727 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
728 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
729 _exit(1);
730 }
731 setpgid(0, getpid());
732 if (ast_test_flag(class, MOH_CUSTOM)) {
733 execv(argv[0], argv);
734 } else {
735 /* Default install is /usr/local/bin */
736 execv(LOCAL_MPG_123, argv);
737 /* Many places have it in /usr/bin */
738 execv(MPG_123, argv);
739 /* Check PATH as a last-ditch effort */
740 execvp("mpg123", argv);
741 }
742 /* Can't use logger, since log FDs are closed */
743 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
744 close(fds[1]);
745 _exit(1);
746 } else {
747 /* Parent */
748 close(fds[1]);
749 }
750 return fds[0];
751}
752
753static int killer(pid_t pid, int signum, enum kill_methods kill_method)
754{
755 switch (kill_method) {
757 return killpg(pid, signum);
759 return kill(pid, signum);
760 }
761
762 return -1;
763}
764
765static void killpid(int pid, size_t delay, enum kill_methods kill_method)
766{
767 if (killer(pid, SIGHUP, kill_method) < 0) {
768 if (errno == ESRCH) {
769 return;
770 }
771 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
772 } else {
773 ast_debug(1, "Sent HUP to pid %d%s\n", pid,
774 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
775 }
776 usleep(delay);
777 if (killer(pid, SIGTERM, kill_method) < 0) {
778 if (errno == ESRCH) {
779 return;
780 }
781 ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
782 } else {
783 ast_debug(1, "Sent TERM to pid %d%s\n", pid,
784 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
785 }
786 usleep(delay);
787 if (killer(pid, SIGKILL, kill_method) < 0) {
788 if (errno == ESRCH) {
789 return;
790 }
791 ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
792 } else {
793 ast_debug(1, "Sent KILL to pid %d%s\n", pid,
794 kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
795 }
796}
797
798static void *monmp3thread(void *data)
799{
800#define MOH_MS_INTERVAL 100
801
802 struct mohclass *class = data;
803 struct mohdata *moh;
804 short sbuf[8192];
805 int res = 0, res2;
806 int len;
807 struct timeval deadline, tv_tmp;
808
809 deadline.tv_sec = 0;
810 deadline.tv_usec = 0;
811 for(;/* ever */;) {
812 pthread_testcancel();
813 /* Spawn mp3 player if it's not there */
814 if (class->srcfd < 0) {
815 if ((class->srcfd = spawn_mp3(class)) < 0) {
816 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
817 /* Try again later */
818 sleep(500);
819 continue;
820 }
821 }
822 if (class->timer) {
823 struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
824
825#ifdef SOLARIS
826 thr_yield();
827#endif
828 /* Pause some amount of time */
829 if (ast_poll(&pfd, 1, -1) > 0) {
830 if (ast_timer_ack(class->timer, 1) < 0) {
831 ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
832 return NULL;
833 }
834 /* 25 samples per second => 40ms framerate => 320 samples */
835 res = 320; /* 320/40 = 8 samples/ms */
836 } else {
837 ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
838 res = 0;
839 }
840 pthread_testcancel();
841 } else {
842 long delta;
843 /* Reliable sleep */
844 tv_tmp = ast_tvnow();
845 if (ast_tvzero(deadline))
846 deadline = tv_tmp;
847 delta = ast_tvdiff_ms(tv_tmp, deadline);
848 if (delta < MOH_MS_INTERVAL) { /* too early */
849 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
850 usleep(1000 * (MOH_MS_INTERVAL - delta));
851 pthread_testcancel();
852 } else {
853 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
854 deadline = tv_tmp;
855 }
856 /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
857 res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
858 }
859 /* For non-8000Hz formats, we need to alter the resolution */
860 res = res * ast_format_get_sample_rate(class->format) / 8000;
861
862 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
863 continue;
864 /* Read mp3 audio */
865 len = ast_format_determine_length(class->format, res);
866
867 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
868 if (!res2) {
869 close(class->srcfd);
870 class->srcfd = -1;
871 pthread_testcancel();
872 if (class->pid > 1) {
873 killpid(class->pid, class->kill_delay, class->kill_method);
874 class->pid = 0;
875 }
876 } else {
877 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
878 }
879 continue;
880 }
881
882 pthread_testcancel();
883
884 ao2_lock(class);
885 AST_LIST_TRAVERSE(&class->members, moh, list) {
886 /* Write data */
887 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
888 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
889 }
890 }
891 ao2_unlock(class);
892 }
893 return NULL;
894}
895
896static int play_moh_exec(struct ast_channel *chan, const char *data)
897{
898 char *parse;
899 char *class;
900 int timeout = -1;
901 int res;
903 AST_APP_ARG(class);
904 AST_APP_ARG(duration);
905 );
906
907 parse = ast_strdupa(data);
908
910
911 if (!ast_strlen_zero(args.duration)) {
912 if (sscanf(args.duration, "%30d", &timeout) == 1) {
913 timeout *= 1000;
914 } else {
915 ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
916 }
917 }
918
919 class = S_OR(args.class, NULL);
920 if (ast_moh_start(chan, class, NULL)) {
921 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
922 return 0;
923 }
924
925 if (timeout > 0)
926 res = ast_safe_sleep(chan, timeout);
927 else {
928 while (!(res = ast_safe_sleep(chan, 10000)));
929 }
930
931 ast_moh_stop(chan);
932
933 return res;
934}
935
936static int start_moh_exec(struct ast_channel *chan, const char *data)
937{
938 char *parse;
939 char *class;
941 AST_APP_ARG(class);
942 );
943
944 parse = ast_strdupa(data);
945
947
948 class = S_OR(args.class, NULL);
949 if (ast_moh_start(chan, class, NULL))
950 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
951
952 return 0;
953}
954
955static int stop_moh_exec(struct ast_channel *chan, const char *data)
956{
957 ast_moh_stop(chan);
958
959 return 0;
960}
961
962#define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
963
964static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
965{
966 struct mohclass *moh = NULL;
967 struct mohclass tmp_class = {
968 .flags = 0,
969 };
970
971 ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
972
973 moh = __ao2_find(mohclasses, &tmp_class, flags,
974 "get_mohbyname", file, lineno, funcname);
975
976 if (!moh && warn) {
977 ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
978 }
979
980 return moh;
981}
982
983static struct mohdata *mohalloc(struct mohclass *cl)
984{
985 struct mohdata *moh;
986
987 if (!(moh = ast_calloc(1, sizeof(*moh))))
988 return NULL;
989
990 if (ast_pipe_nonblock(moh->pipe)) {
991 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
992 ast_free(moh);
993 return NULL;
994 }
995
997 moh->f.subclass.format = cl->format;
999
1000 moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
1001
1002 ao2_lock(cl);
1003 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
1004 ao2_unlock(cl);
1005
1006 return moh;
1007}
1008
1009static void moh_release(struct ast_channel *chan, void *data)
1010{
1011 struct mohdata *moh = data;
1012 struct mohclass *class = moh->parent;
1013 struct ast_format *oldwfmt;
1014
1015 ao2_lock(class);
1016 AST_LIST_REMOVE(&moh->parent->members, moh, list);
1017 ao2_unlock(class);
1018
1019 close(moh->pipe[0]);
1020 close(moh->pipe[1]);
1021
1022 oldwfmt = moh->origwfmt;
1023
1024 moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1025
1026 ast_free(moh);
1027
1028 if (chan) {
1029 struct moh_files_state *state;
1030
1032 if (state && state->class) {
1033 state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1034 }
1035 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1036 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1037 ast_channel_name(chan), ast_format_get_name(oldwfmt));
1038 }
1039
1040 moh_post_stop(chan);
1041 }
1042
1043 ao2_cleanup(oldwfmt);
1044}
1045
1046static void *moh_alloc(struct ast_channel *chan, void *params)
1047{
1048 struct mohdata *res;
1049 struct mohclass *class = params;
1050 struct moh_files_state *state;
1051
1052 /* Initiating music_state for current channel. Channel should know name of moh class */
1054 if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1057 } else {
1058 if (!state) {
1059 return NULL;
1060 }
1061 if (state->class) {
1062 mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1063 ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1064 }
1065 ao2_cleanup(state->origwfmt);
1066 ao2_cleanup(state->mohwfmt);
1067 memset(state, 0, sizeof(*state));
1068 }
1069
1070 if ((res = mohalloc(class))) {
1072 if (ast_set_write_format(chan, class->format)) {
1073 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1075 moh_release(NULL, res);
1076 res = NULL;
1077 } else {
1078 state->class = mohclass_ref(class, "Placing reference into state container");
1079 moh_post_start(chan, class->name);
1080 }
1081 }
1082 return res;
1083}
1084
1085static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1086{
1087 struct mohdata *moh = data;
1088 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1089 int res;
1090
1091 len = ast_format_determine_length(moh->parent->format, samples);
1092
1093 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1094 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1095 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1096 }
1097 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1098 if (res <= 0)
1099 return 0;
1100
1101 moh->f.datalen = res;
1102 moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1103 moh->f.samples = ast_codec_samples_count(&moh->f);
1104
1105 if (ast_write(chan, &moh->f) < 0) {
1106 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1107 return -1;
1108 }
1109
1110 return 0;
1111}
1112
1113static struct ast_generator mohgen = {
1114 .alloc = moh_alloc,
1115 .release = moh_release,
1116 .generate = moh_generate,
1117 .digit = moh_handle_digit,
1118};
1119
1120static void moh_file_vector_destructor(void *obj)
1121{
1122 struct ast_vector_string *files = obj;
1123 AST_VECTOR_RESET(files, ast_free);
1124 AST_VECTOR_FREE(files);
1125}
1126
1127static struct ast_vector_string *moh_file_vector_alloc(int initial_capacity)
1128{
1129 struct ast_vector_string *files = ao2_alloc_options(
1130 sizeof(struct ast_vector_string),
1133 if (files) {
1134 AST_VECTOR_INIT(files, initial_capacity);
1135 }
1136 return files;
1137}
1138
1140{
1141 struct ast_vector_string *playlist_entries = NULL;
1142
1143 for (; var; var = var->next) {
1144 if (!strcasecmp(var->name, "name")) {
1145 ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1146 } else if (!strcasecmp(var->name, "mode")) {
1147 ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1148 } else if (!strcasecmp(var->name, "entry")) {
1149 if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1150 char *dup;
1151
1152 if (!playlist_entries) {
1153 playlist_entries = moh_file_vector_alloc(16);
1154 if (!playlist_entries) {
1155 continue;
1156 }
1157 }
1158
1159 dup = ast_strdup(var->value);
1160 if (!dup) {
1161 continue;
1162 }
1163
1164 if (ast_begins_with(dup, "/")) {
1165 char *last_pos_dot = strrchr(dup, '.');
1166 char *last_pos_slash = strrchr(dup, '/');
1167 if (last_pos_dot && last_pos_dot > last_pos_slash) {
1168 ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1169 dup);
1170 }
1171 }
1172
1173 AST_VECTOR_APPEND(playlist_entries, dup);
1174 } else {
1175 ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1176 }
1177 } else if (!strcasecmp(var->name, "directory")) {
1178 ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1179 } else if (!strcasecmp(var->name, "application")) {
1180 ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1181 } else if (!strcasecmp(var->name, "announcement")) {
1184 } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1185 mohclass->digit = *var->value;
1186 } else if (!strcasecmp(var->name, "random")) {
1187 static int deprecation_warning = 0;
1188 if (!deprecation_warning) {
1189 ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1190 deprecation_warning = 1;
1191 }
1193 } else if (!strcasecmp(var->name, "sort")) {
1194 if (!strcasecmp(var->value, "random")) {
1196 } else if (!strcasecmp(var->value, "alpha")) {
1198 } else if (!strcasecmp(var->value, "randstart")) {
1200 }
1201 } else if (!strcasecmp(var->name, "loop_last")) {
1202 if (ast_true(var->value)) {
1204 } else {
1206 }
1207 } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1210 if (!mohclass->format) {
1211 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1213 }
1214 } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1215 if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1216 mohclass->kill_delay *= 1000;
1217 } else {
1218 ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1219 mohclass->kill_delay = 100000;
1220 }
1221 } else if (!strcasecmp(var->name, "kill_method")) {
1222 if (!strcasecmp(var->value, "process")) {
1224 } else if (!strcasecmp(var->value, "process_group")) {
1226 } else {
1227 ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1229 }
1230 } else if (!strcasecmp(var->name, "answeredonly")) {
1231 mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1232 }
1233 }
1234
1235 if (playlist_entries) {
1236 /* If we aren't in playlist mode, drop any list we may have already built */
1237 if (strcasecmp(mohclass->mode, "playlist")) {
1238 ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1239 mohclass->mode);
1240 ao2_ref(playlist_entries, -1);
1241 return;
1242 }
1243
1244 AST_VECTOR_COMPACT(playlist_entries);
1245
1246 /* We don't need to lock here because we are the thread that
1247 * created this mohclass and we haven't published it yet */
1248 ao2_ref(mohclass->files, -1);
1249 mohclass->files = playlist_entries;
1250 }
1251}
1252
1253static int on_moh_file(const char *directory, const char *filename, void *obj)
1254{
1255 struct ast_vector_string *files = obj;
1256 char *full_path;
1257 char *extension;
1258
1259 /* Skip files that starts with a dot */
1260 if (*filename == '.') {
1261 ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1262 directory, filename);
1263 return 0;
1264 }
1265
1266 /* We can't do anything with files that don't have an extension,
1267 * so check that first and punt if we can't find something */
1268 extension = strrchr(filename, '.');
1269 if (!extension) {
1270 ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1271 directory, filename);
1272 return 0;
1273 }
1274
1275 /* The extension needs at least two characters (after the .) to be useful */
1276 if (strlen(extension) < 3) {
1277 ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1278 "character extension\n", directory, filename);
1279 return 0;
1280 }
1281
1282 /* Build the full path (excluding the extension) */
1283 if (ast_asprintf(&full_path, "%s/%.*s",
1284 directory,
1285 (int) (extension - filename), filename) < 0) {
1286 /* If we don't have enough memory to build this path, there is no
1287 * point in continuing */
1288 return 1;
1289 }
1290
1291 /* If the file is present in multiple formats, ensure we only put it
1292 * into the list once. Pretty sure this is O(n^2). */
1293 if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1294 ast_free(full_path);
1295 return 0;
1296 }
1297
1298 if (AST_VECTOR_APPEND(files, full_path)) {
1299 /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1300 * we stop iterating */
1301 ast_free(full_path);
1302 return 1;
1303 }
1304
1305 return 0;
1306}
1307
1308static int moh_filename_strcasecmp(const void *a, const void *b)
1309{
1310 const char **s1 = (const char **) a;
1311 const char **s2 = (const char **) b;
1312 return strcasecmp(*s1, *s2);
1313}
1314
1315static int moh_scan_files(struct mohclass *class) {
1316
1317 char dir_path[PATH_MAX - sizeof(class->dir)];
1318 struct ast_vector_string *files;
1319
1320 if (class->dir[0] != '/') {
1321 snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1322 } else {
1323 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1324 }
1325
1326 ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1327
1328 /* 16 seems like a reasonable default */
1329 files = moh_file_vector_alloc(16);
1330 if (!files) {
1331 return -1;
1332 }
1333
1334 if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1335 ao2_ref(files, -1);
1336 return -1;
1337 }
1338
1339 if (ast_test_flag(class, MOH_SORTALPHA)) {
1341 }
1342
1343 AST_VECTOR_COMPACT(files);
1344
1345 ao2_lock(class);
1346 ao2_ref(class->files, -1);
1347 class->files = files;
1348 ao2_unlock(class);
1349
1350 return AST_VECTOR_SIZE(files);
1351}
1352
1353static int init_files_class(struct mohclass *class)
1354{
1355 int res;
1356
1357 res = moh_scan_files(class);
1358
1359 if (res < 0) {
1360 return -1;
1361 }
1362
1363 if (!res) {
1364 ast_verb(3, "Files not found in %s for moh class:%s\n",
1365 class->dir, class->name);
1366 return -1;
1367 }
1368
1369 return 0;
1370}
1371
1372static void moh_rescan_files(void) {
1373 struct ao2_iterator i;
1374 struct mohclass *c;
1375
1377
1378 while ((c = ao2_iterator_next(&i))) {
1379 if (!strcasecmp(c->mode, "files")) {
1381 }
1382 ao2_ref(c, -1);
1383 }
1384
1386}
1387
1388static int moh_diff(struct mohclass *old, struct mohclass *new)
1389{
1390 if (!old || !new) {
1391 return -1;
1392 }
1393
1394 if (strcmp(old->dir, new->dir)) {
1395 return -1;
1396 } else if (strcmp(old->mode, new->mode)) {
1397 return -1;
1398 } else if (strcmp(old->args, new->args)) {
1399 return -1;
1400 } else if (old->flags != new->flags) {
1401 return -1;
1402 }
1403
1404 return 0;
1405}
1406
1407static int init_app_class(struct mohclass *class)
1408{
1409 if (!strcasecmp(class->mode, "custom")) {
1410 ast_set_flag(class, MOH_CUSTOM);
1411 } else if (!strcasecmp(class->mode, "mp3nb")) {
1412 ast_set_flag(class, MOH_SINGLE);
1413 } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1415 } else if (!strcasecmp(class->mode, "quietmp3")) {
1416 ast_set_flag(class, MOH_QUIET);
1417 }
1418
1419 class->srcfd = -1;
1420
1421 if (!(class->timer = ast_timer_open())) {
1422 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1423 return -1;
1424 }
1425 if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1426 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1427 ast_timer_close(class->timer);
1428 class->timer = NULL;
1429 }
1430
1431 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1432 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1433 if (class->timer) {
1434 ast_timer_close(class->timer);
1435 class->timer = NULL;
1436 }
1437 return -1;
1438 }
1439
1440 return 0;
1441}
1442
1443/*!
1444 * \note This function owns the reference it gets to moh if unref is true
1445 */
1446#define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1447static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1448{
1449 struct mohclass *mohclass = NULL;
1450
1451 mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1452
1453 if (mohclass && !moh_diff(mohclass, moh)) {
1454 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1455 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1456 if (unref) {
1457 moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1458 }
1459 return -1;
1460 } else if (mohclass) {
1461 /* Found a class, but it's different from the one being registered */
1462 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1463 }
1464
1465 time(&moh->start);
1466 moh->start -= respawn_time;
1467
1468 if (!strcasecmp(moh->mode, "files")) {
1469 if (init_files_class(moh)) {
1470 if (unref) {
1471 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1472 }
1473 return -1;
1474 }
1475 } else if (!strcasecmp(moh->mode, "playlist")) {
1476 size_t file_count;
1477
1478 ao2_lock(moh);
1479 file_count = AST_VECTOR_SIZE(moh->files);
1480 ao2_unlock(moh);
1481
1482 if (!file_count) {
1483 if (unref) {
1484 moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1485 }
1486 return -1;
1487 }
1488 } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1489 !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1490 !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1491 if (init_app_class(moh)) {
1492 if (unref) {
1493 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1494 }
1495 return -1;
1496 }
1497 } else {
1498 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1499 if (unref) {
1500 moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1501 }
1502 return -1;
1503 }
1504
1505 ao2_t_link(mohclasses, moh, "Adding class to container");
1506
1507 if (unref) {
1508 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1509 }
1510
1511 return 0;
1512}
1513
1514#define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1515static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
1516{
1517 ao2_t_unlink(mohclasses, moh, "Removing class from container");
1518 return 0;
1519}
1520
1521static void local_ast_moh_cleanup(struct ast_channel *chan)
1522{
1524
1525 if (state) {
1527 if (state->class) {
1528 /* This should never happen. We likely just leaked some resource. */
1529 state->class =
1530 mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1531 ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1532 }
1533 ao2_cleanup(state->origwfmt);
1534 ao2_cleanup(state->mohwfmt);
1535 ast_free(state);
1536 /* Only held a module reference if we had a music state */
1538 }
1539}
1540
1541/*! \brief Support routing for 'moh unregister class' CLI
1542 * This is in charge of generating all strings that match a prefix in the
1543 * given position. As many functions of this kind, each invokation has
1544 * O(state) time complexity so be careful in using it.
1545 */
1546static char *complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
1547{
1548 int which=0;
1549 struct mohclass *cur;
1550 char *c = NULL;
1551 int wordlen = strlen(word);
1552 struct ao2_iterator i;
1553
1554 if (pos != 3) {
1555 return NULL;
1556 }
1557
1559 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1560 if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1561 c = ast_strdup(cur->name);
1562 mohclass_unref(cur, "drop ref in iterator loop break");
1563 break;
1564 }
1565 mohclass_unref(cur, "drop ref in iterator loop");
1566 }
1568
1569 return c;
1570}
1571
1572static char *handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1573{
1574 struct mohclass *cur;
1575 int len;
1576 int found = 0;
1577 struct ao2_iterator i;
1578
1579 switch (cmd) {
1580 case CLI_INIT:
1581 e->command = "moh unregister class";
1582 e->usage =
1583 "Usage: moh unregister class <class>\n"
1584 " Unregisters a realtime moh class.\n";
1585 return NULL;
1586 case CLI_GENERATE:
1587 return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1588 }
1589
1590 if (a->argc != 4)
1591 return CLI_SHOWUSAGE;
1592
1593 len = strlen(a->argv[3]);
1594
1596 while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1597 if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1598 found = 1;
1599 break;
1600 }
1601 mohclass_unref(cur, "drop ref in iterator loop");
1602 }
1604
1605 if (found) {
1606 moh_unregister(cur);
1607 mohclass_unref(cur, "drop ref after unregister");
1608 } else {
1609 ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1610 }
1611
1612 return CLI_SUCCESS;
1613}
1614
1615
1616
1617static void moh_class_destructor(void *obj);
1618
1619#define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1620
1621static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1622{
1623 struct mohclass *class;
1624
1626 "Allocating new moh class", file, line, funcname);
1627 if (class) {
1628 class->format = ao2_bump(ast_format_slin);
1629 class->srcfd = -1;
1630 class->kill_delay = 100000;
1631
1632 /* We create an empty one by default */
1633 class->files = moh_file_vector_alloc(0);
1634 if (!class->files) {
1635 ao2_ref(class, -1);
1636 return NULL;
1637 }
1638 }
1639
1640 return class;
1641}
1642
1644{
1645 struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1646
1647 if (var) {
1648 const char *mode = ast_variable_find_in_list(var, "mode");
1649 if (ast_strings_equal(mode, "playlist")) {
1650 struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1651 char *category = NULL;
1652 size_t entry_count = 0;
1653
1654 /* entries is NULL if there are no results */
1655 if (entries) {
1656 while ((category = ast_category_browse(entries, category))) {
1657 const char *entry = ast_variable_retrieve(entries, category, "entry");
1658
1659 if (entry) {
1660 struct ast_variable *dup = ast_variable_new("entry", entry, "");
1661 if (dup) {
1662 entry_count++;
1664 }
1665 }
1666 }
1667 ast_config_destroy(entries);
1668 }
1669
1670 if (entry_count == 0) {
1671 /* Behave as though this class doesn't exist */
1673 var = NULL;
1674 }
1675 }
1676 }
1677
1678 if (!var) {
1680 "Music on Hold class '%s' not found in memory/database. "
1681 "Verify your configuration.\n",
1682 name);
1683 }
1684 return var;
1685}
1686
1687static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1688{
1689 struct mohclass *mohclass = NULL;
1691 struct ast_variable *var = NULL;
1692 int res = 0;
1693 int i;
1694 int realtime_possible = ast_check_realtime("musiconhold");
1695 int warn_if_not_in_memory = !realtime_possible;
1696 const char *classes[] = {NULL, NULL, interpclass, "default"};
1697
1699 classes[0] = ast_channel_musicclass(chan);
1700 classes[1] = mclass;
1701 } else {
1702 classes[0] = mclass;
1703 classes[1] = ast_channel_musicclass(chan);
1704 }
1705
1706 /* The following is the order of preference for which class to use:
1707 * 1) The channels explicitly set musicclass, which should *only* be
1708 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1709 * Unless preferchannelclass in musiconhold.conf is false
1710 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1711 * result of receiving a HOLD control frame, this should be the
1712 * payload that came with the frame.
1713 * 3) The channels explicitly set musicclass, which should *only* be
1714 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1715 * 4) The interpclass argument. This would be from the mohinterpret
1716 * option from channel drivers. This is the same as the old musicclass
1717 * option.
1718 * 5) The default class.
1719 */
1720
1721 for (i = 0; i < ARRAY_LEN(classes); ++i) {
1722 if (!ast_strlen_zero(classes[i])) {
1723 mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1724 if (!mohclass && realtime_possible) {
1725 var = load_realtime_musiconhold(classes[i]);
1726 }
1727 if (mohclass || var) {
1728 break;
1729 }
1730 }
1731 }
1732
1733 /* If no moh class found in memory, then check RT. Note that the logic used
1734 * above guarantees that if var is non-NULL, then mohclass must be NULL.
1735 */
1736 if (var) {
1737 if ((mohclass = moh_class_malloc())) {
1738 mohclass->realtime = 1;
1739
1742
1744 if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1745 strcpy(mohclass->dir, "nodir");
1746 } else {
1747 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1748 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1749 return -1;
1750 }
1751 }
1753 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1754 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1755 return -1;
1756 }
1757 if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1758 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1759 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1760 return -1;
1761 }
1762
1764 /* CACHERTCLASSES enabled, let's add this class to default tree */
1765 if (state && state->class) {
1766 /* Class already exist for this channel */
1767 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1768 }
1769 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1770 * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1771 * be that the destructor would be called when the generator on the channel is deactivated. The container then
1772 * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1773 * invalid memory.
1774 */
1775 if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1776 mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1777 return -1;
1778 }
1779 } else {
1780 /* We don't register RT moh class, so let's init it manually */
1781
1782 time(&mohclass->start);
1784
1785 if (!strcasecmp(mohclass->mode, "files")) {
1786 /*
1787 * XXX moh_scan_files returns -1 if it is unable to open the
1788 * configured directory or there is a memory allocation
1789 * failure. Otherwise it returns the number of files for this music
1790 * class. This check is only checking if the number of files is zero
1791 * and it ignores the -1 case. To avoid a behavior change we keep this
1792 * as-is, but we should address what the 'correct' behavior should be.
1793 */
1794 if (!moh_scan_files(mohclass)) {
1795 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1796 return -1;
1797 }
1798 if (strchr(mohclass->args, 'r')) {
1799 static int deprecation_warning = 0;
1800 if (!deprecation_warning) {
1801 ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1802 deprecation_warning = 1;
1803 }
1805 }
1806 } else if (!strcasecmp(mohclass->mode, "playlist")) {
1807 size_t file_count;
1808
1810 file_count = AST_VECTOR_SIZE(mohclass->files);
1812
1813 if (!file_count) {
1814 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1815 return -1;
1816 }
1817 } 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")) {
1818
1819 if (!strcasecmp(mohclass->mode, "custom"))
1821 else if (!strcasecmp(mohclass->mode, "mp3nb"))
1823 else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1825 else if (!strcasecmp(mohclass->mode, "quietmp3"))
1827
1828 mohclass->srcfd = -1;
1829 if (!(mohclass->timer = ast_timer_open())) {
1830 ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1831 }
1833 ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1835 mohclass->timer = NULL;
1836 }
1837
1838 /* Let's check if this channel already had a moh class before */
1839 if (state && state->class) {
1840 /* Class already exist for this channel */
1841 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1842 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1843 /* we found RT class with the same name, seems like we should continue playing existing one */
1844 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1845 mohclass = mohclass_ref(state->class, "using existing class from state");
1846 }
1847 } else {
1849 ast_log(LOG_WARNING, "Unable to create moh...\n");
1850 if (mohclass->timer) {
1852 mohclass->timer = NULL;
1853 }
1854 mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1855 return -1;
1856 }
1857 }
1858 } else {
1859 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1860 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1861 return -1;
1862 }
1863 }
1864 } else {
1866 var = NULL;
1867 }
1868 }
1869
1870 if (!mohclass) {
1871 return -1;
1872 }
1873
1875 ast_verb(3, "The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1876 return -1;
1877 }
1878
1879 /* If we are using a cached realtime class with files, re-scan the files */
1880 if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1881 /*
1882 * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1883 * or there is a memory allocation failure. Otherwise it returns the number of
1884 * files for this music class. This check is only checking if the number of files
1885 * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1886 * as-is, but we should address what the 'correct' behavior should be.
1887 */
1888 if (!moh_scan_files(mohclass)) {
1889 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1890 return -1;
1891 }
1892 }
1893
1894 if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1895 size_t file_count;
1896
1898 file_count = AST_VECTOR_SIZE(mohclass->files);
1900
1901 if (file_count) {
1903 } else {
1904 res = ast_activate_generator(chan, &mohgen, mohclass);
1905 }
1906 }
1907 if (!res) {
1908 ast_channel_lock(chan);
1909 ast_channel_latest_musicclass_set(chan, mohclass->name);
1911 ast_channel_unlock(chan);
1912 }
1913
1914 mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1915
1916 return res;
1917}
1918
1919static void local_ast_moh_stop(struct ast_channel *chan)
1920{
1922
1923 ast_channel_lock(chan);
1925 if (ast_channel_music_state(chan)) {
1926 if (ast_channel_stream(chan)) {
1929 }
1930 }
1931 ast_channel_unlock(chan);
1932}
1933
1934static void moh_class_destructor(void *obj)
1935{
1936 struct mohclass *class = obj;
1937 struct mohdata *member;
1938 pthread_t tid = 0;
1939
1940 ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1941
1942 ao2_lock(class);
1943 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1945 }
1946 ao2_cleanup(class->files);
1947 ao2_unlock(class);
1948
1949 /* Kill the thread first, so it cannot restart the child process while the
1950 * class is being destroyed */
1951 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1952 tid = class->thread;
1953 class->thread = AST_PTHREADT_NULL;
1954 pthread_cancel(tid);
1955 /* We'll collect the exit status later, after we ensure all the readers
1956 * are dead. */
1957 }
1958
1959 if (class->pid > 1) {
1960 char buff[8192];
1961 int bytes, tbytes = 0, stime = 0;
1962
1963 ast_debug(1, "killing %d!\n", class->pid);
1964
1965 stime = time(NULL) + 2;
1966 killpid(class->pid, class->kill_delay, class->kill_method);
1967
1968 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1969 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1970 tbytes = tbytes + bytes;
1971 }
1972
1973 ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1974 class->pid, tbytes);
1975
1976 class->pid = 0;
1977 close(class->srcfd);
1978 class->srcfd = -1;
1979 }
1980
1981 if (class->timer) {
1982 ast_timer_close(class->timer);
1983 class->timer = NULL;
1984 }
1985
1986 ao2_cleanup(class->format);
1987
1988 /* Finally, collect the exit status of the monitor thread */
1989 if (tid > 0) {
1990 pthread_join(tid, NULL);
1991 }
1992
1993}
1994
1995static int moh_class_mark(void *obj, void *arg, int flags)
1996{
1997 struct mohclass *class = obj;
1998
1999 if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
2000 class->delete = 1;
2001 }
2002
2003 return 0;
2004}
2005
2006static int moh_classes_delete_marked(void *obj, void *arg, int flags)
2007{
2008 struct mohclass *class = obj;
2009
2010 return class->delete ? CMP_MATCH : 0;
2011}
2012
2014{
2015 struct ast_config *cfg;
2016 struct ast_variable *var;
2017 struct mohclass *class;
2018 char *cat;
2019 int numclasses = 0;
2020 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2021
2022 cfg = ast_config_load("musiconhold.conf", config_flags);
2023
2024 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2025 if (ast_check_realtime("musiconhold") && reload) {
2026 ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2028 }
2030 return 0;
2031 }
2032
2034 if (ast_check_realtime("musiconhold") && reload) {
2035 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2037 }
2038 return 0;
2039 }
2040
2041 if (reload) {
2042 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2043 }
2044
2047
2048 cat = ast_category_browse(cfg, NULL);
2049 for (; cat; cat = ast_category_browse(cfg, cat)) {
2050 /* Setup common options from [general] section */
2051 if (!strcasecmp(cat, "general")) {
2052 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2053 if (!strcasecmp(var->name, "cachertclasses")) {
2055 } else if (!strcasecmp(var->name, "preferchannelclass")) {
2057 } else {
2058 ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2059 }
2060 }
2061 continue;
2062 }
2063
2064 if (!(class = moh_class_malloc())) {
2065 break;
2066 }
2067
2068 moh_parse_options(ast_variable_browse(cfg, cat), class);
2069 /* For compatibility with the past, we overwrite any name=name
2070 * with the context [name]. */
2071 ast_copy_string(class->name, cat, sizeof(class->name));
2072
2073 if (ast_strlen_zero(class->dir)) {
2074 if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2075 strcpy(class->dir, "nodir");
2076 } else {
2077 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2078 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2079 continue;
2080 }
2081 }
2082 if (ast_strlen_zero(class->mode)) {
2083 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2084 class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2085 continue;
2086 }
2087 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2088 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2089 class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2090 continue;
2091 }
2092
2093 /* Don't leak a class when it's already registered */
2094 if (!moh_register(class, reload, HANDLE_REF)) {
2095 numclasses++;
2096 }
2097 }
2098
2099 ast_config_destroy(cfg);
2100
2102 moh_classes_delete_marked, NULL, "Purge marked classes");
2103
2104 return numclasses;
2105}
2106
2107static void ast_moh_destroy(void)
2108{
2109 ast_verb(2, "Destroying musiconhold processes\n");
2110 if (mohclasses) {
2112 ao2_ref(mohclasses, -1);
2113 mohclasses = NULL;
2114 }
2115}
2116
2117static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2118{
2119 switch (cmd) {
2120 case CLI_INIT:
2121 e->command = "moh reload";
2122 e->usage =
2123 "Usage: moh reload\n"
2124 " Reloads the MusicOnHold module.\n"
2125 " Alias for 'module reload res_musiconhold.so'\n";
2126 return NULL;
2127 case CLI_GENERATE:
2128 return NULL;
2129 }
2130
2131 if (a->argc != e->args)
2132 return CLI_SHOWUSAGE;
2133
2134 /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2135 ast_module_reload("res_musiconhold");
2136
2137 return CLI_SUCCESS;
2138}
2139
2140static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2141{
2142 struct mohclass *class;
2143 struct ao2_iterator i;
2144
2145 switch (cmd) {
2146 case CLI_INIT:
2147 e->command = "moh show files";
2148 e->usage =
2149 "Usage: moh show files\n"
2150 " Lists all loaded file-based MusicOnHold classes and their\n"
2151 " files.\n";
2152 return NULL;
2153 case CLI_GENERATE:
2154 return NULL;
2155 }
2156
2157 if (a->argc != e->args)
2158 return CLI_SHOWUSAGE;
2159
2161 for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2162 struct ast_vector_string *files;
2163
2164 ao2_lock(class);
2165 files = ao2_bump(class->files);
2166 ao2_unlock(class);
2167
2168 if (AST_VECTOR_SIZE(files)) {
2169 int x;
2170 ast_cli(a->fd, "Class: %s\n", class->name);
2171 for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2172 ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2173 }
2174 }
2175
2176 ao2_ref(files, -1);
2177 }
2179
2180 return CLI_SUCCESS;
2181}
2182
2183static char *handle_cli_moh_show_classes(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 classes";
2191 e->usage =
2192 "Usage: moh show classes\n"
2193 " Lists all MusicOnHold classes.\n";
2194 return NULL;
2195 case CLI_GENERATE:
2196 return NULL;
2197 }
2198
2199 if (a->argc != e->args)
2200 return CLI_SHOWUSAGE;
2201
2203 for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2204 ast_cli(a->fd, "Class: %s\n", class->name);
2205 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2206 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2207 if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2208 ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2209 }
2210 if (ast_test_flag(class, MOH_CUSTOM)) {
2211 ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2212 ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2213 ast_cli(a->fd, "\tKill Method: %s\n",
2214 class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2215 }
2216 if (strcasecmp(class->mode, "files")) {
2217 ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2218 }
2219 }
2221
2222 return CLI_SUCCESS;
2223}
2224
2225static struct ast_cli_entry cli_moh[] = {
2226 AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2227 AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2228 AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2229 AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2230};
2231
2232static int moh_class_hash(const void *obj, const int flags)
2233{
2234 const struct mohclass *class = obj;
2235
2236 return ast_str_case_hash(class->name);
2237}
2238
2239static int moh_class_cmp(void *obj, void *arg, int flags)
2240{
2241 struct mohclass *class = obj, *class2 = arg;
2242
2243 return strcasecmp(class->name, class2->name) ? 0 :
2244 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2246}
2247
2248/*!
2249 * \brief Load the module
2250 *
2251 * Module loading including tests for configuration or dependencies.
2252 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2253 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2254 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2255 * configuration file or other non-critical problem return
2256 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2257 */
2258static int load_module(void)
2259{
2260 int res;
2261
2263 moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2264 if (!mohclasses) {
2266 }
2267
2268 if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2269 ast_log(LOG_WARNING, "No music on hold classes configured, "
2270 "disabling music on hold.\n");
2271 } else {
2274 }
2275
2279 if (!res)
2281 if (!res)
2283
2285}
2286
2287static int reload(void)
2288{
2289 if (load_moh_classes(1)) {
2292 }
2293
2295}
2296
2297static int moh_class_inuse(void *obj, void *arg, int flags)
2298{
2299 struct mohclass *class = obj;
2300
2301 return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2302}
2303
2304static int unload_module(void)
2305{
2306 int res = 0;
2307 struct mohclass *class = NULL;
2308
2309 /* XXX This check shouldn't be required if module ref counting was being used
2310 * properly ... */
2311 if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2312 class = mohclass_unref(class, "unref of class from module unload callback");
2313 res = -1;
2314 }
2315
2316 if (res < 0) {
2317 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2318 return res;
2319 }
2320
2322
2329
2330 return res;
2331}
2332
2334 .support_level = AST_MODULE_SUPPORT_CORE,
2335 .load = load_module,
2336 .unload = unload_module,
2337 .reload = reload,
2338 .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:1067
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:1848
#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:2979
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:2970
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2921
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:5161
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
@ AST_FLAG_MOH
Definition: channel.h:1011
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5820
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:1601
#define ast_channel_unlock(chan)
Definition: channel.h:2971
#define MAX_MUSICCLASS
Definition: channel.h:175
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
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
@ CONFIG_FLAG_FILEUNCHANGED
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
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:70
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:7787
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7797
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:7771
void ast_uninstall_music_functions(void)
Definition: channel.c:7780
#define ast_opt_high_priority
Definition: options.h:112
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
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)
#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 int ast_moh_files_next(struct ast_channel *chan)
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.
const ast_string_field name
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.
union ast_frame::@228 data
struct ast_frame_subclass subclass
enum ast_frame_type frametype
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 ast_vector_string * files
struct mohclass::@442 list
char dir[256]
unsigned int realtime
struct mohclass::@441 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::@443 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:739
#define MIN(a, b)
Definition: utils.h:231
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:592
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:1090
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666
#define AST_FLAGS_ALL
Definition: utils.h:196
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:625
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:396
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:731
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:638
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680