From fba990e4e3a25c991101ea74bd750ba82a3f94a8 Mon Sep 17 00:00:00 2001 From: robs Date: Mon, 26 Mar 2001 15:35:38 +0000 Subject: [PATCH] *) Eliminate the use of locks to assist in the clean shutdown of applications. Instead, it is assumed that applications handle termination signals properly (this is now embedded in the C application lib). *) Fix Win32 process termination. Proper operation requires the use of an updated application lib (termination is now signalled with an Event and handled by specialized thread). --- CHANGES | 9 + fcgi.h | 91 +++------ fcgi_config.c | 60 ++++-- fcgi_pm.c | 618 ++++++++++++++++++++++------------------------------------ fcgi_util.c | 184 +++-------------- mod_fastcgi.c | 193 +++++++++--------- 6 files changed, 435 insertions(+), 720 deletions(-) diff --git a/CHANGES b/CHANGES index 335b402..f07ea5c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,14 @@ 2.2.11 + *) Eliminate the use of locks to assist in the clean shutdown of + applications. Instead, it is assumed that applications handle + termination signals properly (this is now embedded in the C + application lib). + + *) Fix Win32 process termination. Proper operation requires the use of + an updated application lib (termination is now signalled with an + Event and handled by specialized thread). + *) Docs cleanup. Andrew Benham [adsb@bigfoot.com] *) Added code so if the last instance of a dynamic application died without diff --git a/fcgi.h b/fcgi.h index ef8f898..93cd3ea 100644 --- a/fcgi.h +++ b/fcgi.h @@ -1,10 +1,14 @@ /* - * $Id: fcgi.h,v 1.29 2001/03/05 15:45:16 robs Exp $ + * $Id: fcgi.h,v 1.30 2001/03/26 15:35:38 robs Exp $ */ #ifndef FCGI_H #define FCGI_H +#ifdef WIN32 +#pragma warning( disable : 4115 ) +#endif + /* Apache header files */ #include "httpd.h" #include "http_config.h" @@ -28,6 +32,7 @@ #ifdef WIN32 #include "multithread.h" +#pragma warning( default : 4115) #else #include #endif @@ -53,13 +58,6 @@ typedef struct { #define TERM_EVENT 1 /* termination event */ #define WAKE_EVENT 2 /* notification of child Fserver dieing */ -/* Reader/Writer lock structure for NT */ -typedef struct _FcgiRWLock { - HANDLE write_lock; - HANDLE mutex; - long counter; /* number of readers */ -} FcgiRWLock; - typedef struct _fcgi_pm_job { char id; char *fs_path; @@ -71,22 +69,25 @@ typedef struct _fcgi_pm_job { } fcgi_pm_job; #endif +enum { + FCGI_RUNNING, /* currently running */ + FCGI_START, /* needs to be started by PM */ + FCGI_VICTIM, /* SIGTERM was sent by PM */ + FCGI_KILLED, /* a wait() collected VICTIM */ + FCGI_READY /* empty cell, init state */ +} process_state; + /* * ServerProcess holds data for each process associated with * a class. It is embedded in fcgi_server below. */ typedef struct _FcgiProcessInfo { #ifdef WIN32 - HANDLE handle; /* handle of associated process */ + HANDLE handle; /* process handle */ + HANDLE terminationEvent; /* Event used to signal process termination */ #endif pid_t pid; /* pid of associated process */ - enum {STATE_STARTED, /* currently running */ - STATE_NEEDS_STARTING, /* needs to be started by PM */ - STATE_KILL, /* kill() is needed */ - STATE_VICTIM, /* SIGTERM was sent by PM */ - STATE_KILLED, /* a wait() collected VICTIM */ - STATE_READY} /* empty cell, init state */ - state; /* state of the process */ + enum process_state state; /* state of the process */ } ServerProcess; /* @@ -120,6 +121,7 @@ typedef struct _FastCgiServerInfo { #ifdef WIN32 struct sockaddr *dest_addr; /* for local apps on NT need socket address */ /* bound to localhost */ + const char *mutex_env_string; /* string holding the accept mutex handle */ #endif int socket_addr_len; /* Length of socket */ enum {APP_CLASS_UNKNOWN, @@ -159,11 +161,6 @@ typedef struct _FastCgiServerInfo { u_long totalQueueTime; /* microseconds spent by the web server * waiting to connect to the fastcgi app * since the last dynamicUpdateInterval. */ -#ifdef WIN32 - FcgiRWLock *dynamic_lock; /* dynamic server lock */ - HANDLE hPipeMutex; -#endif - struct _FastCgiServerInfo *next; } fcgi_server; @@ -211,11 +208,6 @@ typedef struct { struct timeval startTime; /* dynamic app's connect() attempt start time */ struct timeval queueTime; /* dynamic app's connect() complete time */ struct timeval completeTime; /* dynamic app's connection close() time */ -#ifdef WIN32 - FcgiRWLock *lockFd; /* dynamic app's reader/writer lock */ -#else - int lockFd; /* dynamic app's lockfile file descriptor */ -#endif int keepReadingFromFcgiApp; /* still more to read from fcgi app? */ const char *user; /* user used to invoke app (suexec) */ const char *group; /* group used to invoke app (suexec) */ @@ -232,9 +224,10 @@ typedef struct { #define SCAN_CGI_SRV_REDIRECT -3 /* Opcodes for Server->ProcMgr communication */ -#define PLEASE_START 83 /* 'S' - start */ -#define CONN_TIMEOUT 84 /* 'T' - timeout */ -#define REQ_COMPLETE 67 /* 'C' - complete */ +#define FCGI_START 83 /* 'S' - start */ +#define FCGI_RESTART 82 /* 'R' - restart */ +#define FCGI_TIMEOUT 84 /* 'T' - timeout */ +#define FCGI_COMPLETE 67 /* 'C' - complete */ /* Authorizer types, for auth directives handling */ #define FCGI_AUTH_TYPE_AUTHENTICATOR 0 @@ -409,32 +402,10 @@ void fcgi_buf_get_to_array(Buffer *buf,array_header *arr, size_t len); * fcgi_util.c */ -/* Set a shared read lock, wait until you have it. */ -#ifdef WIN32 -#define fcgi_wait_for_shared_read_lock(fd) fcgi_rdwr_lock(fd, READER) -#else -#define fcgi_wait_for_shared_read_lock(fd) fcgi_util_lock_fd((fd), F_SETLKW, F_RDLCK, 0, SEEK_SET, 0) -#endif - -/* Set an exclusive write lock, no wait, failure->errno==EACCES. */ -#ifdef WIN32 -#define fcgi_get_exclusive_write_lock_no_wait(fd) fcgi_rdwr_try_lock(fd, WRITER) -#else -#define fcgi_get_exclusive_write_lock_no_wait(fd) fcgi_util_lock_fd(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) -#endif - -/* Set a shared write lock, wait until you have it. */ -#ifdef WIN32 -#define fcgi_wait_for_shared_write_lock(fd) fcgi_rdwr_lock(fd, WRITER) -#else -#define fcgi_wait_for_shared_write_lock(fd) fcgi_util_lock_fd(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) -#endif - char *fcgi_util_socket_hash_filename(pool *p, const char *path, const char *user, const char *group); const char *fcgi_util_socket_make_path_absolute(pool * const p, const char *const file, const int dynamic); -const char *fcgi_util_socket_get_lock_filename(pool *p, const char *socket_path); #ifndef WIN32 const char *fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr, int *socket_addr_len, const char *socket_path); @@ -446,23 +417,15 @@ const char *fcgi_util_check_access(pool *tp, const int mode, const uid_t uid, const gid_t gid); fcgi_server *fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid); fcgi_server *fcgi_util_fs_get(const char *ePath, const char *user, const char *group); -const char *fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, - struct stat *finfo, const uid_t uid, const gid_t gid); +const char *fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo); fcgi_server *fcgi_util_fs_new(pool *p); void fcgi_util_fs_add(fcgi_server *s); const char *fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid); ServerProcess *fcgi_util_fs_create_procs(pool *p, int num); -int fcgi_util_lock_fd(int fd, int cmd, int type, off_t offset, int whence, off_t len); int fcgi_util_gettimeofday(struct timeval *); #ifdef WIN32 -FcgiRWLock * fcgi_rdwr_create(void); -void fcgi_rdwr_destroy(FcgiRWLock *); -int fcgi_rdwr_lock(FcgiRWLock *, int); -int fcgi_rdwr_try_lock(FcgiRWLock *, int); -int fcgi_rdwr_unlock(FcgiRWLock *, int); - int fcgi_pm_add_job(fcgi_pm_job *new_job); #endif @@ -504,13 +467,13 @@ extern fcgi_pm_job *fcgi_dynamic_mbox; #endif extern u_int dynamicMaxProcs; -extern u_int dynamicMinProcs; -extern u_int dynamicMaxClassProcs; +extern int dynamicMinProcs; +extern int dynamicMaxClassProcs; extern u_int dynamicKillInterval; extern u_int dynamicUpdateInterval; extern float dynamicGain; -extern u_int dynamicThreshold1; -extern u_int dynamicThresholdN; +extern int dynamicThreshold1; +extern int dynamicThresholdN; extern u_int dynamicPleaseStartDelay; extern u_int dynamicAppConnectTimeout; extern char **dynamicEnvp; diff --git a/fcgi_config.c b/fcgi_config.c index 3faaab4..510355c 100644 --- a/fcgi_config.c +++ b/fcgi_config.c @@ -1,9 +1,12 @@ /* - * $Id: fcgi_config.c,v 1.25 2001/02/19 06:00:09 robs Exp $ + * $Id: fcgi_config.c,v 1.26 2001/03/26 15:35:39 robs Exp $ */ #include "fcgi.h" +#ifdef WIN32 +#pragma warning( disable : 4100 4706 ) +#endif /******************************************************************************* * Get the next configuration directive argument, & return an in_addr and port. @@ -67,6 +70,30 @@ static const char *get_u_short(pool *p, const char **arg, return NULL; } +static const char *get_int(pool *p, const char **arg, int *num, int min) +{ + char *cp; + const char *val = ap_getword_conf(p, arg); + + if (*val == '\0') + { + return "\"\""; + } + + *num = (int) strtol(val, &cp, 10); + + if (*cp != '\0') + { + return ap_pstrcat(p, "can't parse ", "\"", val, "\"", NULL); + } + else if (*num < min) + { + return ap_psprintf(p, "\"%d\" must be >= %d", *num, min); + } + + return NULL; +} + /******************************************************************************* * Get the next configuration directive argument, & return an u_int. * The pool arg should be temporary storage. @@ -466,6 +493,12 @@ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const cha char **envp = ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3)); unsigned int envc = 0; +#ifdef WIN32 + HANDLE mutex = ap_create_mutex(NULL); + + SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE); +#endif + if (*fs_path == '\0') return "AppClass requires a pathname!?"; @@ -495,8 +528,7 @@ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const cha } } - err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL, cmd->server->server_uid, - cmd->server->server_gid); + err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL); if (err != NULL) { return ap_psprintf(tp, "%s: \"%s\" %s", name, fs_path, err); } @@ -511,6 +543,8 @@ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const cha // TCP FastCGI applications require SystemRoot be present in the environment // Put it in both for consistency to the application fcgi_config_set_env_var(tp, envp, &envc, "SystemRoot"); + + s->mutex_env_string = ap_psprintf(p, "_FCGI_MUTEX_=%ld", mutex);; #else if (fcgi_wrapper) { struct passwd *pw; @@ -552,7 +586,7 @@ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const cha return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-init-start-delay") == 0) { - if ((err = get_u_int(tp, &arg, &s->initStartDelay, 0))) + if ((err = get_int(tp, &arg, &s->initStartDelay, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-priority") == 0) { @@ -602,7 +636,7 @@ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const cha name, fs_path); } - /* Move env array to a surviving pool, leave an extra slot for WIN32 _FCGI_MUTEX_ */ + /* Move env array to a surviving pool */ ++envc; s->envp = (char **)ap_palloc(p, sizeof(char *) * ++envc); memcpy(s->envp, envp, sizeof(char *) * envc); @@ -783,11 +817,11 @@ const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-minProcesses") == 0) { - if ((err = get_u_int(tp, &arg, &dynamicMinProcs, 0))) + if ((err = get_int(tp, &arg, &dynamicMinProcs, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-maxClassProcesses") == 0) { - if ((err = get_u_int(tp, &arg, &dynamicMaxClassProcs, 1))) + if ((err = get_int(tp, &arg, &dynamicMaxClassProcs, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-killInterval") == 0) { @@ -803,13 +837,15 @@ const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg) return invalid_value(tp, name, NULL, option, err); } else if ((strcasecmp(option, "-singleThreshold") == 0) - || (strcasecmp(option, "-singleThreshhold") == 0)) { - if ((err = get_u_int(tp, &arg, &dynamicThreshold1, 0))) + || (strcasecmp(option, "-singleThreshhold") == 0)) + { + if ((err = get_int(tp, &arg, &dynamicThreshold1, 0))) return invalid_value(tp, name, NULL, option, err); } else if ((strcasecmp(option, "-multiThreshold") == 0) - || (strcasecmp(option, "-multiThreshhold") == 0)) { - if ((err = get_u_int(tp, &arg, &dynamicThresholdN, 0))) + || (strcasecmp(option, "-multiThreshhold") == 0)) + { + if ((err = get_int(tp, &arg, &dynamicThresholdN, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-startDelay") == 0) { @@ -892,7 +928,7 @@ const char *fcgi_config_new_auth_server(cmd_parms * const cmd, /* Make sure its already configured or at least a candidate for dynamic */ if (fcgi_util_fs_get_by_id(auth_server, uid, gid) == NULL) { - const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL, uid, gid); + const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL); if (err) return ap_psprintf(tp, "%s: \"%s\" %s", cmd->cmd->name, auth_server, err); } diff --git a/fcgi_pm.c b/fcgi_pm.c index 1e635d1..757746e 100644 --- a/fcgi_pm.c +++ b/fcgi_pm.c @@ -1,5 +1,5 @@ /* - * $Id: fcgi_pm.c,v 1.51 2001/03/05 18:16:48 robs Exp $ + * $Id: fcgi_pm.c,v 1.52 2001/03/26 15:35:39 robs Exp $ */ @@ -18,19 +18,13 @@ time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was static time_t now = 0; -/* Information about a process we are doing a blocking kill of. */ -struct FuncData { -#ifndef WIN32 - const char *lockFileName; /* name of the lock file to lock */ -#else - FcgiRWLock *lock; /* reader/writer lock for dynamic app */ -#endif - ServerProcess *process; /* process to issue SIGTERM to */ -}; - #ifdef WIN32 +#pragma warning ( disable : 4100 4102 ) static BOOL bTimeToDie = FALSE; /* process termination flag */ HANDLE fcgi_event_handles[3]; +#ifndef SIGKILL +#define SIGKILL 9 +#endif #endif @@ -56,89 +50,92 @@ static int seteuid_user(void) } #endif -static int fcgi_kill(ServerProcess *process, int sig) +/* + * Signal the process to exit. How (or if) the process responds + * depends on the FastCGI application library (esp. on Win32) and + * possibly application code (signal handlers and whether or not + * SA_RESTART is on). At any rate, we send the signal with the + * hopes that the process will exit on its own. Later, as we + * review the state of application processes, if we see one marked + * for death, but that hasn't died within a specified period of + * time, fcgi_kill() is called again with a KILL) + */ +static void fcgi_kill(ServerProcess *process, int sig) { - int rc; - FCGIDBG2("fcgi_kill(%ld)", process->pid); + FCGIDBG3("fcgi_kill(%ld, %d)", process->pid, sig); -#ifndef WIN32 - if (fcgi_wrapper) { + process->state = FCGI_VICTIM; + +#ifdef WIN32 + if (sig == SIGTERM) + { + SetEvent(process->terminationEvent); + } + else if (sig == SIGKILL) + { + TerminateProcess(process->handle, 1); + } + else + { + ap_assert(0); + } +#else + if (fcgi_wrapper) + { seteuid_root(); } - rc = kill(process->pid, sig); + kill(process->pid, sig); - if (fcgi_wrapper) { + if (fcgi_wrapper) + { seteuid_user(); } - -#else - rc = TerminateProcess(process->handle, sig); #endif - - return rc; } /******************************************************************************* - * Send SIGTERM to each process in the server class, remove socket and lock + * Send SIGTERM to each process in the server class, remove socket * file if appropriate. Currently this is only called when the PM is shutting * down and thus memory isn't freed and sockets and files aren't closed. */ -static void kill_fs_procs(pool *p, fcgi_server *s) +static void shutdown_all() { - ServerProcess *proc = s->procs; - int i, numChildren; - - if (s->directive == APP_CLASS_DYNAMIC) - numChildren = dynamicMaxClassProcs; - else - numChildren = s->numProcesses; - - for (i = 0; i < numChildren; i++, proc++) { -#ifndef WIN32 - if (proc->pid > 0) { - fcgi_kill(proc, SIGTERM); - proc->pid = -1; - } -#else - if (proc->handle != INVALID_HANDLE_VALUE) { - fcgi_kill(proc, 1); - CloseHandle(proc->handle); - proc->handle = INVALID_HANDLE_VALUE; - proc->pid = -1; - } -#endif - } - - /* Remove the dead lock file */ - if (s->directive == APP_CLASS_DYNAMIC) { -#ifndef WIN32 - const char *lockFileName = fcgi_util_socket_get_lock_filename(p, s->socket_path); - - if (unlink(lockFileName) != 0) { - ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server, - "FastCGI: unlink() failed to remove lock file \"%s\" for (dynamic) server \"%s\"", - lockFileName, s->fs_path); + fcgi_server *s = fcgi_servers; + + while (s) + { + ServerProcess *proc = s->procs; + int i; + int numChildren = (s->directive == APP_CLASS_DYNAMIC) + ? dynamicMaxClassProcs + : s->numProcesses; + + /* Remove the socket file */ + if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) { + #ifndef WIN32 + if (unlink(s->socket_path) != 0) { + ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server, + "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"", + s->socket_path, + (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path); + } + #else + CloseHandle((HANDLE)s->listenFd); + #endif } -#else - fcgi_rdwr_destroy(s->dynamic_lock); -#endif - } - /* Remove the socket file */ - if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) { -#ifndef WIN32 - if (unlink(s->socket_path) != 0) { - ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server, - "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"", - s->socket_path, - (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path); + /* Send TERM to all processes */ + for (i = 0; i < numChildren; i++, proc++) + { + if (proc->state == FCGI_RUNNING) + { + fcgi_kill(proc, SIGTERM); + } } -#else - CloseHandle((HANDLE)s->listenFd); -#endif - } + fcgi_servers = s->next; + } } static int init_listen_sock(fcgi_server * fs) @@ -229,62 +226,6 @@ static int init_listen_sock(fcgi_server * fs) /* *---------------------------------------------------------------------- * - * dynamic_blocking_kill - * - * Block on the lock file until it is available, and then - * issue a kill signal to the corresponding application. - * Since this function is executed in the child process, - * _exit() is called upon completion. - * - * Inputs - * Pointer to the data structure containing a process id to - * issue a signal to and the full pathname to the lockfile - * that needs to be locked before the issue of the signal. - * - * Notes - * Memory is allocated by the caller, but is freed by this - * function. - * - *---------------------------------------------------------------------- - */ -static void dynamic_blocking_kill(void *data) -{ - struct FuncData *funcData = (struct FuncData *)data; - -#ifndef WIN32 - int lockFd; - - ap_assert(funcData->lockFileName); - if ((lockFd = open(funcData->lockFileName, O_RDWR)) < 0) { - /* There is something terribly wrong here */ - } else { - if (fcgi_wait_for_shared_write_lock(lockFd) < 0) { - /* This is a major problem */ - } else { - fcgi_kill(funcData->process, SIGTERM); - } - } - /* exit() may flush stdio buffers inherited from the parent. */ - _exit(0); - -#else - FCGIDBG1("dynamic_blocking_kill()"); - if (fcgi_wait_for_shared_write_lock(funcData->lock) < 0) { - // This is a major problem - FCGIDBG1("fcgi_wait_for_shared_write_lock() failed >> MAJOR PROBLEM"); - } - else { - fcgi_kill(funcData->process, 1); - fcgi_rdwr_unlock(funcData->lock, WRITER); - } - free(data); - return; -#endif -} - -/* - *---------------------------------------------------------------------- - * * pm_main * * The FastCGI process manager, which runs as a separate @@ -439,15 +380,12 @@ FailedSystemCallExit: /* Adapted from Apache's util_script.c ap_call_exec() */ char *interpreter = NULL; - char *ext = NULL; - char *exename = NULL; - char *s = NULL; char *quoted_filename; char *pCommand; char *pEnvBlock, *pNext; - int i; - int iEnvBlockLen; + int i = 0; + int iEnvBlockLen = 1; file_type_e fileType; @@ -457,22 +395,30 @@ FailedSystemCallExit: request_rec r; pid_t pid = -1; - HANDLE listen_handle, mutex; - char * mutex_string = NULL; - pool * tp = ap_make_sub_pool(fcgi_config_pool); + HANDLE listen_handle; + char * termination_env_string = NULL; + + process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE); + + termination_env_string = ap_psprintf(tp, + "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent); + if (fs->socket_path) { SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; + sa.nLength = sizeof(sa); + + listen_handle = CreateNamedPipe(fs->socket_path, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa); - listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, 4096,4096,0, &sa); if (listen_handle == INVALID_HANDLE_VALUE) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, @@ -480,21 +426,7 @@ FailedSystemCallExit: exit(0); } - // This mutex is not really necessary, but for compatibility with - // the existing library, we need it. - - mutex = ap_create_mutex(NULL); - if (mutex == NULL) - { - ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, - "FastCGI: can't exec server \"%s\", ap_create_mutex() failed", fs->fs_path); - CloseHandle(listen_handle); - exit(0); - } - - SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE); - - mutex_string = ap_psprintf(tp, "_FCGI_MUTEX_=%ld", mutex); +// SetHandleInformation(listen_handle, HANDLE_FLAG_INHERIT, TRUE); } else { @@ -552,60 +484,53 @@ FailedSystemCallExit: * Win32's CreateProcess call requires that the environment * be passed in an environment block, a null terminated block of * null terminated strings. + * @todo we should store the env in this format for win32. */ - i = 0; - iEnvBlockLen = 1; - while (fs->envp[i]) { + while (fs->envp[i]) + { iEnvBlockLen += strlen(fs->envp[i]) + 1; i++; } - if (fs->socket_path) - { - iEnvBlockLen += strlen(mutex_string) + 1; - } + iEnvBlockLen += strlen(termination_env_string) + 1; + iEnvBlockLen += strlen(fs->mutex_env_string) + 1; pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen); i = 0; pNext = pEnvBlock; - while (fs->envp[i]) { + while (fs->envp[i]) + { strcpy(pNext, fs->envp[i]); - pNext = pNext + strlen(pNext) + 1; + pNext += strlen(pNext) + 1; i++; } - if (fs->socket_path) - { - strcpy(pNext, mutex_string); - } - + strcpy(pNext, termination_env_string); + pNext += strlen(pNext) + 1; + strcpy(pNext, fs->mutex_env_string); + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, pEnvBlock, ap_make_dirstr_parent(tp, fs->fs_path), - &si, &pi)) { - if (fileType == eFileTypeEXE16) { - /* Hack to get 16-bit CGI's working. It works for all the - * standard modules shipped with Apache. pi.dwProcessId is 0 - * for 16-bit CGIs and all the Unix specific code that calls - * ap_call_exec interprets this as a failure case. And we can't - * use -1 either because it is mapped to 0 by the caller. - */ - pid = -2; - } - else { - pid = pi.dwProcessId; - process->handle = pi.hProcess; - CloseHandle(pi.hThread); - } + &si, &pi)) + { + /* Hack to get 16-bit CGI's working. It works for all the + * standard modules shipped with Apache. pi.dwProcessId is 0 + * for 16-bit CGIs and all the Unix specific code that calls + * ap_call_exec interprets this as a failure case. And we can't + * use -1 either because it is mapped to 0 by the caller. + */ + pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId; + + process->handle = pi.hProcess; + CloseHandle(pi.hThread); } - // We don't need these anymore.. if (fs->socket_path) { CloseHandle(listen_handle); - CloseHandle(mutex); } ap_destroy_pool(tp); @@ -697,7 +622,7 @@ static void schedule_start(fcgi_server *s, int proc) } FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc); - s->procs[proc].state = STATE_NEEDS_STARTING; + s->procs[proc].state = FCGI_START; if (proc == (int)dynamicMaxClassProcs - 1) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI: scheduled the %sstart of the last (dynamic) server " @@ -865,28 +790,35 @@ static void dynamic_read_msgs(int read_ready) #endif #ifndef WIN32 - if (s==NULL && opcode != REQ_COMPLETE) + if (s==NULL && opcode != FCGI_COMPLETE) #else - if (s==NULL && cjob->id != REQ_COMPLETE) + if (s==NULL && cjob->id != FCGI_COMPLETE) #endif { -#ifndef WIN32 +#ifdef WIN32 + HANDLE mutex = ap_create_mutex(NULL); + + SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE); +#else int fd; - const char *err, *lockPath; + const char *err; #endif - + /* Create a perm subpool to hold the new server data, * we can destroy it if something doesn't pan out */ sp = ap_make_sub_pool(fcgi_config_pool); /* Create a new "dynamic" server */ s = fcgi_util_fs_new(sp); + s->directive = APP_CLASS_DYNAMIC; s->restartDelay = dynamicRestartDelay; s->listenQueueDepth = dynamicListenQueueDepth; s->initStartDelay = dynamicInitStartDelay; s->envp = dynamicEnvp; + #ifdef WIN32 + s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex); s->fs_path = ap_pstrdup(sp, cjob->fs_path); #else s->fs_path = ap_pstrdup(sp, execName); @@ -914,18 +846,6 @@ static void dynamic_read_msgs(int read_ready) goto BagNewServer; } - /* Create the lock file */ - lockPath = fcgi_util_socket_get_lock_filename(tp, s->socket_path); - fd = ap_popenf(tp, lockPath, - O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); - if (fd < 0) { - ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, - "FastCGI: can't create (dynamic) server \"%s\": can't open lock file \"%s\": popenf() failed", - execName, lockPath); - goto BagNewServer; - } - ap_pclosef(tp, fd); - /* If a wrapper is being used, config user/group info */ if (fcgi_wrapper) { if (user[0] == '~') { @@ -968,26 +888,15 @@ static void dynamic_read_msgs(int read_ready) s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group); s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1); s->listenFd = 0; - - /* Create the application lock */ - if ((s->dynamic_lock = fcgi_rdwr_create()) == NULL) { - ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, - "FastCGI: can;t create (dynamic) server \"%s\": fcgi_rdwr_create() failed", - cjob->fs_path); - goto BagNewServer; - } - - // Lock it until the first process is started - fcgi_rdwr_lock(s->dynamic_lock, WRITER); #endif fcgi_util_fs_add(s); } else { #ifndef WIN32 - if (opcode == PLEASE_START) { + if (opcode == FCGI_START) { #else - if(cjob->id==PLEASE_START) { + if (cjob->id==FCGI_START) { #endif if (dynamicAutoUpdate) { /* Check to see if the binary has changed. If so, @@ -995,7 +904,7 @@ static void dynamic_read_msgs(int read_ready) * restart them. */ struct stat stbuf; - unsigned int i; + int i; #ifndef WIN32 if ((stat(execName, &stbuf)==0) && @@ -1031,10 +940,10 @@ static void dynamic_read_msgs(int read_ready) * it if we're not already running at least one * instance. */ - unsigned int i; + int i; for (i = 0; i < dynamicMaxClassProcs; i++) { - if (s->procs[i].state == STATE_STARTED) + if (s->procs[i].state == FCGI_RUNNING) break; } /* if already running, don't start another one */ @@ -1050,10 +959,10 @@ static void dynamic_read_msgs(int read_ready) switch (cjob->id) #endif { - unsigned int i; + int i; - case PLEASE_START: - case CONN_TIMEOUT: + case FCGI_START: + case FCGI_TIMEOUT: if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) { /* @@ -1070,12 +979,12 @@ static void dynamic_read_msgs(int read_ready) /* find next free slot */ for (i = 0; i < dynamicMaxClassProcs; i++) { - if (s->procs[i].state == STATE_NEEDS_STARTING) + if (s->procs[i].state == FCGI_START) { FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i); break; } - else if (s->procs[i].state == STATE_STARTED) + else if (s->procs[i].state == FCGI_RUNNING) { continue; } @@ -1090,7 +999,7 @@ static void dynamic_read_msgs(int read_ready) } #endif break; - case REQ_COMPLETE: + case FCGI_COMPLETE: /* only record stats if we have a structure */ if (s) { #ifndef WIN32 @@ -1159,156 +1068,103 @@ BagNewServer: static void dynamic_kill_idle_fs_procs(void) { fcgi_server *s; - struct FuncData *funcData = NULL; - unsigned long connTime; /* server's smoothed running time, or - * if that's 0, the current total */ - unsigned long totalTime; /* maximum number of microseconds that all - * of a server's running processes together - * could have spent running since the - * last check */ - double loadFactor; /* percentage, 0-100, of totalTime that - * the processes actually used */ - unsigned int i, victims = 0; -#ifndef WIN32 - const char *lockFileName; - int lockFd; - pid_t pid; -#endif - pool *tp = ap_make_sub_pool(fcgi_config_pool); + int victims = 0; - /* pass 1 - locate and mark all victims */ - for(s=fcgi_servers; s!=NULL; s=s->next) { - /* Only kill dynamic apps */ - if (s->directive != APP_CLASS_DYNAMIC) - continue; + for (s = fcgi_servers; s != NULL; s = s->next) + { + /* + * server's smoothed running time, or if that's 0, the current total + */ + unsigned long connTime; + + /* + * maximum number of microseconds that all of a server's running + * processes together could have spent running since the last check + */ + unsigned long totalTime; - /* If the number of non-victims is less than or equal to - the minimum that may be running without being killed off, - don't select any more victims. */ - if ((fcgi_dynamic_total_proc_count - victims) <= (int) dynamicMinProcs) { - break; + /* + * percentage, 0-100, of totalTime that the processes actually used + */ + int loadFactor; + + int i; + + if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0) + { + continue; } connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime; - totalTime = (s->numProcesses)*(now - fcgi_dynamic_epoch)*1000000 + 1; + totalTime = s->numProcesses * (now - fcgi_dynamic_epoch) * 1000000 + 1; - /* XXX producing a heavy load with one client, I haven't been - able to achieve a loadFactor greater than 0.5. Perhaps this - should be scaled up by another order of magnitude or two. */ - loadFactor = 100.0 * connTime / totalTime; + loadFactor = 100 * connTime / totalTime; - if ((s->numProcesses > 1 - && s->numProcesses/(s->numProcesses - 1)*loadFactor < dynamicThresholdN) - || (s->numProcesses == 1 && loadFactor < dynamicThreshold1)) + if (s->numProcesses == 1) { - int got_one = 0; - - for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) { - if (s->procs[i].state == STATE_NEEDS_STARTING) { - s->procs[i].state = STATE_READY; - got_one = 1; - } - else if (s->procs[i].state == STATE_VICTIM || s->procs[i].state == STATE_KILL) { - got_one = 1; - } + if (loadFactor >= dynamicThreshold1) + { + continue; } + } + else + { + int load = s->numProcesses / (s->numProcesses - 1) * loadFactor; + + if (load >= dynamicThresholdN) + { + continue; + } + } - for (i = 0; !got_one && i < dynamicMaxClassProcs; ++i) { - if (s->procs[i].state == STATE_STARTED) { - s->procs[i].state = STATE_KILL; - ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, - "FastCGI: (dynamic) server \"%s\" (pid %ld) termination scheduled", - s->fs_path, s->procs[i].pid); - victims++; - got_one = 1; - } + /* + * Run through the procs to see if we can get away w/o waxing one. + */ + for (i = 0; i < dynamicMaxClassProcs; ++i) + { + if (s->procs[i].state == FCGI_START) + { + s->procs[i].state = FCGI_READY; + break; + } + else if (s->procs[i].state == FCGI_VICTIM) + { + break; } } - } - /* pass 2 - kill procs off */ - for(s=fcgi_servers; s!=NULL; s=s->next) { - /* Only kill dynamic apps */ - if (s->directive != APP_CLASS_DYNAMIC) + if (i < dynamicMaxClassProcs) + { continue; + } - for(i = 0; i < dynamicMaxClassProcs; i++) { - if (s->procs[i].state == STATE_KILL) { -#ifndef WIN32 - lockFileName = fcgi_util_socket_get_lock_filename(tp, s->socket_path); - if ((lockFd = ap_popenf(tp, lockFileName, O_RDWR, 0))<0) { - /* - * If we need to kill an application and the - * corresponding lock file does not exist, then - * that means we are in big trouble here - */ - /*@@@ this should be logged, but since all the lock - * file stuff will be tossed, I'll leave it now */ - ap_pclosef(tp, lockFd); - continue; - } + for (i = 0; i < dynamicMaxClassProcs; ++i) + { + if (s->procs[i].state != FCGI_RUNNING) + { + continue; + } - if (fcgi_get_exclusive_write_lock_no_wait(lockFd) < 0) { -#else - if (fcgi_get_exclusive_write_lock_no_wait(s->dynamic_lock) < 0) { -#endif - FCGIDBG2("fcgi_get_exclusive_write_lock_no_wait() failed (%ld)", GetLastError()); - /* - * Unable to lock the lockfile, indicative - * of WS performing operation with the given - * application class. The simplest solution - * is to spawn off another process and block - * on lock to kill it. This is under assumptions - * that fork() is not very costly and this - * situation occurs very rarely, which it should - */ -#ifndef WIN32 - funcData = ap_pcalloc(tp, sizeof(struct FuncData)); - funcData->lockFileName = lockFileName; -#else - funcData = malloc(sizeof(struct FuncData)); - funcData->lock = s->dynamic_lock; -#endif - funcData->process = &s->procs[i]; - -#ifndef WIN32 - if((pid=fork())<0) { - /*@@@ this should be logged, but since all the lock - * file stuff will be tossed, I'll leave it now */ - ap_pclosef(tp, lockFd); - continue; - } else if(pid==0) { - /* child */ + ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, + "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled", + s->fs_path, s->procs[i].pid); - /* rename the process for ps - best we can easily */ - change_process_name("fcgiBlkKill"); + fcgi_kill(&s->procs[i], SIGTERM); + + victims++; + break; + } - dynamic_blocking_kill(funcData); - } else { - /* parent */ - s->procs[i].state = STATE_VICTIM; - ap_pclosef(tp, lockFd); - } -#else - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) dynamic_blocking_kill, (LPVOID) funcData, 0, NULL); - s->procs[i].state = STATE_VICTIM; -#endif - } - else { - FCGIDBG1("fcgi_get_exclusive_write_lock_no_wait() succeeded"); - s->procs[i].state = STATE_VICTIM; -#ifndef WIN32 - fcgi_kill(&s->procs[i], SIGTERM); - ap_pclosef(tp, lockFd); -#else - fcgi_kill(&s->procs[i], 1); - fcgi_rdwr_unlock(s->dynamic_lock, WRITER); -#endif - } - } + /* + * If the number of non-victims is less than or equal to + * the minimum that may be running without being killed off, + * don't select any more victims. + */ + if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs) + { + break; } } - ap_destroy_pool(tp); } #ifdef WIN32 @@ -1339,7 +1195,10 @@ void child_wait_thread(void *dummy) { } for (i=0; i < numChildren; i++) { - if (s->procs[i].handle != INVALID_HANDLE_VALUE) { + if (s->procs[i].handle != INVALID_HANDLE_VALUE) + { + DWORD exitStatus = 0; + /* timeout is currently set for 100 miliecond */ /* it may need t longer or user customizable */ dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME); @@ -1352,7 +1211,7 @@ void child_wait_thread(void *dummy) { if (s->directive == APP_CLASS_STANDARD) { /* restart static app */ - s->procs[i].state = STATE_NEEDS_STARTING; + s->procs[i].state = FCGI_START; s->numFailures++; } else { @@ -1360,26 +1219,28 @@ void child_wait_thread(void *dummy) { fcgi_dynamic_total_proc_count--; FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count); - if (s->procs[i].state == STATE_VICTIM) { - s->procs[i].state = STATE_KILLED; + if (s->procs[i].state == FCGI_VICTIM) { + s->procs[i].state = FCGI_KILLED; } else { /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/ s->numFailures++; if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) { - s->procs[i].state = STATE_NEEDS_STARTING; + s->procs[i].state = FCGI_START; } else { - s->procs[i].state = STATE_READY; + s->procs[i].state = FCGI_READY; } } } + GetExitCodeProcess(s->procs[i].handle, &exitStatus); + ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, - "FastCGI:%s server \"%s\" (pid %d) terminated", + "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", - s->fs_path, s->procs[i].pid); + s->fs_path, s->procs[i].pid, exitStatus); CloseHandle(s->procs[i].handle); s->procs[i].handle = INVALID_HANDLE_VALUE; @@ -1491,7 +1352,7 @@ void fcgi_pm_main(void *dummy) #endif for (i = 0; i < s->numProcesses; ++i) - s->procs[i].state = STATE_NEEDS_STARTING; + s->procs[i].state = FCGI_START; } #ifdef WIN32 @@ -1542,7 +1403,7 @@ void fcgi_pm_main(void *dummy) for (i = 0; i < numChildren; ++i) { - if (s->procs[i].pid <= 0 && s->procs[i].state == STATE_NEEDS_STARTING) + if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START) { int restart = (s->procs[i].pid < 0); time_t restartTime = s->restartTime; @@ -1551,15 +1412,12 @@ void fcgi_pm_main(void *dummy) if (restartTime <= now) { - s->restartTime = now; - if (s->listenFd < 0 && init_listen_sock(s)) { if (sleepSeconds > s->initStartDelay) sleepSeconds = s->initStartDelay; break; } - #ifndef WIN32 if (caughtSigTerm) { goto ProcessSigTerm; @@ -1579,18 +1437,15 @@ void fcgi_pm_main(void *dummy) break; } + s->restartTime = now; + if (s->directive == APP_CLASS_DYNAMIC) { s->numProcesses++; fcgi_dynamic_total_proc_count++; FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count); -#ifdef WIN32 - if (i == 0 && !restart) { - fcgi_rdwr_unlock(s->dynamic_lock, WRITER); - } -#endif } - s->procs[i].state = STATE_STARTED; + s->procs[i].state = FCGI_RUNNING; if (restart) s->numRestarts++; @@ -1684,10 +1539,7 @@ void fcgi_pm_main(void *dummy) } } - /* @@@ This (comment) needs to go away when dynamic gets cleaned up. - * If we get to this point, we have detected the - * termination of the process that was spawned off by - * the process manager to do a blocking kill above. */ + /* TODO: print something about this unknown child */ continue; ChildFound: @@ -1814,9 +1666,7 @@ ProcessSigTerm: /* * Kill off the children, then exit. */ - while (fcgi_servers != NULL) { - kill_fs_procs(fcgi_config_pool, fcgi_servers); - } + shutdown_all(); #ifdef WIN32 return; diff --git a/fcgi_util.c b/fcgi_util.c index 4b3a658..9de2c4b 100644 --- a/fcgi_util.c +++ b/fcgi_util.c @@ -1,8 +1,12 @@ /* - * $Id: fcgi_util.c,v 1.16 2000/11/12 15:13:24 robs Exp $ + * $Id: fcgi_util.c,v 1.17 2001/03/26 15:35:40 robs Exp $ */ #include "fcgi.h" + +#ifdef WIN32 +#pragma warning( disable : 4100 ) +#endif /******************************************************************************* * Compute printable MD5 hash. Pool p is used for scratch as well as for @@ -39,16 +43,6 @@ fcgi_util_socket_make_path_absolute(pool * const p, } } -/******************************************************************************* - * Allocate a new string from pool p with the name of a Unix/Domain socket's - * lock file (used by dynamic only). - */ -const char * -fcgi_util_socket_get_lock_filename(pool *p, const char *socket_path) -{ - return ap_pstrcat(p, socket_path, ".lock", NULL); -} - #ifndef WIN32 /******************************************************************************* * Build a Domain Socket Address structure, and calculate its size. @@ -283,16 +277,10 @@ fcgi_util_fs_get(const char *ePath, const char *user, const char *group) } const char * -fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, - struct stat *finfo, const uid_t uid, const gid_t gid) +fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo) { const char *err; - /* If a wrapper is in use, let the wrapper determine what it can - * and can't execute */ - if (fcgi_wrapper) - return NULL; - if (finfo == NULL) { finfo = (struct stat *)ap_palloc(p, sizeof(struct stat)); if (stat(fs_path, finfo) < 0) @@ -310,17 +298,21 @@ fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, if (S_ISDIR(finfo->st_mode)) return ap_psprintf(p, "script is a directory!"); + /* Let the wrapper determine what it can and can't execute */ + if (! fcgi_wrapper) + { #ifdef WIN32 - err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id); + err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id); #else - err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id); + err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id); #endif - if (err) { - return ap_psprintf(p, - "access for server (uid %ld, gid %ld) not allowed: %s", - (long)fcgi_user_id, (long)fcgi_group_id, err); + if (err) { + return ap_psprintf(p, + "access for server (uid %ld, gid %ld) not allowed: %s", + (long)fcgi_user_id, (long)fcgi_group_id, err); + } } - + return NULL; } @@ -343,12 +335,13 @@ fcgi_util_fs_new(pool *p) s->restartOnExit = FALSE; s->directive = APP_CLASS_UNKNOWN; s->processPriority = FCGI_DEFAULT_PRIORITY; + s->envp = &fcgi_empty_env; + #ifdef WIN32 - s->listenFd = (int)INVALID_HANDLE_VALUE; + s->listenFd = (int) INVALID_HANDLE_VALUE; #else s->listenFd = -2; #endif - s->envp = &fcgi_empty_env; return s; } @@ -414,57 +407,14 @@ fcgi_util_fs_create_procs(pool *p, int num) for (i = 0; i < num; i++) { #ifdef WIN32 proc[i].handle = INVALID_HANDLE_VALUE; + proc[i].terminationEvent = INVALID_HANDLE_VALUE; #endif proc[i].pid = 0; - proc[i].state = STATE_READY; + proc[i].state = FCGI_READY; } return proc; } -/* - *---------------------------------------------------------------------- - * - * fcgi_util_lock_fd - * - * Provide file locking via fcntl(2) function call. This - * code has been borrowed from Stevens "Advanced Unix - * Programming", page 370. - * - * Inputs: - * File descriptor to be locked, offsets within the file. - * - * Results: - * 0 on successful locking, -1 otherwise - * - * Side effects: - * File pointed to by file descriptor is locked or unlocked. - * Provided macros allow for both blocking and non-blocking - * behavior. - * - *---------------------------------------------------------------------- - */ - -#ifndef WIN32 -int -fcgi_util_lock_fd(int fd, int cmd, int type, off_t offset, int whence, off_t len) -{ - int res = 0; - struct flock lock; - - lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ - lock.l_start = offset; /* byte offset, relative to whence */ - lock.l_whence = whence; /* SEEK_SET, SEET_CUR, SEEK_END */ - lock.l_len = len; /* # of bytes, (0 indicates to EOF) */ - - /* Don't be fooled into thinking we've set a lock when we've - merely caught a signal. */ - - /* This is OK only if there is a hard_timeout() in effect! */ - while ((res = fcntl(fd, cmd, &lock)) == -1 && errno == EINTR); - return res; -} -#endif - int fcgi_util_gettimeofday(struct timeval *Time) { #ifdef WIN32 DWORD clock; @@ -486,94 +436,4 @@ int fcgi_util_gettimeofday(struct timeval *Time) { #endif } -#ifdef WIN32 - -/* - * We allow only one writer at a time and a writer can proceed only - * when there are no readers. - * - * @@@ For named pipes we could lock only the process we're going to waste. - */ - -FcgiRWLock * fcgi_rdwr_create() -{ - FcgiRWLock *newlock = NULL; - - newlock = (FcgiRWLock *) malloc(sizeof(FcgiRWLock)); - ap_assert(newlock); - - newlock->write_lock = CreateEvent(NULL, FALSE, TRUE, NULL); - newlock->mutex = CreateMutex(NULL, FALSE, NULL); - newlock->counter = 0; - return newlock; -} - -void fcgi_rdwr_destroy(FcgiRWLock *lock) -{ - CloseHandle(lock->write_lock); - CloseHandle(lock->mutex); - free(lock); -} - -int fcgi_rdwr_lock(FcgiRWLock *lock, int type) -{ - if (type == WRITER) - { - WaitForSingleObject(lock->write_lock, INFINITE); - } - else - { - WaitForSingleObject(lock->mutex, INFINITE); - if (lock->counter == 0) - { - WaitForSingleObject(lock->write_lock, INFINITE); - } - ++lock->counter; - ReleaseMutex(lock->mutex); - } - - return 0; -} - -int fcgi_rdwr_unlock(FcgiRWLock *lock, int type) -{ - if (type == WRITER) - { - SetEvent(lock->write_lock); - } - else - { - WaitForSingleObject(lock->mutex, INFINITE); - --lock->counter; - if (lock->counter == 0) - { - SetEvent(lock->write_lock); - } - ReleaseMutex(lock->mutex); - } - - return 0; -} - -int fcgi_rdwr_try_lock(FcgiRWLock *lock, int type) -{ - DWORD rc; - - if (type == WRITER) - { - rc = WaitForSingleObject(lock->write_lock, 0); - if (rc == WAIT_TIMEOUT || rc == WAIT_FAILED) - { - return -1; - } - } - else - { - ap_assert(0); - } - - return 0; -} - -#endif diff --git a/mod_fastcgi.c b/mod_fastcgi.c index f4c695c..39726b5 100644 --- a/mod_fastcgi.c +++ b/mod_fastcgi.c @@ -3,7 +3,7 @@ * * Apache server module for FastCGI. * - * $Id: mod_fastcgi.c,v 1.107 2001/03/05 18:19:59 robs Exp $ + * $Id: mod_fastcgi.c,v 1.108 2001/03/26 15:35:40 robs Exp $ * * Copyright (c) 1995-1996 Open Market, Inc. * @@ -73,7 +73,7 @@ #ifndef timersub #define timersub(a, b, result) \ -do { \ +do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ @@ -105,6 +105,7 @@ char *fcgi_dynamic_dir = NULL; /* directory for the dynamic * fastcgi apps' sockets */ #ifdef WIN32 +#pragma warning( disable : 4706 4100 4127) fcgi_pm_job *fcgi_dynamic_mbox = NULL; HANDLE *fcgi_dynamic_mbox_mutex = NULL; HANDLE fcgi_pm_thread = INVALID_HANDLE_VALUE; @@ -113,13 +114,13 @@ HANDLE fcgi_pm_thread = INVALID_HANDLE_VALUE; char *fcgi_empty_env = NULL; u_int dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS; -u_int dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS; -u_int dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS; +int dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS; +int dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS; u_int dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL; u_int dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL; float dynamicGain = FCGI_DEFAULT_GAIN; -u_int dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1; -u_int dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N; +int dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1; +int dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N; u_int dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY; u_int dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT; char **dynamicEnvp = &fcgi_empty_env; @@ -135,7 +136,7 @@ u_int dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT; /******************************************************************************* * Construct a message and write it to the pm_pipe. */ -static void send_to_pm(pool * const p, const char id, const char * const fs_path, +static void send_to_pm(const char id, const char * const fs_path, const char *user, const char * const group, const unsigned long q_usec, const unsigned long req_usec) { @@ -157,7 +158,7 @@ static void send_to_pm(pool * const p, const char id, const char * const fs_path switch(id) { - case PLEASE_START: + case FCGI_START: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); @@ -170,7 +171,7 @@ static void send_to_pm(pool * const p, const char id, const char * const fs_path #endif break; - case CONN_TIMEOUT: + case FCGI_TIMEOUT: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); @@ -183,7 +184,7 @@ static void send_to_pm(pool * const p, const char id, const char * const fs_path #endif break; - case REQ_COMPLETE: + case FCGI_COMPLETE: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); @@ -282,7 +283,7 @@ static void init_module(server_rec *s, pool *p) #endif } -static void fcgi_child_init(server_rec *server_conf, pool *p) +static void fcgi_child_init(server_rec *dc0, pool *dc1) { #ifdef WIN32 /* Create the Event Handlers */ @@ -297,8 +298,8 @@ static void fcgi_child_init(server_rec *server_conf, pool *p) return; } -static void fcgi_child_exit(server_rec *server_conf, pool *p) { - +static void fcgi_child_exit(server_rec *dc0, pool *dc1) +{ #ifdef WIN32 /* Signaling the PM thread tp exit*/ SetEvent(fcgi_event_handles[TERM_EVENT]); @@ -734,17 +735,8 @@ static void close_connection_to_fs(fcgi_request *fr) ap_pclosesocket(rp, fr->fd); } - if (fr->dynamic) { -#ifdef WIN32 - if (fr->lockFd != NULL) { - fcgi_rdwr_unlock(fr->lockFd, READER); - } -#else - if (fr->lockFd >= 0) { - ap_pclosef(rp, fr->lockFd); - } -#endif - + if (fr->dynamic) + { if (fr->keepReadingFromFcgiApp == FALSE) { /* XXX REQ_COMPLETE is only sent for requests which complete * normally WRT the fcgi app. There is no data sent for @@ -763,7 +755,7 @@ static void close_connection_to_fs(fcgi_request *fr) timersub(&fr->queueTime, &fr->startTime, &qtime); timersub(&fr->completeTime, &fr->queueTime, &rtime); - send_to_pm(rp, REQ_COMPLETE, fr->fs_path, + send_to_pm(FCGI_COMPLETE, fr->fs_path, fr->user, fr->group, qtime.tv_sec * 1000000 + qtime.tv_usec, rtime.tv_sec * 1000000 + rtime.tv_usec); @@ -774,11 +766,6 @@ static void close_connection_to_fs(fcgi_request *fr) #ifdef WIN32 -static void lock_cleanup(void * data) -{ - fcgi_rdwr_unlock((FcgiRWLock *) data, READER); -} - static int set_nonblocking(SOCKET fd, int nonblocking) { unsigned long ioctl_arg = (nonblocking) ? 1 : 0; @@ -829,7 +816,8 @@ static int open_connection_to_fs(fcgi_request *fr) #endif /* Create the connection point */ - if (fr->dynamic) { + if (fr->dynamic) + { socket_path = fcgi_util_socket_hash_filename(rp, fr->fs_path, fr->user, fr->group); socket_path = fcgi_util_socket_make_path_absolute(rp, socket_path, 1); @@ -843,7 +831,9 @@ static int open_connection_to_fs(fcgi_request *fr) return FCGI_FAILED; } #endif - } else { + } + else + { #ifdef WIN32 if (fr->fs->dest_addr != NULL) { socket_addr = fr->fs->dest_addr; @@ -860,80 +850,76 @@ static int open_connection_to_fs(fcgi_request *fr) socket_addr_len = fr->fs->socket_addr_len; } - /* Dynamic app's lockfile handling */ - if (fr->dynamic) { -#ifndef WIN32 - const char *lockFileName = fcgi_util_socket_get_lock_filename(rp, socket_path); - struct stat lstbuf; -#endif - struct stat bstbuf; - int result = 0; - - do { + if (fr->dynamic) + { #ifdef WIN32 - if (fr->fs != NULL) + if (fr->fs && fr->fs->restartTime) #else - if (stat(lockFileName, &lstbuf) == 0 && S_ISREG(lstbuf.st_mode)) + struct stat sock_stat; + + if (stat(socket_path, &sock_stat) == 0) #endif + { + // It exists + if (dynamicAutoUpdate) { - if (dynamicAutoUpdate && (stat(fr->fs_path, &bstbuf) == 0) + struct stat app_stat; + + /* TODO: follow sym links */ + + if (stat(fr->fs_path, &app_stat) == 0) + { #ifdef WIN32 - && ((fr->fs->restartTime > 0) && (fr->fs->restartTime < bstbuf.st_mtime))) + if (fr->fs->restartTime < app_stat.st_mtime) #else - && (lstbuf.st_mtime < bstbuf.st_mtime)) + if (sock_stat.st_mtime < app_stat.st_mtime) #endif - { - struct timeval tv = {1, 0}; - - /* Its already running, but there's a newer one, - * ask the process manager to start it. - * it will notice that the binary is newer, - * and do a restart instead. - */ - send_to_pm(rp, PLEASE_START, fr->fs_path, fr->user, fr->group, 0, 0); + { +#ifndef WIN32 + struct timeval tv = {1, 0}; +#endif + /* + * There's a newer one, request a restart. + */ + send_to_pm(FCGI_RESTART, fr->fs_path, fr->user, fr->group, 0, 0); - /* Avoid sleep/alarm interactions */ - ap_select(0, NULL, NULL, NULL, &tv); - } #ifdef WIN32 - fr->lockFd = fr->fs->dynamic_lock; - result = 1; + Sleep(1000); #else - fr->lockFd = ap_popenf(rp, lockFileName, O_APPEND, 0); - result = (fr->lockFd < 0) ? (0) : (1); + /* Avoid sleep/alarm interactions */ + ap_select(0, NULL, NULL, NULL, &tv); #endif - } else { - struct timeval tv = {1, 0}; - - send_to_pm(rp, PLEASE_START, fr->fs_path, fr->user, fr->group, 0, 0); + } + } + } + } + else + { + send_to_pm(FCGI_START, fr->fs_path, fr->user, fr->group, 0, 0); + + /* Wait until it looks like its running */ + for (;;) + { #ifdef WIN32 - Sleep(0); + Sleep(1000); + + fr->fs = fcgi_util_fs_get_by_id(fr->fs_path, 0, 0); + + if (fr->fs && fr->fs->restartTime) #else + struct timeval tv = {1, 0}; + /* Avoid sleep/alarm interactions */ ap_select(0, NULL, NULL, NULL, &tv); + + if (stat(socket_path, &sock_stat) == 0) #endif + { + break; + } } -#ifdef WIN32 - fr->fs = fcgi_util_fs_get_by_id(fr->fs_path, 0, 0); -#endif - } while (result != 1); - - /* Block until we get a shared (non-exclusive) read Lock */ - if (fcgi_wait_for_shared_read_lock(fr->lockFd) < 0) { - ap_log_rerror(FCGI_LOG_ERR, r, - "FastCGI: failed to connect to server \"%s\": " - "can't obtain shared read lock", fr->fs_path); - return FCGI_FAILED; } - -#ifdef WIN32 - ap_block_alarms(); - ap_register_cleanup(rp, (void *) fr->lockFd, lock_cleanup, ap_null_cleanup); - ap_unblock_alarms(); -#endif - - FCGIDBG2("got_dynamic_shared_read_lock: %s", fr->fs_path); } #ifdef WIN32 @@ -985,7 +971,9 @@ static int open_connection_to_fs(fcgi_request *fr) break; } - if (GetLastError() != ERROR_PIPE_BUSY) { + if (GetLastError() != ERROR_PIPE_BUSY + && GetLastError() != ERROR_FILE_NOT_FOUND) + { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "CreateFile() failed", fr->fs_path); @@ -996,7 +984,7 @@ static int open_connection_to_fs(fcgi_request *fr) ready = WaitNamedPipe(socket_path, interval); if (fr->dynamic && !ready) { - send_to_pm(rp, CONN_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); + send_to_pm(FCGI_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); } if (fcgi_util_gettimeofday(&fr->queueTime) < 0) { @@ -1126,7 +1114,7 @@ static int open_connection_to_fs(fcgi_request *fr) break; /* select() timed out */ - send_to_pm(rp, CONN_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); + send_to_pm(FCGI_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); } while ((fr->queueTime.tv_sec - fr->startTime.tv_sec) < (int)dynamicAppConnectTimeout); /* XXX These can be moved down when dynamic vars live is a struct */ @@ -1412,7 +1400,7 @@ static int do_work(request_rec *r, fcgi_request *fr) struct timeval idle_time; timersub(&fr->queueTime, &dynamic_last_activity_time, &idle_time); if (idle_time.tv_sec > idle_timeout) { - send_to_pm(rp, CONN_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); + send_to_pm(FCGI_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with (dynamic) server \"%s\" aborted: (first read) idle timeout (%d sec)", fr->fs_path, idle_timeout); @@ -1430,7 +1418,7 @@ static int do_work(request_rec *r, fcgi_request *fr) } else { /* Killed time somewhere.. client read? */ - send_to_pm(rp, CONN_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); + send_to_pm(FCGI_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_read = qwait.tv_sec / dynamicPleaseStartDelay + 1; timeOut.tv_sec = dynamic_first_read * dynamicPleaseStartDelay; timeOut.tv_usec = 100000; /* fudge for select() slop */ @@ -1458,12 +1446,21 @@ static int do_work(request_rec *r, fcgi_request *fr) else { int stopTime = time(NULL) + timeOut.tv_sec; - if (BufferLength(fr->serverOutputBuffer) == 0) { + if (BufferLength(fr->serverOutputBuffer) == 0) + { status = 0; - while ((timeOut.tv_sec != 0) && (time(NULL) <= stopTime)) { - if (PeekNamedPipe((HANDLE) fr->fd,NULL, 0, NULL, &bytesavail, NULL) && - bytesavail > 0) + while ((timeOut.tv_sec != 0) && (time(NULL) <= stopTime)) + { + BOOL ok = PeekNamedPipe((HANDLE) fr->fd,NULL, 0, NULL, &bytesavail, NULL); + if (! ok) + { + ap_log_rerror(FCGI_LOG_ERR, r, + "FastCGI: comm with sever \"%s\" aborted: PeekNamedPipe() failed", + fr->fs_path); + return server_error(fr); + } + if (bytesavail > 0) { status =1; break; @@ -1491,7 +1488,7 @@ static int do_work(request_rec *r, fcgi_request *fr) timersub(&fr->queueTime, &fr->startTime, &qwait); - send_to_pm(rp, CONN_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); + send_to_pm(FCGI_TIMEOUT, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_read = qwait.tv_sec / dynamicPleaseStartDelay + 1; } @@ -1672,7 +1669,7 @@ static fcgi_request *create_fcgi_request(request_rec * const r, const char *fs_p if (fs == NULL) { /* Its a request for a dynamic FastCGI application */ const char * const err = - fcgi_util_fs_is_path_ok(p, fs_path, my_finfo, r->server->server_uid, r->server->server_gid); + fcgi_util_fs_is_path_ok(p, fs_path, my_finfo); if (err) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: invalid (dynamic) server \"%s\": %s", fs_path, err); -- 2.11.4.GIT