summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Bryant <russell@russellbryant.com>2006-05-25 18:31:19 +0000
committerRussell Bryant <russell@russellbryant.com>2006-05-25 18:31:19 +0000
commit238cdb249fc3f831cc5d28e1c69855a6bb3dca53 (patch)
tree708ea01afbf6bf30580cf15a2baa4b615bfb86c2
parent35d729fb40d57c635bc7f27589c43bbb63c5b575 (diff)
Add the ability to retrieve the exit code of the forked AGI process. If there
is an error executing the AGI script, or the AGI script itself returns a non-zero value, the AGISTATUS variable will now be set to FAILURE instead of SUCCESS. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@30328 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--asterisk.c46
-rw-r--r--include/asterisk/app.h21
-rw-r--r--res/res_agi.c13
3 files changed, 61 insertions, 19 deletions
diff --git a/asterisk.c b/asterisk.c
index 5c1807f86..2ab474ed8 100644
--- a/asterisk.c
+++ b/asterisk.c
@@ -608,21 +608,15 @@ static void null_sig_handler(int signal)
}
AST_MUTEX_DEFINE_STATIC(safe_system_lock);
+/*! Keep track of how many threads are currently trying to wait*() on
+ * a child process */
static unsigned int safe_system_level = 0;
static void *safe_system_prev_handler;
-int ast_safe_system(const char *s)
+void ast_replace_sigchld(void)
{
- pid_t pid;
- int x;
- int res;
- struct rusage rusage;
- int status;
unsigned int level;
- /* keep track of how many ast_safe_system() functions
- are running at this moment
- */
ast_mutex_lock(&safe_system_lock);
level = safe_system_level++;
@@ -631,6 +625,31 @@ int ast_safe_system(const char *s)
safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
ast_mutex_unlock(&safe_system_lock);
+}
+
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
+
+ ast_mutex_lock(&safe_system_lock);
+ level = --safe_system_level;
+
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
+
+ ast_mutex_unlock(&safe_system_lock);
+}
+
+int ast_safe_system(const char *s)
+{
+ pid_t pid;
+ int x;
+ int res;
+ struct rusage rusage;
+ int status;
+
+ ast_replace_sigchld();
pid = fork();
@@ -656,14 +675,7 @@ int ast_safe_system(const char *s)
res = -1;
}
- ast_mutex_lock(&safe_system_lock);
- level = --safe_system_level;
-
- /* only restore the handler if we are the last one */
- if (level == 0)
- signal(SIGCHLD, safe_system_prev_handler);
-
- ast_mutex_unlock(&safe_system_lock);
+ ast_unreplace_sigchld();
return res;
}
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index af3aa31f9..4244c79b5 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -120,6 +120,27 @@ int ast_app_messagecount(const char *context, const char *mailbox, const char *f
int ast_safe_system(const char *s);
/*!
+ * \brief Replace the SIGCHLD handler
+ *
+ * Normally, Asterisk has a SIGCHLD handler that is cleaning up all zombie
+ * processes from forking elsewhere in Asterisk. However, if you want to
+ * wait*() on the process to retrieve information about it's exit status,
+ * then this signal handler needs to be temporaraly replaced.
+ *
+ * Code that executes this function *must* call ast_unreplace_sigchld()
+ * after it is finished doing the wait*().
+ */
+void ast_replace_sigchld(void);
+
+/*!
+ * \brief Restore the SIGCHLD handler
+ *
+ * This function is called after a call to ast_replace_sigchld. It restores
+ * the SIGCHLD handler that cleans up any zombie processes.
+ */
+void ast_unreplace_sigchld(void);
+
+/*!
\brief Send DTMF to a channel
\param chan The channel that will receive the DTMF frames
diff --git a/res/res_agi.c b/res/res_agi.c
index 481edc5bc..7c7e5cee5 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -39,6 +39,7 @@
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
+#include <sys/wait.h>
#include "asterisk.h"
@@ -275,9 +276,11 @@ static enum agi_result launch_script(char *script, char *argv[], int *fds, int *
return AGI_RESULT_FAILURE;
}
}
+ ast_replace_sigchld();
pid = fork();
if (pid < 0) {
ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
+ ast_unreplace_sigchld();
return AGI_RESULT_FAILURE;
}
if (!pid) {
@@ -1781,7 +1784,7 @@ static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
return 0;
}
#define RETRY 3
-static enum agi_result 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 *status, int dead)
{
struct ast_channel *c;
int outfd;
@@ -1830,6 +1833,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
returnstatus = -1;
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
+ waitpid(pid, status, 0);
/* No need to kill the pid anymore, since they closed us */
pid = -1;
break;
@@ -1976,13 +1980,18 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
#endif
res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
if (res == AGI_RESULT_SUCCESS) {
+ int status = 0;
agi.fd = fds[1];
agi.ctrl = fds[0];
agi.audio = efd;
- res = run_agi(chan, argv[0], &agi, pid, dead);
+ res = run_agi(chan, argv[0], &agi, pid, &status, dead);
+ /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
+ if (res == AGI_RESULT_SUCCESS && status)
+ res = AGI_RESULT_FAILURE;
close(fds[1]);
if (efd > -1)
close(efd);
+ ast_unreplace_sigchld();
}
ast_localuser_remove(me, u);