aboutsummaryrefslogtreecommitdiffstats
path: root/res
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-25 20:30:41 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-25 20:30:41 +0000
commit4a60aa360ef46cb3f7802ed453ce2db1d0b96ad6 (patch)
tree302fb634e349342610bd623baaf2ba1bf13f59cb /res
parent32597fb0788b6d8b1ee6d3698354624ddc72b7de (diff)
Add thread to kill zombies, when child processes don't die immediately on
SIGHUP. (closes issue #13968) Reported by: eldadran Patches: 20090114__bug13968.diff.txt uploaded by Corydon76 (license 14) Tested by: eldadran git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@171120 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'res')
-rw-r--r--res/res_agi.c73
1 files changed, 71 insertions, 2 deletions
diff --git a/res/res_agi.c b/res/res_agi.c
index a80198bf3..91f15b86e 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -105,6 +105,8 @@ static char *descrip =
static int agidebug = 0;
+static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+
#define TONE_BLOCK_SIZE 200
/* Max time to connect to an AGI remote host */
@@ -119,6 +121,13 @@ enum agi_result {
AGI_RESULT_HANGUP
};
+struct zombie {
+ pid_t pid;
+ AST_LIST_ENTRY(zombie) list;
+};
+
+static AST_LIST_HEAD_STATIC(zombies, zombie);
+
static int __attribute__((format(printf, 2, 3))) agi_debug_cli(int fd, char *fmt, ...)
{
char *stuff;
@@ -1969,7 +1978,26 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
usleep(1);
}
}
- waitpid(pid, status, WNOHANG);
+ /* This is essentially doing the same as without WNOHANG, except that
+ * it allows the main thread to proceed, even without the child PID
+ * dying immediately (the child may be doing cleanup, etc.). Without
+ * this code, zombie processes accumulate for as long as child
+ * processes exist (which on busy systems may be never, filling up the
+ * process table).
+ *
+ * Note that in trunk, we don't stop interaction at the hangup event
+ * (instead we transparently switch to DeadAGI operation), so this is a
+ * short-lived code addition.
+ */
+ if (waitpid(pid, status, WNOHANG) == 0) {
+ struct zombie *cur = ast_calloc(1, sizeof(*cur));
+ if (cur) {
+ cur->pid = pid;
+ AST_LIST_LOCK(&zombies);
+ AST_LIST_INSERT_TAIL(&zombies, cur, list);
+ AST_LIST_UNLOCK(&zombies);
+ }
+ }
}
fclose(readf);
return returnstatus;
@@ -2203,17 +2231,58 @@ static struct ast_cli_entry cli_agi[] = {
dumpagihtml_help, NULL, &cli_dump_agihtml_deprecated },
};
+static void *shaun_of_the_dead(void *data)
+{
+ struct zombie *cur;
+ int status;
+ for (;;) {
+ if (!AST_LIST_EMPTY(&zombies)) {
+ /* Don't allow cancellation while we have a lock. */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ AST_LIST_LOCK(&zombies);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
+ if (waitpid(cur->pid, &status, WNOHANG) != 0) {
+ AST_LIST_REMOVE_CURRENT(&zombies, list);
+ ast_free(cur);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&zombies);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ }
+ pthread_testcancel();
+ /* Wait for 60 seconds, without engaging in a busy loop. */
+ poll(NULL, 0, 60000);
+ }
+ return NULL;
+}
+
static int unload_module(void)
{
+ int res;
+ struct zombie *cur;
ast_module_user_hangup_all();
ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
ast_unregister_application(eapp);
ast_unregister_application(deadapp);
- return ast_unregister_application(app);
+ res = ast_unregister_application(app);
+ if (shaun_of_the_dead_thread != AST_PTHREADT_NULL) {
+ pthread_cancel(shaun_of_the_dead_thread);
+ pthread_kill(shaun_of_the_dead_thread, SIGURG);
+ pthread_join(shaun_of_the_dead_thread, NULL);
+ }
+ while ((cur = AST_LIST_REMOVE_HEAD(&zombies, list))) {
+ ast_free(cur);
+ }
+ return res;
}
static int load_module(void)
{
+ if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
+ ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
+ shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+ }
ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
ast_register_application(eapp, eagi_exec, esynopsis, descrip);