diff options
Diffstat (limited to 'nsprpub/pr/src/pthreads/ptio.c')
-rw-r--r-- | nsprpub/pr/src/pthreads/ptio.c | 5387 |
1 files changed, 5387 insertions, 0 deletions
diff --git a/nsprpub/pr/src/pthreads/ptio.c b/nsprpub/pr/src/pthreads/ptio.c new file mode 100644 index 0000000000..a2bc3e714d --- /dev/null +++ b/nsprpub/pr/src/pthreads/ptio.c @@ -0,0 +1,5387 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ptio.c +** Descritpion: Implemenation of I/O methods for pthreads +*/ + +#if defined(_PR_PTHREADS) + +#if defined(_PR_POLL_WITH_SELECT) +#if !(defined(HPUX) && defined(_USE_BIG_FDS)) +/* set fd limit for select(), before including system header files */ +#define FD_SETSIZE (16 * 1024) +#endif +#endif + +#include <pthread.h> +#include <string.h> /* for memset() */ +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#if defined(DARWIN) +#include <sys/utsname.h> /* for uname */ +#endif +#if defined(SOLARIS) || defined(UNIXWARE) +#include <sys/filio.h> /* to pick up FIONREAD */ +#endif +#ifdef _PR_POLL_AVAILABLE +#include <poll.h> +#endif +#ifdef AIX +/* To pick up sysconf() */ +#include <unistd.h> +#include <dlfcn.h> /* for dlopen */ +#else +/* To pick up getrlimit() etc. */ +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#ifdef SOLARIS +/* + * Define HAVE_SENDFILEV if the system has the sendfilev() system call. + * Code built this way won't run on a system without sendfilev(). + * We can define HAVE_SENDFILEV by default when the minimum release + * of Solaris that NSPR supports has sendfilev(). + */ +#ifdef HAVE_SENDFILEV + +#include <sys/sendfile.h> + +#define SOLARIS_SENDFILEV(a, b, c, d) sendfilev((a), (b), (c), (d)) + +#else + +#include <dlfcn.h> /* for dlopen */ + +/* + * Match the definitions in <sys/sendfile.h>. + */ +typedef struct sendfilevec { + int sfv_fd; /* input fd */ + uint_t sfv_flag; /* flags */ + off_t sfv_off; /* offset to start reading from */ + size_t sfv_len; /* amount of data */ +} sendfilevec_t; + +#define SFV_FD_SELF (-2) + +/* + * extern ssize_t sendfilev(int, const struct sendfilevec *, int, size_t *); + */ +static ssize_t (*pt_solaris_sendfilev_fptr)() = NULL; + +#define SOLARIS_SENDFILEV(a, b, c, d) \ + (*pt_solaris_sendfilev_fptr)((a), (b), (c), (d)) + +#endif /* HAVE_SENDFILEV */ +#endif /* SOLARIS */ + +/* + * The send_file() system call is available in AIX 4.3.2 or later. + * If this file is compiled on an older AIX system, it attempts to + * look up the send_file symbol at run time to determine whether + * we can use the faster PR_SendFile/PR_TransmitFile implementation based on + * send_file(). On AIX 4.3.2 or later, we can safely skip this + * runtime function dispatching and just use the send_file based + * implementation. + */ +#ifdef AIX +#ifdef SF_CLOSE +#define HAVE_SEND_FILE +#endif + +#ifdef HAVE_SEND_FILE + +#define AIX_SEND_FILE(a, b, c) send_file(a, b, c) + +#else /* HAVE_SEND_FILE */ + +/* + * The following definitions match those in <sys/socket.h> + * on AIX 4.3.2. + */ + +/* + * Structure for the send_file() system call + */ +struct sf_parms { + /* --------- header parms ---------- */ + void *header_data; /* Input/Output. Points to header buf */ + uint_t header_length; /* Input/Output. Length of the header */ + /* --------- file parms ------------ */ + int file_descriptor; /* Input. File descriptor of the file */ + unsigned long long file_size; /* Output. Size of the file */ + unsigned long long file_offset; /* Input/Output. Starting offset */ + long long file_bytes; /* Input/Output. no. of bytes to send */ + /* --------- trailer parms --------- */ + void *trailer_data; /* Input/Output. Points to trailer buf */ + uint_t trailer_length; /* Input/Output. Length of the trailer */ + /* --------- return info ----------- */ + unsigned long long bytes_sent; /* Output. no. of bytes sent */ +}; + +/* + * Flags for the send_file() system call + */ +#define SF_CLOSE 0x00000001 /* close the socket after completion */ +#define SF_REUSE 0x00000002 /* reuse socket. not supported */ +#define SF_DONT_CACHE 0x00000004 /* don't apply network buffer cache */ +#define SF_SYNC_CACHE 0x00000008 /* sync/update network buffer cache */ + +/* + * prototype: size_t send_file(int *, struct sf_parms *, uint_t); + */ +static ssize_t (*pt_aix_sendfile_fptr)() = NULL; + +#define AIX_SEND_FILE(a, b, c) (*pt_aix_sendfile_fptr)(a, b, c) + +#endif /* HAVE_SEND_FILE */ +#endif /* AIX */ + +#ifdef LINUX +#include <sys/sendfile.h> +#endif + +#include "primpl.h" + +#if defined(LINUX) || defined(ANDROID) +#include <netinet/in.h> +#endif + +#ifdef DARWIN +#include <netinet/in.h> +#include <netinet/ip.h> +#endif + +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#endif + +#ifdef LINUX +/* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */ +#ifndef TCP_CORK +#define TCP_CORK 3 +#endif +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif +#endif + +#ifdef _PR_IPV6_V6ONLY_PROBE +static PRBool _pr_ipv6_v6only_on_by_default; +#endif + +#if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11)) +#define _PRSelectFdSetArg_t int * +#elif defined(AIX4_1) +#define _PRSelectFdSetArg_t void * +#elif (defined(AIX) && !defined(AIX4_1)) \ + || defined(SOLARIS) \ + || defined(HPUX10_30) || defined(HPUX11) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \ + || defined(BSDI) || defined(NTO) || defined(DARWIN) \ + || defined(UNIXWARE) || defined(RISCOS) +#define _PRSelectFdSetArg_t fd_set * +#else +#error "Cannot determine architecture" +#endif + +#if defined(SOLARIS) +#ifndef PROTO_SDP +/* on solaris, SDP is a new type of protocol */ +#define PROTO_SDP 257 +#endif +#define _PR_HAVE_SDP +#elif defined(LINUX) +#ifndef AF_INET_SDP +/* on linux, SDP is a new type of address family */ +#define AF_INET_SDP 27 +#endif +#define _PR_HAVE_SDP +#endif /* LINUX */ + +static PRFileDesc *pt_SetMethods( + PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported); + +static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */ +static PRCondVar *_pr_flock_cv; /* For PR_LockFile() etc. */ +static PRLock *_pr_rename_lock; /* For PR_Rename() */ + +/**************************************************************************/ + +/* These two functions are only used in assertions. */ +#if defined(DEBUG) + +PRBool IsValidNetAddr(const PRNetAddr *addr) +{ + if ((addr != NULL) + && (addr->raw.family != AF_UNIX) + && (addr->raw.family != PR_AF_INET6) + && (addr->raw.family != AF_INET)) { + return PR_FALSE; + } + return PR_TRUE; +} + +static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len) +{ + /* + * The definition of the length of a Unix domain socket address + * is not uniform, so we don't check it. + */ + if ((addr != NULL) + && (addr->raw.family != AF_UNIX) + && (PR_NETADDR_SIZE(addr) != addr_len)) { +#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 + /* + * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2 + * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id + * field and is 28 bytes. It is possible for socket functions + * to return an addr_len greater than sizeof(struct sockaddr_in6). + * We need to allow that. (Bugzilla bug #77264) + */ + if ((PR_AF_INET6 == addr->raw.family) + && (sizeof(addr->ipv6) == addr_len)) { + return PR_TRUE; + } +#endif + return PR_FALSE; + } + return PR_TRUE; +} + +#endif /* DEBUG */ + +/*****************************************************************************/ +/************************* I/O Continuation machinery ************************/ +/*****************************************************************************/ + +/* + * The polling interval defines the maximum amount of time that a thread + * might hang up before an interrupt is noticed. + */ +#define PT_DEFAULT_POLL_MSEC 5000 +#if defined(_PR_POLL_WITH_SELECT) +#define PT_DEFAULT_SELECT_SEC (PT_DEFAULT_POLL_MSEC/PR_MSEC_PER_SEC) +#define PT_DEFAULT_SELECT_USEC \ + ((PT_DEFAULT_POLL_MSEC % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC) +#endif + +/* + * pt_SockLen is the type for the length of a socket address + * structure, used in the address length argument to bind, + * connect, accept, getsockname, getpeername, etc. Posix.1g + * defines this type as socklen_t. It is size_t or int on + * most current systems. + */ +#if defined(HAVE_SOCKLEN_T) \ + || (defined(__GLIBC__) && __GLIBC__ >= 2) +typedef socklen_t pt_SockLen; +#elif (defined(AIX) && !defined(AIX4_1)) +typedef PRSize pt_SockLen; +#else +typedef PRIntn pt_SockLen; +#endif + +typedef struct pt_Continuation pt_Continuation; +typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents); + +typedef enum pr_ContuationStatus +{ + pt_continuation_pending, + pt_continuation_done +} pr_ContuationStatus; + +struct pt_Continuation +{ + /* The building of the continuation operation */ + ContinuationFn function; /* what function to continue */ + union { + PRIntn osfd; + } arg1; /* #1 - the op's fd */ + union { + void* buffer; + } arg2; /* #2 - primary transfer buffer */ + union { + PRSize amount; /* #3 - size of 'buffer', or */ + pt_SockLen *addr_len; /* - length of address */ +#ifdef HPUX11 + /* + * For sendfile() + */ + struct file_spec { + off_t offset; /* offset in file to send */ + size_t nbytes; /* length of file data to send */ + size_t st_size; /* file size */ + } file_spec; +#endif + } arg3; + union { + PRIntn flags; + } arg4; /* #4 - read/write flags */ + union { + PRNetAddr *addr; + } arg5; /* #5 - send/recv address */ + +#ifdef HPUX11 + /* + * For sendfile() + */ + int filedesc; /* descriptor of file to send */ + int nbytes_to_send; /* size of header and file */ +#endif /* HPUX11 */ + +#ifdef SOLARIS + /* + * For sendfilev() + */ + int nbytes_to_send; /* size of header and file */ +#endif /* SOLARIS */ + +#ifdef LINUX + /* + * For sendfile() + */ + int in_fd; /* descriptor of file to send */ + off_t offset; + size_t count; +#endif /* LINUX */ + + PRIntervalTime timeout; /* client (relative) timeout */ + + PRInt16 event; /* flags for poll()'s events */ + + /* + ** The representation and notification of the results of the operation. + ** These function can either return an int return code or a pointer to + ** some object. + */ + union { + PRSize code; + void *object; + } result; + + PRIntn syserrno; /* in case it failed, why (errno) */ + pr_ContuationStatus status; /* the status of the operation */ +}; + +#if defined(DEBUG) + +PTDebug pt_debug; /* this is shared between several modules */ + +PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) +{ + PTDebug stats; + char buffer[100]; + PRExplodedTime tod; + PRInt64 elapsed, aMil; + stats = pt_debug; /* a copy */ + PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod); + (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod); + + LL_SUB(elapsed, PR_Now(), stats.timeStarted); + LL_I2L(aMil, 1000000); + LL_DIV(elapsed, elapsed, aMil); + + if (NULL != msg) { + PR_fprintf(debug_out, "%s", msg); + } + PR_fprintf( + debug_out, "\tstarted: %s[%lld]\n", buffer, elapsed); + PR_fprintf( + debug_out, "\tlocks [created: %u, destroyed: %u]\n", + stats.locks_created, stats.locks_destroyed); + PR_fprintf( + debug_out, "\tlocks [acquired: %u, released: %u]\n", + stats.locks_acquired, stats.locks_released); + PR_fprintf( + debug_out, "\tcvars [created: %u, destroyed: %u]\n", + stats.cvars_created, stats.cvars_destroyed); + PR_fprintf( + debug_out, "\tcvars [notified: %u, delayed_delete: %u]\n", + stats.cvars_notified, stats.delayed_cv_deletes); +} /* PT_FPrintStats */ + +#else + +PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) +{ + /* do nothing */ +} /* PT_FPrintStats */ + +#endif /* DEBUG */ + +#if defined(_PR_POLL_WITH_SELECT) +/* + * HPUX report the POLLHUP event for a socket when the + * shutdown(SHUT_WR) operation is called for the remote end, even though + * the socket is still writeable. Use select(), instead of poll(), to + * workaround this problem. + */ +static void pt_poll_now_with_select(pt_Continuation *op) +{ + PRInt32 msecs; + fd_set rd, wr, *rdp, *wrp; + struct timeval tv; + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRThread *self = PR_GetCurrentThread(); + + PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); + PR_ASSERT(op->arg1.osfd < FD_SETSIZE); + + switch (op->timeout) { + case PR_INTERVAL_NO_TIMEOUT: + tv.tv_sec = PT_DEFAULT_SELECT_SEC; + tv.tv_usec = PT_DEFAULT_SELECT_USEC; + do + { + PRIntn rv; + + if (op->event & POLLIN) { + FD_ZERO(&rd); + FD_SET(op->arg1.osfd, &rd); + rdp = &rd; + } else { + rdp = NULL; + } + if (op->event & POLLOUT) { + FD_ZERO(&wr); + FD_SET(op->arg1.osfd, &wr); + wrp = ≀ + } else { + wrp = NULL; + } + + rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) { + continue; /* go around the loop again */ + } + + if (rv > 0) + { + PRInt16 revents = 0; + + if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) { + revents |= POLLIN; + } + if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) { + revents |= POLLOUT; + } + + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + } else if (rv == -1) { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + /* else, select timed out */ + } while (pt_continuation_done != op->status); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = op->timeout; + do + { + PRIntn rv; + + if (op->event & POLLIN) { + FD_ZERO(&rd); + FD_SET(op->arg1.osfd, &rd); + rdp = &rd; + } else { + rdp = NULL; + } + if (op->event & POLLOUT) { + FD_ZERO(&wr); + FD_SET(op->arg1.osfd, &wr); + wrp = ≀ + } else { + wrp = NULL; + } + + wait_for_remaining = PR_TRUE; + msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); + if (msecs > PT_DEFAULT_POLL_MSEC) { + wait_for_remaining = PR_FALSE; + msecs = PT_DEFAULT_POLL_MSEC; + } + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC; + rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if (rv > 0) { + PRInt16 revents = 0; + + if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) { + revents |= POLLIN; + } + if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) { + revents |= POLLOUT; + } + + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + + } else if ((rv == 0) || + ((errno == EINTR) || (errno == EAGAIN))) { + if (rv == 0) { /* select timed out */ + if (wait_for_remaining) { + now += remaining; + } + else { + now += PR_MillisecondsToInterval(msecs); + } + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= op->timeout) { + op->result.code = -1; + op->syserrno = ETIMEDOUT; + op->status = pt_continuation_done; + } else { + remaining = op->timeout - elapsed; + } + } else { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + } while (pt_continuation_done != op->status); + break; + } + +} /* pt_poll_now_with_select */ + +#endif /* _PR_POLL_WITH_SELECT */ + +static void pt_poll_now(pt_Continuation *op) +{ + PRInt32 msecs; + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRThread *self = PR_GetCurrentThread(); + + PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); +#if defined (_PR_POLL_WITH_SELECT) + /* + * If the fd is small enough call the select-based poll operation + */ + if (op->arg1.osfd < FD_SETSIZE) { + pt_poll_now_with_select(op); + return; + } +#endif + + switch (op->timeout) { + case PR_INTERVAL_NO_TIMEOUT: + msecs = PT_DEFAULT_POLL_MSEC; + do + { + PRIntn rv; + struct pollfd tmp_pfd; + + tmp_pfd.revents = 0; + tmp_pfd.fd = op->arg1.osfd; + tmp_pfd.events = op->event; + + rv = poll(&tmp_pfd, 1, msecs); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) { + continue; /* go around the loop again */ + } + + if (rv > 0) + { + PRInt16 events = tmp_pfd.events; + PRInt16 revents = tmp_pfd.revents; + + if ((revents & POLLNVAL) /* busted in all cases */ + || ((events & POLLOUT) && (revents & POLLHUP))) + /* write op & hup */ + { + op->result.code = -1; + if (POLLNVAL & revents) { + op->syserrno = EBADF; + } + else if (POLLHUP & revents) { + op->syserrno = EPIPE; + } + op->status = pt_continuation_done; + } else { + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + } + } else if (rv == -1) { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + /* else, poll timed out */ + } while (pt_continuation_done != op->status); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = op->timeout; + do + { + PRIntn rv; + struct pollfd tmp_pfd; + + tmp_pfd.revents = 0; + tmp_pfd.fd = op->arg1.osfd; + tmp_pfd.events = op->event; + + wait_for_remaining = PR_TRUE; + msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); + if (msecs > PT_DEFAULT_POLL_MSEC) + { + wait_for_remaining = PR_FALSE; + msecs = PT_DEFAULT_POLL_MSEC; + } + rv = poll(&tmp_pfd, 1, msecs); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if (rv > 0) + { + PRInt16 events = tmp_pfd.events; + PRInt16 revents = tmp_pfd.revents; + + if ((revents & POLLNVAL) /* busted in all cases */ + || ((events & POLLOUT) && (revents & POLLHUP))) + /* write op & hup */ + { + op->result.code = -1; + if (POLLNVAL & revents) { + op->syserrno = EBADF; + } + else if (POLLHUP & revents) { + op->syserrno = EPIPE; + } + op->status = pt_continuation_done; + } else { + if (op->function(op, revents)) + { + op->status = pt_continuation_done; + } + } + } else if ((rv == 0) || + ((errno == EINTR) || (errno == EAGAIN))) { + if (rv == 0) /* poll timed out */ + { + if (wait_for_remaining) { + now += remaining; + } + else { + now += PR_MillisecondsToInterval(msecs); + } + } + else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= op->timeout) { + op->result.code = -1; + op->syserrno = ETIMEDOUT; + op->status = pt_continuation_done; + } else { + remaining = op->timeout - elapsed; + } + } else { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + } while (pt_continuation_done != op->status); + break; + } + +} /* pt_poll_now */ + +static PRIntn pt_Continue(pt_Continuation *op) +{ + op->status = pt_continuation_pending; /* set default value */ + /* + * let each thread call poll directly + */ + pt_poll_now(op); + PR_ASSERT(pt_continuation_done == op->status); + return op->result.code; +} /* pt_Continue */ + +/*****************************************************************************/ +/*********************** specific continuation functions *********************/ +/*****************************************************************************/ +static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd); + if (op->syserrno != 0) { + op->result.code = -1; + } else { + op->result.code = 0; + } + return PR_TRUE; /* this one is cooked */ +} /* pt_connect_cont */ + +static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = 0; + op->result.code = accept( + op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len); + if (-1 == op->result.code) + { + op->syserrno = errno; + if (EWOULDBLOCK == errno || EAGAIN == errno || ECONNABORTED == errno) { + return PR_FALSE; /* do nothing - this one ain't finished */ + } + } + return PR_TRUE; +} /* pt_accept_cont */ + +static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ + op->result.code = read( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_read_cont */ + +static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ +#if defined(SOLARIS) + if (0 == op->arg4.flags) + op->result.code = read( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + else + op->result.code = recv( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#else + op->result.code = recv( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#endif + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recv_cont */ + +static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; +#if defined(SOLARIS) + PRInt32 tmp_amount = op->arg3.amount; +#endif + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ +#if defined(SOLARIS) +retry: + bytes = write(op->arg1.osfd, op->arg2.buffer, tmp_amount); +#else + bytes = send( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#endif + op->syserrno = errno; + +#if defined(SOLARIS) + /* + * The write system call has been reported to return the ERANGE error + * on occasion. Try to write in smaller chunks to workaround this bug. + */ + if ((bytes == -1) && (op->syserrno == ERANGE)) + { + if (tmp_amount > 1) + { + tmp_amount = tmp_amount/2; /* half the bytes */ + goto retry; + } + } +#endif + + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_send_cont */ + +static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ + bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_write_cont */ + +static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; + struct iovec *iov = (struct iovec*)op->arg2.buffer; + /* + * Same rules as write, but continuing seems to be a bit more + * complicated. As the number of bytes sent grows, we have to + * redefine the vector we're pointing at. We might have to + * modify an individual vector parms or we might have to eliminate + * a pair altogether. + */ + bytes = writev(op->arg1.osfd, iov, op->arg3.amount); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + PRIntn iov_index; + op->result.code += bytes; /* accumulate the number sent */ + for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index) + { + /* how much progress did we make in the i/o vector? */ + if (bytes < iov[iov_index].iov_len) + { + /* this element's not done yet */ + char **bp = (char**)&(iov[iov_index].iov_base); + iov[iov_index].iov_len -= bytes; /* there's that much left */ + *bp += bytes; /* starting there */ + break; /* go off and do that */ + } + bytes -= iov[iov_index].iov_len; /* that element's consumed */ + } + op->arg2.buffer = &iov[iov_index]; /* new start of array */ + op->arg3.amount -= iov_index; /* and array length */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_writev_cont */ + +static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes = sendto( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags, + (struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr)); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_sendto_cont */ + +static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) +{ + pt_SockLen addr_len = sizeof(PRNetAddr); + op->result.code = recvfrom( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, + op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recvfrom_cont */ + +#ifdef AIX +static PRBool pt_aix_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct sf_parms *sf_struct = (struct sf_parms *) op->arg2.buffer; + ssize_t rv; + unsigned long long saved_file_offset; + long long saved_file_bytes; + + saved_file_offset = sf_struct->file_offset; + saved_file_bytes = sf_struct->file_bytes; + sf_struct->bytes_sent = 0; + + if ((sf_struct->file_bytes > 0) && (sf_struct->file_size > 0)) + PR_ASSERT((sf_struct->file_bytes + sf_struct->file_offset) <= + sf_struct->file_size); + rv = AIX_SEND_FILE(&op->arg1.osfd, sf_struct, op->arg4.flags); + op->syserrno = errno; + + if (rv != -1) { + op->result.code += sf_struct->bytes_sent; + /* + * A bug in AIX 4.3.2 prevents the 'file_bytes' field from + * being updated. So, 'file_bytes' is maintained by NSPR to + * avoid conflict when this bug is fixed in AIX, in the future. + */ + if (saved_file_bytes != -1) { + saved_file_bytes -= (sf_struct->file_offset - saved_file_offset); + } + sf_struct->file_bytes = saved_file_bytes; + } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + } else { + return PR_FALSE; + } + + if (rv == 1) { /* more data to send */ + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* AIX */ + +#ifdef HPUX11 +static PRBool pt_hpux_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct iovec *hdtrl = (struct iovec *) op->arg2.buffer; + int count; + + count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.file_spec.offset, + op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags); + PR_ASSERT(count <= op->nbytes_to_send); + op->syserrno = errno; + + if (count != -1) { + op->result.code += count; + } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + } else { + return PR_FALSE; + } + if (count != -1 && count < op->nbytes_to_send) { + if (count < hdtrl[0].iov_len) { + /* header not sent */ + + hdtrl[0].iov_base = ((char *) hdtrl[0].iov_base) + count; + hdtrl[0].iov_len -= count; + + } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes)) { + /* header sent, file not sent */ + PRUint32 file_nbytes_sent = count - hdtrl[0].iov_len; + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + + op->arg3.file_spec.offset += file_nbytes_sent; + op->arg3.file_spec.nbytes -= file_nbytes_sent; + } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes + + hdtrl[1].iov_len)) { + PRUint32 trailer_nbytes_sent = count - (hdtrl[0].iov_len + + op->arg3.file_spec.nbytes); + + /* header sent, file sent, trailer not sent */ + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + /* + * set file offset and len so that no more file data is + * sent + */ + op->arg3.file_spec.offset = op->arg3.file_spec.st_size; + op->arg3.file_spec.nbytes = 0; + + hdtrl[1].iov_base =((char *) hdtrl[1].iov_base)+ trailer_nbytes_sent; + hdtrl[1].iov_len -= trailer_nbytes_sent; + } + op->nbytes_to_send -= count; + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* HPUX11 */ + +#ifdef SOLARIS +static PRBool pt_solaris_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct sendfilevec *vec = (struct sendfilevec *) op->arg2.buffer; + size_t xferred; + ssize_t count; + + count = SOLARIS_SENDFILEV(op->arg1.osfd, vec, op->arg3.amount, &xferred); + op->syserrno = errno; + PR_ASSERT((count == -1) || (count == xferred)); + + if (count == -1) { + if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN + && op->syserrno != EINTR) { + op->result.code = -1; + return PR_TRUE; + } + count = xferred; + } else if (count == 0) { + /* + * We are now at EOF. The file was truncated. Solaris sendfile is + * supposed to return 0 and no error in this case, though some versions + * may return -1 and EINVAL . + */ + op->result.code = -1; + op->syserrno = 0; /* will be treated as EOF */ + return PR_TRUE; + } + PR_ASSERT(count <= op->nbytes_to_send); + + op->result.code += count; + if (count < op->nbytes_to_send) { + op->nbytes_to_send -= count; + + while (count >= vec->sfv_len) { + count -= vec->sfv_len; + vec++; + op->arg3.amount--; + } + PR_ASSERT(op->arg3.amount > 0); + + vec->sfv_off += count; + vec->sfv_len -= count; + PR_ASSERT(vec->sfv_len > 0); + op->arg2.buffer = vec; + + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* SOLARIS */ + +#ifdef LINUX +static PRBool pt_linux_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + ssize_t rv; + off_t oldoffset; + + oldoffset = op->offset; + rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count); + op->syserrno = errno; + + if (rv == -1) { + if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + return PR_TRUE; + } + rv = 0; + } + PR_ASSERT(rv == op->offset - oldoffset); + op->result.code += rv; + if (rv < op->count) { + op->count -= rv; + return PR_FALSE; + } + return PR_TRUE; +} +#endif /* LINUX */ + +void _PR_InitIO(void) +{ +#if defined(DEBUG) + memset(&pt_debug, 0, sizeof(PTDebug)); + pt_debug.timeStarted = PR_Now(); +#endif + + _pr_flock_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_flock_lock); + _pr_flock_cv = PR_NewCondVar(_pr_flock_lock); + PR_ASSERT(NULL != _pr_flock_cv); + _pr_rename_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_rename_lock); + + _PR_InitFdCache(); /* do that */ + + _pr_stdin = pt_SetMethods(0, PR_DESC_FILE, PR_FALSE, PR_TRUE); + _pr_stdout = pt_SetMethods(1, PR_DESC_FILE, PR_FALSE, PR_TRUE); + _pr_stderr = pt_SetMethods(2, PR_DESC_FILE, PR_FALSE, PR_TRUE); + PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr); + +#ifdef _PR_IPV6_V6ONLY_PROBE + /* In Mac OS X v10.3 Panther Beta the IPV6_V6ONLY socket option + * is turned on by default, contrary to what RFC 3493, Section + * 5.3 says. So we have to turn it off. Find out whether we + * are running on such a system. + */ + { + int osfd; + osfd = socket(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + int on; + socklen_t optlen = sizeof(on); + if (getsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, + &on, &optlen) == 0) { + _pr_ipv6_v6only_on_by_default = on; + } + close(osfd); + } + } +#endif +} /* _PR_InitIO */ + +void _PR_CleanupIO(void) +{ + _PR_Putfd(_pr_stdin); + _pr_stdin = NULL; + _PR_Putfd(_pr_stdout); + _pr_stdout = NULL; + _PR_Putfd(_pr_stderr); + _pr_stderr = NULL; + + _PR_CleanupFdCache(); + + if (_pr_flock_cv) + { + PR_DestroyCondVar(_pr_flock_cv); + _pr_flock_cv = NULL; + } + if (_pr_flock_lock) + { + PR_DestroyLock(_pr_flock_lock); + _pr_flock_lock = NULL; + } + if (_pr_rename_lock) + { + PR_DestroyLock(_pr_rename_lock); + _pr_rename_lock = NULL; + } +} /* _PR_CleanupIO */ + +PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) +{ + PRFileDesc *result = NULL; + PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError); + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + switch (osfd) + { + case PR_StandardInput: result = _pr_stdin; break; + case PR_StandardOutput: result = _pr_stdout; break; + case PR_StandardError: result = _pr_stderr; break; + default: + (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + return result; +} /* PR_GetSpecialFD */ + +/*****************************************************************************/ +/***************************** I/O private methods ***************************/ +/*****************************************************************************/ + +static PRBool pt_TestAbort(void) +{ + PRThread *me = PR_GetCurrentThread(); + if(_PT_THREAD_INTERRUPTED(me)) + { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + me->state &= ~PT_THREAD_ABORTED; + return PR_TRUE; + } + return PR_FALSE; +} /* pt_TestAbort */ + +static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno) +{ + switch (syserrno) + { + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; + default: + mapper(syserrno); + } +} /* pt_MapError */ + +static PRStatus pt_Close(PRFileDesc *fd) +{ + if ((NULL == fd) || (NULL == fd->secret) + || ((_PR_FILEDESC_OPEN != fd->secret->state) + && (_PR_FILEDESC_CLOSED != fd->secret->state))) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (_PR_FILEDESC_OPEN == fd->secret->state) + { + if (-1 == close(fd->secret->md.osfd)) + { + pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + } + _PR_Putfd(fd); + return PR_SUCCESS; +} /* pt_Close */ + +static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + + if (pt_TestAbort()) { + return bytes; + } + + bytes = read(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.function = pt_read_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno); + } + return bytes; +} /* pt_Read */ + +static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + + if (pt_TestAbort()) { + return bytes; + } + + bytes = write(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_write_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) { + pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno); + } + return bytes; +} /* pt_Write */ + +static PRInt32 pt_Writev( + PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout) +{ + PRIntn iov_index; + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes, rv = -1; + struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov; + int osiov_len; + + if (pt_TestAbort()) { + return rv; + } + + /* Ensured by PR_Writev */ + PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE); + + /* + * We can't pass iov to writev because PRIOVec and struct iovec + * may not be binary compatible. Make osiov a copy of iov and + * pass osiov to writev. We can modify osiov if we need to + * continue the operation. + */ + osiov = osiov_local; + osiov_len = iov_len; + for (iov_index = 0; iov_index < osiov_len; iov_index++) + { + osiov[iov_index].iov_base = iov[iov_index].iov_base; + osiov[iov_index].iov_len = iov[iov_index].iov_len; + } + + rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len); + syserrno = errno; + + if (!fd->secret->nonblocking) + { + if (bytes >= 0) + { + /* + * If we moved some bytes, how does that implicate the + * i/o vector list? In other words, exactly where are + * we within that array? What are the parameters for + * resumption? Maybe we're done! + */ + for ( ; osiov_len > 0; osiov++, osiov_len--) + { + if (bytes < osiov->iov_len) + { + /* this one's not done yet */ + osiov->iov_base = (char*)osiov->iov_base + bytes; + osiov->iov_len -= bytes; + break; /* go off and do that */ + } + bytes -= osiov->iov_len; /* this one's done cooked */ + } + PR_ASSERT(osiov_len > 0 || bytes == 0); + if (osiov_len > 0) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + rv = -1; + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + } + else if (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + rv = 0; + fNeedContinue = PR_TRUE; + } + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)osiov; + op.arg3.amount = osiov_len; + op.timeout = timeout; + op.result.code = rv; + op.function = pt_writev_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + } + if (rv == -1) { + pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno); + } + return rv; +} /* pt_Writev */ + +static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + return _PR_MD_LSEEK(fd, offset, whence); +} /* pt_Seek */ + +static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + return _PR_MD_LSEEK64(fd, offset, whence); +} /* pt_Seek64 */ + +static PRInt32 pt_Available_f(PRFileDesc *fd) +{ + PRInt32 result, cur, end; + + cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR); + + if (cur >= 0) { + end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END); + } + + if ((cur < 0) || (end < 0)) { + return -1; + } + + result = end - cur; + _PR_MD_LSEEK(fd, cur, PR_SEEK_SET); + + return result; +} /* pt_Available_f */ + +static PRInt64 pt_Available64_f(PRFileDesc *fd) +{ + PRInt64 result, cur, end; + PRInt64 minus_one; + + LL_I2L(minus_one, -1); + cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR); + + if (LL_GE_ZERO(cur)) { + end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END); + } + + if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) { + return minus_one; + } + + LL_SUB(result, end, cur); + (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET); + + return result; +} /* pt_Available64_f */ + +static PRInt32 pt_Available_s(PRFileDesc *fd) +{ + PRInt32 rv, bytes = -1; + if (pt_TestAbort()) { + return bytes; + } + + rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno); + } + return bytes; +} /* pt_Available_s */ + +static PRInt64 pt_Available64_s(PRFileDesc *fd) +{ + PRInt64 rv; + LL_I2L(rv, pt_Available_s(fd)); + return rv; +} /* pt_Available64_s */ + +static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_FileInfo */ + +static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_FileInfo64 */ + +static PRStatus pt_Synch(PRFileDesc *fd) +{ + return (NULL == fd) ? PR_FAILURE : PR_SUCCESS; +} /* pt_Synch */ + +static PRStatus pt_Fsync(PRFileDesc *fd) +{ + PRIntn rv = -1; + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = fsync(fd->secret->md.osfd); + if (rv < 0) { + pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Fsync */ + +static PRStatus pt_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRIntn rv = -1, syserrno; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + rv = connect(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); + syserrno = errno; + if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)addrp; + op.arg3.amount = addr_len; + op.timeout = timeout; + op.function = pt_connect_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (-1 == rv) { + pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Connect */ + +static PRStatus pt_ConnectContinue( + PRFileDesc *fd, PRInt16 out_flags) +{ + int err; + PRInt32 osfd; + + if (out_flags & PR_POLL_NVAL) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR + | PR_POLL_HUP)) == 0) + { + PR_ASSERT(out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + osfd = fd->secret->md.osfd; + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) + { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_ConnectContinue */ + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + /* Find the NSPR layer and invoke its connectcontinue method */ + PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + + if (NULL == bottom) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return pt_ConnectContinue(bottom, pd->out_flags); +} /* PR_GetConnectStatus */ + +static PRFileDesc* pt_Accept( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRFileDesc *newfd = NULL; + PRIntn syserrno, osfd = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return newfd; + } + +#ifdef _PR_STRICT_ADDR_LEN + if (addr) + { + /* + * Set addr->raw.family just so that we can use the + * PR_NETADDR_SIZE macro. + */ + addr->raw.family = fd->secret->af; + addr_len = PR_NETADDR_SIZE(addr); + } +#endif + + osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if (osfd == -1) + { + if (fd->secret->nonblocking) { + goto failed; + } + + if (EWOULDBLOCK != syserrno && EAGAIN != syserrno + && ECONNABORTED != syserrno) { + goto failed; + } + else + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = addr; + op.arg3.addr_len = &addr_len; + op.timeout = timeout; + op.function = pt_accept_cont; + op.event = POLLIN | POLLPRI; + osfd = pt_Continue(&op); + syserrno = op.syserrno; + } + if (osfd < 0) { + goto failed; + } + } + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_TRUE, PR_FALSE); + if (newfd == NULL) { + close(osfd); /* $$$ whoops! this doesn't work $$$ */ + } + else + { + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); +#ifdef LINUX + /* + * On Linux, experiments showed that the accepted sockets + * inherit the TCP_NODELAY socket option of the listening + * socket. + */ + newfd->secret->md.tcp_nodelay = fd->secret->md.tcp_nodelay; +#endif + } + return newfd; + +failed: + pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno); + return NULL; +} /* pt_Accept */ + +static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRIntn rv; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + if (addr->raw.family == AF_UNIX) + { + /* Disallow relative pathnames */ + if (addr->local.path[0] != '/' +#if defined(LINUX) + /* Linux has abstract socket address support */ + && addr->local.path[0] != 0 +#endif + ) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + } + +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + rv = bind(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_BIND_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Bind */ + +static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog) +{ + PRIntn rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = listen(fd->secret->md.osfd, backlog); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Listen */ + +static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how) +{ + PRIntn rv = -1; + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = shutdown(fd->secret->md.osfd, how); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Shutdown */ + +static PRInt16 pt_Poll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + return in_flags; +} /* pt_Poll */ + +static PRInt32 pt_Recv( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRIntn osflags; + + if (0 == flags) { + osflags = 0; + } + else if (PR_MSG_PEEK == flags) + { + osflags = MSG_PEEK; + } + else + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return bytes; + } + + if (pt_TestAbort()) { + return bytes; + } + + /* recv() is a much slower call on pre-2.6 Solaris than read(). */ +#if defined(SOLARIS) + if (0 == osflags) { + bytes = read(fd->secret->md.osfd, buf, amount); + } + else { + bytes = recv(fd->secret->md.osfd, buf, amount, osflags); + } +#else + bytes = recv(fd->secret->md.osfd, buf, amount, osflags); +#endif + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = osflags; + op.timeout = timeout; + op.function = pt_recv_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno); + } + return bytes; +} /* pt_Recv */ + +static PRInt32 pt_SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return pt_Recv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} /* pt_SocketRead */ + +static PRInt32 pt_Send( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; +#if defined(SOLARIS) + PRInt32 tmp_amount = amount; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + /* + * On pre-2.6 Solaris, send() is much slower than write(). + * On 2.6 and beyond, with in-kernel sockets, send() and + * write() are fairly equivalent in performance. + */ +#if defined(SOLARIS) + PR_ASSERT(0 == flags); +retry: + bytes = write(fd->secret->md.osfd, buf, tmp_amount); +#else + bytes = send(fd->secret->md.osfd, buf, amount, flags); +#endif + syserrno = errno; + +#if defined(SOLARIS) + /* + * The write system call has been reported to return the ERANGE error + * on occasion. Try to write in smaller chunks to workaround this bug. + */ + if ((bytes == -1) && (syserrno == ERANGE)) + { + if (tmp_amount > 1) + { + tmp_amount = tmp_amount/2; /* half the bytes */ + goto retry; + } + } +#endif + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + bytes = -1; + syserrno = ETIMEDOUT; + } + else + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.timeout = timeout; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_send_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) { + pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno); + } + return bytes; +} /* pt_Send */ + +static PRInt32 pt_SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return pt_Send(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} /* pt_SocketWrite */ + +static PRInt32 pt_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + bytes = sendto( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addrp, addr_len); + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addrp; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + } + return bytes; +} /* pt_SendTo */ + +#if defined(LINUX) || defined(DARWIN) +/* Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but + * we will make it imitate the Linux's interface. */ +static PRInt32 pt_TCP_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ +#if defined(LINUX) || HAS_CONNECTX + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + addr_len = PR_NETADDR_SIZE(addr); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + /* If _PR_INET6 is defined and it is PR_AF_INET6 we set family + * to AF_INET6. */ + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + +#ifdef _PR_HAVE_SOCKADDR_LEN + /* if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family + * to AF_INET6 and we set address length. */ + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + +#ifndef HAS_CONNECTX + bytes = sendto( + fd->secret->md.osfd, buf, amount, MSG_FASTOPEN, + (struct sockaddr*)addrp, addr_len); +#else + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = (struct sockaddr *)addrp; + endpoints.sae_dstaddrlen = addr_len; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = amount; + PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY, + CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL); +#endif + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + if (fNeedContinue == PR_TRUE) { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addrp; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + } + return bytes; +#else /* !HAS_CONNECTX */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#endif +} /* pt_TCP_SendTo */ +#endif /* LINUX || DARWIN */ + +static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return bytes; + } + + bytes = recvfrom( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = addr; + op.timeout = timeout; + op.function = pt_recvfrom_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes >= 0) + { +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + } + else { + pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno); + } + return bytes; +} /* pt_RecvFrom */ + +#ifdef AIX +#ifndef HAVE_SEND_FILE +static pthread_once_t pt_aix_sendfile_once_block = PTHREAD_ONCE_INIT; + +static void pt_aix_sendfile_init_routine(void) +{ + void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); + pt_aix_sendfile_fptr = (ssize_t (*)()) dlsym(handle, "send_file"); + dlclose(handle); +} + +/* + * pt_AIXDispatchSendFile + */ +static PRInt32 pt_AIXDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + int rv; + + rv = pthread_once(&pt_aix_sendfile_once_block, + pt_aix_sendfile_init_routine); + PR_ASSERT(0 == rv); + if (pt_aix_sendfile_fptr) { + return pt_AIXSendFile(sd, sfd, flags, timeout); + } else { + return PR_EmulateSendFile(sd, sfd, flags, timeout); + } +} +#endif /* !HAVE_SEND_FILE */ + + +/* + * pt_AIXSendFile + * + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the send_file() system + * call available in AIX 4.3.2. + */ + +static PRInt32 pt_AIXSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct sf_parms sf_struct; + uint_t send_flags; + ssize_t rv; + int syserrno; + PRInt32 count; + unsigned long long saved_file_offset; + long long saved_file_bytes; + + sf_struct.header_data = (void *) sfd->header; /* cast away the 'const' */ + sf_struct.header_length = sfd->hlen; + sf_struct.file_descriptor = sfd->fd->secret->md.osfd; + sf_struct.file_size = 0; + sf_struct.file_offset = sfd->file_offset; + if (sfd->file_nbytes == 0) { + sf_struct.file_bytes = -1; + } + else { + sf_struct.file_bytes = sfd->file_nbytes; + } + sf_struct.trailer_data = (void *) sfd->trailer; + sf_struct.trailer_length = sfd->tlen; + sf_struct.bytes_sent = 0; + + saved_file_offset = sf_struct.file_offset; + saved_file_bytes = sf_struct.file_bytes; + + send_flags = 0; /* flags processed at the end */ + + /* The first argument to send_file() is int*. */ + PR_ASSERT(sizeof(int) == sizeof(sd->secret->md.osfd)); + do { + rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags); + } while (rv == -1 && (syserrno = errno) == EINTR); + + if (rv == -1) { + if (syserrno == EAGAIN || syserrno == EWOULDBLOCK) { + count = 0; /* Not a real error. Need to continue. */ + } else { + count = -1; + } + } else { + count = sf_struct.bytes_sent; + /* + * A bug in AIX 4.3.2 prevents the 'file_bytes' field from + * being updated. So, 'file_bytes' is maintained by NSPR to + * avoid conflict when this bug is fixed in AIX, in the future. + */ + if (saved_file_bytes != -1) { + saved_file_bytes -= (sf_struct.file_offset - saved_file_offset); + } + sf_struct.file_bytes = saved_file_bytes; + } + + if ((rv == 1) || ((rv == -1) && (count == 0))) { + pt_Continuation op; + + op.arg1.osfd = sd->secret->md.osfd; + op.arg2.buffer = &sf_struct; + op.arg4.flags = send_flags; + op.result.code = count; + op.timeout = timeout; + op.function = pt_aix_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + + if (count == -1) { + pt_MapError(_MD_aix_map_sendfile_error, syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == (sfd->hlen + sfd->tlen + + ((sfd->file_nbytes == 0) ? + sf_struct.file_size - sfd->file_offset : + sfd->file_nbytes))); + return count; +} +#endif /* AIX */ + +#ifdef HPUX11 +/* + * pt_HPUXSendFile + * + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in HP-UX B.11.00. + */ + +static PRInt32 pt_HPUXSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t nbytes_to_send, file_nbytes_to_send; + struct iovec hdtrl[2]; /* optional header and trailer buffers */ + int send_flags; + PRInt32 count; + int syserrno; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; + + hdtrl[0].iov_base = (void *) sfd->header; /* cast away the 'const' */ + hdtrl[0].iov_len = sfd->hlen; + hdtrl[1].iov_base = (void *) sfd->trailer; + hdtrl[1].iov_len = sfd->tlen; + /* + * SF_DISCONNECT seems to close the socket even if sendfile() + * only does a partial send on a nonblocking socket. This + * would prevent the subsequent sendfile() calls on that socket + * from working. So we don't use the SD_DISCONNECT flag. + */ + send_flags = 0; + + do { + count = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, + sfd->file_offset, file_nbytes_to_send, hdtrl, send_flags); + } while (count == -1 && (syserrno = errno) == EINTR); + + if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) { + count = 0; + } + if (count != -1 && count < nbytes_to_send) { + pt_Continuation op; + + if (count < sfd->hlen) { + /* header not sent */ + + hdtrl[0].iov_base = ((char *) sfd->header) + count; + hdtrl[0].iov_len = sfd->hlen - count; + op.arg3.file_spec.offset = sfd->file_offset; + op.arg3.file_spec.nbytes = file_nbytes_to_send; + } else if (count < (sfd->hlen + file_nbytes_to_send)) { + /* header sent, file not sent */ + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + + op.arg3.file_spec.offset = sfd->file_offset + count - sfd->hlen; + op.arg3.file_spec.nbytes = file_nbytes_to_send - (count - sfd->hlen); + } else if (count < (sfd->hlen + file_nbytes_to_send + sfd->tlen)) { + PRUint32 trailer_nbytes_sent; + + /* header sent, file sent, trailer not sent */ + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + /* + * set file offset and len so that no more file data is + * sent + */ + op.arg3.file_spec.offset = statbuf.st_size; + op.arg3.file_spec.nbytes = 0; + + trailer_nbytes_sent = count - sfd->hlen - file_nbytes_to_send; + hdtrl[1].iov_base = ((char *) sfd->trailer) + trailer_nbytes_sent; + hdtrl[1].iov_len = sfd->tlen - trailer_nbytes_sent; + } + + op.arg1.osfd = sd->secret->md.osfd; + op.filedesc = sfd->fd->secret->md.osfd; + op.arg2.buffer = hdtrl; + op.arg3.file_spec.st_size = statbuf.st_size; + op.arg4.flags = send_flags; + op.nbytes_to_send = nbytes_to_send - count; + op.result.code = count; + op.timeout = timeout; + op.function = pt_hpux_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + + if (count == -1) { + pt_MapError(_MD_hpux_map_sendfile_error, syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == nbytes_to_send); + return count; +} + +#endif /* HPUX11 */ + +#ifdef SOLARIS + +/* + * pt_SolarisSendFile + * + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfilev() system + * call available in Solaris 8. + */ + +static PRInt32 pt_SolarisSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t nbytes_to_send, file_nbytes_to_send; + struct sendfilevec sfv_struct[3]; + int sfvcnt = 0; + size_t xferred; + PRInt32 count; + int syserrno; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + + nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; + + if (sfd->hlen != 0) { + sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = (off_t) sfd->header; + sfv_struct[sfvcnt].sfv_len = sfd->hlen; + sfvcnt++; + } + + if (file_nbytes_to_send != 0) { + sfv_struct[sfvcnt].sfv_fd = sfd->fd->secret->md.osfd; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = sfd->file_offset; + sfv_struct[sfvcnt].sfv_len = file_nbytes_to_send; + sfvcnt++; + } + + if (sfd->tlen != 0) { + sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = (off_t) sfd->trailer; + sfv_struct[sfvcnt].sfv_len = sfd->tlen; + sfvcnt++; + } + + if (0 == sfvcnt) { + count = 0; + goto done; + } + + /* + * Strictly speaking, we may have sent some bytes when the + * sendfilev() is interrupted and we should retry it from an + * updated offset. We are not doing that here. + */ + count = SOLARIS_SENDFILEV(sd->secret->md.osfd, sfv_struct, + sfvcnt, &xferred); + + PR_ASSERT((count == -1) || (count == xferred)); + + if (count == -1) { + syserrno = errno; + if (syserrno == EINTR + || syserrno == EAGAIN || syserrno == EWOULDBLOCK) { + count = xferred; + } + } else if (count == 0) { + /* + * We are now at EOF. The file was truncated. Solaris sendfile is + * supposed to return 0 and no error in this case, though some versions + * may return -1 and EINVAL . + */ + count = -1; + syserrno = 0; /* will be treated as EOF */ + } + + if (count != -1 && count < nbytes_to_send) { + pt_Continuation op; + struct sendfilevec *vec = sfv_struct; + PRInt32 rem = count; + + while (rem >= vec->sfv_len) { + rem -= vec->sfv_len; + vec++; + sfvcnt--; + } + PR_ASSERT(sfvcnt > 0); + + vec->sfv_off += rem; + vec->sfv_len -= rem; + PR_ASSERT(vec->sfv_len > 0); + + op.arg1.osfd = sd->secret->md.osfd; + op.arg2.buffer = vec; + op.arg3.amount = sfvcnt; + op.arg4.flags = 0; + op.nbytes_to_send = nbytes_to_send - count; + op.result.code = count; + op.timeout = timeout; + op.function = pt_solaris_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + +done: + if (count == -1) { + pt_MapError(_MD_solaris_map_sendfile_error, syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == nbytes_to_send); + return count; +} + +#ifndef HAVE_SENDFILEV +static pthread_once_t pt_solaris_sendfilev_once_block = PTHREAD_ONCE_INIT; + +static void pt_solaris_sendfilev_init_routine(void) +{ + void *handle; + PRBool close_it = PR_FALSE; + + /* + * We do not want to unload libsendfile.so. This handle is leaked + * intentionally. + */ + handle = dlopen("libsendfile.so", RTLD_LAZY | RTLD_GLOBAL); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlopen(libsendfile.so) returns %p", handle)); + + if (NULL == handle) { + /* + * The dlopen(0, mode) call is to allow for the possibility that + * sendfilev() may become part of a standard system library in a + * future Solaris release. + */ + handle = dlopen(0, RTLD_LAZY | RTLD_GLOBAL); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlopen(0) returns %p", handle)); + close_it = PR_TRUE; + } + pt_solaris_sendfilev_fptr = (ssize_t (*)()) dlsym(handle, "sendfilev"); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlsym(sendfilev) returns %p", pt_solaris_sendfilev_fptr)); + + if (close_it) { + dlclose(handle); + } +} + +/* + * pt_SolarisDispatchSendFile + */ +static PRInt32 pt_SolarisDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + int rv; + + rv = pthread_once(&pt_solaris_sendfilev_once_block, + pt_solaris_sendfilev_init_routine); + PR_ASSERT(0 == rv); + if (pt_solaris_sendfilev_fptr) { + return pt_SolarisSendFile(sd, sfd, flags, timeout); + } else { + return PR_EmulateSendFile(sd, sfd, flags, timeout); + } +} +#endif /* !HAVE_SENDFILEV */ + +#endif /* SOLARIS */ + +#ifdef LINUX +/* + * pt_LinuxSendFile + * + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in Linux kernel 2.2 or higher. + */ + +static PRInt32 pt_LinuxSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t file_nbytes_to_send; + PRInt32 count = 0; + ssize_t rv; + int syserrno; + off_t offset; + PRBool tcp_cork_enabled = PR_FALSE; + int tcp_cork; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + + if ((sfd->hlen != 0 || sfd->tlen != 0) + && sd->secret->md.tcp_nodelay == 0) { + tcp_cork = 1; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == 0) { + tcp_cork_enabled = PR_TRUE; + } else { + syserrno = errno; + if (syserrno != EINVAL) { + _PR_MD_MAP_SETSOCKOPT_ERROR(syserrno); + return -1; + } + /* + * The most likely reason for the EINVAL error is that + * TCP_NODELAY is set (with a function other than + * PR_SetSocketOption). This is not fatal, so we keep + * on going. + */ + PR_LOG(_pr_io_lm, PR_LOG_WARNING, + ("pt_LinuxSendFile: " + "setsockopt(TCP_CORK) failed with EINVAL\n")); + } + } + + if (sfd->hlen != 0) { + count = PR_Send(sd, sfd->header, sfd->hlen, 0, timeout); + if (count == -1) { + goto failed; + } + } + + if (file_nbytes_to_send != 0) { + offset = sfd->file_offset; + do { + rv = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, + &offset, file_nbytes_to_send); + } while (rv == -1 && (syserrno = errno) == EINTR); + if (rv == -1) { + if (syserrno != EAGAIN && syserrno != EWOULDBLOCK) { + _MD_linux_map_sendfile_error(syserrno); + count = -1; + goto failed; + } + rv = 0; + } + PR_ASSERT(rv == offset - sfd->file_offset); + count += rv; + + if (rv < file_nbytes_to_send) { + pt_Continuation op; + + op.arg1.osfd = sd->secret->md.osfd; + op.in_fd = sfd->fd->secret->md.osfd; + op.offset = offset; + op.count = file_nbytes_to_send - rv; + op.result.code = count; + op.timeout = timeout; + op.function = pt_linux_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + if (count == -1) { + pt_MapError(_MD_linux_map_sendfile_error, syserrno); + goto failed; + } + } + } + + if (sfd->tlen != 0) { + rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); + if (rv == -1) { + count = -1; + goto failed; + } + count += rv; + } + +failed: + if (tcp_cork_enabled) { + tcp_cork = 0; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == -1 && count != -1) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + count = -1; + } + } + if (count != -1) { + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == sfd->hlen + sfd->tlen + file_nbytes_to_send); + } + return count; +} +#endif /* LINUX */ + +#ifdef AIX +extern int _pr_aix_send_file_use_disabled; +#endif + +static PRInt32 pt_SendFile( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + if (pt_TestAbort()) { + return -1; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +#ifdef HPUX11 + return(pt_HPUXSendFile(sd, sfd, flags, timeout)); +#elif defined(AIX) +#ifdef HAVE_SEND_FILE + /* + * A bug in AIX 4.3.2 results in corruption of data transferred by + * send_file(); AIX patch PTF U463956 contains the fix. A user can + * disable the use of send_file function in NSPR, when this patch is + * not installed on the system, by setting the envionment variable + * NSPR_AIX_SEND_FILE_USE_DISABLED to 1. + */ + if (_pr_aix_send_file_use_disabled) { + return(PR_EmulateSendFile(sd, sfd, flags, timeout)); + } + else { + return(pt_AIXSendFile(sd, sfd, flags, timeout)); + } +#else + return(PR_EmulateSendFile(sd, sfd, flags, timeout)); + /* return(pt_AIXDispatchSendFile(sd, sfd, flags, timeout));*/ +#endif /* HAVE_SEND_FILE */ +#elif defined(SOLARIS) +#ifdef HAVE_SENDFILEV + return(pt_SolarisSendFile(sd, sfd, flags, timeout)); +#else + return(pt_SolarisDispatchSendFile(sd, sfd, flags, timeout)); +#endif /* HAVE_SENDFILEV */ +#elif defined(LINUX) + return(pt_LinuxSendFile(sd, sfd, flags, timeout)); +#else + return(PR_EmulateSendFile(sd, sfd, flags, timeout)); +#endif +} + +static PRInt32 pt_TransmitFile( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, + PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PRSendFileData sfd; + + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + + return(pt_SendFile(sd, &sfd, flags, timeout)); +} /* pt_TransmitFile */ + +static PRInt32 pt_AcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + PRInt32 rv = -1; + + if (pt_TestAbort()) { + return rv; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return rv; + } + + rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); + return rv; +} /* pt_AcceptRead */ + +static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = getsockname( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno); + return PR_FAILURE; + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); + return PR_SUCCESS; +} /* pt_GetSockName */ + +static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = getpeername( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno); + return PR_FAILURE; + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); + return PR_SUCCESS; +} /* pt_GetPeerName */ + +static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + PRIntn rv; + pt_SockLen length; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == data->option) + { + data->value.non_blocking = fd->secret->nonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + length = sizeof(linger); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char *) &linger, &length); + PR_ASSERT((-1 == rv) || (sizeof(linger) == length)); + data->value.linger.polarity = + (linger.l_onoff) ? PR_TRUE : PR_FALSE; + data->value.linger.linger = + PR_SecondsToInterval(linger.l_linger); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 xbool; + length = sizeof(xbool); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&xbool, &length); + PR_ASSERT((-1 == rv) || (sizeof(xbool) == length)); + data->value.mcast_loopback = (0 == xbool) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.recv_buffer_size = value; + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + length = sizeof(PRUintn); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl; + length = sizeof(ttl); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(ttl) == length)); + data->value.mcast_ttl = ttl; + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + length = sizeof(mreq); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&mreq, &length); + PR_ASSERT((-1 == rv) || (sizeof(mreq) == length)); + data->value.add_member.mcaddr.inet.ip = + mreq.imr_multiaddr.s_addr; + data->value.add_member.ifaddr.inet.ip = + mreq.imr_interface.s_addr; + break; + } + case PR_SockOpt_McastInterface: + { + length = sizeof(data->value.mcast_if.inet.ip); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, &length); + PR_ASSERT((-1 == rv) + || (sizeof(data->value.mcast_if.inet.ip) == length)); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else + PRIntn value; + length = sizeof(value); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, + &length); +#if defined(DARWIN) + data->value.dont_fragment = value; +#else + data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0; +#endif +#endif /* !(!defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) { + _PR_MD_MAP_GETSOCKOPT_ERROR(errno); + } + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_GetSocketOption */ + +static PRStatus pt_SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) +{ + PRIntn rv; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == data->option) + { + fd->secret->nonblocking = data->value.non_blocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + linger.l_onoff = data->value.linger.polarity; + linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); + rv = setsockopt( + fd->secret->md.osfd, level, name, (char*)&linger, sizeof(linger)); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { + PRIntn value = (data->value.reuse_addr) ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); +#ifdef LINUX + /* for pt_LinuxSendFile */ + if (name == TCP_NODELAY && rv == 0) { + fd->secret->md.tcp_nodelay = value; + } +#endif + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 xbool = data->value.mcast_loopback ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&xbool, sizeof(xbool)); + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value = data->value.recv_buffer_size; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, sizeof(PRUintn)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl = data->value.mcast_ttl; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, sizeof(ttl)); + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = + data->value.add_member.mcaddr.inet.ip; + mreq.imr_interface.s_addr = + data->value.add_member.ifaddr.inet.ip; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&mreq, sizeof(mreq)); + break; + } + case PR_SockOpt_McastInterface: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, + sizeof(data->value.mcast_if.inet.ip)); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else + PRIntn value; +#if defined(LINUX) || defined(ANDROID) + value = (data->value.dont_fragment) ? IP_PMTUDISC_DO + : IP_PMTUDISC_DONT; +#elif defined(DARWIN) + value = data->value.dont_fragment; +#endif + rv = setsockopt( + fd->secret->md.osfd, level, name, (char*)&value, + sizeof(value)); +#endif /* !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + } + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_SetSocketOption */ + +/*****************************************************************************/ +/****************************** I/O method objects ***************************/ +/*****************************************************************************/ + +static PRIOMethods _pr_file_methods = { + PR_DESC_FILE, + pt_Close, + pt_Read, + pt_Write, + pt_Available_f, + pt_Available64_f, + pt_Fsync, + pt_Seek, + pt_Seek64, + pt_FileInfo, + pt_FileInfo64, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_pipe_methods = { + PR_DESC_PIPE, + pt_Close, + pt_Read, + pt_Write, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_tcp_methods = { + PR_DESC_SOCKET_TCP, + pt_Close, + pt_SocketRead, + pt_SocketWrite, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + pt_Accept, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + (PRRecvfromFN)_PR_InvalidInt, +#if defined(LINUX) || defined(DARWIN) + pt_TCP_SendTo, /* This is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux. */ +#else + (PRSendtoFN)_PR_InvalidInt, +#endif + pt_Poll, + pt_AcceptRead, + pt_TransmitFile, + pt_GetSockName, + pt_GetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + pt_GetSocketOption, + pt_SetSocketOption, + pt_SendFile, + pt_ConnectContinue, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_udp_methods = { + PR_DESC_SOCKET_UDP, + pt_Close, + pt_SocketRead, + pt_SocketWrite, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + (PRAcceptFN)_PR_InvalidDesc, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + pt_RecvFrom, + pt_SendTo, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + pt_GetSockName, + pt_GetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + pt_GetSocketOption, + pt_SetSocketOption, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_socketpollfd_methods = { + (PRDescType) 0, + (PRCloseFN)_PR_InvalidStatus, + (PRReadFN)_PR_InvalidInt, + (PRWriteFN)_PR_InvalidInt, + (PRAvailableFN)_PR_InvalidInt, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +#if defined(HPUX) || defined(SOLARIS) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(AIX) || defined(FREEBSD) || defined(NETBSD) \ + || defined(OPENBSD) || defined(BSDI) || defined(NTO) \ + || defined(DARWIN) || defined(UNIXWARE) || defined(RISCOS) +#define _PR_FCNTL_FLAGS O_NONBLOCK +#else +#error "Can't determine architecture" +#endif + +/* + * Put a Unix file descriptor in non-blocking mode. + */ +static void pt_MakeFdNonblock(PRIntn osfd) +{ + PRIntn flags; + flags = fcntl(osfd, F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(osfd, F_SETFL, flags); +} + +/* + * Put a Unix socket fd in non-blocking mode that can + * ideally be inherited by an accepted socket. + * + * Why doesn't pt_MakeFdNonblock do? This is to deal with + * the special case of HP-UX. HP-UX has three kinds of + * non-blocking modes for sockets: the fcntl() O_NONBLOCK + * and O_NDELAY flags and ioctl() FIOSNBIO request. Only + * the ioctl() FIOSNBIO form of non-blocking mode is + * inherited by an accepted socket. + * + * Other platforms just use the generic pt_MakeFdNonblock + * to put a socket in non-blocking mode. + */ +#ifdef HPUX +static void pt_MakeSocketNonblock(PRIntn osfd) +{ + PRIntn one = 1; + (void)ioctl(osfd, FIOSNBIO, &one); +} +#else +#define pt_MakeSocketNonblock pt_MakeFdNonblock +#endif + +static PRFileDesc *pt_SetMethods( + PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported) +{ + PRFileDesc *fd = _PR_Getfd(); + + if (fd == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->secret->md.osfd = osfd; + fd->secret->state = _PR_FILEDESC_OPEN; + if (imported) { + fd->secret->inheritable = _PR_TRI_UNKNOWN; + } + else + { + /* By default, a Unix fd is not closed on exec. */ +#ifdef DEBUG + PRIntn flags; + flags = fcntl(osfd, F_GETFD, 0); + PR_ASSERT(0 == flags); +#endif + fd->secret->inheritable = _PR_TRI_TRUE; + } + switch (type) + { + case PR_DESC_FILE: + fd->methods = PR_GetFileMethods(); + break; + case PR_DESC_SOCKET_TCP: + fd->methods = PR_GetTCPMethods(); +#ifdef _PR_ACCEPT_INHERIT_NONBLOCK + if (!isAcceptedSocket) { + pt_MakeSocketNonblock(osfd); + } +#else + pt_MakeSocketNonblock(osfd); +#endif + break; + case PR_DESC_SOCKET_UDP: + fd->methods = PR_GetUDPMethods(); + pt_MakeFdNonblock(osfd); + break; + case PR_DESC_PIPE: + fd->methods = PR_GetPipeMethods(); + pt_MakeFdNonblock(osfd); + break; + default: + break; + } + } + return fd; +} /* pt_SetMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetFileMethods(void) +{ + return &_pr_file_methods; +} /* PR_GetFileMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetPipeMethods(void) +{ + return &_pr_pipe_methods; +} /* PR_GetPipeMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods(void) +{ + return &_pr_tcp_methods; +} /* PR_GetTCPMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods(void) +{ + return &_pr_udp_methods; +} /* PR_GetUDPMethods */ + +static const PRIOMethods* PR_GetSocketPollFdMethods(void) +{ + return &_pr_socketpollfd_methods; +} /* PR_GetSocketPollFdMethods */ + +PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc( + PRInt32 osfd, const PRIOMethods *methods) +{ + PRFileDesc *fd = _PR_Getfd(); + + if (NULL == fd) { + goto failed; + } + + fd->methods = methods; + fd->secret->md.osfd = osfd; + /* Make fd non-blocking */ + if (osfd > 2) + { + /* Don't mess around with stdin, stdout or stderr */ + if (&_pr_tcp_methods == methods) { + pt_MakeSocketNonblock(osfd); + } + else { + pt_MakeFdNonblock(osfd); + } + } + fd->secret->state = _PR_FILEDESC_OPEN; + fd->secret->inheritable = _PR_TRI_UNKNOWN; + return fd; + +failed: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return fd; +} /* PR_AllocFileDesc */ + +#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) +PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd); +#if defined(_PR_INET6_PROBE) +extern PRBool _pr_ipv6_is_present(void); +PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket() +{ + int osfd; + +#if defined(DARWIN) + /* + * Disable IPv6 if Darwin version is less than 7.0.0 (OS X 10.3). IPv6 on + * lesser versions is not ready for general use (see bug 222031). + */ + { + struct utsname u; + if (uname(&u) != 0 || atoi(u.release) < 7) { + return PR_FALSE; + } + } +#endif + + /* + * HP-UX only: HP-UX IPv6 Porting Guide (dated February 2001) + * suggests that we call open("/dev/ip6", O_RDWR) to determine + * whether IPv6 APIs and the IPv6 stack are on the system. + * Our portable test below seems to work fine, so I am using it. + */ + osfd = socket(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + close(osfd); + return PR_TRUE; + } + return PR_FALSE; +} +#endif /* _PR_INET6_PROBE */ +#endif + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRIntn osfd; + PRDescType ftype; + PRFileDesc *fd = NULL; +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + PRInt32 tmp_domain = domain; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return NULL; + } + + if (PF_INET != domain + && PR_AF_INET6 != domain +#if defined(_PR_HAVE_SDP) + && PR_AF_INET_SDP != domain +#if defined(SOLARIS) + && PR_AF_INET6_SDP != domain +#endif /* SOLARIS */ +#endif /* _PR_HAVE_SDP */ + && PF_UNIX != domain) + { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } + if (type == SOCK_STREAM) { + ftype = PR_DESC_SOCKET_TCP; + } + else if (type == SOCK_DGRAM) { + ftype = PR_DESC_SOCKET_UDP; + } + else + { + (void)PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } +#if defined(_PR_HAVE_SDP) +#if defined(LINUX) + if (PR_AF_INET_SDP == domain) { + domain = AF_INET_SDP; + } +#elif defined(SOLARIS) + if (PR_AF_INET_SDP == domain) { + domain = AF_INET; + proto = PROTO_SDP; + } else if(PR_AF_INET6_SDP == domain) { + domain = AF_INET6; + proto = PROTO_SDP; + } +#endif /* SOLARIS */ +#endif /* _PR_HAVE_SDP */ +#if defined(_PR_INET6_PROBE) + if (PR_AF_INET6 == domain) { + domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET; + } +#elif defined(_PR_INET6) + if (PR_AF_INET6 == domain) { + domain = AF_INET6; + } +#else + if (PR_AF_INET6 == domain) { + domain = AF_INET; + } +#endif + + osfd = socket(domain, type, proto); + if (osfd == -1) { + pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno); + } + else + { +#ifdef _PR_IPV6_V6ONLY_PROBE + if ((domain == AF_INET6) && _pr_ipv6_v6only_on_by_default) + { + int on = 0; + (void)setsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)); + } +#endif + fd = pt_SetMethods(osfd, ftype, PR_FALSE, PR_FALSE); + if (fd == NULL) { + close(osfd); + } + } +#ifdef _PR_NEED_SECRET_AF + if (fd != NULL) { + fd->secret->af = domain; + } +#endif +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + if (fd != NULL) { + /* + * For platforms with no support for IPv6 + * create layered socket for IPv4-mapped IPv6 addresses + */ + if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) { + if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) { + PR_Close(fd); + fd = NULL; + } + } + } +#endif + return fd; +} /* PR_Socket */ + +/*****************************************************************************/ +/****************************** I/O public methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_OpenFile( + const char *name, PRIntn flags, PRIntn mode) +{ + PRFileDesc *fd = NULL; + PRIntn syserrno, osfd = -1, osflags = 0;; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return NULL; + } + + if (flags & PR_RDONLY) { + osflags |= O_RDONLY; + } + if (flags & PR_WRONLY) { + osflags |= O_WRONLY; + } + if (flags & PR_RDWR) { + osflags |= O_RDWR; + } + if (flags & PR_APPEND) { + osflags |= O_APPEND; + } + if (flags & PR_TRUNCATE) { + osflags |= O_TRUNC; + } + if (flags & PR_EXCL) { + osflags |= O_EXCL; + } + if (flags & PR_SYNC) + { +#if defined(O_SYNC) + osflags |= O_SYNC; +#elif defined(O_FSYNC) + osflags |= O_FSYNC; +#else +#error "Neither O_SYNC nor O_FSYNC is defined on this platform" +#endif + } + + /* + ** We have to hold the lock across the creation in order to + ** enforce the sematics of PR_Rename(). (see the latter for + ** more details) + */ + if (flags & PR_CREATE_FILE) + { + osflags |= O_CREAT; + if (NULL !=_pr_rename_lock) { + PR_Lock(_pr_rename_lock); + } + } + + osfd = _md_iovector._open64(name, osflags, mode); + syserrno = errno; + + if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock)) { + PR_Unlock(_pr_rename_lock); + } + + if (osfd == -1) { + pt_MapError(_PR_MD_MAP_OPEN_ERROR, syserrno); + } + else + { + fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_FALSE); + if (fd == NULL) { + close(osfd); /* $$$ whoops! this is bad $$$ */ + } + } + return fd; +} /* PR_OpenFile */ + +PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode) +{ + return PR_OpenFile(name, flags, mode); +} /* PR_Open */ + +PR_IMPLEMENT(PRStatus) PR_Delete(const char *name) +{ + PRIntn rv = -1; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = unlink(name); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_UNLINK_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PR_Delete */ + +PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how) +{ + PRIntn rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + switch (how) + { + case PR_ACCESS_READ_OK: + rv = access(name, R_OK); + break; + case PR_ACCESS_WRITE_OK: + rv = access(name, W_OK); + break; + case PR_ACCESS_EXISTS: + default: + rv = access(name, F_OK); + } + if (0 == rv) { + return PR_SUCCESS; + } + pt_MapError(_PR_MD_MAP_ACCESS_ERROR, errno); + return PR_FAILURE; + +} /* PR_Access */ + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv = _PR_MD_GETFILEINFO(fn, info); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_GetFileInfo */ + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + rv = _PR_MD_GETFILEINFO64(fn, info); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_GetFileInfo64 */ + +PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to) +{ + PRIntn rv = -1; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + /* + ** We have to acquire a lock here to stiffle anybody trying to create + ** a new file at the same time. And we have to hold that lock while we + ** test to see if the file exists and do the rename. The other place + ** where the lock is held is in PR_Open() when possibly creating a + ** new file. + */ + + PR_Lock(_pr_rename_lock); + rv = access(to, F_OK); + if (0 == rv) + { + PR_SetError(PR_FILE_EXISTS_ERROR, 0); + rv = -1; + } + else + { + rv = rename(from, to); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_RENAME_ERROR, errno); + } + } + PR_Unlock(_pr_rename_lock); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Rename */ + +PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir) +{ + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (NULL != dir->md.d) + { + if (closedir(dir->md.d) == -1) + { + _PR_MD_MAP_CLOSEDIR_ERROR(errno); + return PR_FAILURE; + } + dir->md.d = NULL; + PR_DELETE(dir); + } + return PR_SUCCESS; +} /* PR_CloseDir */ + +PR_IMPLEMENT(PRStatus) PR_MakeDir(const char *name, PRIntn mode) +{ + PRInt32 rv = -1; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + /* + ** This lock is used to enforce rename semantics as described + ** in PR_Rename. + */ + if (NULL !=_pr_rename_lock) { + PR_Lock(_pr_rename_lock); + } + rv = mkdir(name, mode); + if (-1 == rv) { + pt_MapError(_PR_MD_MAP_MKDIR_ERROR, errno); + } + if (NULL !=_pr_rename_lock) { + PR_Unlock(_pr_rename_lock); + } + + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Makedir */ + +PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode) +{ + return PR_MakeDir(name, mode); +} /* PR_Mkdir */ + +PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name) +{ + PRInt32 rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = rmdir(name); + if (0 == rv) { + return PR_SUCCESS; + } + pt_MapError(_PR_MD_MAP_RMDIR_ERROR, errno); + return PR_FAILURE; +} /* PR_Rmdir */ + + +PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name) +{ + DIR *osdir; + PRDir *dir = NULL; + + if (pt_TestAbort()) { + return dir; + } + + osdir = opendir(name); + if (osdir == NULL) { + pt_MapError(_PR_MD_MAP_OPENDIR_ERROR, errno); + } + else + { + dir = PR_NEWZAP(PRDir); + if (dir) { + dir->md.d = osdir; + } + else { + (void)closedir(osdir); + } + } + return dir; +} /* PR_OpenDir */ + +static PRInt32 _pr_poll_with_poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 ready = 0; + /* + * For restarting poll() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + if (pt_TestAbort()) { + return -1; + } + + if (0 == npds) { + PR_Sleep(timeout); + } + else + { +#define STACK_POLL_DESC_COUNT 64 + struct pollfd stack_syspoll[STACK_POLL_DESC_COUNT]; + struct pollfd *syspoll; + PRIntn index, msecs; + + if (npds <= STACK_POLL_DESC_COUNT) + { + syspoll = stack_syspoll; + } + else + { + PRThread *me = PR_GetCurrentThread(); + if (npds > me->syspoll_count) + { + PR_Free(me->syspoll_list); + me->syspoll_list = + (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd)); + if (NULL == me->syspoll_list) + { + me->syspoll_count = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + me->syspoll_count = npds; + } + syspoll = me->syspoll_list; + } + + for (index = 0; index < npds; ++index) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (pds[index].in_flags & PR_POLL_READ) + { + in_flags_read = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_WRITE, + &out_flags_read); + } + if (pds[index].in_flags & PR_POLL_WRITE) + { + in_flags_write = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_READ, + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one is ready right now */ + if (0 == ready) + { + /* + * We will return without calling the system + * poll function. So zero the out_flags + * fields of all the poll descriptors before + * this one. + */ + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; + pds[index].out_flags = out_flags_read | out_flags_write; + } + else + { + /* now locate the NSPR layer at the bottom of the stack */ + PRFileDesc *bottom = PR_GetIdentitiesLayer( + pds[index].fd, PR_NSPR_IO_LAYER); + /* ignore a socket without PR_NSPR_IO_LAYER available */ + + pds[index].out_flags = 0; /* pre-condition */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + syspoll[index].fd = bottom->secret->md.osfd; + syspoll[index].events = 0; + if (in_flags_read & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_read & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (in_flags_write & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_write & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (pds[index].in_flags & PR_POLL_EXCEPT) { + syspoll[index].events |= POLLPRI; + } + } + } + else + { + if (0 == ready) + { + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + /* make poll() ignore this entry */ + syspoll[index].fd = -1; + syspoll[index].events = 0; + pds[index].out_flags = 0; + } + } + if (0 == ready) + { + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: msecs = 0; break; + case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + start = PR_IntervalNow(); + } + +retry: + ready = poll(syspoll, npds, msecs); + if (-1 == ready) + { + PRIntn oserror = errno; + + if (EINTR == oserror) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else if (timeout == PR_INTERVAL_NO_WAIT) { + ready = 0; /* don't retry, just time out */ + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() + - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + goto retry; + } + } + } + else + { + _PR_MD_MAP_POLL_ERROR(oserror); + } + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + PRInt16 out_flags = 0; + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (0 != syspoll[index].revents) + { + if (syspoll[index].revents & POLLIN) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_READ) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_READ) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLOUT) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_WRITE) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_WRITE) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLPRI) { + out_flags |= PR_POLL_EXCEPT; + } + if (syspoll[index].revents & POLLERR) { + out_flags |= PR_POLL_ERR; + } + if (syspoll[index].revents & POLLNVAL) { + out_flags |= PR_POLL_NVAL; + } + if (syspoll[index].revents & POLLHUP) { + out_flags |= PR_POLL_HUP; + } + } + } + pds[index].out_flags = out_flags; + } + } + } + } + return ready; + +} /* _pr_poll_with_poll */ + +#if defined(_PR_POLL_WITH_SELECT) +/* + * HPUX report the POLLHUP event for a socket when the + * shutdown(SHUT_WR) operation is called for the remote end, even though + * the socket is still writeable. Use select(), instead of poll(), to + * workaround this problem. + */ +static PRInt32 _pr_poll_with_select( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 ready = 0; + /* + * For restarting select() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + if (pt_TestAbort()) { + return -1; + } + + if (0 == npds) { + PR_Sleep(timeout); + } + else + { +#define STACK_POLL_DESC_COUNT 64 + int stack_selectfd[STACK_POLL_DESC_COUNT]; + int *selectfd; + fd_set rd, wr, ex, *rdp = NULL, *wrp = NULL, *exp = NULL; + struct timeval tv, *tvp; + PRIntn index, msecs, maxfd = 0; + + if (npds <= STACK_POLL_DESC_COUNT) + { + selectfd = stack_selectfd; + } + else + { + PRThread *me = PR_GetCurrentThread(); + if (npds > me->selectfd_count) + { + PR_Free(me->selectfd_list); + me->selectfd_list = (int *)PR_MALLOC(npds * sizeof(int)); + if (NULL == me->selectfd_list) + { + me->selectfd_count = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + me->selectfd_count = npds; + } + selectfd = me->selectfd_list; + } + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + for (index = 0; index < npds; ++index) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (pds[index].in_flags & PR_POLL_READ) + { + in_flags_read = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_WRITE, + &out_flags_read); + } + if (pds[index].in_flags & PR_POLL_WRITE) + { + in_flags_write = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_READ, + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one is ready right now */ + if (0 == ready) + { + /* + * We will return without calling the system + * poll function. So zero the out_flags + * fields of all the poll descriptors before + * this one. + */ + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; + pds[index].out_flags = out_flags_read | out_flags_write; + } + else + { + /* now locate the NSPR layer at the bottom of the stack */ + PRFileDesc *bottom = PR_GetIdentitiesLayer( + pds[index].fd, PR_NSPR_IO_LAYER); + /* ignore a socket without PR_NSPR_IO_LAYER available */ + + pds[index].out_flags = 0; /* pre-condition */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + PRBool add_to_rd = PR_FALSE; + PRBool add_to_wr = PR_FALSE; + PRBool add_to_ex = PR_FALSE; + + selectfd[index] = bottom->secret->md.osfd; + if (in_flags_read & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_READ; + add_to_rd = PR_TRUE; + } + if (in_flags_read & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_WRITE; + add_to_wr = PR_TRUE; + } + if (in_flags_write & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_READ; + add_to_rd = PR_TRUE; + } + if (in_flags_write & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_WRITE; + add_to_wr = PR_TRUE; + } + if (pds[index].in_flags & PR_POLL_EXCEPT) + { + add_to_ex = PR_TRUE; + } + if ((selectfd[index] > maxfd) && + (add_to_rd || add_to_wr || add_to_ex)) + { + maxfd = selectfd[index]; + /* + * If maxfd is too large to be used with + * select, fall back to calling poll. + */ + if (maxfd >= FD_SETSIZE) { + break; + } + } + if (add_to_rd) + { + FD_SET(bottom->secret->md.osfd, &rd); + rdp = &rd; + } + if (add_to_wr) + { + FD_SET(bottom->secret->md.osfd, &wr); + wrp = ≀ + } + if (add_to_ex) + { + FD_SET(bottom->secret->md.osfd, &ex); + exp = &ex; + } + } + } + else + { + if (0 == ready) + { + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + pds[index].out_flags = 0; + } + } + if (0 == ready) + { + if (maxfd >= FD_SETSIZE) + { + /* + * maxfd too large to be used with select, fall back to + * calling poll + */ + return(_pr_poll_with_poll(pds, npds, timeout)); + } + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: + tv.tv_sec = 0; + tv.tv_usec = 0; + tvp = &tv; + break; + case PR_INTERVAL_NO_TIMEOUT: + tvp = NULL; + break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC; + tvp = &tv; + start = PR_IntervalNow(); + } + +retry: + ready = select(maxfd + 1, rdp, wrp, exp, tvp); + if (-1 == ready) + { + PRIntn oserror = errno; + + if ((EINTR == oserror) || (EAGAIN == oserror)) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else if (timeout == PR_INTERVAL_NO_WAIT) { + ready = 0; /* don't retry, just time out */ + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() + - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * + PR_USEC_PER_MSEC; + goto retry; + } + } + } else if (EBADF == oserror) + { + /* find all the bad fds */ + ready = 0; + for (index = 0; index < npds; ++index) + { + pds[index].out_flags = 0; + if ((NULL != pds[index].fd) && + (0 != pds[index].in_flags)) + { + if (fcntl(selectfd[index], F_GETFL, 0) == -1) + { + pds[index].out_flags = PR_POLL_NVAL; + ready++; + } + } + } + } else { + _PR_MD_MAP_SELECT_ERROR(oserror); + } + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + PRInt16 out_flags = 0; + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (FD_ISSET(selectfd[index], &rd)) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_READ) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_READ) + { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(selectfd[index], &wr)) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_WRITE) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_WRITE) + { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(selectfd[index], &ex)) { + out_flags |= PR_POLL_EXCEPT; + } + } + pds[index].out_flags = out_flags; + } + } + } + } + return ready; + +} /* _pr_poll_with_select */ +#endif /* _PR_POLL_WITH_SELECT */ + +PR_IMPLEMENT(PRInt32) PR_Poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ +#if defined(_PR_POLL_WITH_SELECT) + return(_pr_poll_with_select(pds, npds, timeout)); +#else + return(_pr_poll_with_poll(pds, npds, timeout)); +#endif +} + +PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) +{ + struct dirent *dp; + + if (pt_TestAbort()) { + return NULL; + } + + for (;;) + { + errno = 0; + dp = readdir(dir->md.d); + if (NULL == dp) + { + pt_MapError(_PR_MD_MAP_READDIR_ERROR, errno); + return NULL; + } + if ((flags & PR_SKIP_DOT) + && ('.' == dp->d_name[0]) + && (0 == dp->d_name[1])) { + continue; + } + if ((flags & PR_SKIP_DOT_DOT) + && ('.' == dp->d_name[0]) + && ('.' == dp->d_name[1]) + && (0 == dp->d_name[2])) { + continue; + } + if ((flags & PR_SKIP_HIDDEN) && ('.' == dp->d_name[0])) { + continue; + } + break; + } + dir->d.name = dp->d_name; + return &dir->d; +} /* PR_ReadDir */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) +{ + PRIntn domain = PF_INET; + + return PR_Socket(domain, SOCK_DGRAM, 0); +} /* PR_NewUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewTCPSocket(void) +{ + PRIntn domain = PF_INET; + + return PR_Socket(domain, SOCK_STREAM, 0); +} /* PR_NewTCPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_DGRAM, 0); +} /* PR_NewUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_OpenTCPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_STREAM, 0); +} /* PR_NewTCPSocket */ + +PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]) +{ + PRInt32 osfd[2]; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, osfd) == -1) { + pt_MapError(_PR_MD_MAP_SOCKETPAIR_ERROR, errno); + return PR_FAILURE; + } + + fds[0] = pt_SetMethods(osfd[0], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE); + if (fds[0] == NULL) { + close(osfd[0]); + close(osfd[1]); + return PR_FAILURE; + } + fds[1] = pt_SetMethods(osfd[1], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE); + if (fds[1] == NULL) { + PR_Close(fds[0]); + close(osfd[1]); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PR_NewTCPSocketPair */ + +PR_IMPLEMENT(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +) +{ + int pipefd[2]; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (pipe(pipefd) == -1) + { + /* XXX map pipe error */ + PR_SetError(PR_UNKNOWN_ERROR, errno); + return PR_FAILURE; + } + *readPipe = pt_SetMethods(pipefd[0], PR_DESC_PIPE, PR_FALSE, PR_FALSE); + if (NULL == *readPipe) + { + close(pipefd[0]); + close(pipefd[1]); + return PR_FAILURE; + } + *writePipe = pt_SetMethods(pipefd[1], PR_DESC_PIPE, PR_FALSE, PR_FALSE); + if (NULL == *writePipe) + { + PR_Close(*readPipe); + close(pipefd[1]); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +/* +** Set the inheritance attribute of a file descriptor. +*/ +PR_IMPLEMENT(PRStatus) PR_SetFDInheritable( + PRFileDesc *fd, + PRBool inheritable) +{ + /* + * Only a non-layered, NSPR file descriptor can be inherited + * by a child process. + */ + if (fd->identity != PR_NSPR_IO_LAYER) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + if (fd->secret->inheritable != inheritable) + { + if (fcntl(fd->secret->md.osfd, F_SETFD, + inheritable ? 0 : FD_CLOEXEC) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + fd->secret->inheritable = (_PRTriStateBool) inheritable; + } + return PR_SUCCESS; +} + +/*****************************************************************************/ +/***************************** I/O friends methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportFile */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportPipe(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_PIPE, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportPipe */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } +#ifdef _PR_NEED_SECRET_AF + if (NULL != fd) { + fd->secret->af = PF_INET; + } +#endif + return fd; +} /* PR_ImportTCPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + fd = _PR_Getfd(); + + if (fd == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->secret->md.osfd = osfd; + fd->secret->inheritable = _PR_TRI_FALSE; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->methods = PR_GetSocketPollFdMethods(); + } + + return fd; +} /* PR_CreateSocketPollFD */ + +PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd) +{ + if (NULL == fd) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + _PR_Putfd(fd); + return PR_SUCCESS; +} /* PR_DestroySocketPollFd */ + +PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *bottom) +{ + PRInt32 osfd = -1; + bottom = (NULL == bottom) ? + NULL : PR_GetIdentitiesLayer(bottom, PR_NSPR_IO_LAYER); + if (NULL == bottom) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + else { + osfd = bottom->secret->md.osfd; + } + return osfd; +} /* PR_FileDesc2NativeHandle */ + +PR_IMPLEMENT(void) PR_ChangeFileDescNativeHandle(PRFileDesc *fd, + PRInt32 handle) +{ + if (fd) { + fd->secret->md.osfd = handle; + } +} /* PR_ChangeFileDescNativeHandle*/ + +PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + while (-1 == fd->secret->lockCount) { + PR_WaitCondVar(_pr_flock_cv, PR_INTERVAL_NO_TIMEOUT); + } + if (0 == fd->secret->lockCount) + { + fd->secret->lockCount = -1; + PR_Unlock(_pr_flock_lock); + status = _PR_MD_LOCKFILE(fd->secret->md.osfd); + PR_Lock(_pr_flock_lock); + fd->secret->lockCount = (PR_SUCCESS == status) ? 1 : 0; + PR_NotifyAllCondVar(_pr_flock_cv); + } + else + { + fd->secret->lockCount += 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_LockFile */ + +PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + if (0 == fd->secret->lockCount) + { + status = _PR_MD_TLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) { + fd->secret->lockCount = 1; + } + } + else { + fd->secret->lockCount += 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_TLockFile */ + +PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 1) + { + status = _PR_MD_UNLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) { + fd->secret->lockCount = 0; + } + } + else { + fd->secret->lockCount -= 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} + +/* + * The next two entry points should not be in the API, but they are + * defined here for historical (or hysterical) reasons. + */ + +PR_IMPLEMENT(PRInt32) PR_GetSysfdTableMax(void) +{ +#if defined(AIX) + return sysconf(_SC_OPEN_MAX); +#else + struct rlimit rlim; + + if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + + return rlim.rlim_max; +#endif +} + +PR_IMPLEMENT(PRInt32) PR_SetSysfdTableSize(PRIntn table_size) +{ +#if defined(AIX) + return -1; +#else + struct rlimit rlim; + PRInt32 tableMax = PR_GetSysfdTableMax(); + + if (tableMax < 0) { + return -1; + } + rlim.rlim_max = tableMax; + + /* Grow as much as we can; even if too big */ + if ( rlim.rlim_max < table_size ) { + rlim.rlim_cur = rlim.rlim_max; + } + else { + rlim.rlim_cur = table_size; + } + + if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + + return rlim.rlim_cur; +#endif +} + +/* + * PR_Stat is supported for backward compatibility; some existing Java + * code uses it. New code should use PR_GetFileInfo. + */ + +#ifndef NO_NSPR_10_SUPPORT +PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_Stat", "PR_GetFileInfo"); + } + + if (pt_TestAbort()) { + return -1; + } + + if (-1 == stat(name, buf)) { + pt_MapError(_PR_MD_MAP_STAT_ERROR, errno); + return -1; + } else { + return 0; + } +} +#endif /* ! NO_NSPR_10_SUPPORT */ + + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_ZERO (PR_Select)", "PR_Poll"); + } + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_SET (PR_Select)", "PR_Poll"); + } + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_CLR (PR_Select)", "PR_Poll"); + } + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_ISSET (PR_Select)", "PR_Poll"); + } + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NSET (PR_Select)", "PR_Poll"); + } + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NCLR (PR_Select)", "PR_Poll"); + } + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NISSET (PR_Select)", "PR_Poll"); + } + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + +#include <sys/types.h> +#include <sys/time.h> +#if !defined(HPUX) \ + && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__) +#include <sys/select.h> +#endif + +static PRInt32 +_PR_getset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index; + PRInt32 max = 0; + + if (!pr_set) { + return 0; + } + + FD_ZERO(set); + + /* First set the pr file handle osfds */ + for (index=0; index<pr_set->hsize; index++) { + FD_SET(pr_set->harray[index]->secret->md.osfd, set); + if (pr_set->harray[index]->secret->md.osfd > max) { + max = pr_set->harray[index]->secret->md.osfd; + } + } + /* Second set the native osfds */ + for (index=0; index<pr_set->nsize; index++) { + FD_SET(pr_set->narray[index], set); + if (pr_set->narray[index] > max) { + max = pr_set->narray[index]; + } + } + return max; +} + +static void +_PR_setset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index, last_used; + + if (!pr_set) { + return; + } + + for (last_used=0, index=0; index<pr_set->hsize; index++) { + if ( FD_ISSET(pr_set->harray[index]->secret->md.osfd, set) ) { + pr_set->harray[last_used++] = pr_set->harray[index]; + } + } + pr_set->hsize = last_used; + + for (last_used=0, index=0; index<pr_set->nsize; index++) { + if ( FD_ISSET(pr_set->narray[index], set) ) { + pr_set->narray[last_used++] = pr_set->narray[index]; + } + } + pr_set->nsize = last_used; +} + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + fd_set rd, wr, ex; + struct timeval tv, *tvp; + PRInt32 max, max_fd; + PRInt32 rv; + /* + * For restarting select() if it is interrupted by a Unix signal. + * We use these variables to figure out how much time has elapsed + * and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete( "PR_Select", "PR_Poll"); + } + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + max_fd = _PR_getset(pr_rd, &rd); + max_fd = (max = _PR_getset(pr_wr, &wr))>max_fd?max:max_fd; + max_fd = (max = _PR_getset(pr_ex, &ex))>max_fd?max:max_fd; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(timeout); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + start = PR_IntervalNow(); + } + +retry: + rv = select(max_fd + 1, (_PRSelectFdSetArg_t) &rd, + (_PRSelectFdSetArg_t) &wr, (_PRSelectFdSetArg_t) &ex, tvp); + + if (rv == -1 && errno == EINTR) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } else { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + rv = 0; /* timed out */ + } else { + remaining = timeout - elapsed; + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(remaining); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + remaining - PR_SecondsToInterval(tv.tv_sec)); + goto retry; + } + } + } + + if (rv > 0) { + _PR_setset(pr_rd, &rd); + _PR_setset(pr_wr, &wr); + _PR_setset(pr_ex, &ex); + } else if (rv == -1) { + pt_MapError(_PR_MD_MAP_SELECT_ERROR, errno); + } + return rv; +} +#endif /* defined(_PR_PTHREADS) */ + +#ifdef MOZ_UNICODE +/* ================ UTF16 Interfaces ================================ */ +PR_IMPLEMENT(PRFileDesc*) PR_OpenFileUTF16( + const PRUnichar *name, PRIntn flags, PRIntn mode) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CloseDirUTF16(PRDir *dir) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} +/* ================ UTF16 Interfaces ================================ */ +#endif /* MOZ_UNICODE */ + +/* ptio.c */ |