Asterisk - The Open Source Telephony Project GIT-master-a358458
Data Structures | Macros | Enumerations | Functions | Variables
app_externalivr.c File Reference

External IVR application interface. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/astobj2.h"
Include dependency graph for app_externalivr.c:

Go to the source code of this file.

Data Structures

struct  ivr_localuser::finishlist
 
struct  gen_state
 
struct  ivr_localuser
 
struct  ivr_localuser::playlist
 
struct  playlist_entry
 

Macros

#define ast_chan_log(level, channel, format, ...)   ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
 
#define EIVR_CMD_ANS   'T' /* answer channel */
 
#define EIVR_CMD_APND   'A' /* append to prompt queue */
 
#define EIVR_CMD_DTMF   'D' /* send DTMF */
 
#define EIVR_CMD_EXIT   'E' /* exit */
 
#define EIVR_CMD_GET   'G' /* get channel varable(s) */
 
#define EIVR_CMD_HGUP   'H' /* hangup */
 
#define EIVR_CMD_IRPT   'I' /* interrupt */
 
#define EIVR_CMD_LOG   'L' /* log message */
 
#define EIVR_CMD_OPT   'O' /* option */
 
#define EIVR_CMD_PARM   'P' /* return supplied params */
 
#define EIVR_CMD_SQUE   'S' /* (re)set prompt queue */
 
#define EIVR_CMD_SVAR   'V' /* set channel varable(s) */
 
#define EIVR_CMD_XIT   'X' /* exit **depricated** */
 
#define EXTERNALIVR_PORT   2949
 

Enumerations

enum  options_flags { noanswer = (1 << 0) , ignore_hangup = (1 << 1) , run_dead = (1 << 2) }
 

Functions

static int app_exec (struct ast_channel *chan, const char *data)
 
static void ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
 
static void ast_eivr_senddtmf (struct ast_channel *chan, char *vdata)
 
static void ast_eivr_setvariable (struct ast_channel *chan, char *data)
 
 AST_MODULE_INFO_STANDARD_EXTENDED (ASTERISK_GPL_KEY, "External IVR Interface Application")
 
static int eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, struct ast_iostream *eivr_events, struct ast_iostream *eivr_commands, struct ast_iostream *eivr_errors, const struct ast_str *args, const struct ast_flags flags)
 
static void * gen_alloc (struct ast_channel *chan, void *params)
 
static void gen_closestream (struct gen_state *state)
 
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
 
static int gen_nextfile (struct gen_state *state)
 
static struct ast_framegen_readframe (struct gen_state *state)
 
static void gen_release (struct ast_channel *chan, void *data)
 
static int load_module (void)
 
static struct playlist_entrymake_entry (const char *filename)
 
static void send_eivr_event (struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan)
 
static int unload_module (void)
 

Variables

static const char app [] = "ExternalIVR"
 
static const struct ast_app_option app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead }, }
 
static struct ast_generator gen
 

Detailed Description

External IVR application interface.

Author
Kevin P. Fleming kpfle.nosp@m.ming.nosp@m.@digi.nosp@m.um.c.nosp@m.om
Note
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.

Macro Definition Documentation

◆ ast_chan_log

#define ast_chan_log (   level,
  channel,
  format,
  ... 
)    ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)

Definition at line 98 of file app_externalivr.c.

◆ EIVR_CMD_ANS

#define EIVR_CMD_ANS   'T' /* answer channel */

Definition at line 111 of file app_externalivr.c.

◆ EIVR_CMD_APND

#define EIVR_CMD_APND   'A' /* append to prompt queue */

Definition at line 101 of file app_externalivr.c.

◆ EIVR_CMD_DTMF

#define EIVR_CMD_DTMF   'D' /* send DTMF */

Definition at line 102 of file app_externalivr.c.

◆ EIVR_CMD_EXIT

#define EIVR_CMD_EXIT   'E' /* exit */

Definition at line 103 of file app_externalivr.c.

◆ EIVR_CMD_GET

#define EIVR_CMD_GET   'G' /* get channel varable(s) */

Definition at line 104 of file app_externalivr.c.

◆ EIVR_CMD_HGUP

