fix copyright information in these files
[catagits/fcgi2.git] / libfcgi / os_unix.c
index 3d7dcee..41b717a 100755 (executable)
@@ -1,27 +1,27 @@
 /*
  * os_unix.c --
  *
- *      Description of file.
- *
- *
- *  Copyright (c) 1995 Open Market, Inc.
- *  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.
- *
  *  Bill Snapper
  *  snapper@openmarket.com
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  */
 
 #ifndef lint
-static const char rcsid[] = "$Id: os_unix.c,v 1.11 1999/08/02 19:22:00 skimo Exp $";
+static const char rcsid[] = "$Id: os_unix.c,v 1.38 2003/06/22 00:16:43 robs Exp $";
 #endif /* not lint */
 
 #include "fcgi_config.h"
 
+#include <sys/types.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
 #include <arpa/inet.h>
 #include <assert.h>
 #include <errno.h>
@@ -34,17 +34,13 @@ static const char rcsid[] = "$Id: os_unix.c,v 1.11 1999/08/02 19:22:00 skimo Exp
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/un.h>
+#include <signal.h>
 
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
 
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h> /* for getpeername */
 #endif
@@ -54,17 +50,11 @@ static const char rcsid[] = "$Id: os_unix.c,v 1.11 1999/08/02 19:22:00 skimo Exp
 #endif
 
 #include "fastcgi.h"
-#include "fcgiapp.h"
-#include "fcgiappmisc.h"
 #include "fcgimisc.h"
 #include "fcgios.h"
 
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef TRUE
-#define TRUE 1
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
 #endif
 
 /*
@@ -89,10 +79,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;
 
-static int isFastCGI = FALSE;
 static int libInitialized = FALSE;
 
 static fd_set readFdSet;
@@ -104,7 +94,50 @@ static fd_set writeFdSetPost;
 static int numWrPosted = 0;
 static int volatile maxFd = -1;
 
-\f
+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);
+}
+
 /*
  *--------------------------------------------------------------
  *
@@ -141,11 +174,14 @@ int OS_LibInit(int stdioFds[3])
     FD_ZERO(&writeFdSet);
     FD_ZERO(&readFdSetPost);
     FD_ZERO(&writeFdSetPost);
+
+    OS_InstallSignalHandlers(TRUE);
+
     libInitialized = TRUE;
+
     return 0;
 }
 
-\f
 /*
  *--------------------------------------------------------------
  *
@@ -172,7 +208,6 @@ void OS_LibShutdown()
     return;
 }
 
-\f
 /*
  *----------------------------------------------------------------------
  *
@@ -191,7 +226,7 @@ void OS_LibShutdown()
  *----------------------------------------------------------------------
  */
 
