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