- add support for setting an AGISTATUS variable that indicates successful

execution, failure, or if the channel requested hangup.
- only return -1 from the application if the application requested hangup. If
  there was just a failure in execution of the AGI, just set the status
  variable appropriately and move on in the dialplan.
(issue #7121, original patch by Alessandro Polverini, updated patch by srt,
 committed patch is heavily modified to allow still returning -1 on hangup)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@30272 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Russell Bryant 20 years ago
parent e0612c9835
commit 0cccaf8408

@ -602,6 +602,7 @@ In Asterisk 1.2, many applications return the result in a variable
instead of, as in Asterisk 1.0, changing the dial plan priority (+101). instead of, as in Asterisk 1.0, changing the dial plan priority (+101).
For the various status values, see each application's help text. For the various status values, see each application's help text.
${AGISTATUS} * agi()
${AQMSTATUS} * addqueuemember() ${AQMSTATUS} * addqueuemember()
${AVAILSTATUS} * chanisavail() ${AVAILSTATUS} * chanisavail()
${CHECKGROUPSTATUS} * checkgroup() ${CHECKGROUPSTATUS} * checkgroup()

@ -86,11 +86,15 @@ static char *descrip =
"written in any language to control a telephony channel, play audio,\n" "written in any language to control a telephony channel, play audio,\n"
"read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
"and stdout.\n" "and stdout.\n"
"Returns -1 on hangup (except for DeadAGI) or if application requested\n" " This channel will stop dialplan execution on hangup inside of this\n"
" hangup, or 0 on non-hangup exit. \n" "application, except when using DeadAGI. Otherwise, dialplan execution\n"
"Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n" "will continue normally.\n"
" Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
"on file descriptor 3\n\n" "on file descriptor 3\n\n"
"Use the CLI command 'show agi' to list available agi commands\n"; " Use the CLI command 'show agi' to list available agi commands\n"
" This application sets the following channel variable upon completion:\n"
" AGISTATUS The status of the attempt to the run the AGI script\n"
" text string, one of SUCCESS | FAILED | HANGUP\n";
static int agidebug = 0; static int agidebug = 0;
@ -103,6 +107,12 @@ struct module_symbols *me;
#define AGI_PORT 4573 #define AGI_PORT 4573
enum agi_result {
AGI_RESULT_SUCCESS,
AGI_RESULT_FAILURE,
AGI_RESULT_HANGUP
};
static void agi_debug_cli(int fd, char *fmt, ...) static void agi_debug_cli(int fd, char *fmt, ...)
{ {
char *stuff; char *stuff;
@ -124,7 +134,7 @@ static void agi_debug_cli(int fd, char *fmt, ...)
/* launch_netscript: The fastagi handler. /* launch_netscript: The fastagi handler.
FastAGI defaults to port 4573 */ FastAGI defaults to port 4573 */
static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid) static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
{ {
int s; int s;
int flags; int flags;
@ -181,7 +191,7 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) { if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno)); ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
close(s); close(s);
return -1; return AGI_RESULT_FAILURE;
} }
pfds[0].fd = s; pfds[0].fd = s;
@ -190,7 +200,7 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int
if (errno != EINTR) { if (errno != EINTR) {
ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
close(s); close(s);
return -1; return AGI_RESULT_FAILURE;
} }
} }
/* XXX in theory should check for partial writes... */ /* XXX in theory should check for partial writes... */
@ -198,7 +208,7 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int
if (errno != EINTR) { if (errno != EINTR) {
ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
close(s); close(s);
return -1; return AGI_RESULT_FAILURE;
} }
} }
@ -211,10 +221,10 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int
fds[0] = s; fds[0] = s;
fds[1] = s; fds[1] = s;
*opid = -1; *opid = -1;
return 0; return AGI_RESULT_SUCCESS;
} }
static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
{ {
char tmp[256]; char tmp[256];
int pid; int pid;
@ -234,13 +244,13 @@ static int launch_script(char *script, char *argv[], int *fds, int *efd, int *op
} }
if (pipe(toast)) { if (pipe(toast)) {
ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno)); ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
return -1; return AGI_RESULT_FAILURE;
} }
if (pipe(fromast)) { if (pipe(fromast)) {
ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno)); ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
close(toast[0]); close(toast[0]);
close(toast[1]); close(toast[1]);
return -1; return AGI_RESULT_FAILURE;
} }
if (efd) { if (efd) {
if (pipe(audio)) { if (pipe(audio)) {
@ -249,7 +259,7 @@ static int launch_script(char *script, char *argv[], int *fds, int *efd, int *op
close(fromast[1]); close(fromast[1]);
close(toast[0]); close(toast[0]);
close(toast[1]); close(toast[1]);
return -1; return AGI_RESULT_FAILURE;
} }
res = fcntl(audio[1], F_GETFL); res = fcntl(audio[1], F_GETFL);
if (res > -1) if (res > -1)
@ -262,13 +272,13 @@ static int launch_script(char *script, char *argv[], int *fds, int *efd, int *op
close(toast[1]); close(toast[1]);
close(audio[0]); close(audio[0]);
close(audio[1]); close(audio[1]);
return -1; return AGI_RESULT_FAILURE;
} }
} }
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
return -1; return AGI_RESULT_FAILURE;
} }
if (!pid) { if (!pid) {
/* Pass paths to AGI via environmental variables */ /* Pass paths to AGI via environmental variables */
@ -324,13 +334,11 @@ static int launch_script(char *script, char *argv[], int *fds, int *efd, int *op
close(toast[1]); close(toast[1]);
close(fromast[0]); close(fromast[0]);
if (efd) { if (efd)
/* [PHM 12/18/03] */
close(audio[0]); close(audio[0]);
}
*opid = pid; *opid = pid;
return 0; return AGI_RESULT_SUCCESS;
} }
@ -1773,12 +1781,12 @@ static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
return 0; return 0;
} }
#define RETRY 3 #define RETRY 3
static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead) static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
{ {
struct ast_channel *c; struct ast_channel *c;
int outfd; int outfd;
int ms; int ms;
int returnstatus = 0; enum agi_result returnstatus = AGI_RESULT_SUCCESS;
struct ast_frame *f; struct ast_frame *f;
char buf[2048]; char buf[2048];
FILE *readf; FILE *readf;
@ -1791,7 +1799,7 @@ static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, i
if (pid > -1) if (pid > -1)
kill(pid, SIGHUP); kill(pid, SIGHUP);
close(agi->ctrl); close(agi->ctrl);
return -1; return AGI_RESULT_FAILURE;
} }
setlinebuf(readf); setlinebuf(readf);
setup_env(chan, request, agi->fd, (agi->audio > -1)); setup_env(chan, request, agi->fd, (agi->audio > -1));
@ -1804,7 +1812,7 @@ static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, i
f = ast_read(c); f = ast_read(c);
if (!f) { if (!f) {
ast_log(LOG_DEBUG, "%s hungup\n", chan->name); ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
returnstatus = -1; returnstatus = AGI_RESULT_HANGUP;
break; break;
} else { } else {
/* If it's voice, write it to the audio pipe */ /* If it's voice, write it to the audio pipe */
@ -1839,7 +1847,7 @@ static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, i
} else { } else {
if (--retry <= 0) { if (--retry <= 0) {
ast_log(LOG_WARNING, "No channel, no fd?\n"); ast_log(LOG_WARNING, "No channel, no fd?\n");
returnstatus = -1; returnstatus = AGI_RESULT_FAILURE;
break; break;
} }
} }
@ -1933,7 +1941,7 @@ static int handle_dumpagihtml(int fd, int argc, char *argv[])
static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead) static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
{ {
int res=0; enum agi_result res;
struct localuser *u; struct localuser *u;
char *argv[MAX_ARGS]; char *argv[MAX_ARGS];
char buf[2048]=""; char buf[2048]="";
@ -1967,7 +1975,7 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
} }
#endif #endif
res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid); res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
if (!res) { if (res == AGI_RESULT_SUCCESS) {
agi.fd = fds[1]; agi.fd = fds[1];
agi.ctrl = fds[0]; agi.ctrl = fds[0];
agi.audio = efd; agi.audio = efd;
@ -1977,7 +1985,20 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
close(efd); close(efd);
} }
ast_localuser_remove(me, u); ast_localuser_remove(me, u);
return res;
switch (res) {
case AGI_RESULT_SUCCESS:
pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
break;
case AGI_RESULT_FAILURE:
pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
break;
case AGI_RESULT_HANGUP:
pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
return -1;
}
return 0;
} }
static int agi_exec(struct ast_channel *chan, void *data) static int agi_exec(struct ast_channel *chan, void *data)

Loading…
Cancel
Save