X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=libfcgi%2Fos_unix.c;h=6d7dc534a01636cad3afb89c866eef3fa02def74;hb=2a7273a10e04c7473799d47a38020086d5a8c233;hp=1da9447c9353bf139ecc6f95ebe43cbb5b5da6c8;hpb=4a7222d72a015d25ef141e73d7e82c2ae27cbfac;p=catagits%2Ffcgi2.git diff --git a/libfcgi/os_unix.c b/libfcgi/os_unix.c index 1da9447..6d7dc53 100755 --- a/libfcgi/os_unix.c +++ b/libfcgi/os_unix.c @@ -1,4 +1,4 @@ -/* +/* * os_unix.c -- * * Description of file. @@ -8,60 +8,59 @@ * All rights reserved. * * This file contains proprietary and confidential information and - * remains the unpublished property of Open Market, Inc. Use, - * disclosure, or reproduction is prohibited except as permitted by - * express written license agreement with Open Market, Inc. + * remains the unpublished property of Open Market, Inc. Use, + * disclosure, or reproduction is prohibited except as permitted by + * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com */ #ifndef lint -static const char rcsid[] = "$Id: os_unix.c,v 1.7 1999/02/05 04:08:56 roberts Exp $"; +static const char rcsid[] = "$Id: os_unix.c,v 1.38 2003/06/22 00:16:43 robs Exp $"; #endif /* not lint */ -#include "fcgimisc.h" -#include "fcgiapp.h" -#include "fcgiappmisc.h" -#include "fastcgi.h" +#include "fcgi_config.h" -#include -#ifdef HAVE_UNISTD_H -#include +#include + +#ifdef HAVE_NETINET_IN_H +#include #endif + +#include #include -#include -#include -#include /* for memchr() */ #include -#include +#include /* for fcntl */ #include -#ifdef HAVE_SYS_SOCKET_H -#include /* for getpeername */ -#endif +#include /* for memchr() */ +#include +#include +#include +#include +#include +#include #include -#include /* for fcntl */ +#include + #ifdef HAVE_NETDB_H #include #endif -#include -#include -#ifdef HAVE_NETINET_IN_H -#include +#ifdef HAVE_SYS_SOCKET_H +#include /* for getpeername */ +#endif + +#ifdef HAVE_UNISTD_H +#include #endif -#include -#include +#include "fastcgi.h" +#include "fcgimisc.h" #include "fcgios.h" -#ifndef _CLIENTDATA -# if defined(__STDC__) || defined(__cplusplus) - typedef void *ClientData; -# else - typedef int *ClientData; -# endif /* __STDC__ */ -#define _CLIENTDATA +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) #endif /* @@ -86,21 +85,10 @@ typedef struct { #define AIO_RD_IX(fd) (fd * 2) #define AIO_WR_IX(fd) ((fd * 2) + 1) +static int asyncIoInUse = FALSE; static int asyncIoTableSize = 16; static AioInfo *asyncIoTable = NULL; -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - -static int isFastCGI = FALSE; static int libInitialized = FALSE; static fd_set readFdSet; @@ -112,7 +100,50 @@ static fd_set writeFdSetPost; static int numWrPosted = 0; static int volatile maxFd = -1; - +static int shutdownPending = FALSE; +static int shutdownNow = FALSE; + +void OS_ShutdownPending() +{ + shutdownPending = TRUE; +} + +static void OS_Sigusr1Handler(int signo) +{ + OS_ShutdownPending(); +} + +static void OS_SigpipeHandler(int signo) +{ + ; +} + +static void installSignalHandler(int signo, const struct sigaction * act, int force) +{ + struct sigaction sa; + + sigaction(signo, NULL, &sa); + + if (force || sa.sa_handler == SIG_DFL) + { + sigaction(signo, act, NULL); + } +} + +static void OS_InstallSignalHandlers(int force) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + sa.sa_handler = OS_SigpipeHandler; + installSignalHandler(SIGPIPE, &sa, force); + + sa.sa_handler = OS_Sigusr1Handler; + installSignalHandler(SIGUSR1, &sa, force); +} + /* *-------------------------------------------------------------- * @@ -136,8 +167,8 @@ int OS_LibInit(int stdioFds[3]) { if(libInitialized) return 0; - - asyncIoTable = malloc(asyncIoTableSize * sizeof(AioInfo)); + + asyncIoTable = (AioInfo *)malloc(asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; return -1; @@ -149,11 +180,14 @@ int OS_LibInit(int stdioFds[3]) FD_ZERO(&writeFdSet); FD_ZERO(&readFdSetPost); FD_ZERO(&writeFdSetPost); + + OS_InstallSignalHandlers(FALSE); + libInitialized = TRUE; + return 0; } - /* *-------------------------------------------------------------- * @@ -173,14 +207,13 @@ void OS_LibShutdown() { if(!libInitialized) return; - + free(asyncIoTable); asyncIoTable = NULL; libInitialized = FALSE; return; } - /* *---------------------------------------------------------------------- * @@ -199,7 +232,7 @@ void OS_LibShutdown() *---------------------------------------------------------------------- */ -static int OS_BuildSockAddrUn(char *bindPath, +static int OS_BuildSockAddrUn(const char *bindPath, struct sockaddr_un *servAddrPtr, int *servAddrLen) { @@ -227,13 +260,11 @@ static int OS_BuildSockAddrUn(char *bindPath, #endif return 0; } - union SockAddrUnion { struct sockaddr_un unixVariant; struct sockaddr_in inetVariant; }; - /* * OS_CreateLocalIpcFd -- * @@ -251,13 +282,14 @@ union SockAddrUnion { * *---------------------------------------------------------------------- */ -int OS_CreateLocalIpcFd(char *bindPath) +int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { int listenSock, servLen; - union SockAddrUnion sa; + union SockAddrUnion sa; int tcp = FALSE; + unsigned long tcp_ia = 0; char *tp; - short port; + short port = 0; char host[MAXPATHLEN]; strcpy(host, bindPath); @@ -269,12 +301,26 @@ int OS_CreateLocalIpcFd(char *bindPath) tcp = TRUE; } } - if(tcp && (*host && strcmp(host, "localhost") != 0)) { - fprintf(stderr, "To start a service on a TCP port can not " - "specify a host name.\n" - "You should either use \"localhost:\" or " - " just use \":.\"\n"); - exit(1); + if(tcp) { + if (!*host || !strcmp(host,"*")) { + tcp_ia = htonl(INADDR_ANY); + } else { + tcp_ia = inet_addr(host); + if (tcp_ia == INADDR_NONE) { + struct hostent * hep; + hep = gethostbyname(host); + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + fprintf(stderr, "Cannot resolve host name %s -- exiting!\n", host); + exit(1); + } + if (hep->h_addr_list[1]) { + fprintf(stderr, "Host %s has multiple addresses ---\n", host); + fprintf(stderr, "you must choose one explicitly!!!\n"); + exit(1); + } + tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr; + } + } } if(tcp) { @@ -300,7 +346,7 @@ int OS_CreateLocalIpcFd(char *bindPath) if(tcp) { memset((char *) &sa.inetVariant, 0, sizeof(sa.inetVariant)); sa.inetVariant.sin_family = AF_INET; - sa.inetVariant.sin_addr.s_addr = htonl(INADDR_ANY); + sa.inetVariant.sin_addr.s_addr = tcp_ia; sa.inetVariant.sin_port = htons(port); servLen = sizeof(sa.inetVariant); } else { @@ -311,7 +357,7 @@ int OS_CreateLocalIpcFd(char *bindPath) } } if(bind(listenSock, (struct sockaddr *) &sa.unixVariant, servLen) < 0 - || listen(listenSock, 5) < 0) { + || listen(listenSock, backlog) < 0) { perror("bind/listen"); exit(errno); } @@ -319,7 +365,6 @@ int OS_CreateLocalIpcFd(char *bindPath) return listenSock; } - /* *---------------------------------------------------------------------- * @@ -347,7 +392,7 @@ int OS_FcgiConnect(char *bindPath) int connectStatus; char *tp; char host[MAXPATHLEN]; - short port; + short port = 0; int tcp = FALSE; strcpy(host, bindPath); @@ -378,7 +423,7 @@ int OS_FcgiConnect(char *bindPath) resultSock = socket(AF_UNIX, SOCK_STREAM, 0); } - assert(resultSock >= 0); + ASSERT(resultSock >= 0); connectStatus = connect(resultSock, (struct sockaddr *) &sa.unixVariant, servLen); if(connectStatus >= 0) { @@ -392,8 +437,7 @@ int OS_FcgiConnect(char *bindPath) return -1; } } - - + /* *-------------------------------------------------------------- * @@ -412,9 +456,10 @@ int OS_FcgiConnect(char *bindPath) */ int OS_Read(int fd, char * buf, size_t len) { + if (shutdownNow) return -1; return(read(fd, buf, len)); } - + /* *-------------------------------------------------------------- * @@ -433,10 +478,10 @@ int OS_Read(int fd, char * buf, size_t len) */ int OS_Write(int fd, char * buf, size_t len) { + if (shutdownNow) return -1; return(write(fd, buf, len)); } - /* *---------------------------------------------------------------------- * @@ -502,7 +547,6 @@ int OS_SpawnChild(char *appPath, int listenFd) return 0; } - /* *-------------------------------------------------------------- * @@ -524,11 +568,12 @@ int OS_SpawnChild(char *appPath, int listenFd) * *-------------------------------------------------------------- */ -int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, +int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(STDIN_FILENO); + asyncIoInUse = TRUE; ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; @@ -546,9 +591,9 @@ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, static void GrowAsyncTable(void) { int oldTableSize = asyncIoTableSize; - + asyncIoTableSize = asyncIoTableSize * 2; - asyncIoTable = realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo)); + asyncIoTable = (AioInfo *)realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; exit(errno); @@ -558,7 +603,6 @@ static void GrowAsyncTable(void) } - /* *-------------------------------------------------------------- * @@ -587,13 +631,14 @@ int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(fd); - + ASSERT(asyncIoTable != NULL); + asyncIoInUse = TRUE; if(fd > maxFd) maxFd = fd; - if(index >= asyncIoTableSize) { + while (index >= asyncIoTableSize) { GrowAsyncTable(); } @@ -608,7 +653,7 @@ int OS_AsyncRead(int fd, int offset, void *buf, int len, FD_SET(fd, &readFdSet); return 0; } - + /* *-------------------------------------------------------------- * @@ -632,15 +677,17 @@ int OS_AsyncRead(int fd, int offset, void *buf, int len, * *-------------------------------------------------------------- */ -int OS_AsyncWrite(int fd, int offset, void *buf, int len, +int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_WR_IX(fd); + asyncIoInUse = TRUE; + if(fd > maxFd) maxFd = fd; - if(index >= asyncIoTableSize) { + while (index >= asyncIoTableSize) { GrowAsyncTable(); } @@ -655,7 +702,7 @@ int OS_AsyncWrite(int fd, int offset, void *buf, int len, FD_SET(fd, &writeFdSet); return 0; } - + /* *-------------------------------------------------------------- * @@ -672,27 +719,65 @@ int OS_AsyncWrite(int fd, int offset, void *buf, int len, * *-------------------------------------------------------------- */ -int OS_Close(int fd) +int OS_Close(int fd, int shutdown_ok) { - int index = AIO_RD_IX(fd); - - FD_CLR(fd, &readFdSet); - FD_CLR(fd, &readFdSetPost); - if(asyncIoTable[index].inUse != 0) { - asyncIoTable[index].inUse = 0; + if (fd == -1) + return 0; + + if (asyncIoInUse) { + int index = AIO_RD_IX(fd); + + FD_CLR(fd, &readFdSet); + FD_CLR(fd, &readFdSetPost); + if (asyncIoTable[index].inUse != 0) { + asyncIoTable[index].inUse = 0; + } + + FD_CLR(fd, &writeFdSet); + FD_CLR(fd, &writeFdSetPost); + index = AIO_WR_IX(fd); + if (asyncIoTable[index].inUse != 0) { + asyncIoTable[index].inUse = 0; + } + + if (maxFd == fd) { + maxFd--; + } } - - FD_CLR(fd, &writeFdSet); - FD_CLR(fd, &writeFdSetPost); - index = AIO_WR_IX(fd); - if(asyncIoTable[index].inUse != 0) { - asyncIoTable[index].inUse = 0; + + /* + * shutdown() the send side and then read() from client until EOF + * or a timeout expires. This is done to minimize the potential + * that a TCP RST will be sent by our TCP stack in response to + * receipt of additional data from the client. The RST would + * cause the client to discard potentially useful response data. + */ + + if (shutdown_ok) + { + if (shutdown(fd, 1) == 0) + { + struct timeval tv; + fd_set rfds; + int rv; + char trash[1024]; + + FD_ZERO(&rfds); + + do + { + FD_SET(fd, &rfds); + tv.tv_sec = 2; + tv.tv_usec = 0; + rv = select(fd + 1, &rfds, NULL, NULL, &tv); + } + while (rv > 0 && read(fd, trash, sizeof(trash)) > 0); + } } - if(maxFd == fd) - maxFd--; + return close(fd); } - + /* *-------------------------------------------------------------- * @@ -713,11 +798,10 @@ int OS_CloseRead(int fd) asyncIoTable[AIO_RD_IX(fd)].inUse = 0; FD_CLR(fd, &readFdSet); } - + return shutdown(fd, 0); } - /* *-------------------------------------------------------------- * @@ -744,6 +828,7 @@ int OS_DoIo(struct timeval *tmo) fd_set readFdSetCpy; fd_set writeFdSetCpy; + asyncIoInUse = TRUE; FD_ZERO(&readFdSetCpy); FD_ZERO(&writeFdSetCpy); @@ -755,7 +840,7 @@ int OS_DoIo(struct timeval *tmo) FD_SET(fd, &writeFdSetCpy); } } - + /* * If there were no completed events from a prior call, see if there's * any work to do. @@ -789,18 +874,18 @@ int OS_DoIo(struct timeval *tmo) if(numRdPosted == 0 && numWrPosted == 0) return 0; - + for(fd = 0; fd <= maxFd; fd++) { /* * Do reads and dispatch callback. */ - if(FD_ISSET(fd, &readFdSetPost) + if(FD_ISSET(fd, &readFdSetPost) && asyncIoTable[AIO_RD_IX(fd)].inUse) { numRdPosted--; FD_CLR(fd, &readFdSetPost); aioPtr = &asyncIoTable[AIO_RD_IX(fd)]; - + len = read(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; @@ -820,7 +905,7 @@ int OS_DoIo(struct timeval *tmo) numWrPosted--; FD_CLR(fd, &writeFdSetPost); aioPtr = &asyncIoTable[AIO_WR_IX(fd)]; - + len = write(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; @@ -833,7 +918,20 @@ int OS_DoIo(struct timeval *tmo) return 0; } - +/* + * Not all systems have strdup(). + * @@@ autoconf should determine whether or not this is needed, but for now.. + */ +static char * str_dup(const char * str) +{ + char * sdup = (char *) malloc(strlen(str) + 1); + + if (sdup) + strcpy(sdup, str); + + return sdup; +} + /* *---------------------------------------------------------------------- * @@ -847,38 +945,32 @@ int OS_DoIo(struct timeval *tmo) * *---------------------------------------------------------------------- */ -static int ClientAddrOK(struct sockaddr_in *saPtr, char *clientList) +static int ClientAddrOK(struct sockaddr_in *saPtr, const char *clientList) { int result = FALSE; char *clientListCopy, *cur, *next; - char *newString = NULL; - int strLen; - if(clientList == NULL || *clientList == '\0') { + if (clientList == NULL || *clientList == '\0') { return TRUE; } - strLen = strlen(clientList); - clientListCopy = malloc(strLen + 1); - assert(newString != NULL); - memcpy(newString, clientList, strLen); - newString[strLen] = '\000'; - - for(cur = clientListCopy; cur != NULL; cur = next) { + clientListCopy = str_dup(clientList); + + for (cur = clientListCopy; cur != NULL; cur = next) { next = strchr(cur, ','); - if(next != NULL) { + if (next != NULL) { *next++ = '\0'; - } - if(inet_addr(cur) == saPtr->sin_addr.s_addr) { + } + if (inet_addr(cur) == saPtr->sin_addr.s_addr) { result = TRUE; break; } } + free(clientListCopy); return result; } - /* *---------------------------------------------------------------------- * @@ -897,24 +989,29 @@ static int ClientAddrOK(struct sockaddr_in *saPtr, char *clientList) * *---------------------------------------------------------------------- */ -static int AcquireLock(int blocking) +static int AcquireLock(int sock, int fail_on_intr) { #ifdef USE_LOCKING - struct flock lock; - lock.l_type = F_WRLCK; - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - - if(fcntl(FCGI_LISTENSOCK_FILENO, - blocking ? F_SETLKW : F_SETLK, &lock) < 0) { - if (errno != EINTR) - return -1; - } -#endif /* USE_LOCKING */ + do { + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + if (fcntl(sock, F_SETLKW, &lock) != -1) + return 0; + } while (errno == EINTR + && ! fail_on_intr + && ! shutdownPending); + + return -1; + +#else return 0; +#endif } - + /* *---------------------------------------------------------------------- * @@ -932,27 +1029,109 @@ static int AcquireLock(int blocking) * *---------------------------------------------------------------------- */ -static int ReleaseLock(void) +static int ReleaseLock(int sock) { #ifdef USE_LOCKING - struct flock lock; - lock.l_type = F_UNLCK; - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; + do { + struct flock lock; + lock.l_type = F_UNLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; - if(fcntl(FCGI_LISTENSOCK_FILENO, F_SETLK, &lock) < 0) { - return -1; - } -#endif /* USE_LOCKING */ + if (fcntl(sock, F_SETLK, &lock) != -1) + return 0; + } while (errno == EINTR); + + return -1; + +#else return 0; +#endif +} + +/********************************************************************** + * Determine if the errno resulting from a failed accept() warrants a + * retry or exit(). Based on Apache's http_main.c accept() handling + * and Stevens' Unix Network Programming Vol 1, 2nd Ed, para. 15.6. + */ +static int is_reasonable_accept_errno (const int error) +{ + switch (error) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means ECONNABORTED, so + * we need to ignore it for them. See discussion in new-httpd + * archives nh.9701 search for EPROTO. Also see nh.9603, search + * for EPROTO: There is potentially a bug in Solaris 2.x x<6, and + * other boxes that implement tcp sockets in userland (i.e. on top of + * STREAMS). On these systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to handle both uses of + * EPROTO. */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp stacks (i.e. + * bsd) tend to hide them behind getsockopt() interfaces. They + * occur when the net goes sour or the client disconnects after the + * three-way handshake has been done in the kernel but before + * userland has picked up the socket. */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + return 1; + + default: + return 0; + } +} + +/********************************************************************** + * This works around a problem on Linux 2.0.x and SCO Unixware (maybe + * others?). When a connect() is made to a Unix Domain socket, but its + * not accept()ed before the web server gets impatient and close()s, an + * accept() results in a valid file descriptor, but no data to read. + * This causes a block on the first read() - which never returns! + * + * Another approach to this is to write() to the socket to provoke a + * SIGPIPE, but this is a pain because of the FastCGI protocol, the fact + * that whatever is written has to be universally ignored by all FastCGI + * web servers, and a SIGPIPE handler has to be installed which returns + * (or SIGPIPE is ignored). + * + * READABLE_UNIX_FD_DROP_DEAD_TIMEVAL = 2,0 by default. + * + * Making it shorter is probably safe, but I'll leave that to you. Making + * it 0,0 doesn't work reliably. The shorter you can reliably make it, + * the faster your application will be able to recover (waiting 2 seconds + * may _cause_ the problem when there is a very high demand). At any rate, + * this is better than perma-blocking. + */ +static int is_af_unix_keeper(const int fd) +{ + struct timeval tval = { READABLE_UNIX_FD_DROP_DEAD_TIMEVAL }; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + return select(fd + 1, &read_fds, NULL, NULL, &tval) >= 0 && FD_ISSET(fd, &read_fds); } - /* *---------------------------------------------------------------------- * - * OS_FcgiIpcAccept -- + * OS_Accept -- * * Accepts a new FastCGI connection. This routine knows whether * we're dealing with TCP based sockets or NT Named Pipes for IPC. @@ -965,144 +1144,79 @@ static int ReleaseLock(void) * *---------------------------------------------------------------------- */ -int OS_FcgiIpcAccept(char *clientAddrList) +int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { - int socket; - union u_sockaddr { + int socket = -1; + union { struct sockaddr_un un; struct sockaddr_in in; } sa; -#if defined __linux__ - socklen_t len; -#else - int len; -#endif - if (AcquireLock(TRUE) < 0) { - return (-1); - } for (;;) { - do { - len = sizeof(sa); - socket = accept(FCGI_LISTENSOCK_FILENO, - (struct sockaddr *) &sa.un, &len); - } while ((socket < 0) && (errno == EINTR)); - - if (socket >= 0) { - - if (sa.in.sin_family == AF_INET) { -#ifdef TCP_NODELAY - /* No replies to outgoing data, so disable Nagle algorithm */ - int set = 1; - setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, - (char *)&set, sizeof(set)); -#endif - - /* Check that the client IP address is OK */ - if (ClientAddrOK(&sa.in, clientAddrList)) - break; - } - else { - /* This works around a problem on Linux 2.0.x and - * SCO Unixware (maybe others?). When a connect() is made to - * a Unix Domain socket, but its not accept()ed before the - * web server gets impatient and close()s, an accept() - * here results in a valid file descriptor, but no data to - * read. This causes a block on the first read() - and - * never returns! - * - * Another approach to this is to write() - * to the socket to provoke a SIGPIPE, but this is a pain - * because of the FastCGI protocol, the fact that whatever - * is written has to be universally ignored by all FastCGI - * web servers, and a SIGPIPE handler has to be installed - * which returns (or SIGPIPE is ignored). - * - * READABLE_UNIX_FD_DROP_DEAD_TIMEVAL = 2,0 by default. - * - * Making it shorter is probably safe, but I'll leave that - * to you. Making it 0,0 doesn't work reliably. The - * shorter you can reliably make it, the faster your - * application will be able to recover (waiting 2 seconds - * may _cause_ the problem when there is a very high demand). - * At any rate, this is better than perma-blocking. */ - - struct timeval tval = { READABLE_UNIX_FD_DROP_DEAD_TIMEVAL }; - fd_set read_fds; - - FD_ZERO(&read_fds); - FD_SET(socket, &read_fds); - if (select(socket + 1, &read_fds, NULL, NULL, &tval) > 0 - && FD_ISSET(socket, &read_fds)) - { - break; + if (AcquireLock(listen_sock, fail_on_intr)) + return -1; + + for (;;) { + do { +#ifdef HAVE_SOCKLEN + socklen_t len = sizeof(sa); +#else + int len = sizeof(sa); +#endif + if (shutdownPending) break; + /* There's a window here */ + + socket = accept(listen_sock, (struct sockaddr *)&sa, &len); + } while (socket < 0 + && errno == EINTR + && ! fail_on_intr + && ! shutdownPending); + + if (socket < 0) { + if (shutdownPending || ! is_reasonable_accept_errno(errno)) { + int errnoSave = errno; + + ReleaseLock(listen_sock); + + if (! shutdownPending) { + errno = errnoSave; + } + + return (-1); } + errno = 0; } + else { /* socket >= 0 */ + int set = 1; - close(socket); - continue; - } + if (sa.in.sin_family != AF_INET) + break; - /* Based on Apache's (v1.3.1) http_main.c accept() handling and - * Stevens' Unix Network Programming Vol 1, 2nd Ed, para. 15.6 - */ - switch (errno) { -#ifdef EPROTO - /* EPROTO on certain older kernels really means - * ECONNABORTED, so we need to ignore it for them. - * See discussion in new-httpd archives nh.9701 - * search for EPROTO. - * - * Also see nh.9603, search for EPROTO: - * There is potentially a bug in Solaris 2.x x<6, - * and other boxes that implement tcp sockets in - * userland (i.e. on top of STREAMS). On these - * systems, EPROTO can actually result in a fatal - * loop. See PR#981 for example. It's hard to - * handle both uses of EPROTO. - */ - case EPROTO: -#endif -#ifdef ECONNABORTED - case ECONNABORTED: -#endif - /* Linux generates the rest of these, other tcp - * stacks (i.e. bsd) tend to hide them behind - * getsockopt() interfaces. They occur when - * the net goes sour or the client disconnects - * after the three-way handshake has been done - * in the kernel but before userland has picked - * up the socket. - */ -#ifdef ECONNRESET - case ECONNRESET: -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: -#endif -#ifdef EHOSTUNREACH - case EHOSTUNREACH: -#endif -#ifdef ENETUNREACH - case ENETUNREACH: +#ifdef TCP_NODELAY + /* No replies to outgoing data, so disable Nagle */ + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); #endif - break; /* switch(errno) */ - default: { - int errnoSave = errno; - ReleaseLock(); - errno = errnoSave; - } - return (-1); - } /* switch(errno) */ - } /* for(;;) */ + /* Check that the client IP address is approved */ + if (ClientAddrOK(&sa.in, webServerAddrs)) + break; + + close(socket); + } /* socket >= 0 */ + } /* for(;;) */ + + if (ReleaseLock(listen_sock)) + return (-1); + + if (sa.in.sin_family != AF_UNIX || is_af_unix_keeper(socket)) + break; + + close(socket); + } /* while(1) - lock */ - if (ReleaseLock() < 0) { - return (-1); - } return (socket); } - + /* *---------------------------------------------------------------------- * @@ -1118,12 +1232,11 @@ int OS_FcgiIpcAccept(char *clientAddrList) * *---------------------------------------------------------------------- */ -int OS_IpcClose(int ipcFd) +int OS_IpcClose(int ipcFd, int shutdown) { - return OS_Close(ipcFd); + return OS_Close(ipcFd, shutdown); } - /* *---------------------------------------------------------------------- * @@ -1139,27 +1252,28 @@ int OS_IpcClose(int ipcFd) * *---------------------------------------------------------------------- */ -int OS_IsFcgi() +int OS_IsFcgi(int sock) { union { struct sockaddr_in in; struct sockaddr_un un; } sa; -#if defined __linux__ +#ifdef HAVE_SOCKLEN socklen_t len = sizeof(sa); #else int len = sizeof(sa); #endif - if (getpeername(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&sa, &len) != 0 - && errno == ENOTCONN) - isFastCGI = TRUE; - else - isFastCGI = FALSE; - - return (isFastCGI); + errno = 0; + + if (getpeername(sock, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { + return TRUE; + } + else { + return FALSE; + } } - + /* *---------------------------------------------------------------------- *