#define EIVR_CMD_HGUP   'H' /* hangup */

Definition at line 105 of file app_externalivr.c.

◆ EIVR_CMD_IRPT

#define EIVR_CMD_IRPT   'I' /* interrupt */

Definition at line 106 of file app_externalivr.c.

◆ EIVR_CMD_LOG

#define EIVR_CMD_LOG   'L' /* log message */

Definition at line 107 of file app_externalivr.c.

◆ EIVR_CMD_OPT

#define EIVR_CMD_OPT   'O' /* option */

Definition at line 108 of file app_externalivr.c.

◆ EIVR_CMD_PARM

#define EIVR_CMD_PARM   'P' /* return supplied params */

Definition at line 109 of file app_externalivr.c.

◆ EIVR_CMD_SQUE

#define EIVR_CMD_SQUE   'S' /* (re)set prompt queue */

Definition at line 110 of file app_externalivr.c.

◆ EIVR_CMD_SVAR

#define EIVR_CMD_SVAR   'V' /* set channel varable(s) */

Definition at line 112 of file app_externalivr.c.

◆ EIVR_CMD_XIT

#define EIVR_CMD_XIT   'X' /* exit **depricated** */

Definition at line 113 of file app_externalivr.c.

◆ EXTERNALIVR_PORT

#define EXTERNALIVR_PORT   2949

Definition at line 115 of file app_externalivr.c.

Enumeration Type Documentation

◆ options_flags

Enumerator
noanswer 
ignore_hangup 
run_dead 

Definition at line 117 of file app_externalivr.c.

117 {
118 noanswer = (1 << 0),
119 ignore_hangup = (1 << 1),
120 run_dead = (1 << 2),
121};
@ run_dead
@ ignore_hangup
@ noanswer

Function Documentation

◆ app_exec()

static int app_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 392 of file app_externalivr.c.

