<p>Dan Jenkins has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11657">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add XAGI dialplan application<br><br>This enablesusto be able to pull audio out of asterisk and push it<br>in using file descriptors and a new dialplan application<br><br>Co-authored-by: Torrey Searle <tsearle@gmail.com><br>Change-Id: I32a656294ef28a4c4a98f0afb880e52952aa1eec<br>---<br>M include/asterisk/agi.h<br>M res/res_agi.c<br>2 files changed, 227 insertions(+), 64 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/57/11657/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/agi.h b/include/asterisk/agi.h</span><br><span>index 568cd5d..73c7d48 100644</span><br><span>--- a/include/asterisk/agi.h</span><br><span>+++ b/include/asterisk/agi.h</span><br><span>@@ -34,6 +34,7 @@</span><br><span> typedef struct agi_state {</span><br><span> int fd; /*!< FD for general output */</span><br><span> int audio; /*!< FD for audio output */</span><br><span style="color: hsl(120, 100%, 40%);">+ int audio_in; /* FD for audio output */</span><br><span> int ctrl; /*!< FD for input control */</span><br><span> unsigned int fast:1; /*!< flag for fast agi or not */</span><br><span> struct ast_speech *speech; /*!< Speech structure for speech recognition */</span><br><span>diff --git a/res/res_agi.c b/res/res_agi.c</span><br><span>index d88d511..02eb015 100644</span><br><span>--- a/res/res_agi.c</span><br><span>+++ b/res/res_agi.c</span><br><span>@@ -1211,6 +1211,7 @@</span><br><span> <ref type="manager">AGI</ref></span><br><span> <ref type="managerEvent">AsyncAGIStart</ref></span><br><span> <ref type="managerEvent">AsyncAGIEnd</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">XAGI</ref></span><br><span> <ref type="application">EAGI</ref></span><br><span> <ref type="application">DeadAGI</ref></span><br><span> <ref type="filename">asterisk.conf</ref></span><br><span>@@ -1234,6 +1235,29 @@</span><br><span> </description></span><br><span> <see-also></span><br><span> <ref type="application">AGI</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">XGI</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">DeadAGI</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="XAGI" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Executes an XAGI compliant application.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/application[@name='AGI']/syntax/parameter[@name='command'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/application[@name='AGI']/syntax/parameter[@name='args'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Using 'XAGI' provides enhanced AGI, with incoming and outgoing audio available out of band</span><br><span style="color: hsl(120, 100%, 40%);">+ on file descriptor 3 and 4. In all other respects, it behaves in the same fashion as</span><br><span style="color: hsl(120, 100%, 40%);">+ AGI/EAGI. See the documentation for the <literal>AGI</literal> dialplan application for</span><br><span style="color: hsl(120, 100%, 40%);">+ more information on invoking AGI on a channel.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application sets the following channel variable upon completion:</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/variablelist)" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">AGI</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">EGI</ref></span><br><span> <ref type="application">DeadAGI</ref></span><br><span> </see-also></span><br><span> </application></span><br><span>@@ -1260,6 +1284,7 @@</span><br><span> </description></span><br><span> <see-also></span><br><span> <ref type="application">AGI</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">XAGI</ref></span><br><span> <ref type="application">EAGI</ref></span><br><span> </see-also></span><br><span> </application></span><br><span>@@ -1387,6 +1412,8 @@</span><br><span> </span><br><span> static char *app = "AGI";</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *xapp = "XAGI";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static char *eapp = "EAGI";</span><br><span> </span><br><span> static char *deadapp = "DeadAGI";</span><br><span>@@ -2191,10 +2218,10 @@</span><br><span> return AGI_RESULT_FAILURE;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static enum agi_result launch_script(struct ast_channel *chan, char *script, int argc, char *argv[], int *fds, int *efd, int *opid)</span><br><span style="color: hsl(120, 100%, 40%);">+static enum agi_result launch_script(struct ast_channel *chan, char *script, int argc, char *argv[], int *fds, int *efd, int *efd2, int *opid)</span><br><span> {</span><br><span> char tmp[256];</span><br><span style="color: hsl(0, 100%, 40%);">- int pid, toast[2], fromast[2], audio[2], res;</span><br><span style="color: hsl(120, 100%, 40%);">+ int pid, toast[2], fromast[2], audio[2], audio2[2], res;</span><br><span> struct stat st;</span><br><span> </span><br><span> if (!strncasecmp(script, "agi://", 6)) {</span><br><span>@@ -2251,6 +2278,31 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (efd2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pipe(audio2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ close(fromast[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(fromast[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(toast[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(toast[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AGI_RESULT_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_fd_set_flags(audio2[1], O_NONBLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ close(fromast[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(fromast[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(toast[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(toast[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(audio[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(audio[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(audio2[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(audio2[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AGI_RESULT_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if ((pid = ast_safe_fork(1)) < 0) {</span><br><span> ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));</span><br><span> return AGI_RESULT_FAILURE;</span><br><span>@@ -2280,8 +2332,13 @@</span><br><span> else</span><br><span> close(STDERR_FILENO + 1);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (efd2)</span><br><span style="color: hsl(120, 100%, 40%);">+ dup2(audio2[1], STDERR_FILENO + 2);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ close(STDERR_FILENO + 2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Close everything but stdin/out/error */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_close_fds_above_n(STDERR_FILENO + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_close_fds_above_n(STDERR_FILENO + 2);</span><br><span> </span><br><span> /* Execute script */</span><br><span> /* XXX argv should be deprecated in favor of passing agi_argX paramaters */</span><br><span>@@ -2298,12 +2355,17 @@</span><br><span> fds[1] = fromast[1];</span><br><span> if (efd)</span><br><span> *efd = audio[1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (efd2)</span><br><span style="color: hsl(120, 100%, 40%);">+ *efd2 = audio2[0];</span><br><span> /* close what we're not using in the parent */</span><br><span> close(toast[1]);</span><br><span> close(fromast[0]);</span><br><span> </span><br><span> if (efd)</span><br><span> close(audio[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (efd2)</span><br><span style="color: hsl(120, 100%, 40%);">+ close(audio2[1]);</span><br><span> </span><br><span> *opid = pid;</span><br><span> return AGI_RESULT_SUCCESS;</span><br><span>@@ -2340,7 +2402,7 @@</span><br><span> ast_agi_send(fd, chan, "agi_context: %s\n", ast_channel_context(chan));</span><br><span> ast_agi_send(fd, chan, "agi_extension: %s\n", ast_channel_exten(chan));</span><br><span> ast_agi_send(fd, chan, "agi_priority: %d\n", ast_channel_priority(chan));</span><br><span style="color: hsl(0, 100%, 40%);">- ast_agi_send(fd, chan, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_agi_send(fd, chan, "agi_enhanced: %d%s\n", enhanced, ".0");</span><br><span> </span><br><span> /* User information */</span><br><span> ast_agi_send(fd, chan, "agi_accountcode: %s\n", ast_channel_accountcode(chan) ? ast_channel_accountcode(chan) : "");</span><br><span>@@ -4111,8 +4173,13 @@</span><br><span> int needhup = 0;</span><br><span> enum agi_result returnstatus = AGI_RESULT_SUCCESS;</span><br><span> struct ast_frame *f;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame fr;</span><br><span> char buf[AGI_BUF_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+ char audiobuf[320];</span><br><span> char *res = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int audiobytes;</span><br><span style="color: hsl(120, 100%, 40%);">+ int fds[2];</span><br><span style="color: hsl(120, 100%, 40%);">+ int enhanced = 0;</span><br><span> FILE *readf;</span><br><span> /* how many times we'll retry if ast_waitfor_nandfs will return without either</span><br><span> channel or file descriptor in case select is interrupted by a system call (EINTR) */</span><br><span>@@ -4123,6 +4190,7 @@</span><br><span> int exit_on_hangup;</span><br><span> /*! Running in an interception routine is like DeadAGI mode. No touchy the channel frames. */</span><br><span> int in_intercept = ast_channel_get_intercept_mode();</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval next_xagi_read = ast_tvnow();</span><br><span> </span><br><span> ast_channel_lock(chan);</span><br><span> sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");</span><br><span>@@ -4140,8 +4208,18 @@</span><br><span> }</span><br><span> </span><br><span> setlinebuf(readf);</span><br><span style="color: hsl(0, 100%, 40%);">- setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (agi->audio > -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ enhanced = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (agi->audio_in > -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ enhanced++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ setup_env(chan, request, agi->fd, enhanced, argc, argv);</span><br><span style="color: hsl(120, 100%, 40%);">+ fds[0] = agi->ctrl;</span><br><span style="color: hsl(120, 100%, 40%);">+ fds[1] = agi->audio_in;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> for (;;) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int timeout_expected = 0;</span><br><span> if (needhup) {</span><br><span> needhup = 0;</span><br><span> dead = 1;</span><br><span>@@ -4160,7 +4238,15 @@</span><br><span> if (dead || in_intercept) {</span><br><span> c = ast_waitfor_nandfds(&chan, 0, &agi->ctrl, 1, NULL, &outfd, &ms);</span><br><span> } else if (!ast_check_hangup(chan)) {</span><br><span style="color: hsl(0, 100%, 40%);">- c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval now = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ int delta = ast_tvdiff_ms(next_xagi_read, now);</span><br><span style="color: hsl(120, 100%, 40%);">+ if(delta <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ c = ast_waitfor_nandfds(&chan, 1, fds, 2, NULL, &outfd, &ms);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* don't check if audio is available until the next timeout */</span><br><span style="color: hsl(120, 100%, 40%);">+ c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &delta);</span><br><span style="color: hsl(120, 100%, 40%);">+ timeout_expected = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> } else {</span><br><span> /*</span><br><span> * Read the channel control queue until it is dry so we can</span><br><span>@@ -4188,65 +4274,81 @@</span><br><span> ast_frfree(f);</span><br><span> }</span><br><span> } else if (outfd > -1) {</span><br><span style="color: hsl(0, 100%, 40%);">- size_t len = sizeof(buf);</span><br><span style="color: hsl(0, 100%, 40%);">- size_t buflen = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- enum agi_result cmd_status;</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((agi->audio_in > -1) && (outfd == agi->audio_in)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ audiobytes = read(agi->audio_in, audiobuf, sizeof(audiobuf));</span><br><span style="color: hsl(120, 100%, 40%);">+ next_xagi_read = ast_tvadd(ast_tvnow(), ast_tv(0,20000));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (audiobytes > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&fr, 0, sizeof(fr));</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.frametype = AST_FRAME_VOICE;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.subclass.format = ast_format_slin;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.datalen = audiobytes;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.data.ptr = audiobuf;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.samples = audiobytes/2;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr.offset = AST_FRIENDLY_OFFSET;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- retry = AGI_NANDFS_RETRY;</span><br><span style="color: hsl(0, 100%, 40%);">- buf[0] = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- while (len > 1) {</span><br><span style="color: hsl(0, 100%, 40%);">- res = fgets(buf + buflen, len, readf);</span><br><span style="color: hsl(0, 100%, 40%);">- if (feof(readf))</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- if (res != NULL && !agi->fast)</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- buflen = strlen(buf);</span><br><span style="color: hsl(0, 100%, 40%);">- if (buflen && buf[buflen - 1] == '\n')</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- len = sizeof(buf) - buflen;</span><br><span style="color: hsl(0, 100%, 40%);">- if (agidebug)</span><br><span style="color: hsl(0, 100%, 40%);">- ast_verbose("AGI Rx << temp buffer %s - errno %s\nNo \\n received, checking again.\n", buf, strerror(errno));</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!buf[0]) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* Program terminated */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", ast_channel_name(chan), request, returnstatus);</span><br><span style="color: hsl(0, 100%, 40%);">- if (pid > 0)</span><br><span style="color: hsl(0, 100%, 40%);">- waitpid(pid, status, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- /* No need to kill the pid anymore, since they closed us */</span><br><span style="color: hsl(0, 100%, 40%);">- pid = -1;</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* Special case for inability to execute child process */</span><br><span style="color: hsl(0, 100%, 40%);">- if (*buf && strncasecmp(buf, "failure", 7) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- returnstatus = AGI_RESULT_FAILURE;</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* get rid of trailing newline, if any */</span><br><span style="color: hsl(0, 100%, 40%);">- buflen = strlen(buf);</span><br><span style="color: hsl(0, 100%, 40%);">- if (buflen && buf[buflen - 1] == '\n') {</span><br><span style="color: hsl(0, 100%, 40%);">- buf[buflen - 1] = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (agidebug)</span><br><span style="color: hsl(0, 100%, 40%);">- ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf);</span><br><span style="color: hsl(0, 100%, 40%);">- cmd_status = agi_handle_command(chan, agi, buf, dead || in_intercept);</span><br><span style="color: hsl(0, 100%, 40%);">- switch (cmd_status) {</span><br><span style="color: hsl(0, 100%, 40%);">- case AGI_RESULT_FAILURE:</span><br><span style="color: hsl(0, 100%, 40%);">- if (dead || in_intercept || !ast_check_hangup(chan)) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* The failure was not because of a hangup. */</span><br><span style="color: hsl(0, 100%, 40%);">- returnstatus = AGI_RESULT_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_write(chan, &fr);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t len = sizeof(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t buflen = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum agi_result cmd_status;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ retry = AGI_NANDFS_RETRY;</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (len > 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = fgets(buf + buflen, len, readf);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (feof(readf))</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != NULL && !agi->fast)</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ buflen = strlen(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (buflen && buf[buflen - 1] == '\n')</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ len = sizeof(buf) - buflen;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (agidebug)</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verbose("AGI Rx << temp buffer %s - errno %s\nNo \\n received, checking again.\n", buf, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buf[0]) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Program terminated */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", ast_channel_name(chan), request, returnstatus);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pid > 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ waitpid(pid, status, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No need to kill the pid anymore, since they closed us */</span><br><span style="color: hsl(120, 100%, 40%);">+ pid = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Special case for inability to execute child process */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*buf && strncasecmp(buf, "failure", 7) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ returnstatus = AGI_RESULT_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* get rid of trailing newline, if any */</span><br><span style="color: hsl(120, 100%, 40%);">+ buflen = strlen(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (buflen && buf[buflen - 1] == '\n') {</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[buflen - 1] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (agidebug)</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd_status = agi_handle_command(chan, agi, buf, dead || in_intercept);</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd_status) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AGI_RESULT_FAILURE:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dead || in_intercept || !ast_check_hangup(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The failure was not because of a hangup. */</span><br><span style="color: hsl(120, 100%, 40%);">+ returnstatus = AGI_RESULT_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!timeout_expected) {</span><br><span> if (--retry <= 0) {</span><br><span> ast_log(LOG_WARNING, "No channel, no fd?\n");</span><br><span> returnstatus = AGI_RESULT_FAILURE;</span><br><span>@@ -4489,7 +4591,7 @@</span><br><span> {</span><br><span> enum agi_result res;</span><br><span> char *buf;</span><br><span style="color: hsl(0, 100%, 40%);">- int fds[2], efd = -1, pid = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int fds[2], efd = -1, efd2 = -1, pid = -1;</span><br><span> AST_DECLARE_APP_ARGS(args,</span><br><span> AST_APP_ARG(arg)[MAX_ARGS];</span><br><span> );</span><br><span>@@ -4512,7 +4614,7 @@</span><br><span> return -1;</span><br><span> }</span><br><span> #endif</span><br><span style="color: hsl(0, 100%, 40%);">- res = launch_script(chan, args.argv[0], args.argc, args.argv, fds, enhanced ? &efd : NULL, &pid);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = launch_script(chan, args.argv[0], args.argc, args.argv, fds, enhanced ? &efd : NULL, (enhanced == 2) ? &efd2 : NULL, &pid);</span><br><span> /* Async AGI do not require run_agi(), so just proceed if normal AGI</span><br><span> or Fast AGI are setup with success. */</span><br><span> if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {</span><br><span>@@ -4520,6 +4622,7 @@</span><br><span> agi.fd = fds[1];</span><br><span> agi.ctrl = fds[0];</span><br><span> agi.audio = efd;</span><br><span style="color: hsl(120, 100%, 40%);">+ agi.audio_in = efd2;</span><br><span> agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;</span><br><span> res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);</span><br><span> /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */</span><br><span>@@ -4529,6 +4632,8 @@</span><br><span> close(fds[1]);</span><br><span> if (efd > -1)</span><br><span> close(efd);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (efd2 > -1)</span><br><span style="color: hsl(120, 100%, 40%);">+ close(efd2);</span><br><span> }</span><br><span> ast_safe_fork_cleanup();</span><br><span> </span><br><span>@@ -4602,6 +4707,61 @@</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int xagi_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format *readformat;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format *writeformat;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format *requested_format = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *requested_format_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_check_hangup(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ requested_format_name = pbx_builtin_getvar_helper(chan, "EAGI_AUDIO_FORMAT");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (requested_format_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ requested_format = ast_format_cache_get(requested_format_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (requested_format) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "<%s> Setting EAGI audio pipe format to %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan), ast_format_get_name(requested_format));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Could not find requested format: %s\n", requested_format_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ readformat = ao2_bump(ast_channel_readformat(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, requested_format ?: ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(requested_format);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(readformat);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ writeformat = ao2_bump(ast_channel_writeformat(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, requested_format ?: ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(requested_format);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(writeformat);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = agi_exec_full(chan, data, 2, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, readformat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", ast_channel_name(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_get_name(readformat));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, writeformat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", ast_channel_name(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_get_name(writeformat));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(requested_format);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(readformat);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(writeformat);</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int deadagi_exec(struct ast_channel *chan, const char *data)</span><br><span> {</span><br><span> ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");</span><br><span>@@ -4666,6 +4826,7 @@</span><br><span> ast_cli_unregister_multiple(cli_agi, ARRAY_LEN(cli_agi));</span><br><span> ast_agi_unregister_multiple(commands, ARRAY_LEN(commands));</span><br><span> ast_unregister_application(eapp);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_unregister_application(xapp);</span><br><span> ast_unregister_application(deadapp);</span><br><span> ast_manager_unregister("AGI");</span><br><span> ast_unregister_application(app);</span><br><span>@@ -4687,6 +4848,7 @@</span><br><span> err |= ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));</span><br><span> err |= ast_register_application_xml(deadapp, deadagi_exec);</span><br><span> err |= ast_register_application_xml(eapp, eagi_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+ err |= ast_register_application_xml(xapp, xagi_exec);</span><br><span> err |= ast_manager_register_xml("AGI", EVENT_FLAG_AGI, action_add_agi_cmd);</span><br><span> err |= ast_register_application_xml(app, agi_exec);</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11657">change 11657</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/11657"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: I32a656294ef28a4c4a98f0afb880e52952aa1eec </div>
<div style="display:none"> Gerrit-Change-Number: 11657 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Dan Jenkins <dan@nimbleape.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>