diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_agi.c | 73 |
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); |