393{
394 struct ast_flags flags = { 0, };
395 char *opts[0];
396 struct playlist_entry *entry;
397 int child_stdin[2] = { -1, -1 };
398 int child_stdout[2] = { -1, -1 };
399 int child_stderr[2] = { -1, -1 };
400 struct ast_iostream *stream_stdin = NULL, *stream_stdout = NULL,
401 *stream_stderr = NULL;
402 int res = -1;
403 int pid;
404
405 struct ast_tcptls_session_instance *ser = NULL;
406
407 struct ivr_localuser foo = {
409 .finishlist = AST_LIST_HEAD_INIT_VALUE,
410 .gen_active = 0,
411 .playing_silence = 1,
412 };
413 struct ivr_localuser *u = &foo;
414
415 char *buf;
416 int j;
417 char *s, **app_args, *e;
418 struct ast_str *comma_delim_args = ast_str_alloca(100);
419
420 AST_DECLARE_APP_ARGS(eivr_args,
421 AST_APP_ARG(application);
423 );
424 AST_DECLARE_APP_ARGS(application_args,
425 AST_APP_ARG(cmd)[32];
426 );
427
428 u->abort_current_sound = 0;
429 u->chan = chan;
430
431 if (ast_strlen_zero(data)) {
432 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
433 goto exit;
434 }
435
436 buf = ast_strdupa(data);
437 AST_STANDARD_APP_ARGS(eivr_args, buf);
438
439 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
440 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
441
442 /* Parse out any application arguments */
443 if ((s = strchr(eivr_args.application, '('))) {
444 s[0] = ',';
445 if ((e = strrchr(s, ')'))) {
446 *e = '\0';
447 } else {
448 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
449 goto exit;
450 }
451 }
452
453 AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
454 app_args = application_args.argv;
455
456 /* Put the application + the arguments in a , delimited list */
457 ast_str_reset(comma_delim_args);
458 for (j = 0; application_args.cmd[j] != NULL; j++) {
459 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
460 }
461
462 /* Get rid of any extraneous arguments */
463 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
464 *s = '\0';
465 }
466
467 /* Parse the ExternalIVR() arguments */
468 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
469 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
470 if (ast_test_flag(&flags, noanswer)) {
471 ast_verb(4, "noanswer is set\n");
472 }
473 if (ast_test_flag(&flags, ignore_hangup)) {
474 ast_verb(4, "ignore_hangup is set\n");
475 }
476 if (ast_test_flag(&flags, run_dead)) {
477 ast_verb(4, "run_dead is set\n");
478 }
479
480 if (!(ast_test_flag(&flags, noanswer))) {
481 ast_verb(3, "Answering channel and starting generator\n");
482 if (ast_channel_state(chan) != AST_STATE_UP) {
483 if (ast_test_flag(&flags, run_dead)) {
484 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
485 goto exit;
486 }
487 ast_answer(chan);
488 }
489 if (ast_activate_generator(chan, &gen, u) < 0) {
490 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
491 goto exit;
492 } else {
493 u->gen_active = 1;
494 }
495 }
496
497 if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
498 struct ast_tcptls_session_args ivr_desc = {
499 .accept_fd = -1,
500 .name = "IVR",
501 };
502 struct ast_sockaddr *addrs;
503 int num_addrs = 0, i = 0;
504 char *host = app_args[0] + sizeof("ivr://") - 1;
505
506 /* Communicate through socket to server */
507 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
508
509 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
510 ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
511 goto exit;
512 }
513
514 for (i = 0; i < num_addrs; i++) {
515 if (!ast_sockaddr_port(&addrs[i])) {
516 /* Default port if not specified */
518 }
519 ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
520 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
521 continue;
522 }
523 break;
524 }
525
526 ast_free(addrs);
527
528 if (i == num_addrs) {
529 ast_chan_log(LOG_ERROR, chan, "Could not connect to any host. ExternalIVR failed.\n");
530 goto exit;
531 }
532
533 res = eivr_comm(chan, u, ser->stream, ser->stream, NULL, comma_delim_args, flags);
534
535 } else {
536 if (pipe(child_stdin)) {
537 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
538 goto exit;
539 }
540 if (pipe(child_stdout)) {
541 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
542 goto exit;
543 }
544 if (pipe(child_stderr)) {
545 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
546 goto exit;
547 }
548
549 pid = ast_safe_fork(0);
550 if (pid < 0) {
551 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
552 goto exit;
553 }
554
555 if (!pid) {
556 /* child process */
559
560 dup2(child_stdin[0], STDIN_FILENO);
561 dup2(child_stdout[1], STDOUT_FILENO);
562 dup2(child_stderr[1], STDERR_FILENO);
563 ast_close_fds_above_n(STDERR_FILENO);
564 execv(app_args[0], app_args);
565 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
566 _exit(1);
567 } else {
568 /* parent process */
569 close(child_stdin[0]);
570 child_stdin[0] = -1;
571 close(child_stdout[1]);
572 child_stdout[1] = -1;
573 close(child_stderr[1]);
574 child_stderr[1] = -1;
575
576 stream_stdin = ast_iostream_from_fd(&child_stdin[1]);
577 stream_stdout = ast_iostream_from_fd(&child_stdout[0]);
578 stream_stderr = ast_iostream_from_fd(&child_stderr[0]);
579
580 res = eivr_comm(chan, u, stream_stdin, stream_stdout, stream_stderr, comma_delim_args, flags);
581 }
582 }
583
584 exit:
585 if (u->gen_active) {
587 }
588 if (stream_stdin) {
589 ast_iostream_close(stream_stdin);
590 }
591 if (stream_stdout) {
592 ast_iostream_close(stream_stdout);
593 }
594 if (stream_stderr) {
595 ast_iostream_close(stream_stderr);
596 }
597 if (child_stdin[0] > -1) {
598 close(child_stdin[0]);
599 }
600 if (child_stdin[1] > -1) {
601 close(child_stdin[1]);
602 }
603 if (child_stdout[0] > -1) {
604 close(child_stdout[0]);
605 }
606 if (child_stdout[1] > -1) {
607 close(child_stdout[1]);
608 }
609 if (child_stderr[0] > -1) {
610 close(child_stderr[0]);
611 }
612 if (child_stderr[1] > -1) {
613 close(child_stderr[1]);
614 }
615 if (ser) {
616 ao2_ref(ser, -1);
617 }
618 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
620 }
621 return res;
622}
#define EXTERNALIVR_PORT
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, struct ast_iostream *eivr_events, struct ast_iostream *eivr_commands, struct ast_iostream *eivr_errors, const struct ast_str *args, const struct ast_flags flags)
#define ast_chan_log(level, channel, format,...)
static const struct ast_app_option app_opts[128]
static struct ast_generator gen
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 ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2951
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2893
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#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.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
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
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
struct ast_iostream * ast_iostream_from_fd(int *fd)
Create an iostream from a file descriptor.
Definition: iostream.c:604
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:528
#define AST_LIST_HEAD_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD.
Definition: linkedlists.h:234
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
int errno
@ AST_AF_UNSPEC
Definition: netsock2.h:54
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition: netsock2.h:167
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
#define ast_opt_high_priority
Definition: options.h:112
#define NULL
Definition: resample.c:96
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_alloca(init_len)
Definition: strings.h:848
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Socket address structure.
Definition: netsock2.h:97
Support for dynamic strings.
Definition: strings.h:623
arguments for the accepting thread
Definition: tcptls.h:130
struct ast_sockaddr remote_address
Definition: tcptls.h:133
describes a server instance
Definition: tcptls.h:150
struct ast_iostream * stream
Definition: tcptls.h:161
Definition: search.h:40
struct ast_channel * chan
struct ivr_localuser::playlist playlist
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Creates a client connection's ast_tcptls_session_instance.
Definition: tcptls.c:678
struct ast_tcptls_session_instance * ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
Attempt to connect and start a tcptls session.
Definition: tcptls.c:673
static struct test_options options
#define ast_test_flag(p, flag)
Definition: utils.h:63

References ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, ast_activate_generator(), AST_AF_UNSPEC, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_iostream_close(), ast_iostream_from_fd(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log, ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_copy(), ast_sockaddr_port, ast_sockaddr_resolve(), ast_sockaddr_set_port, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, ast_verb, buf, ivr_localuser::chan, eivr_comm(), errno, EXTERNALIVR_PORT, ast_flags::flags, gen, ivr_localuser::gen_active, ignore_hangup, LOG_ERROR, noanswer, NULL, options, ivr_localuser::playlist, ast_tcptls_session_args::remote_address, run_dead, and ast_tcptls_session_instance::stream.

Referenced by load_module().

◆ ast_eivr_getvariable()

static void ast_eivr_getvariable ( struct ast_channel chan,
char *  data,
char *  outbuf,
int  outbuflen 
)
static

Definition at line 304 of file app_externalivr.c.

305{
306 /* original input data: "G,var1,var2," */
307 /* data passed as "data": "var1,var2" */
308
309 char *inbuf, *variable;
310 const char *value;
311 int j;
312 struct ast_str *newstring = ast_str_alloca(outbuflen);
313
314 outbuf[0] = '\0';
315
316 for (j = 1, inbuf = data; ; j++) {
317 variable = strsep(&inbuf, ",");
318 if (variable == NULL) {
319 int outstrlen = strlen(outbuf);
320 if (outstrlen && outbuf[outstrlen - 1] == ',') {
321 outbuf[outstrlen - 1] = 0;
322 }
323 break;
324 }
325
326 ast_channel_lock(chan);
327 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
328 value = "";
329 }
330
331 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
332 ast_channel_unlock(chan);
333 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
334 }
335}
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_unlock(chan)
Definition: channel.h:2923
char * strsep(char **str, const char *delims)
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int value
Definition: syslog.c:37
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
Definition: utils.c:590

References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), ast_str_buffer(), inbuf(), NULL, pbx_builtin_getvar_helper(), strsep(), and value.

Referenced by eivr_comm().

◆ ast_eivr_senddtmf()

static void ast_eivr_senddtmf ( struct ast_channel chan,
char *  vdata 
)
static

Definition at line 356 of file app_externalivr.c.

