aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);