46 #include <netinet/in.h>
69 #include "asterisk/stasis_channels.h"
74 #include "asterisk/poll-compat.h"
76 #define INITIAL_NUM_FILES 8
123 static const char play_moh[] =
"MusicOnHold";
124 static const char start_moh[] =
"StartMusicOnHold";
125 static const char stop_moh[] =
"StopMusicOnHold";
127 static int respawn_time = 20;
141 char save_pos_filename[PATH_MAX];
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)
150 #define MOH_SORTMODE (3 << 3)
152 #define MOH_CACHERTCLASSES (1 << 5)
153 #define MOH_ANNOUNCEMENT (1 << 6)
154 #define MOH_PREFERCHANNELCLASS (1 << 7)
156 #define MOH_LOOPLAST (1 << 8)
159 #define MOH_NOTDELETED (1 << 30)
160 #define MOH_REALTIME (1 << 31)
165 KILL_METHOD_PROCESS_GROUP = 0,
173 char announcement[256];
195 unsigned int delete:1;
212 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
213 #define MPG_123 "/usr/bin/mpg123"
217 static int reload(
void);
219 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
222 #define mohclass_unref(class,string) ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })
224 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
225 static struct mohclass *_mohclass_unref(
struct mohclass *
class,
const char *tag,
const char *file,
int line,
const char *funcname)
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);
237 __ao2_ref(
class, -1, tag, file, line, funcname);
243 static void moh_post_start(
struct ast_channel *chan,
const char *moh_class_name)
248 ast_verb(3,
"Started music on hold, class '%s', on channel '%s'\n",
249 moh_class_name, ast_channel_name(chan));
251 json_object =
ast_json_pack(
"{s: s}",
"class", moh_class_name);
264 ao2_cleanup(message);
268 static void moh_post_stop(
struct ast_channel *chan)
272 ast_verb(3,
"Stopped music on hold on %s\n", ast_channel_name(chan));
282 ao2_cleanup(message);
289 if (!chan || !ast_channel_music_state(chan)) {
293 state = ast_channel_music_state(chan);
295 if (ast_channel_stream(chan)) {
297 ast_channel_stream_set(chan, NULL);
303 state->mohwfmt = NULL;
305 ast_log(LOG_WARNING,
"Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
308 ao2_cleanup(state->origwfmt);
309 state->origwfmt = NULL;
311 state->save_pos = state->pos;
312 state->announcement = 0;
314 state->
class = mohclass_unref(state->
class,
"Unreffing channel's music class upon deactivation of generator");
317 static int ast_moh_files_next(
struct ast_channel *chan)
325 if (ast_channel_stream(chan)) {
327 ast_channel_stream_set(chan, NULL);
331 state->announcement = 1;
333 ast_debug(1,
"%s Opened announcement '%s'\n", ast_channel_name(chan), state->
class->announcement);
337 state->announcement = 0;
340 ao2_lock(state->
class);
342 ao2_unlock(state->
class);
346 ast_log(LOG_WARNING,
"No files available for class '%s'\n", state->
class->name);
351 if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
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)) {
356 state->pos = state->save_pos;
357 state->save_pos = -1;
358 }
else if (ast_test_flag(state->
class, MOH_SORTMODE) == MOH_RANDOMIZE) {
360 for (tries = 0; tries < 20; tries++) {
361 state->pos = ast_random() % file_count;
366 state->save_pos = -1;
372 state->pos = MIN(file_count - 1, state->pos);
374 state->pos %= file_count;
376 state->save_pos = -1;
380 for (tries = 0; tries < file_count; ++tries) {
385 ast_log(LOG_WARNING,
"Unable to open file '%s': %s\n",
AST_VECTOR_GET(files, state->pos), strerror(errno));
387 state->pos %= file_count;
390 if (tries == file_count) {
398 ast_debug(1,
"%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
400 if (state->samples) {
403 ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
407 if (state->samples > loc && loc) {
424 if (!ast_moh_files_next(chan)) {
436 if (!ast_moh_files_next(chan)) {
446 static void moh_files_write_format_change(
struct ast_channel *chan,
void *data)
453 if (state->origwfmt) {
456 tmp =
ao2_bump(ast_channel_writeformat(chan));
458 if (state->mohwfmt) {
461 state->origwfmt = tmp;
465 static int moh_files_generator(
struct ast_channel *chan,
void *data,
int len,
int samples)
469 int res = 0, sample_queue = 0;
471 ast_channel_lock(chan);
472 state = ast_channel_music_state(chan);
473 state->sample_queue +=
samples;
475 sample_queue = state->sample_queue;
476 ast_channel_unlock(chan);
478 while (sample_queue > 0) {
479 ast_channel_lock(chan);
480 f = moh_files_readframe(chan);
482 ast_channel_unlock(chan);
488 if (!state->announcement) {
492 state->sample_queue -= f->
samples;
506 sample_queue = state->sample_queue;
507 ast_channel_unlock(chan);
512 ast_log(LOG_WARNING,
"Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
519 static void *moh_files_alloc(
struct ast_channel *chan,
void *params)
525 state = ast_channel_music_state(chan);
526 if (!state && (state =
ast_calloc(1,
sizeof(*state)))) {
527 ast_channel_music_state_set(chan, state);
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");
544 if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
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;
554 state->
class = mohclass_ref(
class,
"Reffing music class for channel");
556 ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
557 ao2_replace(state->mohwfmt, ast_channel_writeformat(chan));
560 state->save_total = file_count;
562 moh_post_start(chan, class->
name);
567 static int moh_digit_match(
void *obj,
void *arg,
int flags)
578 return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit,
"digit callback");
581 static void moh_handle_digit(
struct ast_channel *chan,
char digit)
584 const char *classname = NULL;
588 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
589 ast_channel_musicclass_set(chan, classname);
596 .alloc = moh_files_alloc,
597 .release = moh_files_release,
598 .generate = moh_files_generator,
599 .digit = moh_handle_digit,
600 .write_format_change = moh_files_write_format_change,
603 static int spawn_mp3(
struct mohclass *
class)
607 char fns[MAX_MP3S][80];
608 char *argv[MAX_MP3S + 50];
616 if (!strcasecmp(class->dir,
"nodir")) {
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);
626 if (!ast_test_flag(
class, MOH_CUSTOM)) {
627 argv[argc++] =
"mpg123";
630 argv[argc++] =
"--mono";
632 argv[argc++] =
"8000";
634 if (!ast_test_flag(
class, MOH_SINGLE)) {
636 argv[argc++] =
"2048";
641 if (ast_test_flag(
class, MOH_QUIET))
642 argv[argc++] =
"4096";
644 argv[argc++] =
"8192";
649 while (!ast_strlen_zero(argptr)) {
650 argv[argc++] = argptr;
651 strsep(&argptr,
",");
657 while (!ast_strlen_zero(argptr)) {
658 argv[argc++] = argptr;
659 strsep(&argptr,
" ");
663 if (!strncasecmp(class->dir,
"http://", 7)) {
665 argv[argc++] = fns[
files];
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"))) {
675 argv[argc++] = fns[
files];
685 ast_log(LOG_WARNING,
"Pipe failed\n");
689 ast_log(LOG_WARNING,
"Found no files in '%s'\n", class->dir);
694 if (!strncasecmp(class->dir,
"http://", 7) && time(NULL) - class->start < respawn_time) {
695 sleep(respawn_time - (time(NULL) - class->start));
700 if (class->pid < 0) {
703 ast_log(LOG_WARNING,
"Fork failed: %s\n", strerror(errno));
707 if (ast_opt_high_priority)
712 dup2(fds[1], STDOUT_FILENO);
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));
722 setpgid(0, getpid());
723 if (ast_test_flag(
class, MOH_CUSTOM)) {
724 execv(argv[0], argv);
727 execv(LOCAL_MPG_123, argv);
729 execv(MPG_123, argv);
731 execvp(
"mpg123", argv);
734 fprintf(stderr,
"MOH: exec failed: %s\n", strerror(errno));
744 static int killer(pid_t pid,
int signum,
enum kill_methods kill_method)
746 switch (kill_method) {
747 case KILL_METHOD_PROCESS_GROUP:
748 return killpg(pid, signum);
749 case KILL_METHOD_PROCESS:
750 return kill(pid, signum);
756 static void killpid(
int pid,
size_t delay,
enum kill_methods kill_method)
758 if (killer(pid, SIGHUP, kill_method) < 0) {
759 if (errno == ESRCH) {
762 ast_log(LOG_WARNING,
"Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
764 ast_debug(1,
"Sent HUP to pid %d%s\n", pid,
765 kill_method == KILL_METHOD_PROCESS_GROUP ?
" and all children" :
" only");
768 if (killer(pid, SIGTERM, kill_method) < 0) {
769 if (errno == ESRCH) {
772 ast_log(LOG_WARNING,
"Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
774 ast_debug(1,
"Sent TERM to pid %d%s\n", pid,
775 kill_method == KILL_METHOD_PROCESS_GROUP ?
" and all children" :
" only");
778 if (killer(pid, SIGKILL, kill_method) < 0) {
779 if (errno == ESRCH) {
782 ast_log(LOG_WARNING,
"Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
784 ast_debug(1,
"Sent KILL to pid %d%s\n", pid,
785 kill_method == KILL_METHOD_PROCESS_GROUP ?
" and all children" :
" only");
789 static void *monmp3thread(
void *data)
791 #define MOH_MS_INTERVAL 100
798 struct timeval deadline, tv_tmp;
801 deadline.tv_usec = 0;
803 pthread_testcancel();
805 if (class->srcfd < 0) {
806 if ((class->srcfd = spawn_mp3(
class)) < 0) {
807 ast_log(LOG_WARNING,
"Unable to spawn mp3player\n");
814 struct pollfd pfd = { .fd =
ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
820 if (ast_poll(&pfd, 1, -1) > 0) {
822 ast_log(LOG_ERROR,
"Failed to acknowledge timer for mp3player\n");
828 ast_log(LOG_WARNING,
"poll() failed: %s\n", strerror(errno));
831 pthread_testcancel();
839 if (delta < MOH_MS_INTERVAL) {
841 usleep(1000 * (MOH_MS_INTERVAL - delta));
842 pthread_testcancel();
844 ast_log(LOG_NOTICE,
"Request to schedule in the past?!?!\n");
848 res = 8 * MOH_MS_INTERVAL;
853 if ((strncasecmp(class->dir,
"http://", 7) && strcasecmp(class->dir,
"nodir")) &&
AST_LIST_EMPTY(&class->members))
858 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
862 pthread_testcancel();
863 if (class->pid > 1) {
864 killpid(class->pid, class->kill_delay, class->kill_method);
868 ast_debug(1,
"Read %d bytes of audio while expecting %d\n", res2, len);
873 pthread_testcancel();
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);
887 static int play_moh_exec(
struct ast_channel *chan,
const char *data)
902 if (!ast_strlen_zero(args.duration)) {
903 if (sscanf(args.duration,
"%30d", &timeout) == 1) {
906 ast_log(LOG_WARNING,
"Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
910 class =
S_OR(args.class, NULL);
912 ast_log(LOG_WARNING,
"Unable to start music on hold class '%s' on channel %s\n",
class, ast_channel_name(chan));
927 static int start_moh_exec(
struct ast_channel *chan,
const char *data)
939 class =
S_OR(args.class, NULL);
941 ast_log(LOG_WARNING,
"Unable to start music on hold class '%s' on channel %s\n",
class, ast_channel_name(chan));
946 static int stop_moh_exec(
struct ast_channel *chan,
const char *data)
953 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
955 static struct mohclass *_get_mohbyname(
const char *name,
int warn,
int flags,
const char *file,
int lineno,
const char *funcname)
964 moh =
__ao2_find(mohclasses, &tmp_class, flags,
965 "get_mohbyname", file, lineno, funcname);
968 ast_log(LOG_WARNING,
"Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
982 ast_log(LOG_WARNING,
"Failed to create pipe: %s\n", strerror(errno));
991 moh->parent = mohclass_ref(cl,
"Reffing music class for mohdata parent");
1000 static void moh_release(
struct ast_channel *chan,
void *data)
1003 struct mohclass *
class = moh->parent;
1010 close(moh->pipe[0]);
1011 close(moh->pipe[1]);
1013 oldwfmt = moh->origwfmt;
1015 moh->parent =
class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1022 state = ast_channel_music_state(chan);
1023 if (state && state->
class) {
1024 state->
class = mohclass_unref(state->
class,
"Unreffing channel's music class upon deactivation of generator");
1027 ast_log(LOG_WARNING,
"Unable to restore channel '%s' to format %s\n",
1031 moh_post_stop(chan);
1034 ao2_cleanup(oldwfmt);
1037 static void *moh_alloc(
struct ast_channel *chan,
void *params)
1044 state = ast_channel_music_state(chan);
1045 if (!state && (state =
ast_calloc(1,
sizeof(*state)))) {
1046 ast_channel_music_state_set(chan, state);
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");
1056 ao2_cleanup(state->origwfmt);
1057 ao2_cleanup(state->mohwfmt);
1058 memset(state, 0,
sizeof(*state));
1061 if ((res = mohalloc(
class))) {
1062 res->origwfmt =
ao2_bump(ast_channel_writeformat(chan));
1064 ast_log(LOG_WARNING,
"Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1066 moh_release(NULL, res);
1069 state->
class = mohclass_ref(
class,
"Placing reference into state container");
1070 moh_post_start(chan,
class->name);
1076 static int moh_generate(
struct ast_channel *chan,
void *data,
int len,
int samples)
1085 ast_log(LOG_WARNING,
"Only doing %d of %d requested bytes on %s\n", (
int)
sizeof(buf), len, ast_channel_name(chan));
1097 ast_log(LOG_WARNING,
"Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1106 .release = moh_release,
1107 .generate = moh_generate,
1108 .digit = moh_handle_digit,
1111 static void moh_file_vector_destructor(
void *obj)
1122 moh_file_vector_destructor,
1134 for (; var; var = var->
next) {
1135 if (!strcasecmp(var->
name,
"name")) {
1137 }
else if (!strcasecmp(var->
name,
"mode")) {
1139 }
else if (!strcasecmp(var->
name,
"entry")) {
1143 if (!playlist_entries) {
1144 playlist_entries = moh_file_vector_alloc(16);
1145 if (!playlist_entries) {
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",
1166 ast_log(LOG_ERROR,
"Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->
value);
1168 }
else if (!strcasecmp(var->
name,
"directory")) {
1170 }
else if (!strcasecmp(var->
name,
"application")) {
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;
1183 ast_set2_flag(mohclass,
ast_true(var->
value), MOH_RANDOMIZE);
1184 }
else if (!strcasecmp(var->
name,
"sort")) {
1185 if (!strcasecmp(var->
value,
"random")) {
1186 ast_set_flag(mohclass, MOH_RANDOMIZE);
1187 }
else if (!strcasecmp(var->
value,
"alpha")) {
1188 ast_set_flag(mohclass, MOH_SORTALPHA);
1189 }
else if (!strcasecmp(var->
value,
"randstart")) {
1192 }
else if (!strcasecmp(var->
name,
"loop_last")) {
1198 }
else if (!strcasecmp(var->
name,
"format") && !ast_strlen_zero(var->
value)) {
1199 ao2_cleanup(mohclass->
format);
1202 ast_log(LOG_WARNING,
"Unknown format '%s' -- defaulting to SLIN\n", var->
value);
1205 }
else if (!strcasecmp(var->
name,
"kill_escalation_delay")) {
1209 ast_log(LOG_WARNING,
"kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->
value);
1212 }
else if (!strcasecmp(var->
name,
"kill_method")) {
1213 if (!strcasecmp(var->
value,
"process")) {
1215 }
else if (!strcasecmp(var->
value,
"process_group")) {
1216 mohclass->
kill_method = KILL_METHOD_PROCESS_GROUP;
1218 ast_log(LOG_WARNING,
"kill_method '%s' is invalid. Setting to 'process_group'\n", var->
value);
1219 mohclass->
kill_method = KILL_METHOD_PROCESS_GROUP;
1221 }
else if (!strcasecmp(var->
name,
"answeredonly")) {
1226 if (playlist_entries) {
1228 if (strcasecmp(mohclass->mode,
"playlist")) {
1229 ast_log(LOG_NOTICE,
"Ignoring playlist entries because we are in '%s' mode.\n",
1231 ao2_ref(playlist_entries, -1);
1240 mohclass->
files = playlist_entries;
1244 static int on_moh_file(
const char *directory,
const char *filename,
void *obj)
1251 if (*filename ==
'.') {
1252 ast_debug(4,
"Skipping '%s/%s' because it starts with a dot\n",
1253 directory, filename);
1259 extension = strrchr(filename,
'.');
1261 ast_debug(4,
"Skipping '%s/%s' because it doesn't have an extension\n",
1262 directory, filename);
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);
1276 (
int) (extension - filename), filename) < 0) {
1285 ast_free(full_path);
1292 ast_free(full_path);
1299 static int moh_filename_strcasecmp(
const void *a,
const void *b)
1301 const char **s1 = (
const char **) a;
1302 const char **s2 = (
const char **) b;
1303 return strcasecmp(*s1, *s2);
1306 static int moh_scan_files(
struct mohclass *
class) {
1308 char dir_path[PATH_MAX -
sizeof(
class->dir)];
1311 if (class->dir[0] !=
'/') {
1312 snprintf(dir_path,
sizeof(dir_path),
"%s/%s", ast_config_AST_DATA_DIR, class->dir);
1317 ast_debug(4,
"Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1320 files = moh_file_vector_alloc(16);
1330 if (ast_test_flag(
class, MOH_SORTALPHA)) {
1338 class->files =
files;
1344 static int init_files_class(
struct mohclass *
class)
1348 res = moh_scan_files(
class);
1355 ast_verb(3,
"Files not found in %s for moh class:%s\n",
1356 class->dir, class->name);
1363 static void moh_rescan_files(
void) {
1369 while ((c = ao2_iterator_next(&i))) {
1370 if (!strcasecmp(c->mode,
"files")) {
1379 static int moh_diff(
struct mohclass *old,
struct mohclass *
new)
1385 if (strcmp(old->dir, new->dir)) {
1387 }
else if (strcmp(old->mode, new->mode)) {
1389 }
else if (strcmp(old->args, new->args)) {
1391 }
else if (old->flags != new->flags) {
1398 static int init_app_class(
struct mohclass *
class)
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")) {
1405 ast_set_flag(
class, MOH_SINGLE | MOH_QUIET);
1406 }
else if (!strcasecmp(class->mode,
"quietmp3")) {
1407 ast_set_flag(
class, MOH_QUIET);
1413 ast_log(LOG_WARNING,
"Unable to create timer: %s\n", strerror(errno));
1417 ast_log(LOG_WARNING,
"Unable to set 40ms frame rate: %s\n", strerror(errno));
1419 class->timer = NULL;
1422 if (ast_pthread_create_background(&class->thread, NULL, monmp3thread,
class)) {
1423 ast_log(LOG_WARNING,
"Unable to create moh thread...\n");
1426 class->timer = NULL;
1437 #define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1438 static int _moh_register(
struct mohclass *moh,
int reload,
int unref,
const char *file,
int line,
const char *funcname)
1440 struct mohclass *mohclass = NULL;
1442 mohclass = _get_mohbyname(moh->name, 0,
MOH_NOTDELETED, file, line, funcname);
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");
1448 moh = mohclass_unref(moh,
"unreffing potential new moh class (it is a duplicate)");
1451 }
else if (mohclass) {
1453 mohclass = mohclass_unref(mohclass,
"unreffing mohclass we just found by name");
1457 moh->start -= respawn_time;
1459 if (!strcasecmp(moh->mode,
"files")) {
1460 if (init_files_class(moh)) {
1462 moh = mohclass_unref(moh,
"unreffing potential new moh class (init_files_class failed)");
1466 }
else if (!strcasecmp(moh->mode,
"playlist")) {
1475 moh = mohclass_unref(moh,
"unreffing potential new moh class (no playlist entries)");
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)) {
1484 moh = mohclass_unref(moh,
"unreffing potential new moh class (init_app_class_failed)");
1489 ast_log(LOG_WARNING,
"Don't know how to do a mode '%s' music on hold\n", moh->mode);
1491 moh = mohclass_unref(moh,
"unreffing potential new moh class (unknown mode)");
1496 ao2_t_link(mohclasses, moh,
"Adding class to container");
1499 moh = mohclass_unref(moh,
"Unreffing new moh class because we just added it to the container");
1505 #define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1506 static int _moh_unregister(
struct mohclass *moh,
const char *file,
int line,
const char *funcname)
1508 ao2_t_unlink(mohclasses, moh,
"Removing class from container");
1512 static void local_ast_moh_cleanup(
struct ast_channel *chan)
1517 ast_channel_music_state_set(chan, NULL);
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");
1524 ao2_cleanup(state->origwfmt);
1525 ao2_cleanup(state->mohwfmt);
1540 struct mohclass *cur;
1542 int wordlen = strlen(word);
1550 while ((cur = ao2_t_iterator_next(&i,
"iterate thru mohclasses"))) {
1551 if (cur->
realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1553 mohclass_unref(cur,
"drop ref in iterator loop break");
1556 mohclass_unref(cur,
"drop ref in iterator loop");
1565 struct mohclass *cur;
1572 e->
command =
"moh unregister class";
1574 "Usage: moh unregister class <class>\n"
1575 " Unregisters a realtime moh class.\n";
1582 return CLI_SHOWUSAGE;
1584 len = strlen(a->argv[3]);
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)) {
1592 mohclass_unref(cur,
"drop ref in iterator loop");
1597 moh_unregister(cur);
1598 mohclass_unref(cur,
"drop ref after unregister");
1600 ast_cli(a->fd,
"No such realtime moh class '%s'\n", a->argv[3]);
1608 static void moh_class_destructor(
void *obj);
1610 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1612 static struct mohclass *_moh_class_malloc(
const char *file,
int line,
const char *funcname)
1614 struct mohclass *
class;
1617 "Allocating new moh class", file, line, funcname);
1621 class->kill_delay = 100000;
1624 class->files = moh_file_vector_alloc(0);
1625 if (!class->files) {
1634 static struct ast_variable *load_realtime_musiconhold(
const char *name)
1636 struct ast_variable *var = ast_load_realtime(
"musiconhold",
"name", name, SENTINEL);
1642 char *category = NULL;
1643 size_t entry_count = 0;
1648 const char *
entry = ast_variable_retrieve(entries, category,
"entry");
1651 struct ast_variable *dup = ast_variable_new(
"entry", entry,
"");
1654 ast_variable_list_append(&var, dup);
1661 if (entry_count == 0) {
1670 ast_log(LOG_WARNING,
1671 "Music on Hold class '%s' not found in memory/database. "
1672 "Verify your configuration.\n",
1678 static int local_ast_moh_start(
struct ast_channel *chan,
const char *mclass,
const char *interpclass)
1680 struct mohclass *mohclass = NULL;
1686 int warn_if_not_in_memory = !realtime_possible;
1687 const char *classes[] = {NULL, NULL, interpclass,
"default"};
1690 classes[0] = ast_channel_musicclass(chan);
1691 classes[1] = mclass;
1693 classes[0] = mclass;
1694 classes[1] = ast_channel_musicclass(chan);
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]);
1718 if (mohclass || var) {
1728 if ((mohclass = moh_class_malloc())) {
1731 moh_parse_options(var, mohclass);
1734 if (ast_strlen_zero(mohclass->dir)) {
1735 if (!strcasecmp(mohclass->mode,
"custom") || !strcasecmp(mohclass->mode,
"playlist")) {
1736 strcpy(mohclass->dir,
"nodir");
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)");
1743 if (ast_strlen_zero(mohclass->mode)) {
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)");
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");
1756 if (state && state->
class) {
1758 ast_log(LOG_NOTICE,
"This channel already has a MOH class attached (%s)!\n", state->
class->name);
1767 mohclass = mohclass_unref(mohclass,
"unreffing mohclass failed to register");
1773 time(&mohclass->start);
1774 mohclass->start -= respawn_time;
1776 if (!strcasecmp(mohclass->mode,
"files")) {
1785 if (!moh_scan_files(mohclass)) {
1786 mohclass = mohclass_unref(mohclass,
"unreffing potential mohclass (moh_scan_files failed)");
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;
1795 ast_set_flag(mohclass, MOH_RANDOMIZE);
1797 }
else if (!strcasecmp(mohclass->mode,
"playlist")) {
1802 ao2_unlock(mohclass);
1805 mohclass = mohclass_unref(mohclass,
"unreffing potential mohclass (no playlist entries)");
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")) {
1810 if (!strcasecmp(mohclass->mode,
"custom"))
1811 ast_set_flag(mohclass, MOH_CUSTOM);
1812 else if (!strcasecmp(mohclass->mode,
"mp3nb"))
1813 ast_set_flag(mohclass, MOH_SINGLE);
1814 else if (!strcasecmp(mohclass->mode,
"quietmp3nb"))
1815 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1816 else if (!strcasecmp(mohclass->mode,
"quietmp3"))
1817 ast_set_flag(mohclass, MOH_QUIET);
1819 mohclass->
srcfd = -1;
1821 ast_log(LOG_WARNING,
"Unable to create timer: %s\n", strerror(errno));
1824 ast_log(LOG_WARNING,
"Unable to set 40ms frame rate: %s\n", strerror(errno));
1826 mohclass->
timer = NULL;
1830 if (state && state->
class) {
1832 ast_log(LOG_NOTICE,
"This channel already has a MOH class attached (%s)!\n", state->
class->name);
1835 mohclass = mohclass_unref(mohclass,
"unreffing potential mohclass (channel already has one)");
1836 mohclass = mohclass_ref(state->
class,
"using existing class from state");
1839 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1840 ast_log(LOG_WARNING,
"Unable to create moh...\n");
1841 if (mohclass->
timer) {
1843 mohclass->
timer = NULL;
1845 mohclass = mohclass_unref(mohclass,
"Unreffing potential mohclass (failed to create background thread)");
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)");
1866 ast_verb(3,
"The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1879 if (!moh_scan_files(mohclass)) {
1880 mohclass = mohclass_unref(mohclass,
"unreffing potential mohclass (moh_scan_files failed)");
1885 if (!state || !state->
class || strcmp(mohclass->name, state->
class->name)) {
1890 ao2_unlock(mohclass);
1899 ast_channel_lock(chan);
1900 ast_channel_latest_musicclass_set(chan, mohclass->name);
1902 ast_channel_unlock(chan);
1905 mohclass = mohclass_unref(mohclass,
"unreffing local reference to mohclass in local_ast_moh_start");
1910 static void local_ast_moh_stop(
struct ast_channel *chan)
1914 ast_channel_lock(chan);
1916 if (ast_channel_music_state(chan)) {
1917 if (ast_channel_stream(chan)) {
1919 ast_channel_stream_set(chan, NULL);
1922 ast_channel_unlock(chan);
1925 static void moh_class_destructor(
void *obj)
1927 struct mohclass *
class = obj;
1931 ast_debug(1,
"Destroying MOH class '%s'\n", class->name);
1937 ao2_cleanup(class->files);
1942 if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1943 tid =
class->thread;
1944 class->thread = AST_PTHREADT_NULL;
1945 pthread_cancel(tid);
1950 if (class->pid > 1) {
1952 int bytes, tbytes = 0, stime = 0;
1954 ast_debug(1,
"killing %d!\n", class->pid);
1956 stime = time(NULL) + 2;
1957 killpid(class->pid, class->kill_delay, class->kill_method);
1959 while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1960 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1961 tbytes = tbytes + bytes;
1964 ast_debug(1,
"mpg123 pid %d and child died after %d bytes read\n",
1965 class->pid, tbytes);
1968 close(class->srcfd);
1974 class->timer = NULL;
1977 ao2_cleanup(class->format);
1981 pthread_join(tid, NULL);
1986 static int moh_class_mark(
void *obj,
void *arg,
int flags)
1988 struct mohclass *
class = obj;
1990 if ( ((flags &
MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1997 static int moh_classes_delete_marked(
void *obj,
void *arg,
int flags)
1999 struct mohclass *
class = obj;
2004 static int load_moh_classes(
int reload)
2008 struct mohclass *
class;
2015 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2017 ao2_t_callback(mohclasses,
OBJ_NODATA |
MOH_REALTIME, moh_class_mark, NULL,
"Mark realtime classes for deletion");
2024 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
2026 ao2_t_callback(mohclasses,
OBJ_NODATA, moh_class_mark, NULL,
"Mark deleted classes");
2033 ao2_t_callback(mohclasses,
OBJ_NODATA, moh_class_mark, NULL,
"Mark deleted classes");
2036 ast_clear_flag(global_flags, AST_FLAGS_ALL);
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")) {
2049 ast_log(LOG_WARNING,
"Unknown option '%s' in [general] section of musiconhold.conf\n", var->
name);
2055 if (!(
class = moh_class_malloc())) {
2059 moh_parse_options(ast_variable_browse(cfg, cat),
class);
2064 if (ast_strlen_zero(class->dir)) {
2065 if (!strcasecmp(class->mode,
"custom") || !strcasecmp(class->mode,
"playlist")) {
2066 strcpy(class->dir,
"nodir");
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)");
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)");
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)");
2093 moh_classes_delete_marked, NULL,
"Purge marked classes");
2098 static void ast_moh_destroy(
void)
2100 ast_verb(2,
"Destroying musiconhold processes\n");
2114 "Usage: moh reload\n"
2115 " Reloads the MusicOnHold module.\n"
2116 " Alias for 'module reload res_musiconhold.so'\n";
2122 if (a->argc != e->
args)
2123 return CLI_SHOWUSAGE;
2133 struct mohclass *
class;
2138 e->
command =
"moh show files";
2140 "Usage: moh show files\n"
2141 " Lists all loaded file-based MusicOnHold classes and their\n"
2148 if (a->argc != e->
args)
2149 return CLI_SHOWUSAGE;
2152 for (; (
class = ao2_t_iterator_next(&i,
"Show files iterator")); mohclass_unref(
class,
"Unref iterator in moh show files")) {
2161 ast_cli(a->fd,
"Class: %s\n", class->name);
2176 struct mohclass *
class;
2181 e->
command =
"moh show classes";
2183 "Usage: moh show classes\n"
2184 " Lists all MusicOnHold classes.\n";
2190 if (a->argc != e->
args)
2191 return CLI_SHOWUSAGE;
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>"));
2199 ast_cli(a->fd,
"\tAnnouncement: %s\n",
S_OR(class->announcement,
"<none>"));
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");
2207 if (strcasecmp(class->mode,
"files")) {
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")
2223 static int moh_class_hash(
const void *obj,
const int flags)
2225 const struct mohclass *
class = obj;
2230 static int moh_class_cmp(
void *obj,
void *arg,
int flags)
2232 struct mohclass *
class = obj, *class2 = arg;
2234 return strcasecmp(class->name, class2->name) ? 0 :
2235 (flags &
MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2254 moh_class_hash, NULL, moh_class_cmp,
"Moh class container");
2260 ast_log(LOG_WARNING,
"No music on hold classes configured, "
2261 "disabling music on hold.\n");
2263 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2264 local_ast_moh_cleanup);
2278 static int reload(
void)
2280 if (load_moh_classes(1)) {
2281 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2282 local_ast_moh_cleanup);
2288 static int moh_class_inuse(
void *obj,
void *arg,
int flags)
2290 struct mohclass *
class = obj;
2295 static int unload_module(
void)
2298 struct mohclass *
class = NULL;
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");
2308 ast_log(LOG_WARNING,
"Unable to unload res_musiconhold due to active MOH channels\n");
2312 ast_uninstall_music_functions();
2324 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"Music On Hold Resource",
2325 .support_level = AST_MODULE_SUPPORT_CORE,
2327 .unload = unload_module,
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
struct ast_variable * next
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
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 ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Support for translation of data formats. translate.c.
#define moh_register(moh, reload, unref)
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Structure for variables, used for configurations and for channel variables.
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
ast_channel_state
ast_channel states
struct ast_timer * ast_timer_open(void)
Open a timer.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
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.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
int ast_unregister_application(const char *app)
Unregister an application.
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
struct ast_frame_subclass subclass
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Blob of data associated with a channel.
int args
This gets set in ast_cli_register()
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
#define MOH_CACHERTCLASSES
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
structure to hold extensions
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
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 ...
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
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.
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define MOH_PREFERCHANNELCLASS
struct ast_vector_string * files
Core PBX routines and definitions.
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...
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child...
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
const ast_string_field name
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
#define ast_module_ref(mod)
Hold a reference to the module.
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
union ast_frame::@224 data
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.
#define ast_calloc(num, len)
A wrapper for calloc()
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
int ast_closestream(struct ast_filestream *f)
Closes a stream.
struct ast_format * format
enum kill_methods kill_method
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.
Module has failed to load, may be in an inconsistent state.
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Structure used to handle boolean flags.
static struct ast_flags global_flags[1]
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.
#define ast_module_unref(mod)
Release a reference to the module.
void ast_deactivate_generator(struct ast_channel *chan)
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
struct mohclass::@446 list
Data structure associated with a single frame of data.
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Abstract JSON element (object, array, string, int, ...).
enum ast_frame_type frametype
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...
String vector definitions.
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.
struct ast_format * format
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
static struct mohclass * get_mohbydigit(char digit)
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int load_module(void)
Load the module.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Timing source management.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
#define AST_APP_ARG(name)
Define an application argument.