357{
358
359 char *data;
360 int dinterval = 0, duration = 0;
362 AST_APP_ARG(digits);
363 AST_APP_ARG(dinterval);
364 AST_APP_ARG(duration);
365 );
366
367 data = ast_strdupa(vdata);
369
370 if (!ast_strlen_zero(args.dinterval)) {
371 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
372 }
373 if (!ast_strlen_zero(args.duration)) {
374 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
375 }
376 ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
377 ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
378}
int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit)
Common routine to parse time lengths, with optional time unit specifier.
Definition: main/app.c:3263
@ TIMELEN_MILLISECONDS
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
Send a string of DTMF digits to a channel.
Definition: main/app.c:1127
const char * args

References args, AST_APP_ARG, ast_app_parse_timelen(), AST_DECLARE_APP_ARGS, ast_dtmf_stream(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, NULL, and TIMELEN_MILLISECONDS.

Referenced by eivr_comm().

◆ ast_eivr_setvariable()

static void ast_eivr_setvariable ( struct ast_channel chan,
char *  data 
)
static

Definition at line 337 of file app_externalivr.c.

338{
339 char *value;
340
341 char *inbuf = ast_strdupa(data), *variable;
342
343 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
344 ast_debug(1, "Setting up a variable: %s\n", variable);
345 /* variable contains "varname=value" */
346 value = strchr(variable, '=');
347 if (!value) {
348 value = "";
349 } else {
350 *value++ = '\0';
351 }
352 pbx_builtin_setvar_helper(chan, variable, value);
353 }
354}
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.

References ast_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), strsep(), and value.

Referenced by eivr_comm().

◆ AST_MODULE_INFO_STANDARD_EXTENDED()

AST_MODULE_INFO_STANDARD_EXTENDED ( ASTERISK_GPL_KEY  ,
"External IVR Interface Application"   
)

◆ eivr_comm()

static int eivr_comm ( struct ast_channel chan,
struct ivr_localuser u,
struct ast_iostream eivr_events,
struct ast_iostream eivr_commands,
struct ast_iostream eivr_errors,
const struct ast_str args,
const struct ast_flags  flags 
)
static

Definition at line 624 of file app_externalivr.c.

