diff --git a/asterisk.c b/asterisk.c
index 5c1807f864f38ca7eef1223a267d1077d50c6994..2ab474ed832717ae9e215734dfde376ad6d56a3b 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 af3aa31f958102453ce3a5a3892b0c1edd43e32b..4244c79b5310a2ebf568ebebb0bdeffee9131b48 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -119,6 +119,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
 
diff --git a/res/res_agi.c b/res/res_agi.c
index 481edc5bc776dde2d8b4a634df21407b8f3c4b4b..7c7e5cee5283c0a20d7a5bd3524565c9960abee4 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);