/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is AOLserver Code and related documentation * distributed by AOL. * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. */ /* * fd.c -- * * Manipulate file descriptors of open files. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/fd.c,v 1.12 2005/10/08 20:20:51 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" #ifdef _WIN32 #define DEVNULL "nul:" #include #else #define DEVNULL "/dev/null" static int ClosePipeOnExec(int *fds); #ifdef USE_DUPHIGH static int dupHigh = 0; #endif #endif /* * The following structure maitains and open temp fd. */ typedef struct Tmp { struct Tmp *nextPtr; int fd; } Tmp; static Tmp *firstTmpPtr; static Ns_Mutex lock; static int devNull; /* * The following constants are defined for this file */ #ifndef F_CLOEXEC #define F_CLOEXEC 1 #endif /* *---------------------------------------------------------------------- * * NsInitFd -- * * Initialize the fd API's. * * Results: * None. * * Side effects: * Will open a shared fd to /dev/null and ensure stdin, stdout, * and stderr are open on something. * *---------------------------------------------------------------------- */ void NsInitFd(void) { #ifndef _WIN32 struct rlimit rl; #endif int fd; /* * Ensure fd 0, 1, and 2 are open on at least /dev/null. */ fd = open(DEVNULL, O_RDONLY); if (fd > 0) { close(fd); } fd = open(DEVNULL, O_WRONLY); if (fd > 0 && fd != 1) { close(fd); } fd = open(DEVNULL, O_WRONLY); if (fd > 0 && fd != 2) { close(fd); } #ifndef _WIN32 /* * AOLserver now uses poll() but Tcl and other components may * still use select() which will likely break when fd's exceed * FD_SETSIZE. We now allow setting the fd limit above FD_SETSIZE, * but do so at your own risk. */ if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { Ns_Log(Warning, "fd: getrlimit(RLIMIT_NOFILE) failed: %s", strerror(errno)); } else { if (rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rl) != 0) { Ns_Log(Warning, "fd: setrlimit(RLIMIT_NOFILE, %d) failed: %s", rl.rlim_max, strerror(errno)); } } #ifdef USE_DUPHIGH if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_cur > 256) { dupHigh = 1; } #endif } #endif /* * Open a fd on /dev/null which can be later re-used. */ devNull = open(DEVNULL, O_RDWR); if (devNull < 0) { Ns_Fatal("fd: open(%s) failed: %s", DEVNULL, strerror(errno)); } Ns_DupHigh(&devNull); Ns_CloseOnExec(devNull); } /* *---------------------------------------------------------------------- * Ns_CloseOnExec -- * * Set the close-on-exec flag for a file descriptor * * Results: * Return NS_OK on success or NS_ERROR on failure * * Side effects: * None. * *---------------------------------------------------------------------- */ int Ns_CloseOnExec(int fd) { #ifdef _WIN32 return NS_OK; #else int i; int status = NS_ERROR; i = fcntl(fd, F_GETFD); if (i != -1) { i |= F_CLOEXEC; i = fcntl(fd, F_SETFD, i); status = NS_OK; } return status; #endif } /* *---------------------------------------------------------------------- * Ns_NoCloseOnExec -- * * Clear the close-on-exec flag for a file descriptor * * Results: * Return NS_OK on success or NS_ERROR on failure * * Side effects: * None. * *---------------------------------------------------------------------- */ int ns_closeonexec(int fd) { return Ns_CloseOnExec(fd); } int Ns_NoCloseOnExec(int fd) { #ifdef _WIN32 return NS_OK; #else int i; int status = NS_ERROR; i = fcntl(fd, F_GETFD); if (i != -1) { i &= ~F_CLOEXEC; i = fcntl(fd, F_SETFD, i); status = NS_OK; } return status; #endif } /* *---------------------------------------------------------------------- * Ns_DupHigh -- * * Dup a file descriptor to be 256 or higher * * Results: * Returns new file discriptor. * * Side effects: * Original file descriptor is closed. * *---------------------------------------------------------------------- */ int ns_duphigh(int *fdPtr) { return Ns_DupHigh(fdPtr); } int Ns_DupHigh(int *fdPtr) { #ifdef USE_DUPHIGH int nfd, ofd, flags; if (dupHigh) { ofd = *fdPtr; if ((flags = fcntl(ofd, F_GETFD)) < 0) { Ns_Log(Warning, "fd: duphigh failed: fcntl(%d, F_GETFD): '%s'", ofd, strerror(errno)); } else if ((nfd = fcntl(ofd, F_DUPFD, 256)) < 0) { Ns_Log(Warning, "fd: duphigh failed: fcntl(%d, F_DUPFD, 256): '%s'", ofd, strerror(errno)); } else if (fcntl(nfd, F_SETFD, flags) < 0) { Ns_Log(Warning, "fd: duphigh failed: fcntl(%d, F_SETFD, %d): '%s'", nfd, flags, strerror(errno)); close(nfd); } else { close(ofd); *fdPtr = nfd; } } #endif return *fdPtr; } /* *---------------------------------------------------------------------- * * Ns_GetTemp -- * * Pop or allocate a temp file. Temp files are immediately * removed on Unix and marked non-shared and delete on close * on NT to avoid snooping of data being sent to the CGI. * * Results: * Open file descriptor. * * Side effects: * File may be opened. * *---------------------------------------------------------------------- */ int Ns_GetTemp(void) { Tmp *tmpPtr; Ns_Time now; Ns_DString ds; char *path, buf[64]; int fd, flags, trys; Ns_MutexLock(&lock); tmpPtr = firstTmpPtr; if (tmpPtr != NULL) { firstTmpPtr = tmpPtr->nextPtr; } Ns_MutexUnlock(&lock); if (tmpPtr != NULL) { fd = tmpPtr->fd; ns_free(tmpPtr); return fd; } Ns_DStringInit(&ds); flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL; #ifdef _WIN32 flags |= _O_SHORT_LIVED|_O_NOINHERIT|_O_TEMPORARY|_O_BINARY; #endif trys = 0; do { Ns_GetTime(&now); sprintf(buf, "nstmp.%d.%d", (int) now.sec, (int) now.usec); path = Ns_MakePath(&ds, P_tmpdir, buf, NULL); #ifdef _WIN32 fd = _sopen(path, flags, _SH_DENYRW, _S_IREAD|_S_IWRITE); #else fd = open(path, flags, 0600); #endif } while (fd < 0 && trys++ < 10 && errno == EEXIST); if (fd < 0) { Ns_Log(Error, "tmp: could not open temp file %s: %s", path, strerror(errno)); #ifndef _WIN32 } else { Ns_DupHigh(&fd); Ns_CloseOnExec(fd); if (unlink(path) != 0) { Ns_Log(Warning, "tmp: unlink(%s) failed: %s", path, strerror(errno)); } #endif } Ns_DStringFree(&ds); return fd; } /* *---------------------------------------------------------------------- * * Ns_ReleaseTemp -- * * Return a temp file to the pool. * * Results: * None. * * Side effects: * File may be closed on error. * *---------------------------------------------------------------------- */ void Ns_ReleaseTemp(int fd) { Tmp *tmpPtr; off_t zero = 0; if (lseek(fd, zero, SEEK_SET) != 0 || ftruncate(fd, zero) != 0) { close(fd); } else { tmpPtr = ns_malloc(sizeof(Tmp)); tmpPtr->fd = fd; Ns_MutexLock(&lock); tmpPtr->nextPtr = firstTmpPtr; firstTmpPtr = tmpPtr; Ns_MutexUnlock(&lock); } } /* *---------------------------------------------------------------------- * * Ns_DevNull -- * * Return an open fd to /dev/null. This is a read-only, shared * fd which can not be closed. * * Results: * Open file descriptor. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Ns_DevNull(void) { return devNull; } /* *---------------------------------------------------------------------- * ns_sockpair, ns_pipe -- * * Create a pipe/socketpair with fd's set close on exec. * * Results: * 0 if ok, -1 otherwise. * * Side effects: * Updates given fd array. * *---------------------------------------------------------------------- */ int ns_pipe(int *fds) { #ifndef _WIN32 if (pipe(fds) == 0) { return ClosePipeOnExec(fds); } #else if (_pipe(fds, 4096, _O_NOINHERIT|_O_BINARY) == 0) { return 0; } #endif return -1; } int ns_sockpair(SOCKET *socks) { #ifndef _WIN32 if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == 0) { return ClosePipeOnExec(socks); } return -1; #else SOCKET sock; struct sockaddr_in ia[2]; int size; size = sizeof(struct sockaddr_in); sock = Ns_SockListen("127.0.0.1", 0); if (sock == INVALID_SOCKET || getsockname(sock, (struct sockaddr *) &ia[0], &size) != 0) { return -1; } size = sizeof(struct sockaddr_in); socks[1] = Ns_SockConnect("127.0.0.1", (int) ntohs(ia[0].sin_port)); if (socks[1] == INVALID_SOCKET || getsockname(socks[1], (struct sockaddr *) &ia[1], &size) != 0) { ns_sockclose(sock); return -1; } size = sizeof(struct sockaddr_in); socks[0] = accept(sock, (struct sockaddr *) &ia[0], &size); ns_sockclose(sock); if (socks[0] == INVALID_SOCKET) { ns_sockclose(socks[1]); return -1; } if (ia[0].sin_addr.s_addr != ia[1].sin_addr.s_addr || ia[0].sin_port != ia[1].sin_port) { ns_sockclose(socks[0]); ns_sockclose(socks[1]); return -1; } return 0; #endif } #ifndef _WIN32 static int ClosePipeOnExec(int *fds) { if (Ns_CloseOnExec(fds[0]) == NS_OK && Ns_CloseOnExec(fds[1]) == NS_OK) { return 0; } close(fds[0]); close(fds[1]); return -1; } #endif /* *---------------------------------------------------------------------- * * NsMap -- * * Memory map a region of a file. * * Results: * Pointer to mapped region or NULL if mapping failed. * * Side effects: * None. * *---------------------------------------------------------------------- */ void * NsMap(int fd, off_t start, size_t len, int writable, void **argPtr) { #ifdef _WIN32 /* TODO: Make this work on Win32. */ return NULL; #else int prot; prot = PROT_READ; if (writable) { prot |= PROT_WRITE; } *argPtr = (void *) len; return mmap(NULL, len, prot, MAP_SHARED, fd, start); #endif } /* *---------------------------------------------------------------------- * * NsUnMap -- * * Unmap a previosly mmapped region. * * Results: * None. * * Side effects: * Failure is considered fatal. * *---------------------------------------------------------------------- */ void NsUnMap(void *addr, void *arg) { #ifdef _WIN32 /* TODO: Make this work on Win32. */ Ns_Fatal("NsUnMap not supported"); #else size_t len = (size_t) arg; if (munmap(addr, len) != 0) { Ns_Fatal("munmap(%p, %u) failed: %s", addr, len, strerror(errno)); } #endif }