629{
630 char input[1024];
631 struct playlist_entry *entry;
632 struct ast_frame *f;
633 int ms;
634 int exception;
635 int ready_fd;
636 int waitfds[2];
637 int r;
638 struct ast_channel *rchan;
639 int res = -1;
640 int hangup_info_sent = 0;
641
642 waitfds[0] = ast_iostream_get_fd(eivr_commands);
643 waitfds[1] = eivr_errors ? ast_iostream_get_fd(eivr_errors) : -1;
644
645 while (1) {
647 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
648 break;
649 }
650 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
652 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
653 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
654 hangup_info_sent = 1;
655 } else {
656 ast_verb(3, "Got check_hangup\n");
657 send_eivr_event(eivr_events, 'H', NULL, chan);
658 break;
659 }
660 }
661
662 ready_fd = 0;
663 ms = 100;
664 errno = 0;
665 exception = 0;
666
667 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors) ? 2 : 1, &exception, &ready_fd, &ms);
668
671 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
672 send_eivr_event(eivr_events, 'F', entry->filename, chan);
674 }
676 }
677
678 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
679 /* the channel has something */
680 f = ast_read(chan);
681 if (!f) {
682 ast_verb(3, "Returned no frame\n");
683 send_eivr_event(eivr_events, 'H', NULL, chan);
684 break;
685 }
686 if (f->frametype == AST_FRAME_DTMF) {
687 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
688 if (u->option_autoclear) {
690 if (!u->abort_current_sound && !u->playing_silence) {
691 /* send interrupted file as T data */
692 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
693 send_eivr_event(eivr_events, 'T', entry->filename, chan);
695 }
696 }
697 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
698 send_eivr_event(eivr_events, 'D', entry->filename, chan);
700 }
701 if (!u->playing_silence)
702 u->abort_current_sound = 1;
704 }
705 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
706 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
707 send_eivr_event(eivr_events, 'H', NULL, chan);
708 if (f->data.uint32) {
710 }
711 ast_frfree(f);
712 break;
713 }
714 ast_frfree(f);
715 } else if (ready_fd == waitfds[0]) {
716 if (exception) {
717 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
718 break;
719 }
720
721 r = ast_iostream_gets(eivr_commands, input, sizeof(input));
722 if (r <= 0) {
723 if (r == 0) {
724 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
725 break;
726 }
727 continue;
728 }
729
731 ast_verb(4, "got command '%s'\n", input);
732
733 if (strlen(input) < 3) {
734 continue;
735 }
736
737 if (input[0] == EIVR_CMD_PARM) {
738 struct ast_str *tmp = (struct ast_str *) args;
739 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
740 } else if (input[0] == EIVR_CMD_DTMF) {
741 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
742 ast_eivr_senddtmf(chan, &input[2]);
743 } else if (input[0] == EIVR_CMD_ANS) {
744 ast_verb(3, "Answering channel if needed and starting generator\n");
745 if (ast_channel_state(chan) != AST_STATE_UP) {
746 if (ast_test_flag(&flags, run_dead)) {
747 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
748 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
749 continue;
750 }
751 if (ast_answer(chan)) {
752 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
753 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
754 continue;
755 }
756 }
757 if (!(u->gen_active)) {
758 if (ast_activate_generator(chan, &gen, u) < 0) {
759 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
760 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
761 } else {
762 u->gen_active = 1;
763 }
764 }
765 } else if (input[0] == EIVR_CMD_IRPT) {
766 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
767 ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
768 send_eivr_event(eivr_events, 'Z', NULL, chan);
769 continue;
770 }
772 if (!u->abort_current_sound && !u->playing_silence) {
773 /* send interrupted file as T data */
774 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
775 send_eivr_event(eivr_events, 'T', entry->filename, chan);
777 }
778 }
779 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780 send_eivr_event(eivr_events, 'D', entry->filename, chan);
782 }
783 if (!u->playing_silence) {
784 u->abort_current_sound = 1;
785 }
787 } else if (input[0] == EIVR_CMD_SQUE) {
788 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
789 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
790 send_eivr_event(eivr_events, 'Z', NULL, chan);
791 continue;
792 }
794 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
795 send_eivr_event(eivr_events, 'Z', &input[2], chan);
796 } else {
798 if (!u->abort_current_sound && !u->playing_silence) {
799 /* send interrupted file as T data */
800 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
801 send_eivr_event(eivr_events, 'T', entry->filename, chan);
803 }
804 }
805 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806 send_eivr_event(eivr_events, 'D', entry->filename, chan);
808 }
809 if (!u->playing_silence) {
810 u->abort_current_sound = 1;
811 }
812 entry = make_entry(&input[2]);
813 if (entry) {
815 }
817 }
818 } else if (input[0] == EIVR_CMD_APND) {
819 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
820 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
821 send_eivr_event(eivr_events, 'Z', NULL, chan);
822 continue;
823 }
825 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
826 send_eivr_event(eivr_events, 'Z', &input[2], chan);
827 } else {
828 entry = make_entry(&input[2]);
829 if (entry) {
833 }
834 }
835 } else if (input[0] == EIVR_CMD_GET) {
836 char response[2048];
837 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
838 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
839 send_eivr_event(eivr_events, 'G', response, chan);
840 } else if (input[0] == EIVR_CMD_SVAR) {
841 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
842 ast_eivr_setvariable(chan, &input[2]);
843 } else if (input[0] == EIVR_CMD_LOG) {
844 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
845 } else if (input[0] == EIVR_CMD_XIT) {
846 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
847 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
848 res = 0;
849 break;
850 } else if (input[0] == EIVR_CMD_EXIT) {
851 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
852 send_eivr_event(eivr_events, 'E', NULL, chan);
853 res = 0;
854 break;
855 } else if (input[0] == EIVR_CMD_HGUP) {
856 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
857 send_eivr_event(eivr_events, 'H', NULL, chan);
858 break;
859 } else if (input[0] == EIVR_CMD_OPT) {
860 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
861 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
862 send_eivr_event(eivr_events, 'Z', NULL, chan);
863 continue;
864 }
865 if (!strcasecmp(&input[2], "autoclear"))
866 u->option_autoclear = 1;
867 else if (!strcasecmp(&input[2], "noautoclear"))
868 u->option_autoclear = 0;
869 else
870 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
871 }
872 } else if (ready_fd == waitfds[1]) {
873 if (exception) {
874 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
875 break;
876 }
877
878 r = ast_iostream_gets(eivr_errors, input, sizeof(input));
879 if (r > 0) {
880 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
881 } else if (r == 0) {
882 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
883 break;
884 }
885 } else if ((ready_fd < 0) && ms) {
886 if (errno == 0 || errno == EINTR)
887 continue;
888
889 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
890 break;
891 }
892 }
893
894 return res;
895}
#define EIVR_CMD_EXIT
#define EIVR_CMD_XIT
#define EIVR_CMD_SVAR
#define EIVR_CMD_DTMF
#define EIVR_CMD_OPT
static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan)
static struct playlist_entry * make_entry(const char *filename)
#define EIVR_CMD_ANS
#define EIVR_CMD_GET
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
#define EIVR_CMD_APND
#define EIVR_CMD_IRPT
#define EIVR_CMD_LOG
#define EIVR_CMD_HGUP
#define EIVR_CMD_PARM
static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
#define EIVR_CMD_SQUE
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1570
static int tmp()
Definition: bt_open.c:389
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2988
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
const char * ast_channel_language(const struct ast_channel *chan)
@ AST_FLAG_ZOMBIE
Definition: channel.h:987
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_HANGUP
#define LOG_NOTICE
#define LOG_WARNING
ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
Read a LF-terminated string from an iostream.
Definition: iostream.c:300
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:84
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Main Channel structure associated with a channel.
struct ast_flags flags
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
struct ivr_localuser::finishlist finishlist

