<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>