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