References ivr_localuser::abort_current_sound, args, ast_activate_generator(), ast_answer(), ast_chan_log, ast_channel_flags(), ast_channel_hangupcause_set(), ast_channel_language(), ast_check_hangup(), AST_CONTROL_HANGUP, ast_eivr_getvariable(), ast_eivr_senddtmf(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, ast_iostream_get_fd(), ast_iostream_gets(), AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_str_buffer(), ast_strip(), ast_test_flag, ast_verb, ast_waitfor_nandfds(), ivr_localuser::chan, ast_frame::data, EIVR_CMD_ANS, EIVR_CMD_APND, EIVR_CMD_DTMF, EIVR_CMD_EXIT, EIVR_CMD_GET, EIVR_CMD_HGUP, EIVR_CMD_IRPT, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, errno, ivr_localuser::finishlist, ast_channel::flags, ast_frame::frametype, gen, ivr_localuser::gen_active, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), NULL, ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_frame::subclass, tmp(), and ast_frame::uint32.

Referenced by app_exec().

◆ gen_alloc()

static void * gen_alloc ( struct ast_channel chan,
void *  params 
)
static

Definition at line 175 of file app_externalivr.c.

176{
177 struct ivr_localuser *u = params;
178 struct gen_state *state;
179
180 if (!(state = ast_calloc(1, sizeof(*state))))
181 return NULL;
182
183 state->u = u;
184
185 return state;
186}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
enum cc_state state
Definition: ccss.c:393
struct ivr_localuser * u

References ast_calloc, NULL, state, and gen_state::u.

◆ gen_closestream()

static void gen_closestream ( struct gen_state state)
static

Definition at line 188 of file app_externalivr.c.

189{
190 if (!state->stream)
191 return;
192
193 ast_closestream(state->stream);
195 state->stream = NULL;
196}
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111

References ast_channel_stream_set(), ast_closestream(), and NULL.

Referenced by gen_nextfile(), gen_readframe(), and gen_release().

◆ gen_generate()

static int gen_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
)
static

Definition at line 273 of file app_externalivr.c.

274{
275 struct gen_state *state = data;
276 struct ast_frame *f = NULL;
277 int res = 0;
278
279 state->sample_queue += samples;
280
281 while (state->sample_queue > 0) {
282 if (!(f = gen_readframe(state)))
283 return -1;
284
285 res = ast_write(chan, f);
286 ast_frfree(f);
287 if (res < 0) {
288 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
289 return -1;
290 }
291 state->sample_queue -= f->samples;
292 }
293
294 return res;
295}
static struct ast_frame * gen_readframe(struct gen_state *state)
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