-static int OS_BuildSockAddrUn(char *bindPath,
+static int OS_BuildSockAddrUn(const char *bindPath,
                               struct sockaddr_un *servAddrPtr,
                               int *servAddrLen)
 {
@@ -219,13 +254,11 @@ static int OS_BuildSockAddrUn(char *bindPath,
 #endif
     return 0;
 }
-\f
 union SockAddrUnion {
     struct  sockaddr_un        unixVariant;
     struct  sockaddr_in        inetVariant;
 };
 
-\f
 /*
  * OS_CreateLocalIpcFd --
  *
@@ -243,13 +276,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);
@@ -261,12 +295,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:<port>\" or "
-                       " just use \":<port>.\"\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) {
@@ -292,7 +340,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 {
@@ -303,7 +351,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);
     }
@@ -311,7 +359,6 @@ int OS_CreateLocalIpcFd(char *bindPath)
     return listenSock;
 }
 
-\f
 /*
  *----------------------------------------------------------------------
  *
@@ -339,7 +386,7 @@ int OS_FcgiConnect(char *bindPath)
     int connectStatus;
     char    *tp;
     char    host[MAXPATHLEN];
-    short   port;
+    short   port = 0;
     int            tcp = FALSE;
 
     strcpy(host, bindPath);
@@ -370,7 +417,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) {
@@ -385,7 +432,6 @@ int OS_FcgiConnect(char *bindPath)
     }
 }
 
-\f
 /*
  *--------------------------------------------------------------
  *
@@ -404,9 +450,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));
 }
-\f
+
 /*
  *--------------------------------------------------------------
  *
@@ -425,10 +472,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));
 }
 
-\f
 /*
  *----------------------------------------------------------------------
  *
@@ -494,7 +541,6 @@ int OS_SpawnChild(char *appPath, int listenFd)
     return 0;
 }
 
-\f
 /*
  *--------------------------------------------------------------
  *
@@ -521,6 +567,7 @@ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
 {
     int index = AIO_RD_IX(STDIN_FILENO);
 
+    asyncIoInUse = TRUE;
     ASSERT(asyncIoTable[index].inUse == 0);
     asyncIoTable[index].procPtr = procPtr;
     asyncIoTable[index].clientData = clientData;
@@ -550,7 +597,6 @@ static void GrowAsyncTable(void)
 
 }
 
-\f
 /*
  *--------------------------------------------------------------
  *
@@ -581,11 +627,12 @@ int OS_AsyncRead(int fd, int offset, void *buf, int len,
     int index = AIO_RD_IX(fd);
 
     ASSERT(asyncIoTable != NULL);
+    asyncIoInUse = TRUE;
 
     if(fd > maxFd)
         maxFd = fd;
 
-    if(index >= asyncIoTableSize) {
+    while (index >= asyncIoTableSize) {
         GrowAsyncTable();
     }
 
@@ -600,7 +647,7 @@ int OS_AsyncRead(int fd, int offset, void *buf, int len,
     FD_SET(fd, &readFdSet);
     return 0;
 }
-\f
+
 /*
  *--------------------------------------------------------------
  *
@@ -629,10 +676,12 @@ int OS_AsyncWrite(int fd, int offset, void *buf, int len,
 {
     int index = AIO_WR_IX(fd);
 
+    asyncIoInUse = TRUE;
+
     if(fd > maxFd)
         maxFd = fd;
 
-    if(index >= asyncIoTableSize) {
+    while (index >= asyncIoTableSize) {
         GrowAsyncTable();
     }
 
@@ -647,7 +696,7 @@ int OS_AsyncWrite(int fd, int offset, void *buf, int len,
     FD_SET(fd, &writeFdSet);
     return 0;
 }
-\f
+
 /*
  *--------------------------------------------------------------
  *
@@ -664,27 +713,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);
+    if (fd == -1)
+        return 0;
 
-    FD_CLR(fd, &readFdSet);
-    FD_CLR(fd, &readFdSetPost);
-    if(asyncIoTable[index].inUse != 0) {
-        asyncIoTable[index].inUse = 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);
 }
-\f
+
 /*
  *--------------------------------------------------------------
  *
@@ -709,7 +796,6 @@ int OS_CloseRead(int fd)
     return shutdown(fd, 0);
 }
 
-\f
 /*
  *--------------------------------------------------------------
  *
@@ -736,6 +822,7 @@ int OS_DoIo(struct timeval *tmo)
     fd_set readFdSetCpy;
     fd_set writeFdSetCpy;
 
+    asyncIoInUse = TRUE;
     FD_ZERO(&readFdSetCpy);
     FD_ZERO(&writeFdSetCpy);
 
@@ -825,7 +912,20 @@ int OS_DoIo(struct timeval *tmo)
     return 0;
 }
 
-\f
+/* 
+ * 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;
+}
+
 /*
  *----------------------------------------------------------------------
  *
@@ -839,38 +939,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 = (char *)malloc(strLen + 1);
-    assert(newString != NULL);
-    memcpy(newString, clientList, strLen);
-    newString[strLen] = '\000';
+    clientListCopy = str_dup(clientList);
 
-    for(cur = clientListCopy; cur != NULL; cur = next) {
+    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;
 }
 
-\f
 /*
  *----------------------------------------------------------------------
  *
@@ -889,24 +983,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
 }
-\f
+
 /*
  *----------------------------------------------------------------------
  *
@@ -924,23 +1023,27 @@ 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(sock, F_SETLK, &lock) != -1)
+            return 0;
+    } while (errno == EINTR);
 
-    if(fcntl(FCGI_LISTENSOCK_FILENO, F_SETLK, &lock) < 0) {
-        return -1;
-    }
-#endif /* USE_LOCKING */
+    return -1;
+
+#else
     return 0;
+#endif
 }
 
-\f
 /**********************************************************************
  * Determine if the errno resulting from a failed accept() warrants a
  * retry or exit().  Based on Apache's http_main.c accept() handling
@@ -1022,7 +1125,7 @@ static int is_af_unix_keeper(const int fd)
 /*
  *----------------------------------------------------------------------
  *
- * 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.
@@ -1035,40 +1138,49 @@ static int is_af_unix_keeper(const int fd)
  *
  *----------------------------------------------------------------------
  */
-int OS_FcgiIpcAccept(char *clientAddrList)
+int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
 {
-    int socket;
+    int socket = -1;
     union {
         struct sockaddr_un un;
         struct sockaddr_in in;
     } sa;
+
+    for (;;) {
+        if (AcquireLock(listen_sock, fail_on_intr))
+            return -1;
+
+        for (;;) {
+            do {
 #ifdef HAVE_SOCKLEN
-    socklen_t len;
+                socklen_t len = sizeof(sa);
 #else
-    int len;
+                int len = sizeof(sa);
 #endif
+                if (shutdownPending) break;
+                /* There's a window here */
 
-    while (1) {
-        if (AcquireLock(TRUE) < 0)
-            return (-1);
-
-        while (1) {
-            do {
-                len = sizeof(sa);
-                socket = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *) &sa.un, &len);
-            } while (socket < 0 && errno == EINTR);
+                socket = accept(listen_sock, (struct sockaddr *)&sa, &len);
+            } while (socket < 0 
+                     && errno == EINTR 
+                     && ! fail_on_intr 
+                     && ! shutdownPending);
 
             if (socket < 0) {
-                if (!is_reasonable_accept_errno(errno)) {
+                if (shutdownPending || ! is_reasonable_accept_errno(errno)) {
                     int errnoSave = errno;
 
-                    ReleaseLock();
-                    errno = errnoSave;
+                    ReleaseLock(listen_sock);
+                    
+                    if (! shutdownPending) {
+                        errno = errnoSave;
+                    }
+
                     return (-1);
                 }
                 errno = 0;
             }
-            else {
+            else {  /* socket >= 0 */
                 int set = 1;
 
                 if (sa.in.sin_family != AF_INET)
@@ -1080,14 +1192,14 @@ int OS_FcgiIpcAccept(char *clientAddrList)
 #endif
 
                 /* Check that the client IP address is approved */
-                if (ClientAddrOK(&sa.in, clientAddrList))
+                if (ClientAddrOK(&sa.in, webServerAddrs))
                     break;
 
                 close(socket);
-            }
-        }  /* while(1) - accept */
+            }  /* socket >= 0 */
+        }  /* for(;;) */
 
-        if (ReleaseLock() < 0)
+        if (ReleaseLock(listen_sock))
             return (-1);
 
         if (sa.in.sin_family != AF_UNIX || is_af_unix_keeper(socket))
@@ -1098,7 +1210,7 @@ int OS_FcgiIpcAccept(char *clientAddrList)
 
     return (socket);
 }
-\f
+
 /*
  *----------------------------------------------------------------------
  *
@@ -1114,12 +1226,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);
 }
 
-\f
 /*
  *----------------------------------------------------------------------
  *
@@ -1135,7 +1246,7 @@ int OS_IpcClose(int ipcFd)
  *
  *----------------------------------------------------------------------
  */
-int OS_IsFcgi()
+int OS_IsFcgi(int sock)
 {
        union {
         struct sockaddr_in in;
@@ -1147,15 +1258,16 @@ int OS_IsFcgi()
     int len = sizeof(sa);
 #endif
 
-    if (getpeername(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&sa, &len) != 0
-            && errno == ENOTCONN)
-        isFastCGI = TRUE;
-    else
-        isFastCGI = FALSE;
+    errno = 0;
 
-    return (isFastCGI);
+    if (getpeername(sock, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
+        return TRUE;
+    }
+    else {
+        return FALSE;
+    }
 }
-\f
+
 /*
  *----------------------------------------------------------------------
  *