--- mod_cgid.c (revision 679776) +++ mod_cgid.c (working copy) @@ -35,6 +35,9 @@ #include "apr_buckets.h" #include "apr_optional.h" #include "apr_signal.h" +#include "apr_time.h" +#include "apr_thread_proc.h" +#include "apr_thread_mutex.h" #define APR_WANT_STRFUNC #include "apr_want.h" @@ -185,6 +188,12 @@ int loglevel; /* to stuff in server_rec */ } cgid_req_t; + +#define EXPIRE_CHECK_INTERVAL 15L * 1000L * 1000L +static apr_hash_t *cgi_pid_hash; +static apr_thread_mutex_t *cgi_pid_lock; +static apr_time_t cgi_timeout; + /* This routine is called to create the argument list to be passed * to the CGI script. When suexec is enabled, the suexec path, user, and * group are the first three arguments to be passed; if not, all three @@ -564,6 +573,67 @@ ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, "%s", description); } +static void cgiwatcher_add(pid_t pid, apr_pool_t *pool) { + pid_t *key = apr_palloc(pool, sizeof(pid_t)); + memcpy(key, &pid, sizeof(pid_t)); + + apr_time_t *val = apr_palloc(pool, sizeof(apr_time_t)); + /* when the timeout will happen*/ + apr_time_t last = apr_time_now() + cgi_timeout; + memcpy(val, &last, sizeof(apr_time_t)); + + apr_thread_mutex_lock(cgi_pid_lock); + apr_hash_set(cgi_pid_hash, key, sizeof(apr_time_t),val); + apr_thread_mutex_unlock(cgi_pid_lock); +} + +static void cgiwatcher_remove(pid_t pid) { + apr_thread_mutex_lock(cgi_pid_lock); + /* TODO: How does the key and value allocated get reclaimed? */ + apr_hash_set(cgi_pid_hash, &pid, 0, (void*)0); + apr_thread_mutex_unlock(cgi_pid_lock); +} + +static void check_child(pid_t pid, apr_time_t time, apr_pool_t *pool) { + apr_time_t rem = time - apr_time_now(); + ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, pool, "cgiwatcher: on the loop for cgi [%d] (%d)", pid, apr_time_sec(rem)); + + if (time < apr_time_now()) { /* timed out..*/ + ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, pool, "cgiwatcher: killed cgi [%d]", pid); + kill(pid, SIGHUP); + } +} + +static void * APR_THREAD_FUNC cgiwatcher(apr_thread_t *thd, void *data) +{ + apr_hash_index_t *hi; + apr_pool_t *pool_iter; + apr_pool_create(&pool_iter, pcgi); + while(!daemon_should_exit) { + apr_thread_mutex_lock(cgi_pid_lock); + for (hi = apr_hash_first(pool_iter, cgi_pid_hash); hi; hi = apr_hash_next(hi)) { + pid_t *key; + apr_ssize_t len; + apr_time_t *val; + apr_hash_this(hi, (void*)&key, &len, (void*)&val); + if (!val) continue; + check_child(*key, *val, pool_iter); + /* cleanup script will take out the entry from the hash.*/ + } + apr_thread_mutex_unlock(cgi_pid_lock); + apr_pool_clear(pool_iter); + apr_sleep(EXPIRE_CHECK_INTERVAL); + } +} + +static apr_status_t setup_timeout_thread(apr_pool_t *pool) +{ + apr_thread_mutex_create(&cgi_pid_lock, APR_THREAD_MUTEX_DEFAULT, pcgi); + cgi_pid_hash = apr_hash_make(pcgi); + apr_thread_t *timeout_thread; + return apr_thread_create(&timeout_thread, NULL, cgiwatcher, pcgi, pcgi); +} + static int cgid_server(void *data) { struct sockaddr_un unix_addr; @@ -631,6 +701,14 @@ return errno; } } + + /* Set up the thread to watch for timeout */ + rv = setup_timeout_thread(pcgi); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server, + "Couldn't start the cgiwatcher thread."); + return rv; + } unixd_setup_child(); /* if running as root, switch to configured user/group */ @@ -700,6 +778,12 @@ "Error writing pid %" APR_PID_T_FMT " to handler", pid); } close(sd2); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + main_server, + "cgiwatcher: remove pid from list %d", pid); + + /* GETPID request is initiated by the cleanup script which means the pid is no longer alive.*/ + cgiwatcher_remove(pid); continue; } @@ -782,6 +866,11 @@ apr_filepath_name_get(r->filename)); procnew->pid = 0; /* no process to clean up */ + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "cgiwatcher: Adding process to timeout list: %d: %s", procnew->pid, + apr_filepath_name_get(r->filename)); + cgiwatcher_add(procnew->pid, pcgi); } } @@ -818,6 +907,10 @@ { daemon_should_exit = 0; /* clear setting from previous generation */ + cgi_timeout = main_server->timeout; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "cgiwatcher: timeout - %" APR_TIME_T_FMT ".", main_server->timeout); + if ((daemon_pid = fork()) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_cgid: Couldn't spawn cgid daemon process");