References ast_chan_log, ast_frfree, ast_write(), errno, gen_readframe(), LOG_WARNING, NULL, and ast_frame::samples.

◆ gen_nextfile()

static int gen_nextfile ( struct gen_state state)
static

Definition at line 207 of file app_externalivr.c.

208{
209 struct ivr_localuser *u = state->u;
210 char *file_to_stream;
211
212 u->abort_current_sound = 0;
213 u->playing_silence = 0;
215
216 while (!state->stream) {
217 state->current = AST_LIST_FIRST(&u->playlist);
218 if (state->current) {
219 file_to_stream = state->current->filename;
220 } else {
221 file_to_stream = "silence/10";
222 u->playing_silence = 1;
223 }
224
225 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
226 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
230 if (!u->playing_silence) {
231 continue;
232 } else {
233 break;
234 }
235 }
236 }
237
238 return (!state->stream);
239}
static void gen_closestream(struct gen_state *state)
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
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421

References ivr_localuser::abort_current_sound, ast_chan_log, ast_channel_language(), AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, errno, gen_closestream(), LOG_WARNING, ivr_localuser::playing_silence, and ivr_localuser::playlist.

Referenced by gen_readframe().

◆ gen_readframe()

static struct ast_frame * gen_readframe ( struct gen_state state)
static

Definition at line 241 of file app_externalivr.c.

242{
243 struct ast_frame *f = NULL;
244 struct ivr_localuser *u = state->u;
245
246 if (u->abort_current_sound ||
252 }
253
254 if (!(state->stream && (f = ast_readframe(state->stream)))) {
255 if (state->current) {
256 /* remove finished file from playlist */
260 /* add finished file to finishlist */
262 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
264 state->current = NULL;
265 }
266 if (!gen_nextfile(state))
267 f = ast_readframe(state->stream);
268 }
269
270 return f;
271}
static int gen_nextfile(struct gen_state *state)
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:936

References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_readframe(), ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), NULL, ivr_localuser::playing_silence, and ivr_localuser::playlist.

Referenced by gen_generate().

◆ gen_release()

static void gen_release ( struct ast_channel chan,
void *  data 
)
static

Definition at line 198 of file app_externalivr.c.

199{
200 struct gen_state *state = data;
201
203 ast_free(data);
204}

References ast_free, and gen_closestream().

◆ load_module()

static int load_module ( void  )
static

Definition at line 902 of file app_externalivr.c.

903{
905}
static const char app[]
static int app_exec(struct ast_channel *chan, const char *data)
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626

References app, app_exec(), and ast_register_application_xml.

◆ make_entry()

static struct playlist_entry * make_entry ( const char *  filename)
static

Definition at line 380 of file app_externalivr.c.

381{
382 struct playlist_entry *entry;
383
384 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
385 return NULL;
386
387 strcpy(entry->filename, filename);
388
389 return entry;
390}
char filename[1]

References ast_calloc, playlist_entry::filename, and NULL.

Referenced by eivr_comm().

◆ send_eivr_event()

static void send_eivr_event ( struct ast_iostream stream,
const char  event,
const char *  data,
const struct ast_channel chan 
)
static

Definition at line 158 of file app_externalivr.c.

160{
161 struct ast_str *tmp = ast_str_create(12);
162
163 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
164 if (data) {
165 ast_str_append(&tmp, 0, ",%s", data);
166 }
167 ast_str_append(&tmp, 0, "\n");
170
171 ast_debug(1, "sent '%s'", ast_str_buffer(tmp));
172 ast_free(tmp);
173}
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:374
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:786
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
Definition: astman.c:222

References ast_debug, ast_free, ast_iostream_write(), ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_truncate(), NULL, and tmp().

Referenced by eivr_comm().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 897 of file app_externalivr.c.

898{
900}
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392

References app, and ast_unregister_application().

Variable Documentation

◆ app

const char app[] = "ExternalIVR"
static

Definition at line 95 of file app_externalivr.c.

Referenced by load_module(), and unload_module().

◆ app_opts

const struct ast_app_option app_opts[128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead }, }
static

Definition at line 127 of file app_externalivr.c.

Referenced by app_exec().

◆ gen

struct ast_generator gen
static