| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| /* |
| ** File: ptio.c |
| ** Descritpion: Implemenation of I/O methods for pthreads |
| */ |
| |
| #if defined(_PR_PTHREADS) |
| |
| #if defined(_PR_POLL_WITH_SELECT) |
| #if !(defined(HPUX) && defined(_USE_BIG_FDS)) |
| /* set fd limit for select(), before including system header files */ |
| #define FD_SETSIZE (16 * 1024) |
| #endif |
| #endif |
| |
| #include <pthread.h> |
| #include <string.h> /* for memset() */ |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/uio.h> |
| #include <sys/file.h> |
| #include <sys/ioctl.h> |
| #if defined(DARWIN) |
| #include <sys/utsname.h> /* for uname */ |
| #endif |
| #if defined(SOLARIS) || defined(UNIXWARE) |
| #include <sys/filio.h> /* to pick up FIONREAD */ |
| #endif |
| #ifdef _PR_POLL_AVAILABLE |
| #include <poll.h> |
| #endif |
| #ifdef AIX |
| /* To pick up sysconf() */ |
| #include <unistd.h> |
| #include <dlfcn.h> /* for dlopen */ |
| #else |
| /* To pick up getrlimit() etc. */ |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #endif |
| |
| #ifdef SOLARIS |
| /* |
| * Define HAVE_SENDFILEV if the system has the sendfilev() system call. |
| * Code built this way won't run on a system without sendfilev(). |
| * We can define HAVE_SENDFILEV by default when the minimum release |
| * of Solaris that NSPR supports has sendfilev(). |
| */ |
| #ifdef HAVE_SENDFILEV |
| |
| #include <sys/sendfile.h> |
| |
| #define SOLARIS_SENDFILEV(a, b, c, d) sendfilev((a), (b), (c), (d)) |
| |
| #else |
| |
| #include <dlfcn.h> /* for dlopen */ |
| |
| /* |
| * Match the definitions in <sys/sendfile.h>. |
| */ |
| typedef struct sendfilevec { |
| int sfv_fd; /* input fd */ |
| uint_t sfv_flag; /* flags */ |
| off_t sfv_off; /* offset to start reading from */ |
| size_t sfv_len; /* amount of data */ |
| } sendfilevec_t; |
| |
| #define SFV_FD_SELF (-2) |
| |
| /* |
| * extern ssize_t sendfilev(int, const struct sendfilevec *, int, size_t *); |
| */ |
| static ssize_t (*pt_solaris_sendfilev_fptr)() = NULL; |
| |
| #define SOLARIS_SENDFILEV(a, b, c, d) \ |
| (*pt_solaris_sendfilev_fptr)((a), (b), (c), (d)) |
| |
| #endif /* HAVE_SENDFILEV */ |
| #endif /* SOLARIS */ |
| |
| /* |
| * The send_file() system call is available in AIX 4.3.2 or later. |
| * If this file is compiled on an older AIX system, it attempts to |
| * look up the send_file symbol at run time to determine whether |
| * we can use the faster PR_SendFile/PR_TransmitFile implementation based on |
| * send_file(). On AIX 4.3.2 or later, we can safely skip this |
| * runtime function dispatching and just use the send_file based |
| * implementation. |
| */ |
| #ifdef AIX |
| #ifdef SF_CLOSE |
| #define HAVE_SEND_FILE |
| #endif |
| |
| #ifdef HAVE_SEND_FILE |
| |
| #define AIX_SEND_FILE(a, b, c) send_file(a, b, c) |
| |
| #else /* HAVE_SEND_FILE */ |
| |
| /* |
| * The following definitions match those in <sys/socket.h> |
| * on AIX 4.3.2. |
| */ |
| |
| /* |
| * Structure for the send_file() system call |
| */ |
| struct sf_parms { |
| /* --------- header parms ---------- */ |
| void *header_data; /* Input/Output. Points to header buf */ |
| uint_t header_length; /* Input/Output. Length of the header */ |
| /* --------- file parms ------------ */ |
| int file_descriptor; /* Input. File descriptor of the file */ |
| unsigned long long file_size; /* Output. Size of the file */ |
| unsigned long long file_offset; /* Input/Output. Starting offset */ |
| long long file_bytes; /* Input/Output. no. of bytes to send */ |
| /* --------- trailer parms --------- */ |
| void *trailer_data; /* Input/Output. Points to trailer buf */ |
| uint_t trailer_length; /* Input/Output. Length of the trailer */ |
| /* --------- return info ----------- */ |
| unsigned long long bytes_sent; /* Output. no. of bytes sent */ |
| }; |
| |
| /* |
| * Flags for the send_file() system call |
| */ |
| #define SF_CLOSE 0x00000001 /* close the socket after completion */ |
| #define SF_REUSE 0x00000002 /* reuse socket. not supported */ |
| #define SF_DONT_CACHE 0x00000004 /* don't apply network buffer cache */ |
| #define SF_SYNC_CACHE 0x00000008 /* sync/update network buffer cache */ |
| |
| /* |
| * prototype: size_t send_file(int *, struct sf_parms *, uint_t); |
| */ |
| static ssize_t (*pt_aix_sendfile_fptr)() = NULL; |
| |
| #define AIX_SEND_FILE(a, b, c) (*pt_aix_sendfile_fptr)(a, b, c) |
| |
| #endif /* HAVE_SEND_FILE */ |
| #endif /* AIX */ |
| |
| #ifdef LINUX |
| #include <sys/sendfile.h> |
| #endif |
| |
| #include "primpl.h" |
| |
| #ifdef HAVE_NETINET_TCP_H |
| #include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ |
| #endif |
| |
| #ifdef LINUX |
| /* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */ |
| #ifndef TCP_CORK |
| #define TCP_CORK 3 |
| #endif |
| #endif |
| |
| #ifdef _PR_IPV6_V6ONLY_PROBE |
| static PRBool _pr_ipv6_v6only_on_by_default; |
| #endif |
| |
| #if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11)) |
| #define _PRSelectFdSetArg_t int * |
| #elif defined(AIX4_1) |
| #define _PRSelectFdSetArg_t void * |
| #elif defined(IRIX) || (defined(AIX) && !defined(AIX4_1)) \ |
| || defined(OSF1) || defined(SOLARIS) \ |
| || defined(HPUX10_30) || defined(HPUX11) \ |
| || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ |
| || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \ |
| || defined(BSDI) || defined(NTO) || defined(DARWIN) \ |
| || defined(UNIXWARE) || defined(RISCOS) || defined(SYMBIAN) |
| #define _PRSelectFdSetArg_t fd_set * |
| #else |
| #error "Cannot determine architecture" |
| #endif |
| |
| static PRFileDesc *pt_SetMethods( |
| PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported); |
| |
| static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */ |
| static PRCondVar *_pr_flock_cv; /* For PR_LockFile() etc. */ |
| static PRLock *_pr_rename_lock; /* For PR_Rename() */ |
| |
| /**************************************************************************/ |
| |
| /* These two functions are only used in assertions. */ |
| #if defined(DEBUG) |
| |
| PRBool IsValidNetAddr(const PRNetAddr *addr) |
| { |
| if ((addr != NULL) |
| && (addr->raw.family != AF_UNIX) |
| && (addr->raw.family != PR_AF_INET6) |
| && (addr->raw.family != AF_INET)) { |
| return PR_FALSE; |
| } |
| return PR_TRUE; |
| } |
| |
| static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len) |
| { |
| /* |
| * The definition of the length of a Unix domain socket address |
| * is not uniform, so we don't check it. |
| */ |
| if ((addr != NULL) |
| && (addr->raw.family != AF_UNIX) |
| && (PR_NETADDR_SIZE(addr) != addr_len)) { |
| #if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 |
| /* |
| * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2 |
| * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id |
| * field and is 28 bytes. It is possible for socket functions |
| * to return an addr_len greater than sizeof(struct sockaddr_in6). |
| * We need to allow that. (Bugzilla bug #77264) |
| */ |
| if ((PR_AF_INET6 == addr->raw.family) |
| && (sizeof(addr->ipv6) == addr_len)) { |
| return PR_TRUE; |
| } |
| #endif |
| return PR_FALSE; |
| } |
| return PR_TRUE; |
| } |
| |
| #endif /* DEBUG */ |
| |
| /*****************************************************************************/ |
| /************************* I/O Continuation machinery ************************/ |
| /*****************************************************************************/ |
| |
| /* |
| * The polling interval defines the maximum amount of time that a thread |
| * might hang up before an interrupt is noticed. |
| */ |
| #define PT_DEFAULT_POLL_MSEC 5000 |
| #if defined(_PR_POLL_WITH_SELECT) |
| #define PT_DEFAULT_SELECT_SEC (PT_DEFAULT_POLL_MSEC/PR_MSEC_PER_SEC) |
| #define PT_DEFAULT_SELECT_USEC \ |
| ((PT_DEFAULT_POLL_MSEC % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC) |
| #endif |
| |
| /* |
| * pt_SockLen is the type for the length of a socket address |
| * structure, used in the address length argument to bind, |
| * connect, accept, getsockname, getpeername, etc. Posix.1g |
| * defines this type as socklen_t. It is size_t or int on |
| * most current systems. |
| */ |
| #if defined(HAVE_SOCKLEN_T) \ |
| || (defined(__GLIBC__) && __GLIBC__ >= 2) |
| typedef socklen_t pt_SockLen; |
| #elif (defined(AIX) && !defined(AIX4_1)) |
| typedef PRSize pt_SockLen; |
| #else |
| typedef PRIntn pt_SockLen; |
| #endif |
| |
| typedef struct pt_Continuation pt_Continuation; |
| typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents); |
| |
| typedef enum pr_ContuationStatus |
| { |
| pt_continuation_pending, |
| pt_continuation_done |
| } pr_ContuationStatus; |
| |
| struct pt_Continuation |
| { |
| /* The building of the continuation operation */ |
| ContinuationFn function; /* what function to continue */ |
| union { PRIntn osfd; } arg1; /* #1 - the op's fd */ |
| union { void* buffer; } arg2; /* #2 - primary transfer buffer */ |
| union { |
| PRSize amount; /* #3 - size of 'buffer', or */ |
| pt_SockLen *addr_len; /* - length of address */ |
| #ifdef HPUX11 |
| /* |
| * For sendfile() |
| */ |
| struct file_spec { |
| off_t offset; /* offset in file to send */ |
| size_t nbytes; /* length of file data to send */ |
| size_t st_size; /* file size */ |
| } file_spec; |
| #endif |
| } arg3; |
| union { PRIntn flags; } arg4; /* #4 - read/write flags */ |
| union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */ |
| |
| #ifdef HPUX11 |
| /* |
| * For sendfile() |
| */ |
| int filedesc; /* descriptor of file to send */ |
| int nbytes_to_send; /* size of header and file */ |
| #endif /* HPUX11 */ |
| |
| #ifdef SOLARIS |
| /* |
| * For sendfilev() |
| */ |
| int nbytes_to_send; /* size of header and file */ |
| #endif /* SOLARIS */ |
| |
| #ifdef LINUX |
| /* |
| * For sendfile() |
| */ |
| int in_fd; /* descriptor of file to send */ |
| off_t offset; |
| size_t count; |
| #endif /* LINUX */ |
| |
| PRIntervalTime timeout; /* client (relative) timeout */ |
| |
| PRInt16 event; /* flags for poll()'s events */ |
| |
| /* |
| ** The representation and notification of the results of the operation. |
| ** These function can either return an int return code or a pointer to |
| ** some object. |
| */ |
| union { PRSize code; void *object; } result; |
| |
| PRIntn syserrno; /* in case it failed, why (errno) */ |
| pr_ContuationStatus status; /* the status of the operation */ |
| }; |
| |
| #if defined(DEBUG) |
| |
| PTDebug pt_debug; /* this is shared between several modules */ |
| |
| PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) |
| { |
| PTDebug stats; |
| char buffer[100]; |
| PRExplodedTime tod; |
| PRInt64 elapsed, aMil; |
| stats = pt_debug; /* a copy */ |
| PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod); |
| (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod); |
| |
| LL_SUB(elapsed, PR_Now(), stats.timeStarted); |
| LL_I2L(aMil, 1000000); |
| LL_DIV(elapsed, elapsed, aMil); |
| |
| if (NULL != msg) PR_fprintf(debug_out, "%s", msg); |
| PR_fprintf( |
| debug_out, "\tstarted: %s[%lld]\n", buffer, elapsed); |
| PR_fprintf( |
| debug_out, "\tlocks [created: %u, destroyed: %u]\n", |
| stats.locks_created, stats.locks_destroyed); |
| PR_fprintf( |
| debug_out, "\tlocks [acquired: %u, released: %u]\n", |
| stats.locks_acquired, stats.locks_released); |
| PR_fprintf( |
| debug_out, "\tcvars [created: %u, destroyed: %u]\n", |
| stats.cvars_created, stats.cvars_destroyed); |
| PR_fprintf( |
| debug_out, "\tcvars [notified: %u, delayed_delete: %u]\n", |
| stats.cvars_notified, stats.delayed_cv_deletes); |
| } /* PT_FPrintStats */ |
| |
| #else |
| |
| PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) |
| { |
| /* do nothing */ |
| } /* PT_FPrintStats */ |
| |
| #endif /* DEBUG */ |
| |
| #if defined(_PR_POLL_WITH_SELECT) |
| /* |
| * OSF1 and HPUX report the POLLHUP event for a socket when the |
| * shutdown(SHUT_WR) operation is called for the remote end, even though |
| * the socket is still writeable. Use select(), instead of poll(), to |
| * workaround this problem. |
| */ |
| static void pt_poll_now_with_select(pt_Continuation *op) |
| { |
| PRInt32 msecs; |
| fd_set rd, wr, *rdp, *wrp; |
| struct timeval tv; |
| PRIntervalTime epoch, now, elapsed, remaining; |
| PRBool wait_for_remaining; |
| PRThread *self = PR_GetCurrentThread(); |
| |
| PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); |
| PR_ASSERT(op->arg1.osfd < FD_SETSIZE); |
| |
| switch (op->timeout) { |
| case PR_INTERVAL_NO_TIMEOUT: |
| tv.tv_sec = PT_DEFAULT_SELECT_SEC; |
| tv.tv_usec = PT_DEFAULT_SELECT_USEC; |
| do |
| { |
| PRIntn rv; |
| |
| if (op->event & POLLIN) { |
| FD_ZERO(&rd); |
| FD_SET(op->arg1.osfd, &rd); |
| rdp = &rd; |
| } else |
| rdp = NULL; |
| if (op->event & POLLOUT) { |
| FD_ZERO(&wr); |
| FD_SET(op->arg1.osfd, &wr); |
| wrp = ≀ |
| } else |
| wrp = NULL; |
| |
| rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); |
| |
| if (_PT_THREAD_INTERRUPTED(self)) |
| { |
| self->state &= ~PT_THREAD_ABORTED; |
| op->result.code = -1; |
| op->syserrno = EINTR; |
| op->status = pt_continuation_done; |
| return; |
| } |
| |
| if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) |
| continue; /* go around the loop again */ |
| |
| if (rv > 0) |
| { |
| PRInt16 revents = 0; |
| |
| if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) |
| revents |= POLLIN; |
| if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) |
| revents |= POLLOUT; |
| |
| if (op->function(op, revents)) |
| op->status = pt_continuation_done; |
| } else if (rv == -1) { |
| op->result.code = -1; |
| op->syserrno = errno; |
| op->status = pt_continuation_done; |
| } |
| /* else, select timed out */ |
| } while (pt_continuation_done != op->status); |
| break; |
| default: |
| now = epoch = PR_IntervalNow(); |
| remaining = op->timeout; |
| do |
| { |
| PRIntn rv; |
| |
| if (op->event & POLLIN) { |
| FD_ZERO(&rd); |
| FD_SET(op->arg1.osfd, &rd); |
| rdp = &rd; |
| } else |
| rdp = NULL; |
| if (op->event & POLLOUT) { |
| FD_ZERO(&wr); |
| FD_SET(op->arg1.osfd, &wr); |
| wrp = ≀ |
| } else |
| wrp = NULL; |
| |
| wait_for_remaining = PR_TRUE; |
| msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); |
| if (msecs > PT_DEFAULT_POLL_MSEC) { |
| wait_for_remaining = PR_FALSE; |
| msecs = PT_DEFAULT_POLL_MSEC; |
| } |
| tv.tv_sec = msecs/PR_MSEC_PER_SEC; |
| tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC; |
| rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); |
| |
| if (_PT_THREAD_INTERRUPTED(self)) |
| { |
| self->state &= ~PT_THREAD_ABORTED; |
| op->result.code = -1; |
| op->syserrno = EINTR; |
| op->status = pt_continuation_done; |
| return; |
| } |
| |
| if (rv > 0) { |
| PRInt16 revents = 0; |
| |
| if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) |
| revents |= POLLIN; |
| if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) |
| revents |= POLLOUT; |
| |
| if (op->function(op, revents)) |
| op->status = pt_continuation_done; |
| |
| } else if ((rv == 0) || |
| ((errno == EINTR) || (errno == EAGAIN))) { |
| if (rv == 0) { /* select timed out */ |
| if (wait_for_remaining) |
| now += remaining; |
| else |
| now += PR_MillisecondsToInterval(msecs); |
| } else |
| now = PR_IntervalNow(); |
| elapsed = (PRIntervalTime) (now - epoch); |
| if (elapsed >= op->timeout) { |
| op->result.code = -1; |
| op->syserrno = ETIMEDOUT; |
| op->status = pt_continuation_done; |
| } else |
| remaining = op->timeout - elapsed; |
| } else { |
| op->result.code = -1; |
| op->syserrno = errno; |
| op->status = pt_continuation_done; |
| } |
| } while (pt_continuation_done != op->status); |
| break; |
| } |
| |
| } /* pt_poll_now_with_select */ |
| |
| #endif /* _PR_POLL_WITH_SELECT */ |
| |
| static void pt_poll_now(pt_Continuation *op) |
| { |
| PRInt32 msecs; |
| PRIntervalTime epoch, now, elapsed, remaining; |
| PRBool wait_for_remaining; |
| PRThread *self = PR_GetCurrentThread(); |
| |
| PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); |
| #if defined (_PR_POLL_WITH_SELECT) |
| /* |
| * If the fd is small enough call the select-based poll operation |
| */ |
| if (op->arg1.osfd < FD_SETSIZE) { |
| pt_poll_now_with_select(op); |
| return; |
| } |
| #endif |
| |
| switch (op->timeout) { |
| case PR_INTERVAL_NO_TIMEOUT: |
| msecs = PT_DEFAULT_POLL_MSEC; |
| do |
| { |
| PRIntn rv; |
| struct pollfd tmp_pfd; |
| |
| tmp_pfd.revents = 0; |
| tmp_pfd.fd = op->arg1.osfd; |
| tmp_pfd.events = op->event; |
| |
| rv = poll(&tmp_pfd, 1, msecs); |
| |
| if (_PT_THREAD_INTERRUPTED(self)) |
| { |
| self->state &= ~PT_THREAD_ABORTED; |
| op->result.code = -1; |
| op->syserrno = EINTR; |
| op->status = pt_continuation_done; |
| return; |
| } |
| |
| if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) |
| continue; /* go around the loop again */ |
| |
| if (rv > 0) |
| { |
| PRInt16 events = tmp_pfd.events; |
| PRInt16 revents = tmp_pfd.revents; |
| |
| if ((revents & POLLNVAL) /* busted in all cases */ |
| || ((events & POLLOUT) && (revents & POLLHUP))) |
| /* write op & hup */ |
| { |
| op->result.code = -1; |
| if (POLLNVAL & revents) op->syserrno = EBADF; |
| else if (POLLHUP & revents) op->syserrno = EPIPE; |
| op->status = pt_continuation_done; |
| } else { |
| if (op->function(op, revents)) |
| op->status = pt_continuation_done; |
| } |
| } else if (rv == -1) { |
| op->result.code = -1; |
| op->syserrno = errno; |
| op->status = pt_continuation_done; |
| } |
| /* else, poll timed out */ |
| } while (pt_continuation_done != op->status); |
| break; |
| default: |
| now = epoch = PR_IntervalNow(); |
| remaining = op->timeout; |
| do |
| { |
| PRIntn rv; |
| struct pollfd tmp_pfd; |
| |
| tmp_pfd.revents = 0; |
| tmp_pfd.fd = op->arg1.osfd; |
| tmp_pfd.events = op->event; |
| |
| wait_for_remaining = PR_TRUE; |
| msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); |
| if (msecs > PT_DEFAULT_POLL_MSEC) |
| { |
| wait_for_remaining = PR_FALSE; |
| msecs = PT_DEFAULT_POLL_MSEC; |
| } |
| rv = poll(&tmp_pfd, 1, msecs); |
| |
| if (_PT_THREAD_INTERRUPTED(self)) |
| { |
| self->state &= ~PT_THREAD_ABORTED; |
| op->result.code = -1; |
| op->syserrno = EINTR; |
| op->status = pt_continuation_done; |
| return; |
| } |
| |
| if (rv > 0) |
| { |
| PRInt16 events = tmp_pfd.events; |
| PRInt16 revents = tmp_pfd.revents; |
| |
| if ((revents & POLLNVAL) /* busted in all cases */ |
| || ((events & POLLOUT) && (revents & POLLHUP))) |
| /* write op & hup */ |
| { |
| op->result.code = -1; |
| if (POLLNVAL & revents) op->syserrno = EBADF; |
| else if (POLLHUP & revents) op->syserrno = EPIPE; |
| op->status = pt_continuation_done; |
| } else { |
| if (op->function(op, revents)) |
| { |
| op->status = pt_continuation_done; |
| } |
| } |
| } else if ((rv == 0) || |
| ((errno == EINTR) || (errno == EAGAIN))) { |
| if (rv == 0) /* poll timed out */ |
| { |
| if (wait_for_remaining) |
| now += remaining; |
| else |
| now += PR_MillisecondsToInterval(msecs); |
| } |
| else |
| now = PR_IntervalNow(); |
| elapsed = (PRIntervalTime) (now - epoch); |
| if (elapsed >= op->timeout) { |
| op->result.code = -1; |
| op->syserrno = ETIMEDOUT; |
| op->status = pt_continuation_done; |
| } else |
| remaining = op->timeout - elapsed; |
| } else { |
| op->result.code = -1; |
| op->syserrno = errno; |
| op->status = pt_continuation_done; |
| } |
| } while (pt_continuation_done != op->status); |
| break; |
| } |
| |
| } /* pt_poll_now */ |
| |
| static PRIntn pt_Continue(pt_Continuation *op) |
| { |
| op->status = pt_continuation_pending; /* set default value */ |
| /* |
| * let each thread call poll directly |
| */ |
| pt_poll_now(op); |
| PR_ASSERT(pt_continuation_done == op->status); |
| return op->result.code; |
| } /* pt_Continue */ |
| |
| /*****************************************************************************/ |
| /*********************** specific continuation functions *********************/ |
| /*****************************************************************************/ |
| static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd); |
| if (op->syserrno != 0) { |
| op->result.code = -1; |
| } else { |
| op->result.code = 0; |
| } |
| return PR_TRUE; /* this one is cooked */ |
| } /* pt_connect_cont */ |
| |
| static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| op->syserrno = 0; |
| op->result.code = accept( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len); |
| if (-1 == op->result.code) |
| { |
| op->syserrno = errno; |
| if (EWOULDBLOCK == errno || EAGAIN == errno || ECONNABORTED == errno) |
| return PR_FALSE; /* do nothing - this one ain't finished */ |
| } |
| return PR_TRUE; |
| } /* pt_accept_cont */ |
| |
| static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| /* |
| * Any number of bytes will complete the operation. It need |
| * not (and probably will not) satisfy the request. The only |
| * error we continue is EWOULDBLOCK|EAGAIN. |
| */ |
| op->result.code = read( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount); |
| op->syserrno = errno; |
| return ((-1 == op->result.code) && |
| (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? |
| PR_FALSE : PR_TRUE; |
| } /* pt_read_cont */ |
| |
| static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| /* |
| * Any number of bytes will complete the operation. It need |
| * not (and probably will not) satisfy the request. The only |
| * error we continue is EWOULDBLOCK|EAGAIN. |
| */ |
| #if defined(SOLARIS) |
| if (0 == op->arg4.flags) |
| op->result.code = read( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount); |
| else |
| op->result.code = recv( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); |
| #else |
| op->result.code = recv( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); |
| #endif |
| op->syserrno = errno; |
| return ((-1 == op->result.code) && |
| (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? |
| PR_FALSE : PR_TRUE; |
| } /* pt_recv_cont */ |
| |
| static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| PRIntn bytes; |
| #if defined(SOLARIS) |
| PRInt32 tmp_amount = op->arg3.amount; |
| #endif |
| /* |
| * We want to write the entire amount out, no matter how many |
| * tries it takes. Keep advancing the buffer and the decrementing |
| * the amount until the amount goes away. Return the total bytes |
| * (which should be the original amount) when finished (or an |
| * error). |
| */ |
| #if defined(SOLARIS) |
| retry: |
| bytes = write(op->arg1.osfd, op->arg2.buffer, tmp_amount); |
| #else |
| bytes = send( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); |
| #endif |
| op->syserrno = errno; |
| |
| #if defined(SOLARIS) |
| /* |
| * The write system call has been reported to return the ERANGE error |
| * on occasion. Try to write in smaller chunks to workaround this bug. |
| */ |
| if ((bytes == -1) && (op->syserrno == ERANGE)) |
| { |
| if (tmp_amount > 1) |
| { |
| tmp_amount = tmp_amount/2; /* half the bytes */ |
| goto retry; |
| } |
| } |
| #endif |
| |
| if (bytes >= 0) /* this is progress */ |
| { |
| char *bp = (char*)op->arg2.buffer; |
| bp += bytes; /* adjust the buffer pointer */ |
| op->arg2.buffer = bp; |
| op->result.code += bytes; /* accumulate the number sent */ |
| op->arg3.amount -= bytes; /* and reduce the required count */ |
| return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; |
| } |
| else if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) |
| { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| else return PR_FALSE; |
| } /* pt_send_cont */ |
| |
| static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| PRIntn bytes; |
| /* |
| * We want to write the entire amount out, no matter how many |
| * tries it takes. Keep advancing the buffer and the decrementing |
| * the amount until the amount goes away. Return the total bytes |
| * (which should be the original amount) when finished (or an |
| * error). |
| */ |
| bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount); |
| op->syserrno = errno; |
| if (bytes >= 0) /* this is progress */ |
| { |
| char *bp = (char*)op->arg2.buffer; |
| bp += bytes; /* adjust the buffer pointer */ |
| op->arg2.buffer = bp; |
| op->result.code += bytes; /* accumulate the number sent */ |
| op->arg3.amount -= bytes; /* and reduce the required count */ |
| return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; |
| } |
| else if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) |
| { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| else return PR_FALSE; |
| } /* pt_write_cont */ |
| |
| static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| PRIntn bytes; |
| struct iovec *iov = (struct iovec*)op->arg2.buffer; |
| /* |
| * Same rules as write, but continuing seems to be a bit more |
| * complicated. As the number of bytes sent grows, we have to |
| * redefine the vector we're pointing at. We might have to |
| * modify an individual vector parms or we might have to eliminate |
| * a pair altogether. |
| */ |
| bytes = writev(op->arg1.osfd, iov, op->arg3.amount); |
| op->syserrno = errno; |
| if (bytes >= 0) /* this is progress */ |
| { |
| PRIntn iov_index; |
| op->result.code += bytes; /* accumulate the number sent */ |
| for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index) |
| { |
| /* how much progress did we make in the i/o vector? */ |
| if (bytes < iov[iov_index].iov_len) |
| { |
| /* this element's not done yet */ |
| char **bp = (char**)&(iov[iov_index].iov_base); |
| iov[iov_index].iov_len -= bytes; /* there's that much left */ |
| *bp += bytes; /* starting there */ |
| break; /* go off and do that */ |
| } |
| bytes -= iov[iov_index].iov_len; /* that element's consumed */ |
| } |
| op->arg2.buffer = &iov[iov_index]; /* new start of array */ |
| op->arg3.amount -= iov_index; /* and array length */ |
| return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; |
| } |
| else if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) |
| { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| else return PR_FALSE; |
| } /* pt_writev_cont */ |
| |
| static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| PRIntn bytes = sendto( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags, |
| (struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr)); |
| op->syserrno = errno; |
| if (bytes >= 0) /* this is progress */ |
| { |
| char *bp = (char*)op->arg2.buffer; |
| bp += bytes; /* adjust the buffer pointer */ |
| op->arg2.buffer = bp; |
| op->result.code += bytes; /* accumulate the number sent */ |
| op->arg3.amount -= bytes; /* and reduce the required count */ |
| return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; |
| } |
| else if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) |
| { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| else return PR_FALSE; |
| } /* pt_sendto_cont */ |
| |
| static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| pt_SockLen addr_len = sizeof(PRNetAddr); |
| op->result.code = recvfrom( |
| op->arg1.osfd, op->arg2.buffer, op->arg3.amount, |
| op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len); |
| op->syserrno = errno; |
| return ((-1 == op->result.code) && |
| (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? |
| PR_FALSE : PR_TRUE; |
| } /* pt_recvfrom_cont */ |
| |
| #ifdef AIX |
| static PRBool pt_aix_sendfile_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| struct sf_parms *sf_struct = (struct sf_parms *) op->arg2.buffer; |
| ssize_t rv; |
| unsigned long long saved_file_offset; |
| long long saved_file_bytes; |
| |
| saved_file_offset = sf_struct->file_offset; |
| saved_file_bytes = sf_struct->file_bytes; |
| sf_struct->bytes_sent = 0; |
| |
| if ((sf_struct->file_bytes > 0) && (sf_struct->file_size > 0)) |
| PR_ASSERT((sf_struct->file_bytes + sf_struct->file_offset) <= |
| sf_struct->file_size); |
| rv = AIX_SEND_FILE(&op->arg1.osfd, sf_struct, op->arg4.flags); |
| op->syserrno = errno; |
| |
| if (rv != -1) { |
| op->result.code += sf_struct->bytes_sent; |
| /* |
| * A bug in AIX 4.3.2 prevents the 'file_bytes' field from |
| * being updated. So, 'file_bytes' is maintained by NSPR to |
| * avoid conflict when this bug is fixed in AIX, in the future. |
| */ |
| if (saved_file_bytes != -1) |
| saved_file_bytes -= (sf_struct->file_offset - saved_file_offset); |
| sf_struct->file_bytes = saved_file_bytes; |
| } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { |
| op->result.code = -1; |
| } else { |
| return PR_FALSE; |
| } |
| |
| if (rv == 1) { /* more data to send */ |
| return PR_FALSE; |
| } |
| |
| return PR_TRUE; |
| } |
| #endif /* AIX */ |
| |
| #ifdef HPUX11 |
| static PRBool pt_hpux_sendfile_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| struct iovec *hdtrl = (struct iovec *) op->arg2.buffer; |
| int count; |
| |
| count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.file_spec.offset, |
| op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags); |
| PR_ASSERT(count <= op->nbytes_to_send); |
| op->syserrno = errno; |
| |
| if (count != -1) { |
| op->result.code += count; |
| } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { |
| op->result.code = -1; |
| } else { |
| return PR_FALSE; |
| } |
| if (count != -1 && count < op->nbytes_to_send) { |
| if (count < hdtrl[0].iov_len) { |
| /* header not sent */ |
| |
| hdtrl[0].iov_base = ((char *) hdtrl[0].iov_base) + count; |
| hdtrl[0].iov_len -= count; |
| |
| } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes)) { |
| /* header sent, file not sent */ |
| PRUint32 file_nbytes_sent = count - hdtrl[0].iov_len; |
| |
| hdtrl[0].iov_base = NULL; |
| hdtrl[0].iov_len = 0; |
| |
| op->arg3.file_spec.offset += file_nbytes_sent; |
| op->arg3.file_spec.nbytes -= file_nbytes_sent; |
| } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes + |
| hdtrl[1].iov_len)) { |
| PRUint32 trailer_nbytes_sent = count - (hdtrl[0].iov_len + |
| op->arg3.file_spec.nbytes); |
| |
| /* header sent, file sent, trailer not sent */ |
| |
| hdtrl[0].iov_base = NULL; |
| hdtrl[0].iov_len = 0; |
| /* |
| * set file offset and len so that no more file data is |
| * sent |
| */ |
| op->arg3.file_spec.offset = op->arg3.file_spec.st_size; |
| op->arg3.file_spec.nbytes = 0; |
| |
| hdtrl[1].iov_base =((char *) hdtrl[1].iov_base)+ trailer_nbytes_sent; |
| hdtrl[1].iov_len -= trailer_nbytes_sent; |
| } |
| op->nbytes_to_send -= count; |
| return PR_FALSE; |
| } |
| |
| return PR_TRUE; |
| } |
| #endif /* HPUX11 */ |
| |
| #ifdef SOLARIS |
| static PRBool pt_solaris_sendfile_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| struct sendfilevec *vec = (struct sendfilevec *) op->arg2.buffer; |
| size_t xferred; |
| ssize_t count; |
| |
| count = SOLARIS_SENDFILEV(op->arg1.osfd, vec, op->arg3.amount, &xferred); |
| op->syserrno = errno; |
| PR_ASSERT((count == -1) || (count == xferred)); |
| |
| if (count == -1) { |
| if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN |
| && op->syserrno != EINTR) { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| count = xferred; |
| } else if (count == 0) { |
| /* |
| * We are now at EOF. The file was truncated. Solaris sendfile is |
| * supposed to return 0 and no error in this case, though some versions |
| * may return -1 and EINVAL . |
| */ |
| op->result.code = -1; |
| op->syserrno = 0; /* will be treated as EOF */ |
| return PR_TRUE; |
| } |
| PR_ASSERT(count <= op->nbytes_to_send); |
| |
| op->result.code += count; |
| if (count < op->nbytes_to_send) { |
| op->nbytes_to_send -= count; |
| |
| while (count >= vec->sfv_len) { |
| count -= vec->sfv_len; |
| vec++; |
| op->arg3.amount--; |
| } |
| PR_ASSERT(op->arg3.amount > 0); |
| |
| vec->sfv_off += count; |
| vec->sfv_len -= count; |
| PR_ASSERT(vec->sfv_len > 0); |
| op->arg2.buffer = vec; |
| |
| return PR_FALSE; |
| } |
| |
| return PR_TRUE; |
| } |
| #endif /* SOLARIS */ |
| |
| #ifdef LINUX |
| static PRBool pt_linux_sendfile_cont(pt_Continuation *op, PRInt16 revents) |
| { |
| ssize_t rv; |
| off_t oldoffset; |
| |
| oldoffset = op->offset; |
| rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count); |
| op->syserrno = errno; |
| |
| if (rv == -1) { |
| if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { |
| op->result.code = -1; |
| return PR_TRUE; |
| } |
| rv = 0; |
| } |
| PR_ASSERT(rv == op->offset - oldoffset); |
| op->result.code += rv; |
| if (rv < op->count) { |
| op->count -= rv; |
| return PR_FALSE; |
| } |
| return PR_TRUE; |
| } |
| #endif /* LINUX */ |
| |
| void _PR_InitIO(void) |
| { |
| #if defined(DEBUG) |
| memset(&pt_debug, 0, sizeof(PTDebug)); |
| pt_debug.timeStarted = PR_Now(); |
| #endif |
| |
| _pr_flock_lock = PR_NewLock(); |
| PR_ASSERT(NULL != _pr_flock_lock); |
| _pr_flock_cv = PR_NewCondVar(_pr_flock_lock); |
| PR_ASSERT(NULL != _pr_flock_cv); |
| _pr_rename_lock = PR_NewLock(); |
| PR_ASSERT(NULL != _pr_rename_lock); |
| |
| _PR_InitFdCache(); /* do that */ |
| |
| _pr_stdin = pt_SetMethods(0, PR_DESC_FILE, PR_FALSE, PR_TRUE); |
| _pr_stdout = pt_SetMethods(1, PR_DESC_FILE, PR_FALSE, PR_TRUE); |
| _pr_stderr = pt_SetMethods(2, PR_DESC_FILE, PR_FALSE, PR_TRUE); |
| PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr); |
| |
| #ifdef _PR_IPV6_V6ONLY_PROBE |
| /* In Mac OS X v10.3 Panther Beta the IPV6_V6ONLY socket option |
| * is turned on by default, contrary to what RFC 3493, Section |
| * 5.3 says. So we have to turn it off. Find out whether we |
| * are running on such a system. |
| */ |
| { |
| int osfd; |
| osfd = socket(AF_INET6, SOCK_STREAM, 0); |
| if (osfd != -1) { |
| int on; |
| int optlen = sizeof(on); |
| if (getsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, |
| &on, &optlen) == 0) { |
| _pr_ipv6_v6only_on_by_default = on; |
| } |
| close(osfd); |
| } |
| } |
| #endif |
| } /* _PR_InitIO */ |
| |
| void _PR_CleanupIO(void) |
| { |
| _PR_Putfd(_pr_stdin); |
| _pr_stdin = NULL; |
| _PR_Putfd(_pr_stdout); |
| _pr_stdout = NULL; |
| _PR_Putfd(_pr_stderr); |
| _pr_stderr = NULL; |
| |
| _PR_CleanupFdCache(); |
| |
| if (_pr_flock_cv) |
| { |
| PR_DestroyCondVar(_pr_flock_cv); |
| _pr_flock_cv = NULL; |
| } |
| if (_pr_flock_lock) |
| { |
| PR_DestroyLock(_pr_flock_lock); |
| _pr_flock_lock = NULL; |
| } |
| if (_pr_rename_lock) |
| { |
| PR_DestroyLock(_pr_rename_lock); |
| _pr_rename_lock = NULL; |
| } |
| } /* _PR_CleanupIO */ |
| |
| PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) |
| { |
| PRFileDesc *result = NULL; |
| PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError); |
| |
| if (!_pr_initialized) _PR_ImplicitInitialization(); |
| |
| switch (osfd) |
| { |
| case PR_StandardInput: result = _pr_stdin; break; |
| case PR_StandardOutput: result = _pr_stdout; break; |
| case PR_StandardError: result = _pr_stderr; break; |
| default: |
| (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| } |
| return result; |
| } /* PR_GetSpecialFD */ |
| |
| /*****************************************************************************/ |
| /***************************** I/O private methods ***************************/ |
| /*****************************************************************************/ |
| |
| static PRBool pt_TestAbort(void) |
| { |
| PRThread *me = PR_GetCurrentThread(); |
| if(_PT_THREAD_INTERRUPTED(me)) |
| { |
| PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); |
| me->state &= ~PT_THREAD_ABORTED; |
| return PR_TRUE; |
| } |
| return PR_FALSE; |
| } /* pt_TestAbort */ |
| |
| static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno) |
| { |
| switch (syserrno) |
| { |
| case EINTR: |
| PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break; |
| case ETIMEDOUT: |
| PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; |
| default: |
| mapper(syserrno); |
| } |
| } /* pt_MapError */ |
| |
| static PRStatus pt_Close(PRFileDesc *fd) |
| { |
| if ((NULL == fd) || (NULL == fd->secret) |
| || ((_PR_FILEDESC_OPEN != fd->secret->state) |
| && (_PR_FILEDESC_CLOSED != fd->secret->state))) |
| { |
| PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); |
| return PR_FAILURE; |
| } |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| if (_PR_FILEDESC_OPEN == fd->secret->state) |
| { |
| if (-1 == close(fd->secret->md.osfd)) |
| { |
| #ifdef OSF1 |
| /* |
| * Bug 86941: On Tru64 UNIX V5.0A and V5.1, the close() |
| * system call, when called to close a TCP socket, may |
| * return -1 with errno set to EINVAL but the system call |
| * does close the socket successfully. An application |
| * may safely ignore the EINVAL error. This bug is fixed |
| * on Tru64 UNIX V5.1A and later. The defect tracking |
| * number is QAR 81431. |
| */ |
| if (PR_DESC_SOCKET_TCP != fd->methods->file_type |
| || EINVAL != errno) |
| { |
| pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno); |
| return PR_FAILURE; |
| } |
| #else |
| pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno); |
| return PR_FAILURE; |
| #endif |
| } |
| fd->secret->state = _PR_FILEDESC_CLOSED; |
| } |
| _PR_Putfd(fd); |
| return PR_SUCCESS; |
| } /* pt_Close */ |
| |
| static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount) |
| { |
| PRInt32 syserrno, bytes = -1; |
| |
| if (pt_TestAbort()) return bytes; |
| |
| bytes = read(fd->secret->md.osfd, buf, amount); |
| syserrno = errno; |
| |
| if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking)) |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = buf; |
| op.arg3.amount = amount; |
| op.timeout = PR_INTERVAL_NO_TIMEOUT; |
| op.function = pt_read_cont; |
| op.event = POLLIN | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (bytes < 0) |
| pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno); |
| return bytes; |
| } /* pt_Read */ |
| |
| static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) |
| { |
| PRInt32 syserrno, bytes = -1; |
| PRBool fNeedContinue = PR_FALSE; |
| |
| if (pt_TestAbort()) return bytes; |
| |
| bytes = write(fd->secret->md.osfd, buf, amount); |
| syserrno = errno; |
| |
| if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) |
| { |
| buf = (char *) buf + bytes; |
| amount -= bytes; |
| fNeedContinue = PR_TRUE; |
| } |
| if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking) ) |
| { |
| bytes = 0; |
| fNeedContinue = PR_TRUE; |
| } |
| |
| if (fNeedContinue == PR_TRUE) |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = (void*)buf; |
| op.arg3.amount = amount; |
| op.timeout = PR_INTERVAL_NO_TIMEOUT; |
| op.result.code = bytes; /* initialize the number sent */ |
| op.function = pt_write_cont; |
| op.event = POLLOUT | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (bytes == -1) |
| pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno); |
| return bytes; |
| } /* pt_Write */ |
| |
| static PRInt32 pt_Writev( |
| PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout) |
| { |
| PRIntn iov_index; |
| PRBool fNeedContinue = PR_FALSE; |
| PRInt32 syserrno, bytes, rv = -1; |
| struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov; |
| int osiov_len; |
| |
| if (pt_TestAbort()) return rv; |
| |
| /* Ensured by PR_Writev */ |
| PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE); |
| |
| /* |
| * We can't pass iov to writev because PRIOVec and struct iovec |
| * may not be binary compatible. Make osiov a copy of iov and |
| * pass osiov to writev. We can modify osiov if we need to |
| * continue the operation. |
| */ |
| osiov = osiov_local; |
| osiov_len = iov_len; |
| for (iov_index = 0; iov_index < osiov_len; iov_index++) |
| { |
| osiov[iov_index].iov_base = iov[iov_index].iov_base; |
| osiov[iov_index].iov_len = iov[iov_index].iov_len; |
| } |
| |
| rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len); |
| syserrno = errno; |
| |
| if (!fd->secret->nonblocking) |
| { |
| if (bytes >= 0) |
| { |
| /* |
| * If we moved some bytes, how does that implicate the |
| * i/o vector list? In other words, exactly where are |
| * we within that array? What are the parameters for |
| * resumption? Maybe we're done! |
| */ |
| for ( ;osiov_len > 0; osiov++, osiov_len--) |
| { |
| if (bytes < osiov->iov_len) |
| { |
| /* this one's not done yet */ |
| osiov->iov_base = (char*)osiov->iov_base + bytes; |
| osiov->iov_len -= bytes; |
| break; /* go off and do that */ |
| } |
| bytes -= osiov->iov_len; /* this one's done cooked */ |
| } |
| PR_ASSERT(osiov_len > 0 || bytes == 0); |
| if (osiov_len > 0) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) |
| { |
| rv = -1; |
| syserrno = ETIMEDOUT; |
| } |
| else fNeedContinue = PR_TRUE; |
| } |
| } |
| else if (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else |
| { |
| rv = 0; |
| fNeedContinue = PR_TRUE; |
| } |
| } |
| } |
| |
| if (fNeedContinue == PR_TRUE) |
| { |
| pt_Continuation op; |
| |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = (void*)osiov; |
| op.arg3.amount = osiov_len; |
| op.timeout = timeout; |
| op.result.code = rv; |
| op.function = pt_writev_cont; |
| op.event = POLLOUT | POLLPRI; |
| rv = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (rv == -1) pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno); |
| return rv; |
| } /* pt_Writev */ |
| |
| static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) |
| { |
| return _PR_MD_LSEEK(fd, offset, whence); |
| } /* pt_Seek */ |
| |
| static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) |
| { |
| return _PR_MD_LSEEK64(fd, offset, whence); |
| } /* pt_Seek64 */ |
| |
| static PRInt32 pt_Available_f(PRFileDesc *fd) |
| { |
| PRInt32 result, cur, end; |
| |
| cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR); |
| |
| if (cur >= 0) |
| end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END); |
| |
| if ((cur < 0) || (end < 0)) { |
| return -1; |
| } |
| |
| result = end - cur; |
| _PR_MD_LSEEK(fd, cur, PR_SEEK_SET); |
| |
| return result; |
| } /* pt_Available_f */ |
| |
| static PRInt64 pt_Available64_f(PRFileDesc *fd) |
| { |
| PRInt64 result, cur, end; |
| PRInt64 minus_one; |
| |
| LL_I2L(minus_one, -1); |
| cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR); |
| |
| if (LL_GE_ZERO(cur)) |
| end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END); |
| |
| if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) return minus_one; |
| |
| LL_SUB(result, end, cur); |
| (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET); |
| |
| return result; |
| } /* pt_Available64_f */ |
| |
| static PRInt32 pt_Available_s(PRFileDesc *fd) |
| { |
| PRInt32 rv, bytes = -1; |
| if (pt_TestAbort()) return bytes; |
| |
| rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes); |
| |
| if (rv == -1) |
| pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno); |
| return bytes; |
| } /* pt_Available_s */ |
| |
| static PRInt64 pt_Available64_s(PRFileDesc *fd) |
| { |
| PRInt64 rv; |
| LL_I2L(rv, pt_Available_s(fd)); |
| return rv; |
| } /* pt_Available64_s */ |
| |
| static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info) |
| { |
| PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info); |
| return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; |
| } /* pt_FileInfo */ |
| |
| static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info) |
| { |
| PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info); |
| return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; |
| } /* pt_FileInfo64 */ |
| |
| static PRStatus pt_Synch(PRFileDesc *fd) |
| { |
| return (NULL == fd) ? PR_FAILURE : PR_SUCCESS; |
| } /* pt_Synch */ |
| |
| static PRStatus pt_Fsync(PRFileDesc *fd) |
| { |
| PRIntn rv = -1; |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| rv = fsync(fd->secret->md.osfd); |
| if (rv < 0) { |
| pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_Fsync */ |
| |
| static PRStatus pt_Connect( |
| PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) |
| { |
| PRIntn rv = -1, syserrno; |
| pt_SockLen addr_len; |
| const PRNetAddr *addrp = addr; |
| #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) |
| PRUint16 md_af = addr->raw.family; |
| PRNetAddr addrCopy; |
| #endif |
| |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| addr_len = PR_NETADDR_SIZE(addr); |
| #if defined(_PR_INET6) |
| if (addr->raw.family == PR_AF_INET6) { |
| md_af = AF_INET6; |
| #ifndef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| addrCopy.raw.family = AF_INET6; |
| addrp = &addrCopy; |
| #endif |
| } |
| #endif |
| |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| ((struct sockaddr*)&addrCopy)->sa_len = addr_len; |
| ((struct sockaddr*)&addrCopy)->sa_family = md_af; |
| addrp = &addrCopy; |
| #endif |
| rv = connect(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); |
| syserrno = errno; |
| if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking)) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = (void*)addrp; |
| op.arg3.amount = addr_len; |
| op.timeout = timeout; |
| op.function = pt_connect_cont; |
| op.event = POLLOUT | POLLPRI; |
| rv = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| } |
| if (-1 == rv) { |
| pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_Connect */ |
| |
| static PRStatus pt_ConnectContinue( |
| PRFileDesc *fd, PRInt16 out_flags) |
| { |
| int err; |
| PRInt32 osfd; |
| |
| if (out_flags & PR_POLL_NVAL) |
| { |
| PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); |
| return PR_FAILURE; |
| } |
| if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) |
| { |
| PR_ASSERT(out_flags == 0); |
| PR_SetError(PR_IN_PROGRESS_ERROR, 0); |
| return PR_FAILURE; |
| } |
| |
| osfd = fd->secret->md.osfd; |
| |
| err = _MD_unix_get_nonblocking_connect_error(osfd); |
| if (err != 0) |
| { |
| _PR_MD_MAP_CONNECT_ERROR(err); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_ConnectContinue */ |
| |
| PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) |
| { |
| /* Find the NSPR layer and invoke its connectcontinue method */ |
| PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); |
| |
| if (NULL == bottom) |
| { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return PR_FAILURE; |
| } |
| return pt_ConnectContinue(bottom, pd->out_flags); |
| } /* PR_GetConnectStatus */ |
| |
| static PRFileDesc* pt_Accept( |
| PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) |
| { |
| PRFileDesc *newfd = NULL; |
| PRIntn syserrno, osfd = -1; |
| pt_SockLen addr_len = sizeof(PRNetAddr); |
| #ifdef SYMBIAN |
| PRNetAddr dummy_addr; |
| #endif |
| |
| if (pt_TestAbort()) return newfd; |
| |
| #ifdef SYMBIAN |
| /* On Symbian OS, accept crashes if addr is NULL. */ |
| if (!addr) |
| addr = &dummy_addr; |
| #endif |
| |
| #ifdef _PR_STRICT_ADDR_LEN |
| if (addr) |
| { |
| /* |
| * Set addr->raw.family just so that we can use the |
| * PR_NETADDR_SIZE macro. |
| */ |
| addr->raw.family = fd->secret->af; |
| addr_len = PR_NETADDR_SIZE(addr); |
| } |
| #endif |
| |
| osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); |
| syserrno = errno; |
| |
| if (osfd == -1) |
| { |
| if (fd->secret->nonblocking) goto failed; |
| |
| if (EWOULDBLOCK != syserrno && EAGAIN != syserrno |
| && ECONNABORTED != syserrno) |
| goto failed; |
| else |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = addr; |
| op.arg3.addr_len = &addr_len; |
| op.timeout = timeout; |
| op.function = pt_accept_cont; |
| op.event = POLLIN | POLLPRI; |
| osfd = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (osfd < 0) goto failed; |
| } |
| } |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) |
| { |
| addr->raw.family = ((struct sockaddr*)addr)->sa_family; |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| #ifdef _PR_INET6 |
| if (addr && (AF_INET6 == addr->raw.family)) |
| addr->raw.family = PR_AF_INET6; |
| #endif |
| newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_TRUE, PR_FALSE); |
| if (newfd == NULL) close(osfd); /* $$$ whoops! this doesn't work $$$ */ |
| else |
| { |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); |
| #ifdef LINUX |
| /* |
| * On Linux, experiments showed that the accepted sockets |
| * inherit the TCP_NODELAY socket option of the listening |
| * socket. |
| */ |
| newfd->secret->md.tcp_nodelay = fd->secret->md.tcp_nodelay; |
| #endif |
| } |
| return newfd; |
| |
| failed: |
| pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno); |
| return NULL; |
| } /* pt_Accept */ |
| |
| static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr) |
| { |
| PRIntn rv; |
| pt_SockLen addr_len; |
| const PRNetAddr *addrp = addr; |
| #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) |
| PRUint16 md_af = addr->raw.family; |
| PRNetAddr addrCopy; |
| #endif |
| |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| if (addr->raw.family == AF_UNIX) |
| { |
| /* Disallow relative pathnames */ |
| if (addr->local.path[0] != '/') |
| { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return PR_FAILURE; |
| } |
| } |
| |
| #if defined(_PR_INET6) |
| if (addr->raw.family == PR_AF_INET6) { |
| md_af = AF_INET6; |
| #ifndef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| addrCopy.raw.family = AF_INET6; |
| addrp = &addrCopy; |
| #endif |
| } |
| #endif |
| |
| addr_len = PR_NETADDR_SIZE(addr); |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| ((struct sockaddr*)&addrCopy)->sa_len = addr_len; |
| ((struct sockaddr*)&addrCopy)->sa_family = md_af; |
| addrp = &addrCopy; |
| #endif |
| rv = bind(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); |
| |
| if (rv == -1) { |
| pt_MapError(_PR_MD_MAP_BIND_ERROR, errno); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_Bind */ |
| |
| static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog) |
| { |
| PRIntn rv; |
| |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| rv = listen(fd->secret->md.osfd, backlog); |
| if (rv == -1) { |
| pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_Listen */ |
| |
| static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how) |
| { |
| PRIntn rv = -1; |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| rv = shutdown(fd->secret->md.osfd, how); |
| |
| if (rv == -1) { |
| pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } /* pt_Shutdown */ |
| |
| static PRInt16 pt_Poll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) |
| { |
| *out_flags = 0; |
| return in_flags; |
| } /* pt_Poll */ |
| |
| static PRInt32 pt_Recv( |
| PRFileDesc *fd, void *buf, PRInt32 amount, |
| PRIntn flags, PRIntervalTime timeout) |
| { |
| PRInt32 syserrno, bytes = -1; |
| PRIntn osflags; |
| |
| if (0 == flags) |
| osflags = 0; |
| else if (PR_MSG_PEEK == flags) |
| { |
| #ifdef SYMBIAN |
| /* MSG_PEEK doesn't work as expected. */ |
| PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
| return bytes; |
| #else |
| osflags = MSG_PEEK; |
| #endif |
| } |
| else |
| { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return bytes; |
| } |
| |
| if (pt_TestAbort()) return bytes; |
| |
| /* recv() is a much slower call on pre-2.6 Solaris than read(). */ |
| #if defined(SOLARIS) |
| if (0 == osflags) |
| bytes = read(fd->secret->md.osfd, buf, amount); |
| else |
| bytes = recv(fd->secret->md.osfd, buf, amount, osflags); |
| #else |
| bytes = recv(fd->secret->md.osfd, buf, amount, osflags); |
| #endif |
| syserrno = errno; |
| |
| if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking)) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = buf; |
| op.arg3.amount = amount; |
| op.arg4.flags = osflags; |
| op.timeout = timeout; |
| op.function = pt_recv_cont; |
| op.event = POLLIN | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| } |
| if (bytes < 0) |
| pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno); |
| return bytes; |
| } /* pt_Recv */ |
| |
| static PRInt32 pt_SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) |
| { |
| return pt_Recv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); |
| } /* pt_SocketRead */ |
| |
| static PRInt32 pt_Send( |
| PRFileDesc *fd, const void *buf, PRInt32 amount, |
| PRIntn flags, PRIntervalTime timeout) |
| { |
| PRInt32 syserrno, bytes = -1; |
| PRBool fNeedContinue = PR_FALSE; |
| #if defined(SOLARIS) |
| PRInt32 tmp_amount = amount; |
| #endif |
| |
| /* |
| * Under HP-UX DCE threads, pthread.h includes dce/cma_ux.h, |
| * which has the following: |
| * # define send cma_send |
| * extern int cma_send (int , void *, int, int ); |
| * So we need to cast away the 'const' of argument #2 for send(). |
| */ |
| #if defined (HPUX) && defined(_PR_DCETHREADS) |
| #define PT_SENDBUF_CAST (void *) |
| #else |
| #define PT_SENDBUF_CAST |
| #endif |
| |
| if (pt_TestAbort()) return bytes; |
| |
| /* |
| * On pre-2.6 Solaris, send() is much slower than write(). |
| * On 2.6 and beyond, with in-kernel sockets, send() and |
| * write() are fairly equivalent in performance. |
| */ |
| #if defined(SOLARIS) |
| PR_ASSERT(0 == flags); |
| retry: |
| bytes = write(fd->secret->md.osfd, PT_SENDBUF_CAST buf, tmp_amount); |
| #else |
| bytes = send(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount, flags); |
| #endif |
| syserrno = errno; |
| |
| #if defined(SOLARIS) |
| /* |
| * The write system call has been reported to return the ERANGE error |
| * on occasion. Try to write in smaller chunks to workaround this bug. |
| */ |
| if ((bytes == -1) && (syserrno == ERANGE)) |
| { |
| if (tmp_amount > 1) |
| { |
| tmp_amount = tmp_amount/2; /* half the bytes */ |
| goto retry; |
| } |
| } |
| #endif |
| |
| if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) |
| { |
| bytes = -1; |
| syserrno = ETIMEDOUT; |
| } |
| else |
| { |
| buf = (char *) buf + bytes; |
| amount -= bytes; |
| fNeedContinue = PR_TRUE; |
| } |
| } |
| if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking) ) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else |
| { |
| bytes = 0; |
| fNeedContinue = PR_TRUE; |
| } |
| } |
| |
| if (fNeedContinue == PR_TRUE) |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = (void*)buf; |
| op.arg3.amount = amount; |
| op.arg4.flags = flags; |
| op.timeout = timeout; |
| op.result.code = bytes; /* initialize the number sent */ |
| op.function = pt_send_cont; |
| op.event = POLLOUT | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (bytes == -1) |
| pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno); |
| return bytes; |
| } /* pt_Send */ |
| |
| static PRInt32 pt_SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) |
| { |
| return pt_Send(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); |
| } /* pt_SocketWrite */ |
| |
| static PRInt32 pt_SendTo( |
| PRFileDesc *fd, const void *buf, |
| PRInt32 amount, PRIntn flags, const PRNetAddr *addr, |
| PRIntervalTime timeout) |
| { |
| PRInt32 syserrno, bytes = -1; |
| PRBool fNeedContinue = PR_FALSE; |
| pt_SockLen addr_len; |
| const PRNetAddr *addrp = addr; |
| #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) |
| PRUint16 md_af = addr->raw.family; |
| PRNetAddr addrCopy; |
| #endif |
| |
| if (pt_TestAbort()) return bytes; |
| |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| #if defined(_PR_INET6) |
| if (addr->raw.family == PR_AF_INET6) { |
| md_af = AF_INET6; |
| #ifndef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| addrCopy.raw.family = AF_INET6; |
| addrp = &addrCopy; |
| #endif |
| } |
| #endif |
| |
| addr_len = PR_NETADDR_SIZE(addr); |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| addrCopy = *addr; |
| ((struct sockaddr*)&addrCopy)->sa_len = addr_len; |
| ((struct sockaddr*)&addrCopy)->sa_family = md_af; |
| addrp = &addrCopy; |
| #endif |
| bytes = sendto( |
| fd->secret->md.osfd, buf, amount, flags, |
| (struct sockaddr*)addrp, addr_len); |
| syserrno = errno; |
| if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking) ) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else fNeedContinue = PR_TRUE; |
| } |
| if (fNeedContinue == PR_TRUE) |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = (void*)buf; |
| op.arg3.amount = amount; |
| op.arg4.flags = flags; |
| op.arg5.addr = (PRNetAddr*)addrp; |
| op.timeout = timeout; |
| op.result.code = 0; /* initialize the number sent */ |
| op.function = pt_sendto_cont; |
| op.event = POLLOUT | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (bytes < 0) |
| pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); |
| return bytes; |
| } /* pt_SendTo */ |
| |
| static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, |
| PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) |
| { |
| PRBool fNeedContinue = PR_FALSE; |
| PRInt32 syserrno, bytes = -1; |
| pt_SockLen addr_len = sizeof(PRNetAddr); |
| |
| if (pt_TestAbort()) return bytes; |
| |
| bytes = recvfrom( |
| fd->secret->md.osfd, buf, amount, flags, |
| (struct sockaddr*)addr, &addr_len); |
| syserrno = errno; |
| |
| if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) |
| && (!fd->secret->nonblocking) ) |
| { |
| if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; |
| else fNeedContinue = PR_TRUE; |
| } |
| |
| if (fNeedContinue == PR_TRUE) |
| { |
| pt_Continuation op; |
| op.arg1.osfd = fd->secret->md.osfd; |
| op.arg2.buffer = buf; |
| op.arg3.amount = amount; |
| op.arg4.flags = flags; |
| op.arg5.addr = addr; |
| op.timeout = timeout; |
| op.function = pt_recvfrom_cont; |
| op.event = POLLIN | POLLPRI; |
| bytes = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| if (bytes >= 0) |
| { |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) |
| { |
| addr->raw.family = ((struct sockaddr*)addr)->sa_family; |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| #ifdef _PR_INET6 |
| if (addr && (AF_INET6 == addr->raw.family)) |
| addr->raw.family = PR_AF_INET6; |
| #endif |
| } |
| else |
| pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno); |
| return bytes; |
| } /* pt_RecvFrom */ |
| |
| #ifdef AIX |
| #ifndef HAVE_SEND_FILE |
| static pthread_once_t pt_aix_sendfile_once_block = PTHREAD_ONCE_INIT; |
| |
| static void pt_aix_sendfile_init_routine(void) |
| { |
| void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); |
| pt_aix_sendfile_fptr = (ssize_t (*)()) dlsym(handle, "send_file"); |
| dlclose(handle); |
| } |
| |
| /* |
| * pt_AIXDispatchSendFile |
| */ |
| static PRInt32 pt_AIXDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| int rv; |
| |
| rv = pthread_once(&pt_aix_sendfile_once_block, |
| pt_aix_sendfile_init_routine); |
| PR_ASSERT(0 == rv); |
| if (pt_aix_sendfile_fptr) { |
| return pt_AIXSendFile(sd, sfd, flags, timeout); |
| } else { |
| return PR_EmulateSendFile(sd, sfd, flags, timeout); |
| } |
| } |
| #endif /* !HAVE_SEND_FILE */ |
| |
| |
| /* |
| * pt_AIXSendFile |
| * |
| * Send file sfd->fd across socket sd. If specified, header and trailer |
| * buffers are sent before and after the file, respectively. |
| * |
| * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file |
| * |
| * return number of bytes sent or -1 on error |
| * |
| * This implementation takes advantage of the send_file() system |
| * call available in AIX 4.3.2. |
| */ |
| |
| static PRInt32 pt_AIXSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| struct sf_parms sf_struct; |
| uint_t send_flags; |
| ssize_t rv; |
| int syserrno; |
| PRInt32 count; |
| unsigned long long saved_file_offset; |
| long long saved_file_bytes; |
| |
| sf_struct.header_data = (void *) sfd->header; /* cast away the 'const' */ |
| sf_struct.header_length = sfd->hlen; |
| sf_struct.file_descriptor = sfd->fd->secret->md.osfd; |
| sf_struct.file_size = 0; |
| sf_struct.file_offset = sfd->file_offset; |
| if (sfd->file_nbytes == 0) |
| sf_struct.file_bytes = -1; |
| else |
| sf_struct.file_bytes = sfd->file_nbytes; |
| sf_struct.trailer_data = (void *) sfd->trailer; |
| sf_struct.trailer_length = sfd->tlen; |
| sf_struct.bytes_sent = 0; |
| |
| saved_file_offset = sf_struct.file_offset; |
| saved_file_bytes = sf_struct.file_bytes; |
| |
| send_flags = 0; /* flags processed at the end */ |
| |
| /* The first argument to send_file() is int*. */ |
| PR_ASSERT(sizeof(int) == sizeof(sd->secret->md.osfd)); |
| do { |
| rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags); |
| } while (rv == -1 && (syserrno = errno) == EINTR); |
| |
| if (rv == -1) { |
| if (syserrno == EAGAIN || syserrno == EWOULDBLOCK) { |
| count = 0; /* Not a real error. Need to continue. */ |
| } else { |
| count = -1; |
| } |
| } else { |
| count = sf_struct.bytes_sent; |
| /* |
| * A bug in AIX 4.3.2 prevents the 'file_bytes' field from |
| * being updated. So, 'file_bytes' is maintained by NSPR to |
| * avoid conflict when this bug is fixed in AIX, in the future. |
| */ |
| if (saved_file_bytes != -1) |
| saved_file_bytes -= (sf_struct.file_offset - saved_file_offset); |
| sf_struct.file_bytes = saved_file_bytes; |
| } |
| |
| if ((rv == 1) || ((rv == -1) && (count == 0))) { |
| pt_Continuation op; |
| |
| op.arg1.osfd = sd->secret->md.osfd; |
| op.arg2.buffer = &sf_struct; |
| op.arg4.flags = send_flags; |
| op.result.code = count; |
| op.timeout = timeout; |
| op.function = pt_aix_sendfile_cont; |
| op.event = POLLOUT | POLLPRI; |
| count = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| |
| if (count == -1) { |
| pt_MapError(_MD_aix_map_sendfile_error, syserrno); |
| return -1; |
| } |
| if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { |
| PR_Close(sd); |
| } |
| PR_ASSERT(count == (sfd->hlen + sfd->tlen + |
| ((sfd->file_nbytes == 0) ? |
| sf_struct.file_size - sfd->file_offset : |
| sfd->file_nbytes))); |
| return count; |
| } |
| #endif /* AIX */ |
| |
| #ifdef HPUX11 |
| /* |
| * pt_HPUXSendFile |
| * |
| * Send file sfd->fd across socket sd. If specified, header and trailer |
| * buffers are sent before and after the file, respectively. |
| * |
| * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file |
| * |
| * return number of bytes sent or -1 on error |
| * |
| * This implementation takes advantage of the sendfile() system |
| * call available in HP-UX B.11.00. |
| */ |
| |
| static PRInt32 pt_HPUXSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| struct stat statbuf; |
| size_t nbytes_to_send, file_nbytes_to_send; |
| struct iovec hdtrl[2]; /* optional header and trailer buffers */ |
| int send_flags; |
| PRInt32 count; |
| int syserrno; |
| |
| if (sfd->file_nbytes == 0) { |
| /* Get file size */ |
| if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { |
| _PR_MD_MAP_FSTAT_ERROR(errno); |
| return -1; |
| } |
| file_nbytes_to_send = statbuf.st_size - sfd->file_offset; |
| } else { |
| file_nbytes_to_send = sfd->file_nbytes; |
| } |
| nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; |
| |
| hdtrl[0].iov_base = (void *) sfd->header; /* cast away the 'const' */ |
| hdtrl[0].iov_len = sfd->hlen; |
| hdtrl[1].iov_base = (void *) sfd->trailer; |
| hdtrl[1].iov_len = sfd->tlen; |
| /* |
| * SF_DISCONNECT seems to close the socket even if sendfile() |
| * only does a partial send on a nonblocking socket. This |
| * would prevent the subsequent sendfile() calls on that socket |
| * from working. So we don't use the SD_DISCONNECT flag. |
| */ |
| send_flags = 0; |
| |
| do { |
| count = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, |
| sfd->file_offset, file_nbytes_to_send, hdtrl, send_flags); |
| } while (count == -1 && (syserrno = errno) == EINTR); |
| |
| if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) { |
| count = 0; |
| } |
| if (count != -1 && count < nbytes_to_send) { |
| pt_Continuation op; |
| |
| if (count < sfd->hlen) { |
| /* header not sent */ |
| |
| hdtrl[0].iov_base = ((char *) sfd->header) + count; |
| hdtrl[0].iov_len = sfd->hlen - count; |
| op.arg3.file_spec.offset = sfd->file_offset; |
| op.arg3.file_spec.nbytes = file_nbytes_to_send; |
| } else if (count < (sfd->hlen + file_nbytes_to_send)) { |
| /* header sent, file not sent */ |
| |
| hdtrl[0].iov_base = NULL; |
| hdtrl[0].iov_len = 0; |
| |
| op.arg3.file_spec.offset = sfd->file_offset + count - sfd->hlen; |
| op.arg3.file_spec.nbytes = file_nbytes_to_send - (count - sfd->hlen); |
| } else if (count < (sfd->hlen + file_nbytes_to_send + sfd->tlen)) { |
| PRUint32 trailer_nbytes_sent; |
| |
| /* header sent, file sent, trailer not sent */ |
| |
| hdtrl[0].iov_base = NULL; |
| hdtrl[0].iov_len = 0; |
| /* |
| * set file offset and len so that no more file data is |
| * sent |
| */ |
| op.arg3.file_spec.offset = statbuf.st_size; |
| op.arg3.file_spec.nbytes = 0; |
| |
| trailer_nbytes_sent = count - sfd->hlen - file_nbytes_to_send; |
| hdtrl[1].iov_base = ((char *) sfd->trailer) + trailer_nbytes_sent; |
| hdtrl[1].iov_len = sfd->tlen - trailer_nbytes_sent; |
| } |
| |
| op.arg1.osfd = sd->secret->md.osfd; |
| op.filedesc = sfd->fd->secret->md.osfd; |
| op.arg2.buffer = hdtrl; |
| op.arg3.file_spec.st_size = statbuf.st_size; |
| op.arg4.flags = send_flags; |
| op.nbytes_to_send = nbytes_to_send - count; |
| op.result.code = count; |
| op.timeout = timeout; |
| op.function = pt_hpux_sendfile_cont; |
| op.event = POLLOUT | POLLPRI; |
| count = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| |
| if (count == -1) { |
| pt_MapError(_MD_hpux_map_sendfile_error, syserrno); |
| return -1; |
| } |
| if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { |
| PR_Close(sd); |
| } |
| PR_ASSERT(count == nbytes_to_send); |
| return count; |
| } |
| |
| #endif /* HPUX11 */ |
| |
| #ifdef SOLARIS |
| |
| /* |
| * pt_SolarisSendFile |
| * |
| * Send file sfd->fd across socket sd. If specified, header and trailer |
| * buffers are sent before and after the file, respectively. |
| * |
| * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file |
| * |
| * return number of bytes sent or -1 on error |
| * |
| * This implementation takes advantage of the sendfilev() system |
| * call available in Solaris 8. |
| */ |
| |
| static PRInt32 pt_SolarisSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| struct stat statbuf; |
| size_t nbytes_to_send, file_nbytes_to_send; |
| struct sendfilevec sfv_struct[3]; |
| int sfvcnt = 0; |
| size_t xferred; |
| PRInt32 count; |
| int syserrno; |
| |
| if (sfd->file_nbytes == 0) { |
| /* Get file size */ |
| if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { |
| _PR_MD_MAP_FSTAT_ERROR(errno); |
| return -1; |
| } |
| file_nbytes_to_send = statbuf.st_size - sfd->file_offset; |
| } else { |
| file_nbytes_to_send = sfd->file_nbytes; |
| } |
| |
| nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; |
| |
| if (sfd->hlen != 0) { |
| sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; |
| sfv_struct[sfvcnt].sfv_flag = 0; |
| sfv_struct[sfvcnt].sfv_off = (off_t) sfd->header; |
| sfv_struct[sfvcnt].sfv_len = sfd->hlen; |
| sfvcnt++; |
| } |
| |
| if (file_nbytes_to_send != 0) { |
| sfv_struct[sfvcnt].sfv_fd = sfd->fd->secret->md.osfd; |
| sfv_struct[sfvcnt].sfv_flag = 0; |
| sfv_struct[sfvcnt].sfv_off = sfd->file_offset; |
| sfv_struct[sfvcnt].sfv_len = file_nbytes_to_send; |
| sfvcnt++; |
| } |
| |
| if (sfd->tlen != 0) { |
| sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; |
| sfv_struct[sfvcnt].sfv_flag = 0; |
| sfv_struct[sfvcnt].sfv_off = (off_t) sfd->trailer; |
| sfv_struct[sfvcnt].sfv_len = sfd->tlen; |
| sfvcnt++; |
| } |
| |
| if (0 == sfvcnt) { |
| count = 0; |
| goto done; |
| } |
| |
| /* |
| * Strictly speaking, we may have sent some bytes when the |
| * sendfilev() is interrupted and we should retry it from an |
| * updated offset. We are not doing that here. |
| */ |
| count = SOLARIS_SENDFILEV(sd->secret->md.osfd, sfv_struct, |
| sfvcnt, &xferred); |
| |
| PR_ASSERT((count == -1) || (count == xferred)); |
| |
| if (count == -1) { |
| syserrno = errno; |
| if (syserrno == EINTR |
| || syserrno == EAGAIN || syserrno == EWOULDBLOCK) { |
| count = xferred; |
| } |
| } else if (count == 0) { |
| /* |
| * We are now at EOF. The file was truncated. Solaris sendfile is |
| * supposed to return 0 and no error in this case, though some versions |
| * may return -1 and EINVAL . |
| */ |
| count = -1; |
| syserrno = 0; /* will be treated as EOF */ |
| } |
| |
| if (count != -1 && count < nbytes_to_send) { |
| pt_Continuation op; |
| struct sendfilevec *vec = sfv_struct; |
| PRInt32 rem = count; |
| |
| while (rem >= vec->sfv_len) { |
| rem -= vec->sfv_len; |
| vec++; |
| sfvcnt--; |
| } |
| PR_ASSERT(sfvcnt > 0); |
| |
| vec->sfv_off += rem; |
| vec->sfv_len -= rem; |
| PR_ASSERT(vec->sfv_len > 0); |
| |
| op.arg1.osfd = sd->secret->md.osfd; |
| op.arg2.buffer = vec; |
| op.arg3.amount = sfvcnt; |
| op.arg4.flags = 0; |
| op.nbytes_to_send = nbytes_to_send - count; |
| op.result.code = count; |
| op.timeout = timeout; |
| op.function = pt_solaris_sendfile_cont; |
| op.event = POLLOUT | POLLPRI; |
| count = pt_Continue(&op); |
| syserrno = op.syserrno; |
| } |
| |
| done: |
| if (count == -1) { |
| pt_MapError(_MD_solaris_map_sendfile_error, syserrno); |
| return -1; |
| } |
| if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { |
| PR_Close(sd); |
| } |
| PR_ASSERT(count == nbytes_to_send); |
| return count; |
| } |
| |
| #ifndef HAVE_SENDFILEV |
| static pthread_once_t pt_solaris_sendfilev_once_block = PTHREAD_ONCE_INIT; |
| |
| static void pt_solaris_sendfilev_init_routine(void) |
| { |
| void *handle; |
| PRBool close_it = PR_FALSE; |
| |
| /* |
| * We do not want to unload libsendfile.so. This handle is leaked |
| * intentionally. |
| */ |
| handle = dlopen("libsendfile.so", RTLD_LAZY | RTLD_GLOBAL); |
| PR_LOG(_pr_io_lm, PR_LOG_DEBUG, |
| ("dlopen(libsendfile.so) returns %p", handle)); |
| |
| if (NULL == handle) { |
| /* |
| * The dlopen(0, mode) call is to allow for the possibility that |
| * sendfilev() may become part of a standard system library in a |
| * future Solaris release. |
| */ |
| handle = dlopen(0, RTLD_LAZY | RTLD_GLOBAL); |
| PR_LOG(_pr_io_lm, PR_LOG_DEBUG, |
| ("dlopen(0) returns %p", handle)); |
| close_it = PR_TRUE; |
| } |
| pt_solaris_sendfilev_fptr = (ssize_t (*)()) dlsym(handle, "sendfilev"); |
| PR_LOG(_pr_io_lm, PR_LOG_DEBUG, |
| ("dlsym(sendfilev) returns %p", pt_solaris_sendfilev_fptr)); |
| |
| if (close_it) { |
| dlclose(handle); |
| } |
| } |
| |
| /* |
| * pt_SolarisDispatchSendFile |
| */ |
| static PRInt32 pt_SolarisDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| int rv; |
| |
| rv = pthread_once(&pt_solaris_sendfilev_once_block, |
| pt_solaris_sendfilev_init_routine); |
| PR_ASSERT(0 == rv); |
| if (pt_solaris_sendfilev_fptr) { |
| return pt_SolarisSendFile(sd, sfd, flags, timeout); |
| } else { |
| return PR_EmulateSendFile(sd, sfd, flags, timeout); |
| } |
| } |
| #endif /* !HAVE_SENDFILEV */ |
| |
| #endif /* SOLARIS */ |
| |
| #ifdef LINUX |
| /* |
| * pt_LinuxSendFile |
| * |
| * Send file sfd->fd across socket sd. If specified, header and trailer |
| * buffers are sent before and after the file, respectively. |
| * |
| * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file |
| * |
| * return number of bytes sent or -1 on error |
| * |
| * This implementation takes advantage of the sendfile() system |
| * call available in Linux kernel 2.2 or higher. |
| */ |
| |
| static PRInt32 pt_LinuxSendFile(PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| struct stat statbuf; |
| size_t file_nbytes_to_send; |
| PRInt32 count = 0; |
| ssize_t rv; |
| int syserrno; |
| off_t offset; |
| PRBool tcp_cork_enabled = PR_FALSE; |
| int tcp_cork; |
| |
| if (sfd->file_nbytes == 0) { |
| /* Get file size */ |
| if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { |
| _PR_MD_MAP_FSTAT_ERROR(errno); |
| return -1; |
| } |
| file_nbytes_to_send = statbuf.st_size - sfd->file_offset; |
| } else { |
| file_nbytes_to_send = sfd->file_nbytes; |
| } |
| |
| if ((sfd->hlen != 0 || sfd->tlen != 0) |
| && sd->secret->md.tcp_nodelay == 0) { |
| tcp_cork = 1; |
| if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, |
| &tcp_cork, sizeof tcp_cork) == 0) { |
| tcp_cork_enabled = PR_TRUE; |
| } else { |
| syserrno = errno; |
| if (syserrno != EINVAL) { |
| _PR_MD_MAP_SETSOCKOPT_ERROR(syserrno); |
| return -1; |
| } |
| /* |
| * The most likely reason for the EINVAL error is that |
| * TCP_NODELAY is set (with a function other than |
| * PR_SetSocketOption). This is not fatal, so we keep |
| * on going. |
| */ |
| PR_LOG(_pr_io_lm, PR_LOG_WARNING, |
| ("pt_LinuxSendFile: " |
| "setsockopt(TCP_CORK) failed with EINVAL\n")); |
| } |
| } |
| |
| if (sfd->hlen != 0) { |
| count = PR_Send(sd, sfd->header, sfd->hlen, 0, timeout); |
| if (count == -1) { |
| goto failed; |
| } |
| } |
| |
| if (file_nbytes_to_send != 0) { |
| offset = sfd->file_offset; |
| do { |
| rv = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, |
| &offset, file_nbytes_to_send); |
| } while (rv == -1 && (syserrno = errno) == EINTR); |
| if (rv == -1) { |
| if (syserrno != EAGAIN && syserrno != EWOULDBLOCK) { |
| _MD_linux_map_sendfile_error(syserrno); |
| count = -1; |
| goto failed; |
| } |
| rv = 0; |
| } |
| PR_ASSERT(rv == offset - sfd->file_offset); |
| count += rv; |
| |
| if (rv < file_nbytes_to_send) { |
| pt_Continuation op; |
| |
| op.arg1.osfd = sd->secret->md.osfd; |
| op.in_fd = sfd->fd->secret->md.osfd; |
| op.offset = offset; |
| op.count = file_nbytes_to_send - rv; |
| op.result.code = count; |
| op.timeout = timeout; |
| op.function = pt_linux_sendfile_cont; |
| op.event = POLLOUT | POLLPRI; |
| count = pt_Continue(&op); |
| syserrno = op.syserrno; |
| if (count == -1) { |
| pt_MapError(_MD_linux_map_sendfile_error, syserrno); |
| goto failed; |
| } |
| } |
| } |
| |
| if (sfd->tlen != 0) { |
| rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); |
| if (rv == -1) { |
| count = -1; |
| goto failed; |
| } |
| count += rv; |
| } |
| |
| failed: |
| if (tcp_cork_enabled) { |
| tcp_cork = 0; |
| if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, |
| &tcp_cork, sizeof tcp_cork) == -1 && count != -1) { |
| _PR_MD_MAP_SETSOCKOPT_ERROR(errno); |
| count = -1; |
| } |
| } |
| if (count != -1) { |
| if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { |
| PR_Close(sd); |
| } |
| PR_ASSERT(count == sfd->hlen + sfd->tlen + file_nbytes_to_send); |
| } |
| return count; |
| } |
| #endif /* LINUX */ |
| |
| #ifdef AIX |
| extern int _pr_aix_send_file_use_disabled; |
| #endif |
| |
| static PRInt32 pt_SendFile( |
| PRFileDesc *sd, PRSendFileData *sfd, |
| PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| if (pt_TestAbort()) return -1; |
| /* The socket must be in blocking mode. */ |
| if (sd->secret->nonblocking) |
| { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return -1; |
| } |
| #ifdef HPUX11 |
| return(pt_HPUXSendFile(sd, sfd, flags, timeout)); |
| #elif defined(AIX) |
| #ifdef HAVE_SEND_FILE |
| /* |
| * A bug in AIX 4.3.2 results in corruption of data transferred by |
| * send_file(); AIX patch PTF U463956 contains the fix. A user can |
| * disable the use of send_file function in NSPR, when this patch is |
| * not installed on the system, by setting the envionment variable |
| * NSPR_AIX_SEND_FILE_USE_DISABLED to 1. |
| */ |
| if (_pr_aix_send_file_use_disabled) |
| return(PR_EmulateSendFile(sd, sfd, flags, timeout)); |
| else |
| return(pt_AIXSendFile(sd, sfd, flags, timeout)); |
| #else |
| return(PR_EmulateSendFile(sd, sfd, flags, timeout)); |
| /* return(pt_AIXDispatchSendFile(sd, sfd, flags, timeout));*/ |
| #endif /* HAVE_SEND_FILE */ |
| #elif defined(SOLARIS) |
| #ifdef HAVE_SENDFILEV |
| return(pt_SolarisSendFile(sd, sfd, flags, timeout)); |
| #else |
| return(pt_SolarisDispatchSendFile(sd, sfd, flags, timeout)); |
| #endif /* HAVE_SENDFILEV */ |
| #elif defined(LINUX) |
| return(pt_LinuxSendFile(sd, sfd, flags, timeout)); |
| #else |
| return(PR_EmulateSendFile(sd, sfd, flags, timeout)); |
| #endif |
| } |
| |
| static PRInt32 pt_TransmitFile( |
| PRFileDesc *sd, PRFileDesc *fd, const void *headers, |
| PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout) |
| { |
| PRSendFileData sfd; |
| |
| sfd.fd = fd; |
| sfd.file_offset = 0; |
| sfd.file_nbytes = 0; |
| sfd.header = headers; |
| sfd.hlen = hlen; |
| sfd.trailer = NULL; |
| sfd.tlen = 0; |
| |
| return(pt_SendFile(sd, &sfd, flags, timeout)); |
| } /* pt_TransmitFile */ |
| |
| static PRInt32 pt_AcceptRead( |
| PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, |
| void *buf, PRInt32 amount, PRIntervalTime timeout) |
| { |
| PRInt32 rv = -1; |
| |
| if (pt_TestAbort()) return rv; |
| /* The socket must be in blocking mode. */ |
| if (sd->secret->nonblocking) |
| { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| return rv; |
| } |
| |
| rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); |
| return rv; |
| } /* pt_AcceptRead */ |
| |
| static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr) |
| { |
| PRIntn rv = -1; |
| pt_SockLen addr_len = sizeof(PRNetAddr); |
| |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| rv = getsockname( |
| fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); |
| if (rv == -1) { |
| pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno); |
| return PR_FAILURE; |
| } else { |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) |
| { |
| addr->raw.family = ((struct sockaddr*)addr)->sa_family; |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| #ifdef _PR_INET6 |
| if (AF_INET6 == addr->raw.family) |
| addr->raw.family = PR_AF_INET6; |
| #endif |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); |
| return PR_SUCCESS; |
| } |
| } /* pt_GetSockName */ |
| |
| static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) |
| { |
| PRIntn rv = -1; |
| pt_SockLen addr_len = sizeof(PRNetAddr); |
| |
| if (pt_TestAbort()) return PR_FAILURE; |
| |
| rv = getpeername( |
| fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); |
| |
| if (rv == -1) { |
| pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno); |
| return PR_FAILURE; |
| } else { |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) |
| { |
| addr->raw.family = ((struct sockaddr*)addr)->sa_family; |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| #ifdef _PR_INET6 |
| if (AF_INET6 == addr->raw.family) |
| addr->raw.family = PR_AF_INET6; |
| #endif |
| PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); |
| PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); |
| return PR_SUCCESS; |
| } |
| } /* pt_GetPeerName */ |
| |
| static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) |
| { |
| PRIntn rv; |
| pt_SockLen length; |
| PRInt32 level, name; |
| |
| /* |
| * PR_SockOpt_Nonblocking is a special case that does not |
| * translate to a getsockopt() call |
| */ |
| if (PR_SockOpt_Nonblocking == data->option) |
| { |
| data->value.non_blocking = fd->secret->nonblocking; |
| return PR_SUCCESS; |
| } |
| |
| rv = _PR_MapOptionName(data->option, &level, &name); |
| if (PR_SUCCESS == rv) |
| { |
| switch (data->option) |
| { |
| case PR_SockOpt_Linger: |
| { |
| struct linger linger; |
| length = sizeof(linger); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, (char *) &linger, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(linger) == length)); |
| data->value.linger.polarity = |
| (linger.l_onoff) ? PR_TRUE : PR_FALSE; |
| data->value.linger.linger = |
| PR_SecondsToInterval(linger.l_linger); |
| break; |
| } |
| case PR_SockOpt_Reuseaddr: |
| case PR_SockOpt_Keepalive: |
| case PR_SockOpt_NoDelay: |
| case PR_SockOpt_Broadcast: |
| { |
| PRIntn value; |
| length = sizeof(PRIntn); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, (char*)&value, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); |
| data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; |
| break; |
| } |
| case PR_SockOpt_McastLoopback: |
| { |
| PRUint8 xbool; |
| length = sizeof(xbool); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, |
| (char*)&xbool, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(xbool) == length)); |
| data->value.mcast_loopback = (0 == xbool) ? PR_FALSE : PR_TRUE; |
| break; |
| } |
| case PR_SockOpt_RecvBufferSize: |
| case PR_SockOpt_SendBufferSize: |
| case PR_SockOpt_MaxSegment: |
| { |
| PRIntn value; |
| length = sizeof(PRIntn); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, (char*)&value, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); |
| data->value.recv_buffer_size = value; |
| break; |
| } |
| case PR_SockOpt_IpTimeToLive: |
| case PR_SockOpt_IpTypeOfService: |
| { |
| length = sizeof(PRUintn); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, |
| (char*)&data->value.ip_ttl, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); |
| break; |
| } |
| case PR_SockOpt_McastTimeToLive: |
| { |
| PRUint8 ttl; |
| length = sizeof(ttl); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, |
| (char*)&ttl, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(ttl) == length)); |
| data->value.mcast_ttl = ttl; |
| break; |
| } |
| case PR_SockOpt_AddMember: |
| case PR_SockOpt_DropMember: |
| { |
| struct ip_mreq mreq; |
| length = sizeof(mreq); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, (char*)&mreq, &length); |
| PR_ASSERT((-1 == rv) || (sizeof(mreq) == length)); |
| data->value.add_member.mcaddr.inet.ip = |
| mreq.imr_multiaddr.s_addr; |
| data->value.add_member.ifaddr.inet.ip = |
| mreq.imr_interface.s_addr; |
| break; |
| } |
| case PR_SockOpt_McastInterface: |
| { |
| length = sizeof(data->value.mcast_if.inet.ip); |
| rv = getsockopt( |
| fd->secret->md.osfd, level, name, |
| (char*)&data->value.mcast_if.inet.ip, &length); |
| PR_ASSERT((-1 == rv) |
| || (sizeof(data->value.mcast_if.inet.ip) == |