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