/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "primpl.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef _PR_POLL_AVAILABLE #include #endif #if defined(ANDROID) #include #endif /* To get FIONREAD */ #if defined(UNIXWARE) #include #endif #if defined(NTO) #include #endif /* * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or * PRInt32* pointer to a _PRSockLen_t* pointer. */ #if defined(HAVE_SOCKLEN_T) \ || (defined(__GLIBC__) && __GLIBC__ >= 2) #define _PRSockLen_t socklen_t #elif defined(HPUX) || defined(SOLARIS) \ || defined(AIX4_1) || defined(LINUX) \ || defined(BSDI) || defined(SCO) \ || defined(DARWIN) \ || defined(QNX) #define _PRSockLen_t int #elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \ || defined(NETBSD) || defined(OPENBSD) || defined(UNIXWARE) \ || defined(NTO) || defined(RISCOS) #define _PRSockLen_t size_t #else #error "Cannot determine architecture" #endif /* ** Global lock variable used to bracket calls into rusty libraries that ** aren't thread safe (like libc, libX, etc). */ static PRLock *_pr_unix_rename_lock = NULL; static PRMonitor *_pr_Xfe_mon = NULL; static PRInt64 minus_one; sigset_t timer_set; #if !defined(_PR_PTHREADS) static sigset_t empty_set; #ifdef SOLARIS #include #include #endif #ifndef PIPE_BUF #define PIPE_BUF 512 #endif /* * _nspr_noclock - if set clock interrupts are disabled */ int _nspr_noclock = 1; /* * There is an assertion in this code that NSPR's definition of PRIOVec * is bit compatible with UNIX' definition of a struct iovec. This is * applicable to the 'writev()' operations where the types are casually * cast to avoid warnings. */ int _pr_md_pipefd[2] = { -1, -1 }; static char _pr_md_pipebuf[PIPE_BUF]; static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag, PRIntervalTime timeout); _PRInterruptTable _pr_interruptTable[] = { { "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, }, { 0 } }; void _MD_unix_init_running_cpu(_PRCPU *cpu) { PR_INIT_CLIST(&(cpu->md.md_unix.ioQ)); cpu->md.md_unix.ioq_max_osfd = -1; cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT; } PRStatus _MD_open_dir(_MDDir *d, const char *name) { int err; d->d = opendir(name); if (!d->d) { err = _MD_ERRNO(); _PR_MD_MAP_OPENDIR_ERROR(err); return PR_FAILURE; } return PR_SUCCESS; } PRInt32 _MD_close_dir(_MDDir *d) { int rv = 0, err; if (d->d) { rv = closedir(d->d); if (rv == -1) { err = _MD_ERRNO(); _PR_MD_MAP_CLOSEDIR_ERROR(err); } } return rv; } char * _MD_read_dir(_MDDir *d, PRIntn flags) { struct dirent *de; int err; for (;;) { /* * XXX: readdir() is not MT-safe. There is an MT-safe version * readdir_r() on some systems. */ _MD_ERRNO() = 0; de = readdir(d->d); if (!de) { err = _MD_ERRNO(); _PR_MD_MAP_READDIR_ERROR(err); return 0; } if ((flags & PR_SKIP_DOT) && (de->d_name[0] == '.') && (de->d_name[1] == 0)) { continue; } if ((flags & PR_SKIP_DOT_DOT) && (de->d_name[0] == '.') && (de->d_name[1] == '.') && (de->d_name[2] == 0)) { continue; } if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.')) { continue; } break; } return de->d_name; } PRInt32 _MD_delete(const char *name) { PRInt32 rv, err; #ifdef UNIXWARE sigset_t set, oset; #endif #ifdef UNIXWARE sigfillset(&set); sigprocmask(SIG_SETMASK, &set, &oset); #endif rv = unlink(name); #ifdef UNIXWARE sigprocmask(SIG_SETMASK, &oset, NULL); #endif if (rv == -1) { err = _MD_ERRNO(); _PR_MD_MAP_UNLINK_ERROR(err); } return(rv); } PRInt32 _MD_rename(const char *from, const char *to) { PRInt32 rv = -1, err; /* ** This is trying to enforce the semantics of WINDOZE' rename ** operation. That means one is not allowed to rename over top ** of an existing file. Holding a lock across these two function ** and the open function is known to be a bad idea, but .... */ if (NULL != _pr_unix_rename_lock) { PR_Lock(_pr_unix_rename_lock); } if (0 == access(to, F_OK)) { PR_SetError(PR_FILE_EXISTS_ERROR, 0); } else { rv = rename(from, to); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_RENAME_ERROR(err); } } if (NULL != _pr_unix_rename_lock) { PR_Unlock(_pr_unix_rename_lock); } return rv; } PRInt32 _MD_access(const char *name, PRAccessHow how) { PRInt32 rv, err; int amode; switch (how) { case PR_ACCESS_WRITE_OK: amode = W_OK; break; case PR_ACCESS_READ_OK: amode = R_OK; break; case PR_ACCESS_EXISTS: amode = F_OK; break; default: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); rv = -1; goto done; } rv = access(name, amode); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_ACCESS_ERROR(err); } done: return(rv); } PRInt32 _MD_mkdir(const char *name, PRIntn mode) { int rv, err; /* ** This lock is used to enforce rename semantics as described ** in PR_Rename. Look there for more fun details. */ if (NULL !=_pr_unix_rename_lock) { PR_Lock(_pr_unix_rename_lock); } rv = mkdir(name, mode); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_MKDIR_ERROR(err); } if (NULL !=_pr_unix_rename_lock) { PR_Unlock(_pr_unix_rename_lock); } return rv; } PRInt32 _MD_rmdir(const char *name) { int rv, err; rv = rmdir(name); if (rv == -1) { err = _MD_ERRNO(); _PR_MD_MAP_RMDIR_ERROR(err); } return rv; } PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 rv, err; #ifndef _PR_USE_POLL fd_set rd; #else struct pollfd pfd; #endif /* _PR_USE_POLL */ PRInt32 osfd = fd->secret->md.osfd; #ifndef _PR_USE_POLL FD_ZERO(&rd); FD_SET(osfd, &rd); #else pfd.fd = osfd; pfd.events = POLLIN; #endif /* _PR_USE_POLL */ while ((rv = read(osfd,buf,amount)) == -1) { err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, PR_INTERVAL_NO_TIMEOUT)) < 0) { goto done; } } else { #ifndef _PR_USE_POLL while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL)) == -1 && (err = _MD_ERRNO()) == EINTR) { /* retry _MD_SELECT() if it is interrupted */ } #else /* _PR_USE_POLL */ while ((rv = _MD_POLL(&pfd, 1, -1)) == -1 && (err = _MD_ERRNO()) == EINTR) { /* retry _MD_POLL() if it is interrupted */ } #endif /* _PR_USE_POLL */ if (rv == -1) { break; } } if (_PR_PENDING_INTERRUPT(me)) { break; } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); } else { _PR_MD_MAP_READ_ERROR(err); } } done: return(rv); } PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 rv, err; #ifndef _PR_USE_POLL fd_set wd; #else struct pollfd pfd; #endif /* _PR_USE_POLL */ PRInt32 osfd = fd->secret->md.osfd; #ifndef _PR_USE_POLL FD_ZERO(&wd); FD_SET(osfd, &wd); #else pfd.fd = osfd; pfd.events = POLLOUT; #endif /* _PR_USE_POLL */ while ((rv = write(osfd,buf,amount)) == -1) { err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, PR_INTERVAL_NO_TIMEOUT)) < 0) { goto done; } } else { #ifndef _PR_USE_POLL while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL)) == -1 && (err = _MD_ERRNO()) == EINTR) { /* retry _MD_SELECT() if it is interrupted */ } #else /* _PR_USE_POLL */ while ((rv = _MD_POLL(&pfd, 1, -1)) == -1 && (err = _MD_ERRNO()) == EINTR) { /* retry _MD_POLL() if it is interrupted */ } #endif /* _PR_USE_POLL */ if (rv == -1) { break; } } if (_PR_PENDING_INTERRUPT(me)) { break; } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); } else { _PR_MD_MAP_WRITE_ERROR(err); } } done: return(rv); } PRInt32 _MD_fsync(PRFileDesc *fd) { PRInt32 rv, err; rv = fsync(fd->secret->md.osfd); if (rv == -1) { err = _MD_ERRNO(); _PR_MD_MAP_FSYNC_ERROR(err); } return(rv); } PRInt32 _MD_close(PRInt32 osfd) { PRInt32 rv, err; rv = close(osfd); if (rv == -1) { err = _MD_ERRNO(); _PR_MD_MAP_CLOSE_ERROR(err); } return(rv); } PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto) { PRInt32 osfd, err; osfd = socket(domain, type, proto); if (osfd == -1) { err = _MD_ERRNO(); _PR_MD_MAP_SOCKET_ERROR(err); return(osfd); } return(osfd); } PRInt32 _MD_socketavailable(PRFileDesc *fd) { PRInt32 result; if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) { _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO()); return -1; } return result; } PRInt64 _MD_socketavailable64(PRFileDesc *fd) { PRInt64 result; LL_I2L(result, _MD_socketavailable(fd)); return result; } /* _MD_socketavailable64 */ #define READ_FD 1 #define WRITE_FD 2 /* * socket_io_wait -- * * wait for socket i/o, periodically checking for interrupt * * The first implementation uses select(), for platforms without * poll(). The second (preferred) implementation uses poll(). */ #ifndef _PR_USE_POLL static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, PRIntervalTime timeout) { PRInt32 rv = -1; struct timeval tv; PRThread *me = _PR_MD_CURRENT_THREAD(); PRIntervalTime epoch, now, elapsed, remaining; PRBool wait_for_remaining; PRInt32 syserror; fd_set rd_wr; switch (timeout) { case PR_INTERVAL_NO_WAIT: PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; case PR_INTERVAL_NO_TIMEOUT: /* * This is a special case of the 'default' case below. * Please see the comments there. */ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; tv.tv_usec = 0; FD_ZERO(&rd_wr); do { FD_SET(osfd, &rd_wr); if (fd_type == READ_FD) { rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); } else { rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); } if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { _PR_MD_MAP_SELECT_ERROR(syserror); break; } if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); rv = -1; break; } } while (rv == 0 || (rv == -1 && syserror == EINTR)); break; default: now = epoch = PR_IntervalNow(); remaining = timeout; FD_ZERO(&rd_wr); do { /* * We block in _MD_SELECT for at most * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, * so that there is an upper limit on the delay * before the interrupt bit is checked. */ wait_for_remaining = PR_TRUE; tv.tv_sec = PR_IntervalToSeconds(remaining); if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { wait_for_remaining = PR_FALSE; tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; tv.tv_usec = 0; } else { tv.tv_usec = PR_IntervalToMicroseconds( remaining - PR_SecondsToInterval(tv.tv_sec)); } FD_SET(osfd, &rd_wr); if (fd_type == READ_FD) { rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); } else { rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); } /* * we don't consider EINTR a real error */ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { _PR_MD_MAP_SELECT_ERROR(syserror); break; } if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); rv = -1; break; } /* * We loop again if _MD_SELECT timed out or got interrupted * by a signal, and the timeout deadline has not passed yet. */ if (rv == 0 || (rv == -1 && syserror == EINTR)) { /* * If _MD_SELECT timed out, we know how much time * we spent in blocking, so we can avoid a * PR_IntervalNow() call. */ if (rv == 0) { if (wait_for_remaining) { now += remaining; } else { now += PR_SecondsToInterval(tv.tv_sec) + PR_MicrosecondsToInterval(tv.tv_usec); } } else { now = PR_IntervalNow(); } elapsed = (PRIntervalTime) (now - epoch); if (elapsed >= timeout) { PR_SetError(PR_IO_TIMEOUT_ERROR, 0); rv = -1; break; } else { remaining = timeout - elapsed; } } } while (rv == 0 || (rv == -1 && syserror == EINTR)); break; } return(rv); } #else /* _PR_USE_POLL */ static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, PRIntervalTime timeout) { PRInt32 rv = -1; int msecs; PRThread *me = _PR_MD_CURRENT_THREAD(); PRIntervalTime epoch, now, elapsed, remaining; PRBool wait_for_remaining; PRInt32 syserror; struct pollfd pfd; switch (timeout) { case PR_INTERVAL_NO_WAIT: PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; case PR_INTERVAL_NO_TIMEOUT: /* * This is a special case of the 'default' case below. * Please see the comments there. */ msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; pfd.fd = osfd; if (fd_type == READ_FD) { pfd.events = POLLIN; } else { pfd.events = POLLOUT; } do { rv = _MD_POLL(&pfd, 1, msecs); if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { _PR_MD_MAP_POLL_ERROR(syserror); break; } /* * If POLLERR is set, don't process it; retry the operation */ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) { rv = -1; _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents); break; } if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); rv = -1; break; } } while (rv == 0 || (rv == -1 && syserror == EINTR)); break; default: now = epoch = PR_IntervalNow(); remaining = timeout; pfd.fd = osfd; if (fd_type == READ_FD) { pfd.events = POLLIN; } else { pfd.events = POLLOUT; } do { /* * We block in _MD_POLL for at most * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, * so that there is an upper limit on the delay * before the interrupt bit is checked. */ wait_for_remaining = PR_TRUE; msecs = PR_IntervalToMilliseconds(remaining); if (msecs > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) { wait_for_remaining = PR_FALSE; msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; } rv = _MD_POLL(&pfd, 1, msecs); /* * we don't consider EINTR a real error */ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { _PR_MD_MAP_POLL_ERROR(syserror); break; } if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); rv = -1; break; } /* * If POLLERR is set, don't process it; retry the operation */ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) { rv = -1; _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents); break; } /* * We loop again if _MD_POLL timed out or got interrupted * by a signal, and the timeout deadline has not passed yet. */ if (rv == 0 || (rv == -1 && syserror == EINTR)) { /* * If _MD_POLL timed out, we know how much time * we spent in blocking, so we can avoid a * PR_IntervalNow() call. */ if (rv == 0) { if (wait_for_remaining) { now += remaining; } else { now += PR_MillisecondsToInterval(msecs); } } else { now = PR_IntervalNow(); } elapsed = (PRIntervalTime) (now - epoch); if (elapsed >= timeout) { PR_SetError(PR_IO_TIMEOUT_ERROR, 0); rv = -1; break; } else { remaining = timeout - elapsed; } } } while (rv == 0 || (rv == -1 && syserror == EINTR)); break; } return(rv); } #endif /* _PR_USE_POLL */ static PRInt32 local_io_wait( PRInt32 osfd, PRInt32 wait_flag, PRIntervalTime timeout) { _PRUnixPollDesc pd; PRInt32 rv; PR_LOG(_pr_io_lm, PR_LOG_MIN, ("waiting to %s on osfd=%d", (wait_flag == _PR_UNIX_POLL_READ) ? "read" : "write", osfd)); if (timeout == PR_INTERVAL_NO_WAIT) { return 0; } pd.osfd = osfd; pd.in_flags = wait_flag; pd.out_flags = 0; rv = _PR_WaitForMultipleFDs(&pd, 1, timeout); if (rv == 0) { PR_SetError(PR_IO_TIMEOUT_ERROR, 0); rv = -1; } return rv; } PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, PRInt32 flags, PRIntervalTime timeout) { PRInt32 osfd = fd->secret->md.osfd; PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); /* * Many OS's (Solaris, Unixware) have a broken recv which won't read * from socketpairs. As long as we don't use flags on socketpairs, this * is a decent fix. - mikep */ #if defined(UNIXWARE) || defined(SOLARIS) while ((rv = read(osfd,buf,amount)) == -1) { #else while ((rv = recv(osfd,buf,amount,flags)) == -1) { #endif err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd,_PR_UNIX_POLL_READ,timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { _PR_MD_MAP_RECV_ERROR(err); } done: return(rv); } PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) { PRInt32 osfd = fd->secret->md.osfd; PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); while ((*addrlen = PR_NETADDR_SIZE(addr)), ((rv = recvfrom(osfd, buf, amount, flags, (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1)) { err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { _PR_MD_MAP_RECVFROM_ERROR(err); } done: #ifdef _PR_HAVE_SOCKADDR_LEN if (rv != -1) { /* ignore the sa_len field of struct sockaddr */ if (addr) { addr->raw.family = ((struct sockaddr *) addr)->sa_family; } } #endif /* _PR_HAVE_SOCKADDR_LEN */ return(rv); } PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRInt32 flags, PRIntervalTime timeout) { PRInt32 osfd = fd->secret->md.osfd; PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); #if defined(SOLARIS) PRInt32 tmp_amount = amount; #endif /* * 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); while ((rv = write(osfd,buf,tmp_amount)) == -1) { #else while ((rv = send(osfd,buf,amount,flags)) == -1) { #endif err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { #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 (err == ERANGE) { if (tmp_amount > 1) { tmp_amount = tmp_amount/2; /* half the bytes */ continue; } } #endif break; } } /* * optimization; if bytes sent is less than "amount" call * select before returning. This is because it is likely that * the next send() call will return EWOULDBLOCK. */ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) && (timeout != PR_INTERVAL_NO_WAIT)) { if (_PR_IS_NATIVE_THREAD(me)) { if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) { rv = -1; goto done; } } else { if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) { rv = -1; goto done; } } } if (rv < 0) { _PR_MD_MAP_SEND_ERROR(err); } done: return(rv); } PRInt32 _MD_sendto( PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) { PRInt32 osfd = fd->secret->md.osfd; PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); #ifdef _PR_HAVE_SOCKADDR_LEN PRNetAddr addrCopy; addrCopy = *addr; ((struct sockaddr *) &addrCopy)->sa_len = addrlen; ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; while ((rv = sendto(osfd, buf, amount, flags, (struct sockaddr *) &addrCopy, addrlen)) == -1) { #else while ((rv = sendto(osfd, buf, amount, flags, (struct sockaddr *) addr, addrlen)) == -1) { #endif err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { _PR_MD_MAP_SENDTO_ERROR(err); } done: return(rv); } PRInt32 _MD_writev( PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) { PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 index, amount = 0; PRInt32 osfd = fd->secret->md.osfd; /* * Calculate the total number of bytes to be sent; needed for * optimization later. * We could avoid this if this number was passed in; but it is * probably not a big deal because iov_size is usually small (less than * 3) */ if (!fd->secret->nonblocking) { for (index=0; indexsecret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } /* * optimization; if bytes sent is less than "amount" call * select before returning. This is because it is likely that * the next writev() call will return EWOULDBLOCK. */ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) && (timeout != PR_INTERVAL_NO_WAIT)) { if (_PR_IS_NATIVE_THREAD(me)) { if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) { rv = -1; goto done; } } else { if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) { rv = -1; goto done; } } } if (rv < 0) { _PR_MD_MAP_WRITEV_ERROR(err); } done: return(rv); } PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) { PRInt32 osfd = fd->secret->md.osfd; PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); while ((rv = accept(osfd, (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1) { err = _MD_ERRNO(); if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == ECONNABORTED)) { if (fd->secret->nonblocking) { break; } if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) { goto done; } } else { if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { goto done; } } } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { continue; } else { break; } } if (rv < 0) { _PR_MD_MAP_ACCEPT_ERROR(err); } done: #ifdef _PR_HAVE_SOCKADDR_LEN if (rv != -1) { /* ignore the sa_len field of struct sockaddr */ if (addr) { addr->raw.family = ((struct sockaddr *) addr)->sa_family; } } #endif /* _PR_HAVE_SOCKADDR_LEN */ return(rv); } extern int _connect (int s, const struct sockaddr *name, int namelen); PRInt32 _MD_connect( PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) { PRInt32 rv, err; PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 osfd = fd->secret->md.osfd; #ifdef _PR_HAVE_SOCKADDR_LEN PRNetAddr addrCopy; addrCopy = *addr; ((struct sockaddr *) &addrCopy)->sa_len = addrlen; ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; #endif /* * We initiate the connection setup by making a nonblocking connect() * call. If the connect() call fails, there are two cases we handle * specially: * 1. The connect() call was interrupted by a signal. In this case * we simply retry connect(). * 2. The NSPR socket is nonblocking and connect() fails with * EINPROGRESS. We first wait until the socket becomes writable. * Then we try to find out whether the connection setup succeeded * or failed. */ retry: #ifdef _PR_HAVE_SOCKADDR_LEN if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1) { #else if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) { #endif err = _MD_ERRNO(); if (err == EINTR) { if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); return -1; } goto retry; } if (!fd->secret->nonblocking && (err == EINPROGRESS)) { if (!_PR_IS_NATIVE_THREAD(me)) { if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { return -1; } } else { /* * socket_io_wait() may return -1 or 1. */ rv = socket_io_wait(osfd, WRITE_FD, timeout); if (rv == -1) { return -1; } } PR_ASSERT(rv == 1); if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); return -1; } err = _MD_unix_get_nonblocking_connect_error(osfd); if (err != 0) { _PR_MD_MAP_CONNECT_ERROR(err); return -1; } return 0; } _PR_MD_MAP_CONNECT_ERROR(err); } return rv; } /* _MD_connect */ PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) { PRInt32 rv, err; #ifdef _PR_HAVE_SOCKADDR_LEN PRNetAddr addrCopy; addrCopy = *addr; ((struct sockaddr *) &addrCopy)->sa_len = addrlen; ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; rv = bind(fd->secret->md.osfd, (struct sockaddr *) &addrCopy, (int )addrlen); #else rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen); #endif if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_BIND_ERROR(err); } return(rv); } PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog) { PRInt32 rv, err; rv = listen(fd->secret->md.osfd, backlog); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_LISTEN_ERROR(err); } return(rv); } PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how) { PRInt32 rv, err; rv = shutdown(fd->secret->md.osfd, how); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_SHUTDOWN_ERROR(err); } return(rv); } PRInt32 _MD_socketpair(int af, int type, int flags, PRInt32 *osfd) { PRInt32 rv, err; rv = socketpair(af, type, flags, osfd); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_SOCKETPAIR_ERROR(err); } return rv; } PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) { PRInt32 rv, err; rv = getsockname(fd->secret->md.osfd, (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); #ifdef _PR_HAVE_SOCKADDR_LEN if (rv == 0) { /* ignore the sa_len field of struct sockaddr */ if (addr) { addr->raw.family = ((struct sockaddr *) addr)->sa_family; } } #endif /* _PR_HAVE_SOCKADDR_LEN */ if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_GETSOCKNAME_ERROR(err); } return rv==0?PR_SUCCESS:PR_FAILURE; } PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) { PRInt32 rv, err; rv = getpeername(fd->secret->md.osfd, (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); #ifdef _PR_HAVE_SOCKADDR_LEN if (rv == 0) { /* ignore the sa_len field of struct sockaddr */ if (addr) { addr->raw.family = ((struct sockaddr *) addr)->sa_family; } } #endif /* _PR_HAVE_SOCKADDR_LEN */ if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_GETPEERNAME_ERROR(err); } return rv==0?PR_SUCCESS:PR_FAILURE; } PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) { PRInt32 rv, err; rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (_PRSockLen_t *)optlen); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_GETSOCKOPT_ERROR(err); } return rv==0?PR_SUCCESS:PR_FAILURE; } PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) { PRInt32 rv, err; rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_SETSOCKOPT_ERROR(err); } return rv==0?PR_SUCCESS:PR_FAILURE; } PRStatus _MD_set_fd_inheritable(PRFileDesc *fd, PRBool inheritable) { int rv; rv = fcntl(fd->secret->md.osfd, F_SETFD, inheritable ? 0 : FD_CLOEXEC); if (-1 == rv) { PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO()); return PR_FAILURE; } return PR_SUCCESS; } void _MD_init_fd_inheritable(PRFileDesc *fd, PRBool imported) { if (imported) { fd->secret->inheritable = _PR_TRI_UNKNOWN; } else { /* By default, a Unix fd is not closed on exec. */ #ifdef DEBUG { int flags = fcntl(fd->secret->md.osfd, F_GETFD, 0); PR_ASSERT(0 == flags); } #endif fd->secret->inheritable = _PR_TRI_TRUE; } } /************************************************************************/ #if !defined(_PR_USE_POLL) /* ** Scan through io queue and find any bad fd's that triggered the error ** from _MD_SELECT */ static void FindBadFDs(void) { PRCList *q; PRThread *me = _MD_CURRENT_THREAD(); PR_ASSERT(!_PR_IS_NATIVE_THREAD(me)); q = (_PR_IOQ(me->cpu)).next; _PR_IOQ_MAX_OSFD(me->cpu) = -1; _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; while (q != &_PR_IOQ(me->cpu)) { PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); PRBool notify = PR_FALSE; _PRUnixPollDesc *pds = pq->pds; _PRUnixPollDesc *epds = pds + pq->npds; PRInt32 pq_max_osfd = -1; q = q->next; for (; pds < epds; pds++) { PRInt32 osfd = pds->osfd; pds->out_flags = 0; PR_ASSERT(osfd >= 0 || pds->in_flags == 0); if (pds->in_flags == 0) { continue; /* skip this fd */ } if (fcntl(osfd, F_GETFL, 0) == -1) { /* Found a bad descriptor, remove it from the fd_sets. */ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("file descriptor %d is bad", osfd)); pds->out_flags = _PR_UNIX_POLL_NVAL; notify = PR_TRUE; } if (osfd > pq_max_osfd) { pq_max_osfd = osfd; } } if (notify) { PRIntn pri; PR_REMOVE_LINK(&pq->links); pq->on_ioq = PR_FALSE; /* * Decrement the count of descriptors for each desciptor/event * because this I/O request is being removed from the * ioq */ pds = pq->pds; for (; pds < epds; pds++) { PRInt32 osfd = pds->osfd; PRInt16 in_flags = pds->in_flags; PR_ASSERT(osfd >= 0 || in_flags == 0); if (in_flags & _PR_UNIX_POLL_READ) { if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_WRITE) { if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_EXCEPT) { if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); } } } _PR_THREAD_LOCK(pq->thr); if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { _PRCPU *cpu = pq->thr->cpu; _PR_SLEEPQ_LOCK(pq->thr->cpu); _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); _PR_SLEEPQ_UNLOCK(pq->thr->cpu); if (pq->thr->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; * a Resume operation on the thread * will move it to the runQ */ pq->thr->state = _PR_SUSPENDED; _PR_MISCQ_LOCK(pq->thr->cpu); _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); _PR_MISCQ_UNLOCK(pq->thr->cpu); } else { pri = pq->thr->priority; pq->thr->state = _PR_RUNNABLE; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(pq->thr, cpu, pri); _PR_RUNQ_UNLOCK(cpu); } } _PR_THREAD_UNLOCK(pq->thr); } else { if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; } if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; } } } if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; } } } #endif /* !defined(_PR_USE_POLL) */ /************************************************************************/ /* ** Called by the scheduler when there is nothing to do. This means that ** all threads are blocked on some monitor somewhere. ** ** Note: this code doesn't release the scheduler lock. */ /* ** Pause the current CPU. longjmp to the cpu's pause stack ** ** This must be called with the scheduler locked */ void _MD_PauseCPU(PRIntervalTime ticks) { PRThread *me = _MD_CURRENT_THREAD(); #ifdef _PR_USE_POLL int timeout; struct pollfd *pollfds; /* an array of pollfd structures */ struct pollfd *pollfdPtr; /* a pointer that steps through the array */ unsigned long npollfds; /* number of pollfd structures in array */ unsigned long pollfds_size; int nfd; /* to hold the return value of poll() */ #else struct timeval timeout, *tvp; fd_set r, w, e; fd_set *rp, *wp, *ep; PRInt32 max_osfd, nfd; #endif /* _PR_USE_POLL */ PRInt32 rv; PRCList *q; PRUint32 min_timeout; sigset_t oldset; PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); _PR_MD_IOQ_LOCK(); #ifdef _PR_USE_POLL /* Build up the pollfd structure array to wait on */ /* Find out how many pollfd structures are needed */ npollfds = _PR_IOQ_OSFD_CNT(me->cpu); PR_ASSERT(npollfds >= 0); /* * We use a pipe to wake up a native thread. An fd is needed * for the pipe and we poll it for reading. */ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { npollfds++; } /* * if the cpu's pollfd array is not big enough, release it and allocate a new one */ if (npollfds > _PR_IOQ_POLLFDS_SIZE(me->cpu)) { if (_PR_IOQ_POLLFDS(me->cpu) != NULL) { PR_DELETE(_PR_IOQ_POLLFDS(me->cpu)); } pollfds_size = PR_MAX(_PR_IOQ_MIN_POLLFDS_SIZE(me->cpu), npollfds); pollfds = (struct pollfd *) PR_MALLOC(pollfds_size * sizeof(struct pollfd)); _PR_IOQ_POLLFDS(me->cpu) = pollfds; _PR_IOQ_POLLFDS_SIZE(me->cpu) = pollfds_size; } else { pollfds = _PR_IOQ_POLLFDS(me->cpu); } pollfdPtr = pollfds; /* * If we need to poll the pipe for waking up a native thread, * the pipe's fd is the first element in the pollfds array. */ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { pollfdPtr->fd = _pr_md_pipefd[0]; pollfdPtr->events = POLLIN; pollfdPtr++; } min_timeout = PR_INTERVAL_NO_TIMEOUT; for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); _PRUnixPollDesc *pds = pq->pds; _PRUnixPollDesc *epds = pds + pq->npds; if (pq->timeout < min_timeout) { min_timeout = pq->timeout; } for (; pds < epds; pds++, pollfdPtr++) { /* * Assert that the pollfdPtr pointer does not go * beyond the end of the pollfds array */ PR_ASSERT(pollfdPtr < pollfds + npollfds); pollfdPtr->fd = pds->osfd; /* direct copy of poll flags */ pollfdPtr->events = pds->in_flags; } } _PR_IOQ_TIMEOUT(me->cpu) = min_timeout; #else /* * assigment of fd_sets */ r = _PR_FD_READ_SET(me->cpu); w = _PR_FD_WRITE_SET(me->cpu); e = _PR_FD_EXCEPTION_SET(me->cpu); rp = &r; wp = &w; ep = &e; max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1; min_timeout = _PR_IOQ_TIMEOUT(me->cpu); #endif /* _PR_USE_POLL */ /* ** Compute the minimum timeout value: make it the smaller of the ** timeouts specified by the i/o pollers or the timeout of the first ** sleeping thread. */ q = _PR_SLEEPQ(me->cpu).next; if (q != &_PR_SLEEPQ(me->cpu)) { PRThread *t = _PR_THREAD_PTR(q); if (t->sleep < min_timeout) { min_timeout = t->sleep; } } if (min_timeout > ticks) { min_timeout = ticks; } #ifdef _PR_USE_POLL if (min_timeout == PR_INTERVAL_NO_TIMEOUT) { timeout = -1; } else { timeout = PR_IntervalToMilliseconds(min_timeout); } #else if (min_timeout == PR_INTERVAL_NO_TIMEOUT) { tvp = NULL; } else { timeout.tv_sec = PR_IntervalToSeconds(min_timeout); timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout) % PR_USEC_PER_SEC; tvp = &timeout; } #endif /* _PR_USE_POLL */ _PR_MD_IOQ_UNLOCK(); _MD_CHECK_FOR_EXIT(); /* * check for i/o operations */ #ifndef _PR_NO_CLOCK_TIMER /* * Disable the clock interrupts while we are in select, if clock interrupts * are enabled. Otherwise, when the select/poll calls are interrupted, the * timer value starts ticking from zero again when the system call is restarted. */ if (!_nspr_noclock) { PR_ASSERT(sigismember(&timer_set, SIGALRM)); } sigprocmask(SIG_BLOCK, &timer_set, &oldset); #endif /* !_PR_NO_CLOCK_TIMER */ #ifndef _PR_USE_POLL PR_ASSERT(FD_ISSET(_pr_md_pipefd[0],rp)); nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp); #else nfd = _MD_POLL(pollfds, npollfds, timeout); #endif /* !_PR_USE_POLL */ #ifndef _PR_NO_CLOCK_TIMER if (!_nspr_noclock) { sigprocmask(SIG_SETMASK, &oldset, 0); } #endif /* !_PR_NO_CLOCK_TIMER */ _MD_CHECK_FOR_EXIT(); _PR_MD_primordial_cpu(); _PR_MD_IOQ_LOCK(); /* ** Notify monitors that are associated with the selected descriptors. */ #ifdef _PR_USE_POLL if (nfd > 0) { pollfdPtr = pollfds; if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { /* * Assert that the pipe is the first element in the * pollfds array. */ PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]); if ((pollfds[0].revents & POLLIN) && (nfd == 1)) { /* * woken up by another thread; read all the data * in the pipe to empty the pipe */ while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) == PIPE_BUF) { } PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN))); } pollfdPtr++; } for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); PRBool notify = PR_FALSE; _PRUnixPollDesc *pds = pq->pds; _PRUnixPollDesc *epds = pds + pq->npds; for (; pds < epds; pds++, pollfdPtr++) { /* * Assert that the pollfdPtr pointer does not go beyond * the end of the pollfds array. */ PR_ASSERT(pollfdPtr < pollfds + npollfds); /* * Assert that the fd's in the pollfds array (stepped * through by pollfdPtr) are in the same order as * the fd's in _PR_IOQ() (stepped through by q and pds). * This is how the pollfds array was created earlier. */ PR_ASSERT(pollfdPtr->fd == pds->osfd); pds->out_flags = pollfdPtr->revents; /* Negative fd's are ignored by poll() */ if (pds->osfd >= 0 && pds->out_flags) { notify = PR_TRUE; } } if (notify) { PRIntn pri; PRThread *thred; PR_REMOVE_LINK(&pq->links); pq->on_ioq = PR_FALSE; thred = pq->thr; _PR_THREAD_LOCK(thred); if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { _PRCPU *cpu = pq->thr->cpu; _PR_SLEEPQ_LOCK(pq->thr->cpu); _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); _PR_SLEEPQ_UNLOCK(pq->thr->cpu); if (pq->thr->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; * a Resume operation on the thread * will move it to the runQ */ pq->thr->state = _PR_SUSPENDED; _PR_MISCQ_LOCK(pq->thr->cpu); _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); _PR_MISCQ_UNLOCK(pq->thr->cpu); } else { pri = pq->thr->priority; pq->thr->state = _PR_RUNNABLE; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(pq->thr, cpu, pri); _PR_RUNQ_UNLOCK(cpu); if (_pr_md_idle_cpus > 1) { _PR_MD_WAKEUP_WAITER(thred); } } } _PR_THREAD_UNLOCK(thred); _PR_IOQ_OSFD_CNT(me->cpu) -= pq->npds; PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0); } } } else if (nfd == -1) { PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno)); } #else if (nfd > 0) { q = _PR_IOQ(me->cpu).next; _PR_IOQ_MAX_OSFD(me->cpu) = -1; _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; while (q != &_PR_IOQ(me->cpu)) { PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); PRBool notify = PR_FALSE; _PRUnixPollDesc *pds = pq->pds; _PRUnixPollDesc *epds = pds + pq->npds; PRInt32 pq_max_osfd = -1; q = q->next; for (; pds < epds; pds++) { PRInt32 osfd = pds->osfd; PRInt16 in_flags = pds->in_flags; PRInt16 out_flags = 0; PR_ASSERT(osfd >= 0 || in_flags == 0); if ((in_flags & _PR_UNIX_POLL_READ) && FD_ISSET(osfd, rp)) { out_flags |= _PR_UNIX_POLL_READ; } if ((in_flags & _PR_UNIX_POLL_WRITE) && FD_ISSET(osfd, wp)) { out_flags |= _PR_UNIX_POLL_WRITE; } if ((in_flags & _PR_UNIX_POLL_EXCEPT) && FD_ISSET(osfd, ep)) { out_flags |= _PR_UNIX_POLL_EXCEPT; } pds->out_flags = out_flags; if (out_flags) { notify = PR_TRUE; } if (osfd > pq_max_osfd) { pq_max_osfd = osfd; } } if (notify == PR_TRUE) { PRIntn pri; PRThread *thred; PR_REMOVE_LINK(&pq->links); pq->on_ioq = PR_FALSE; /* * Decrement the count of descriptors for each desciptor/event * because this I/O request is being removed from the * ioq */ pds = pq->pds; for (; pds < epds; pds++) { PRInt32 osfd = pds->osfd; PRInt16 in_flags = pds->in_flags; PR_ASSERT(osfd >= 0 || in_flags == 0); if (in_flags & _PR_UNIX_POLL_READ) { if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_WRITE) { if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_EXCEPT) { if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); } } } /* * Because this thread can run on a different cpu right * after being added to the run queue, do not dereference * pq */ thred = pq->thr; _PR_THREAD_LOCK(thred); if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { _PRCPU *cpu = thred->cpu; _PR_SLEEPQ_LOCK(pq->thr->cpu); _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); _PR_SLEEPQ_UNLOCK(pq->thr->cpu); if (pq->thr->flags & _PR_SUSPENDING) { /* * set thread state to SUSPENDED; * a Resume operation on the thread * will move it to the runQ */ pq->thr->state = _PR_SUSPENDED; _PR_MISCQ_LOCK(pq->thr->cpu); _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); _PR_MISCQ_UNLOCK(pq->thr->cpu); } else { pri = pq->thr->priority; pq->thr->state = _PR_RUNNABLE; pq->thr->cpu = cpu; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(pq->thr, cpu, pri); _PR_RUNQ_UNLOCK(cpu); if (_pr_md_idle_cpus > 1) { _PR_MD_WAKEUP_WAITER(thred); } } } _PR_THREAD_UNLOCK(thred); } else { if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; } if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; } } } if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) { /* * woken up by another thread; read all the data * in the pipe to empty the pipe */ while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) == PIPE_BUF) { } PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN))); } if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; } } } else if (nfd < 0) { if (errno == EBADF) { FindBadFDs(); } else { PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d", errno)); } } else { PR_ASSERT(nfd == 0); /* * compute the new value of _PR_IOQ_TIMEOUT */ q = _PR_IOQ(me->cpu).next; _PR_IOQ_MAX_OSFD(me->cpu) = -1; _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; while (q != &_PR_IOQ(me->cpu)) { PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); _PRUnixPollDesc *pds = pq->pds; _PRUnixPollDesc *epds = pds + pq->npds; PRInt32 pq_max_osfd = -1; q = q->next; for (; pds < epds; pds++) { if (pds->osfd > pq_max_osfd) { pq_max_osfd = pds->osfd; } } if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; } if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; } } if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; } } } #endif /* _PR_USE_POLL */ _PR_MD_IOQ_UNLOCK(); } void _MD_Wakeup_CPUs() { PRInt32 rv, data; data = 0; rv = write(_pr_md_pipefd[1], &data, 1); while ((rv < 0) && (errno == EAGAIN)) { /* * pipe full, read all data in pipe to empty it */ while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) == PIPE_BUF) { } PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN))); rv = write(_pr_md_pipefd[1], &data, 1); } } void _MD_InitCPUS() { PRInt32 rv, flags; PRThread *me = _MD_CURRENT_THREAD(); rv = pipe(_pr_md_pipefd); PR_ASSERT(rv == 0); _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; #ifndef _PR_USE_POLL FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu)); #endif flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0); fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK); flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0); fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK); } /* ** Unix SIGALRM (clock) signal handler */ static void ClockInterruptHandler() { int olderrno; PRUintn pri; _PRCPU *cpu = _PR_MD_CURRENT_CPU(); PRThread *me = _MD_CURRENT_THREAD(); #ifdef SOLARIS if (!me || _PR_IS_NATIVE_THREAD(me)) { _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK; return; } #endif if (_PR_MD_GET_INTSOFF() != 0) { cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK; return; } _PR_MD_SET_INTSOFF(1); olderrno = errno; _PR_ClockInterrupt(); errno = olderrno; /* ** If the interrupt wants a resched or if some other thread at ** the same priority needs the cpu, reschedule. */ pri = me->priority; if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) { #ifdef _PR_NO_PREEMPT cpu->resched = PR_TRUE; if (pr_interruptSwitchHook) { (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg); } #else /* _PR_NO_PREEMPT */ /* ** Re-enable unix interrupts (so that we can use ** setjmp/longjmp for context switching without having to ** worry about the signal state) */ sigprocmask(SIG_SETMASK, &empty_set, 0); PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch")); if(!(me->flags & _PR_IDLE_THREAD)) { _PR_THREAD_LOCK(me); me->state = _PR_RUNNABLE; me->cpu = cpu; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(me, cpu, pri); _PR_RUNQ_UNLOCK(cpu); _PR_THREAD_UNLOCK(me); } else { me->state = _PR_RUNNABLE; } _MD_SWITCH_CONTEXT(me); PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch")); #endif /* _PR_NO_PREEMPT */ } /* * Because this thread could be running on a different cpu after * a context switch the current cpu should be accessed and the * value of the 'cpu' variable should not be used. */ _PR_MD_SET_INTSOFF(0); } /* * On HP-UX 9, we have to use the sigvector() interface to restart * interrupted system calls, because sigaction() does not have the * SA_RESTART flag. */ #ifdef HPUX9 static void HPUX9_ClockInterruptHandler( int sig, int code, struct sigcontext *scp) { ClockInterruptHandler(); scp->sc_syscall_action = SIG_RESTART; } #endif /* HPUX9 */ /* # of milliseconds per clock tick that we will use */ #define MSEC_PER_TICK 50 void _MD_StartInterrupts() { char *eval; if ((eval = getenv("NSPR_NOCLOCK")) != NULL) { if (atoi(eval) == 0) { _nspr_noclock = 0; } else { _nspr_noclock = 1; } } #ifndef _PR_NO_CLOCK_TIMER if (!_nspr_noclock) { _MD_EnableClockInterrupts(); } #endif } void _MD_StopInterrupts() { sigprocmask(SIG_BLOCK, &timer_set, 0); } void _MD_EnableClockInterrupts() { struct itimerval itval; extern PRUintn _pr_numCPU; #ifdef HPUX9 struct sigvec vec; vec.sv_handler = (void (*)()) HPUX9_ClockInterruptHandler; vec.sv_mask = 0; vec.sv_flags = 0; sigvector(SIGALRM, &vec, 0); #else struct sigaction vtact; vtact.sa_handler = (void (*)()) ClockInterruptHandler; sigemptyset(&vtact.sa_mask); vtact.sa_flags = SA_RESTART; sigaction(SIGALRM, &vtact, 0); #endif /* HPUX9 */ PR_ASSERT(_pr_numCPU == 1); itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC; itval.it_value = itval.it_interval; setitimer(ITIMER_REAL, &itval, 0); } void _MD_DisableClockInterrupts() { struct itimerval itval; extern PRUintn _pr_numCPU; PR_ASSERT(_pr_numCPU == 1); itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = 0; itval.it_value = itval.it_interval; setitimer(ITIMER_REAL, &itval, 0); } void _MD_BlockClockInterrupts() { sigprocmask(SIG_BLOCK, &timer_set, 0); } void _MD_UnblockClockInterrupts() { sigprocmask(SIG_UNBLOCK, &timer_set, 0); } void _MD_MakeNonblock(PRFileDesc *fd) { PRInt32 osfd = fd->secret->md.osfd; int flags; if (osfd <= 2) { /* Don't mess around with stdin, stdout or stderr */ return; } flags = fcntl(osfd, F_GETFL, 0); /* * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible. * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O), * otherwise connect() still blocks and can be interrupted by SIGALRM. */ fcntl(osfd, F_SETFL, flags | O_NONBLOCK); } PRInt32 _MD_open(const char *name, PRIntn flags, PRIntn mode) { PRInt32 osflags; PRInt32 rv, err; if (flags & PR_RDWR) { osflags = O_RDWR; } else if (flags & PR_WRONLY) { osflags = O_WRONLY; } else { osflags = O_RDONLY; } if (flags & PR_EXCL) { osflags |= O_EXCL; } if (flags & PR_APPEND) { osflags |= O_APPEND; } if (flags & PR_TRUNCATE) { osflags |= O_TRUNC; } if (flags & PR_SYNC) { #if defined(O_SYNC) osflags |= O_SYNC; #elif defined(O_FSYNC) osflags |= O_FSYNC; #else #error "Neither O_SYNC nor O_FSYNC is defined on this platform" #endif } /* ** On creations we hold the 'create' lock in order to enforce ** the semantics of PR_Rename. (see the latter for more details) */ if (flags & PR_CREATE_FILE) { osflags |= O_CREAT; if (NULL !=_pr_unix_rename_lock) { PR_Lock(_pr_unix_rename_lock); } } #if defined(ANDROID) osflags |= O_LARGEFILE; #endif rv = _md_iovector._open64(name, osflags, mode); if (rv < 0) { err = _MD_ERRNO(); _PR_MD_MAP_OPEN_ERROR(err); } if ((flags & PR_CREATE_FILE) && (NULL !=_pr_unix_rename_lock)) { PR_Unlock(_pr_unix_rename_lock); } return rv; } PRIntervalTime intr_timeout_ticks; #if defined(SOLARIS) static void sigsegvhandler() { fprintf(stderr,"Received SIGSEGV\n"); fflush(stderr); pause(); } static void sigaborthandler() { fprintf(stderr,"Received SIGABRT\n"); fflush(stderr); pause(); } static void sigbushandler() { fprintf(stderr,"Received SIGBUS\n"); fflush(stderr); pause(); } #endif /* SOLARIS */ #endif /* !defined(_PR_PTHREADS) */ void _MD_query_fd_inheritable(PRFileDesc *fd) { int flags; PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); flags = fcntl(fd->secret->md.osfd, F_GETFD, 0); PR_ASSERT(-1 != flags); fd->secret->inheritable = (flags & FD_CLOEXEC) ? _PR_TRI_FALSE : _PR_TRI_TRUE; } PROffset32 _MD_lseek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) { PROffset32 rv, where; switch (whence) { case PR_SEEK_SET: where = SEEK_SET; break; case PR_SEEK_CUR: where = SEEK_CUR; break; case PR_SEEK_END: where = SEEK_END; break; default: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); rv = -1; goto done; } rv = lseek(fd->secret->md.osfd,offset,where); if (rv == -1) { PRInt32 syserr = _MD_ERRNO(); _PR_MD_MAP_LSEEK_ERROR(syserr); } done: return(rv); } PROffset64 _MD_lseek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) { PRInt32 where; PROffset64 rv; switch (whence) { case PR_SEEK_SET: where = SEEK_SET; break; case PR_SEEK_CUR: where = SEEK_CUR; break; case PR_SEEK_END: where = SEEK_END; break; default: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); rv = minus_one; goto done; } rv = _md_iovector._lseek64(fd->secret->md.osfd, offset, where); if (LL_EQ(rv, minus_one)) { PRInt32 syserr = _MD_ERRNO(); _PR_MD_MAP_LSEEK_ERROR(syserr); } done: return rv; } /* _MD_lseek64 */ /* ** _MD_set_fileinfo_times -- ** Set the modifyTime and creationTime of the PRFileInfo ** structure using the values in struct stat. ** ** _MD_set_fileinfo64_times -- ** Set the modifyTime and creationTime of the PRFileInfo64 ** structure using the values in _MDStat64. */ #if defined(_PR_STAT_HAS_ST_ATIM) /* ** struct stat has st_atim, st_mtim, and st_ctim fields of ** type timestruc_t. */ static void _MD_set_fileinfo_times( const struct stat *sb, PRFileInfo *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtim.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtim.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctim.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctim.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } static void _MD_set_fileinfo64_times( const _MDStat64 *sb, PRFileInfo64 *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtim.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtim.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctim.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctim.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } #elif defined(_PR_STAT_HAS_ST_ATIM_UNION) /* ** The st_atim, st_mtim, and st_ctim fields in struct stat are ** unions with a st__tim union member of type timestruc_t. */ static void _MD_set_fileinfo_times( const struct stat *sb, PRFileInfo *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } static void _MD_set_fileinfo64_times( const _MDStat64 *sb, PRFileInfo64 *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } #elif defined(_PR_STAT_HAS_ST_ATIMESPEC) /* ** struct stat has st_atimespec, st_mtimespec, and st_ctimespec ** fields of type struct timespec. */ #if defined(_PR_TIMESPEC_HAS_TS_SEC) static void _MD_set_fileinfo_times( const struct stat *sb, PRFileInfo *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } static void _MD_set_fileinfo64_times( const _MDStat64 *sb, PRFileInfo64 *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } #else /* _PR_TIMESPEC_HAS_TS_SEC */ /* ** The POSIX timespec structure has tv_sec and tv_nsec. */ static void _MD_set_fileinfo_times( const struct stat *sb, PRFileInfo *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } static void _MD_set_fileinfo64_times( const _MDStat64 *sb, PRFileInfo64 *info) { PRInt64 us, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec); LL_MUL(info->modifyTime, info->modifyTime, s2us); LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000); LL_ADD(info->modifyTime, info->modifyTime, us); LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec); LL_MUL(info->creationTime, info->creationTime, s2us); LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000); LL_ADD(info->creationTime, info->creationTime, us); } #endif /* _PR_TIMESPEC_HAS_TS_SEC */ #elif defined(_PR_STAT_HAS_ONLY_ST_ATIME) /* ** struct stat only has st_atime, st_mtime, and st_ctime fields ** of type time_t. */ static void _MD_set_fileinfo_times( const struct stat *sb, PRFileInfo *info) { PRInt64 s, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(s, sb->st_mtime); LL_MUL(s, s, s2us); info->modifyTime = s; LL_I2L(s, sb->st_ctime); LL_MUL(s, s, s2us); info->creationTime = s; } static void _MD_set_fileinfo64_times( const _MDStat64 *sb, PRFileInfo64 *info) { PRInt64 s, s2us; LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(s, sb->st_mtime); LL_MUL(s, s, s2us); info->modifyTime = s; LL_I2L(s, sb->st_ctime); LL_MUL(s, s, s2us); info->creationTime = s; } #else #error "I don't know yet" #endif static int _MD_convert_stat_to_fileinfo( const struct stat *sb, PRFileInfo *info) { if (S_IFREG & sb->st_mode) { info->type = PR_FILE_FILE; } else if (S_IFDIR & sb->st_mode) { info->type = PR_FILE_DIRECTORY; } else { info->type = PR_FILE_OTHER; } #if defined(_PR_HAVE_LARGE_OFF_T) if (0x7fffffffL < sb->st_size) { PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); return -1; } #endif /* defined(_PR_HAVE_LARGE_OFF_T) */ info->size = sb->st_size; _MD_set_fileinfo_times(sb, info); return 0; } /* _MD_convert_stat_to_fileinfo */ static int _MD_convert_stat64_to_fileinfo64( const _MDStat64 *sb, PRFileInfo64 *info) { if (S_IFREG & sb->st_mode) { info->type = PR_FILE_FILE; } else if (S_IFDIR & sb->st_mode) { info->type = PR_FILE_DIRECTORY; } else { info->type = PR_FILE_OTHER; } LL_I2L(info->size, sb->st_size); _MD_set_fileinfo64_times(sb, info); return 0; } /* _MD_convert_stat64_to_fileinfo64 */ PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info) { PRInt32 rv; struct stat sb; rv = stat(fn, &sb); if (rv < 0) { _PR_MD_MAP_STAT_ERROR(_MD_ERRNO()); } else if (NULL != info) { rv = _MD_convert_stat_to_fileinfo(&sb, info); } return rv; } PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info) { _MDStat64 sb; PRInt32 rv = _md_iovector._stat64(fn, &sb); if (rv < 0) { _PR_MD_MAP_STAT_ERROR(_MD_ERRNO()); } else if (NULL != info) { rv = _MD_convert_stat64_to_fileinfo64(&sb, info); } return rv; } PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info) { struct stat sb; PRInt32 rv = fstat(fd->secret->md.osfd, &sb); if (rv < 0) { _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO()); } else if (NULL != info) { rv = _MD_convert_stat_to_fileinfo(&sb, info); } return rv; } PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info) { _MDStat64 sb; PRInt32 rv = _md_iovector._fstat64(fd->secret->md.osfd, &sb); if (rv < 0) { _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO()); } else if (NULL != info) { rv = _MD_convert_stat64_to_fileinfo64(&sb, info); } return rv; } /* * _md_iovector._open64 must be initialized to 'open' so that _PR_InitLog can * open the log file during NSPR initialization, before _md_iovector is * initialized by _PR_MD_FINAL_INIT. This means the log file cannot be a * large file on some platforms. */ struct _MD_IOVector _md_iovector = { open }; /* ** These implementations are to emulate large file routines on systems that ** don't have them. Their goal is to check in case overflow occurs. Otherwise ** they will just operate as normal using 32-bit file routines. ** ** The checking might be pre- or post-op, depending on the semantics. */ #if defined(SOLARIS2_5) static PRIntn _MD_solaris25_fstat64(PRIntn osfd, _MDStat64 *buf) { PRInt32 rv; struct stat sb; rv = fstat(osfd, &sb); if (rv >= 0) { /* ** I'm only copying the fields that are immediately needed. ** If somebody else calls this function, some of the fields ** may not be defined. */ (void)memset(buf, 0, sizeof(_MDStat64)); buf->st_mode = sb.st_mode; buf->st_ctim = sb.st_ctim; buf->st_mtim = sb.st_mtim; buf->st_size = sb.st_size; } return rv; } /* _MD_solaris25_fstat64 */ static PRIntn _MD_solaris25_stat64(const char *fn, _MDStat64 *buf) { PRInt32 rv; struct stat sb; rv = stat(fn, &sb); if (rv >= 0) { /* ** I'm only copying the fields that are immediately needed. ** If somebody else calls this function, some of the fields ** may not be defined. */ (void)memset(buf, 0, sizeof(_MDStat64)); buf->st_mode = sb.st_mode; buf->st_ctim = sb.st_ctim; buf->st_mtim = sb.st_mtim; buf->st_size = sb.st_size; } return rv; } /* _MD_solaris25_stat64 */ #endif /* defined(SOLARIS2_5) */ #if defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) static PROffset64 _MD_Unix_lseek64(PRIntn osfd, PROffset64 offset, PRIntn whence) { PRUint64 maxoff; PROffset64 rv = minus_one; LL_I2L(maxoff, 0x7fffffff); if (LL_CMP(offset, <=, maxoff)) { off_t off; LL_L2I(off, offset); LL_I2L(rv, lseek(osfd, off, whence)); } else { errno = EFBIG; /* we can't go there */ } return rv; } /* _MD_Unix_lseek64 */ static void* _MD_Unix_mmap64( void *addr, PRSize len, PRIntn prot, PRIntn flags, PRIntn fildes, PRInt64 offset) { PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); return NULL; } /* _MD_Unix_mmap64 */ #endif /* defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) */ /* NDK non-unified headers for API < 21 don't have mmap64. However, * NDK unified headers do provide mmap64 for all API versions when building * with clang. Therefore, we should provide mmap64 here for API < 21 if we're * not using clang or if we're using non-unified headers. We check for * non-unified headers by the lack of __ANDROID_API_L__ macro. */ #if defined(ANDROID) && __ANDROID_API__ < 21 && \ (!defined(__clang__) || !defined(__ANDROID_API_L__)) PR_IMPORT(void) *__mmap2(void *, size_t, int, int, int, size_t); #define ANDROID_PAGE_SIZE 4096 static void * mmap64(void *addr, size_t len, int prot, int flags, int fd, loff_t offset) { if (offset & (ANDROID_PAGE_SIZE - 1)) { errno = EINVAL; return MAP_FAILED; } return __mmap2(addr, len, prot, flags, fd, offset / ANDROID_PAGE_SIZE); } #endif static void _PR_InitIOV(void) { #if defined(SOLARIS2_5) PRLibrary *lib; void *open64_func; open64_func = PR_FindSymbolAndLibrary("open64", &lib); if (NULL != open64_func) { PR_ASSERT(NULL != lib); _md_iovector._open64 = (_MD_Open64)open64_func; _md_iovector._mmap64 = (_MD_Mmap64)PR_FindSymbol(lib, "mmap64"); _md_iovector._fstat64 = (_MD_Fstat64)PR_FindSymbol(lib, "fstat64"); _md_iovector._stat64 = (_MD_Stat64)PR_FindSymbol(lib, "stat64"); _md_iovector._lseek64 = (_MD_Lseek64)PR_FindSymbol(lib, "lseek64"); (void)PR_UnloadLibrary(lib); } else { _md_iovector._open64 = open; _md_iovector._mmap64 = _MD_Unix_mmap64; _md_iovector._fstat64 = _MD_solaris25_fstat64; _md_iovector._stat64 = _MD_solaris25_stat64; _md_iovector._lseek64 = _MD_Unix_lseek64; } #elif defined(_PR_NO_LARGE_FILES) _md_iovector._open64 = open; _md_iovector._mmap64 = _MD_Unix_mmap64; _md_iovector._fstat64 = fstat; _md_iovector._stat64 = stat; _md_iovector._lseek64 = _MD_Unix_lseek64; #elif defined(_PR_HAVE_OFF64_T) #if (defined(ANDROID) && __ANDROID_API__ < 21) /* * Android < 21 doesn't have open64. We pass the O_LARGEFILE flag to open * in _MD_open. */ _md_iovector._open64 = open; #else _md_iovector._open64 = open64; #endif _md_iovector._mmap64 = mmap64; #if (defined(ANDROID) && __ANDROID_API__ < 21) /* Same as the open64 case for Android. */ _md_iovector._fstat64 = fstat; _md_iovector._stat64 = stat; #else _md_iovector._fstat64 = fstat64; _md_iovector._stat64 = stat64; #endif _md_iovector._lseek64 = lseek64; #elif defined(_PR_HAVE_LARGE_OFF_T) _md_iovector._open64 = open; _md_iovector._mmap64 = mmap; _md_iovector._fstat64 = fstat; _md_iovector._stat64 = stat; _md_iovector._lseek64 = lseek; #else #error "I don't know yet" #endif LL_I2L(minus_one, -1); } /* _PR_InitIOV */ void _PR_UnixInit(void) { struct sigaction sigact; int rv; sigemptyset(&timer_set); #if !defined(_PR_PTHREADS) sigaddset(&timer_set, SIGALRM); sigemptyset(&empty_set); intr_timeout_ticks = PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS); #if defined(SOLARIS) if (getenv("NSPR_SIGSEGV_HANDLE")) { sigact.sa_handler = sigsegvhandler; sigact.sa_flags = 0; sigact.sa_mask = timer_set; sigaction(SIGSEGV, &sigact, 0); } if (getenv("NSPR_SIGABRT_HANDLE")) { sigact.sa_handler = sigaborthandler; sigact.sa_flags = 0; sigact.sa_mask = timer_set; sigaction(SIGABRT, &sigact, 0); } if (getenv("NSPR_SIGBUS_HANDLE")) { sigact.sa_handler = sigbushandler; sigact.sa_flags = 0; sigact.sa_mask = timer_set; sigaction(SIGBUS, &sigact, 0); } #endif #endif /* !defined(_PR_PTHREADS) */ sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; rv = sigaction(SIGPIPE, &sigact, 0); PR_ASSERT(0 == rv); _pr_unix_rename_lock = PR_NewLock(); PR_ASSERT(NULL != _pr_unix_rename_lock); _pr_Xfe_mon = PR_NewMonitor(); PR_ASSERT(NULL != _pr_Xfe_mon); _PR_InitIOV(); /* one last hack */ } void _PR_UnixCleanup(void) { if (_pr_unix_rename_lock) { PR_DestroyLock(_pr_unix_rename_lock); _pr_unix_rename_lock = NULL; } if (_pr_Xfe_mon) { PR_DestroyMonitor(_pr_Xfe_mon); _pr_Xfe_mon = NULL; } } #if !defined(_PR_PTHREADS) /* * Variables used by the GC code, initialized in _MD_InitSegs(). */ static PRInt32 _pr_zero_fd = -1; static PRLock *_pr_md_lock = NULL; /* * _MD_InitSegs -- * * This is Unix's version of _PR_MD_INIT_SEGS(), which is * called by _PR_InitSegs(), which in turn is called by * PR_Init(). */ void _MD_InitSegs(void) { #ifdef DEBUG /* ** Disable using mmap(2) if NSPR_NO_MMAP is set */ if (getenv("NSPR_NO_MMAP")) { _pr_zero_fd = -2; return; } #endif _pr_zero_fd = open("/dev/zero",O_RDWR, 0); /* Prevent the fd from being inherited by child processes */ fcntl(_pr_zero_fd, F_SETFD, FD_CLOEXEC); _pr_md_lock = PR_NewLock(); } PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr) { static char *lastaddr = (char*) _PR_STACK_VMBASE; PRStatus retval = PR_SUCCESS; int prot; void *rv; PR_ASSERT(seg != 0); PR_ASSERT(size != 0); PR_Lock(_pr_md_lock); if (_pr_zero_fd < 0) { from_heap: seg->vaddr = PR_MALLOC(size); if (!seg->vaddr) { retval = PR_FAILURE; } else { seg->size = size; } goto exit; } prot = PROT_READ|PROT_WRITE; /* * On Alpha Linux, the user-level thread stack needs * to be made executable because longjmp/signal seem * to put machine instructions on the stack. */ #if defined(LINUX) && defined(__alpha) prot |= PROT_EXEC; #endif rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot, _MD_MMAP_FLAGS, _pr_zero_fd, 0); if (rv == (void*)-1) { goto from_heap; } lastaddr += size; seg->vaddr = rv; seg->size = size; seg->flags = _PR_SEG_VM; exit: PR_Unlock(_pr_md_lock); return retval; } void _MD_FreeSegment(PRSegment *seg) { if (seg->flags & _PR_SEG_VM) { (void) munmap(seg->vaddr, seg->size); } else { PR_DELETE(seg->vaddr); } } #endif /* _PR_PTHREADS */ /* *----------------------------------------------------------------------- * * PR_Now -- * * Returns the current time in microseconds since the epoch. * The epoch is midnight January 1, 1970 GMT. * The implementation is machine dependent. This is the Unix * implementation. * Cf. time_t time(time_t *tp) * *----------------------------------------------------------------------- */ PR_IMPLEMENT(PRTime) PR_Now(void) { struct timeval tv; PRInt64 s, us, s2us; GETTIMEOFDAY(&tv); LL_I2L(s2us, PR_USEC_PER_SEC); LL_I2L(s, tv.tv_sec); LL_I2L(us, tv.tv_usec); LL_MUL(s, s, s2us); LL_ADD(s, s, us); return s; } #if defined(_MD_INTERVAL_USE_GTOD) /* * This version of interval times is based on the time of day * capability offered by the system. This isn't valid for two reasons: * 1) The time of day is neither linear nor montonically increasing * 2) The units here are milliseconds. That's not appropriate for our use. */ PRIntervalTime _PR_UNIX_GetInterval() { struct timeval time; PRIntervalTime ticks; (void)GETTIMEOFDAY(&time); /* fallicy of course */ ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */ ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */ return ticks; } /* _PR_UNIX_GetInterval */ PRIntervalTime _PR_UNIX_TicksPerSecond() { return 1000; /* this needs some work :) */ } #endif #if defined(_PR_HAVE_CLOCK_MONOTONIC) PRIntervalTime _PR_UNIX_GetInterval2() { struct timespec time; PRIntervalTime ticks; if (clock_gettime(CLOCK_MONOTONIC, &time) != 0) { fprintf(stderr, "clock_gettime failed: %d\n", errno); abort(); } ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; ticks += (PRUint32)time.tv_nsec / PR_NSEC_PER_MSEC; return ticks; } PRIntervalTime _PR_UNIX_TicksPerSecond2() { return 1000; } #endif #if !defined(_PR_PTHREADS) /* * Wait for I/O on multiple descriptors. * * Return 0 if timed out, return -1 if interrupted, * else return the number of ready descriptors. */ PRInt32 _PR_WaitForMultipleFDs( _PRUnixPollDesc *unixpds, PRInt32 pdcnt, PRIntervalTime timeout) { PRPollQueue pq; PRIntn is; PRInt32 rv; _PRCPU *io_cpu; _PRUnixPollDesc *unixpd, *eunixpd; PRThread *me = _PR_MD_CURRENT_THREAD(); PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return -1; } pq.pds = unixpds; pq.npds = pdcnt; _PR_INTSOFF(is); _PR_MD_IOQ_LOCK(); _PR_THREAD_LOCK(me); pq.thr = me; io_cpu = me->cpu; pq.on_ioq = PR_TRUE; pq.timeout = timeout; _PR_ADD_TO_IOQ(pq, me->cpu); #if !defined(_PR_USE_POLL) eunixpd = unixpds + pdcnt; for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { PRInt32 osfd = unixpd->osfd; if (unixpd->in_flags & _PR_UNIX_POLL_READ) { FD_SET(osfd, &_PR_FD_READ_SET(me->cpu)); _PR_FD_READ_CNT(me->cpu)[osfd]++; } if (unixpd->in_flags & _PR_UNIX_POLL_WRITE) { FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu)); (_PR_FD_WRITE_CNT(me->cpu))[osfd]++; } if (unixpd->in_flags & _PR_UNIX_POLL_EXCEPT) { FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++; } if (osfd > _PR_IOQ_MAX_OSFD(me->cpu)) { _PR_IOQ_MAX_OSFD(me->cpu) = osfd; } } #endif /* !defined(_PR_USE_POLL) */ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout) { _PR_IOQ_TIMEOUT(me->cpu) = timeout; } _PR_IOQ_OSFD_CNT(me->cpu) += pdcnt; _PR_SLEEPQ_LOCK(me->cpu); _PR_ADD_SLEEPQ(me, timeout); me->state = _PR_IO_WAIT; me->io_pending = PR_TRUE; me->io_suspended = PR_FALSE; _PR_SLEEPQ_UNLOCK(me->cpu); _PR_THREAD_UNLOCK(me); _PR_MD_IOQ_UNLOCK(); _PR_MD_WAIT(me, timeout); me->io_pending = PR_FALSE; me->io_suspended = PR_FALSE; /* * This thread should run on the same cpu on which it was blocked; when * the IO request times out the fd sets and fd counts for the * cpu are updated below. */ PR_ASSERT(me->cpu == io_cpu); /* ** If we timed out the pollq might still be on the ioq. Remove it ** before continuing. */ if (pq.on_ioq) { _PR_MD_IOQ_LOCK(); /* * Need to check pq.on_ioq again */ if (pq.on_ioq) { PR_REMOVE_LINK(&pq.links); #ifndef _PR_USE_POLL eunixpd = unixpds + pdcnt; for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { PRInt32 osfd = unixpd->osfd; PRInt16 in_flags = unixpd->in_flags; if (in_flags & _PR_UNIX_POLL_READ) { if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_WRITE) { if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); } } if (in_flags & _PR_UNIX_POLL_EXCEPT) { if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); } } } #endif /* _PR_USE_POLL */ PR_ASSERT(pq.npds == pdcnt); _PR_IOQ_OSFD_CNT(me->cpu) -= pdcnt; PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0); } _PR_MD_IOQ_UNLOCK(); } /* XXX Should we use _PR_FAST_INTSON or _PR_INTSON? */ if (1 == pdcnt) { _PR_FAST_INTSON(is); } else { _PR_INTSON(is); } if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return -1; } rv = 0; if (pq.on_ioq == PR_FALSE) { /* Count the number of ready descriptors */ while (--pdcnt >= 0) { if (unixpds->out_flags != 0) { rv++; } unixpds++; } } return rv; } /* * Unblock threads waiting for I/O * used when interrupting threads * * NOTE: The thread lock should held when this function is called. * On return, the thread lock is released. */ void _PR_Unblock_IO_Wait(PRThread *thr) { int pri = thr->priority; _PRCPU *cpu = thr->cpu; /* * GLOBAL threads wakeup periodically to check for interrupt */ if (_PR_IS_NATIVE_THREAD(thr)) { _PR_THREAD_UNLOCK(thr); return; } PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ)); _PR_SLEEPQ_LOCK(cpu); _PR_DEL_SLEEPQ(thr, PR_TRUE); _PR_SLEEPQ_UNLOCK(cpu); PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD)); thr->state = _PR_RUNNABLE; _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(thr, cpu, pri); _PR_RUNQ_UNLOCK(cpu); _PR_THREAD_UNLOCK(thr); _PR_MD_WAKEUP_WAITER(thr); } #endif /* !defined(_PR_PTHREADS) */ /* * When a nonblocking connect has completed, determine whether it * succeeded or failed, and if it failed, what the error code is. * * The function returns the error code. An error code of 0 means * that the nonblocking connect succeeded. */ int _MD_unix_get_nonblocking_connect_error(int osfd) { #if defined(NTO) /* Neutrino does not support the SO_ERROR socket option */ PRInt32 rv; PRNetAddr addr; _PRSockLen_t addrlen = sizeof(addr); /* Test to see if we are using the Tiny TCP/IP Stack or the Full one. */ struct statvfs superblock; rv = fstatvfs(osfd, &superblock); if (rv == 0) { if (strcmp(superblock.f_basetype, "ttcpip") == 0) { /* Using the Tiny Stack! */ rv = getpeername(osfd, (struct sockaddr *) &addr, (_PRSockLen_t *) &addrlen); if (rv == -1) { int errno_copy = errno; /* make a copy so I don't * accidentally reset */ if (errno_copy == ENOTCONN) { struct stat StatInfo; rv = fstat(osfd, &StatInfo); if (rv == 0) { time_t current_time = time(NULL); /* * this is a real hack, can't explain why it * works it just does */ if (abs(current_time - StatInfo.st_atime) < 5) { return ECONNREFUSED; } else { return ETIMEDOUT; } } else { return ECONNREFUSED; } } else { return errno_copy; } } else { /* No Error */ return 0; } } else { /* Have the FULL Stack which supports SO_ERROR */ /* Hasn't been written yet, never been tested! */ /* Jerry.Kirk@Nexwarecorp.com */ int err; _PRSockLen_t optlen = sizeof(err); if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) == -1) { return errno; } else { return err; } } } else { return ECONNREFUSED; } #elif defined(UNIXWARE) /* * getsockopt() fails with EPIPE, so use getmsg() instead. */ int rv; int flags = 0; rv = getmsg(osfd, NULL, NULL, &flags); PR_ASSERT(-1 == rv || 0 == rv); if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) { return errno; } return 0; /* no error */ #else int err; _PRSockLen_t optlen = sizeof(err); if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) == -1) { return errno; } return err; #endif } /************************************************************************/ /* ** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread ** safe. Unfortunately, neither is mozilla. To make these programs work ** in a pre-emptive threaded environment, we need to use a lock. */ void PR_XLock(void) { PR_EnterMonitor(_pr_Xfe_mon); } void PR_XUnlock(void) { PR_ExitMonitor(_pr_Xfe_mon); } PRBool PR_XIsLocked(void) { return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE; } void PR_XWait(int ms) { PR_Wait(_pr_Xfe_mon, PR_MillisecondsToInterval(ms)); } void PR_XNotify(void) { PR_Notify(_pr_Xfe_mon); } void PR_XNotifyAll(void) { PR_NotifyAll(_pr_Xfe_mon); } #if defined(HAVE_FCNTL_FILE_LOCKING) PRStatus _MD_LockFile(PRInt32 f) { PRInt32 rv; struct flock arg; arg.l_type = F_WRLCK; arg.l_whence = SEEK_SET; arg.l_start = 0; arg.l_len = 0; /* until EOF */ rv = fcntl(f, F_SETLKW, &arg); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_TLockFile(PRInt32 f) { PRInt32 rv; struct flock arg; arg.l_type = F_WRLCK; arg.l_whence = SEEK_SET; arg.l_start = 0; arg.l_len = 0; /* until EOF */ rv = fcntl(f, F_SETLK, &arg); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_UnlockFile(PRInt32 f) { PRInt32 rv; struct flock arg; arg.l_type = F_UNLCK; arg.l_whence = SEEK_SET; arg.l_start = 0; arg.l_len = 0; /* until EOF */ rv = fcntl(f, F_SETLK, &arg); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } #elif defined(HAVE_BSD_FLOCK) #include PRStatus _MD_LockFile(PRInt32 f) { PRInt32 rv; rv = flock(f, LOCK_EX); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_TLockFile(PRInt32 f) { PRInt32 rv; rv = flock(f, LOCK_EX|LOCK_NB); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_UnlockFile(PRInt32 f) { PRInt32 rv; rv = flock(f, LOCK_UN); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); return PR_FAILURE; } #else PRStatus _MD_LockFile(PRInt32 f) { PRInt32 rv; rv = lockf(f, F_LOCK, 0); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_TLockFile(PRInt32 f) { PRInt32 rv; rv = lockf(f, F_TLOCK, 0); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_UnlockFile(PRInt32 f) { PRInt32 rv; rv = lockf(f, F_ULOCK, 0); if (rv == 0) { return PR_SUCCESS; } _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); return PR_FAILURE; } #endif PRStatus _MD_gethostname(char *name, PRUint32 namelen) { PRIntn rv; rv = gethostname(name, namelen); if (0 == rv) { return PR_SUCCESS; } _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO()); return PR_FAILURE; } PRStatus _MD_getsysinfo(PRSysInfo cmd, char *name, PRUint32 namelen) { struct utsname info; PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) || (cmd == PR_SI_RELEASE_BUILD)); if (uname(&info) == -1) { _PR_MD_MAP_DEFAULT_ERROR(errno); return PR_FAILURE; } if (PR_SI_SYSNAME == cmd) { (void)PR_snprintf(name, namelen, info.sysname); } else if (PR_SI_RELEASE == cmd) { (void)PR_snprintf(name, namelen, info.release); } else if (PR_SI_RELEASE_BUILD == cmd) { (void)PR_snprintf(name, namelen, info.version); } else { return PR_FAILURE; } return PR_SUCCESS; } /* ******************************************************************* * * Memory-mapped files * ******************************************************************* */ PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) { PRFileInfo info; PRUint32 sz; LL_L2UI(sz, size); if (sz) { if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) { return PR_FAILURE; } if (sz > info.size) { /* * Need to extend the file */ if (fmap->prot != PR_PROT_READWRITE) { PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); return PR_FAILURE; } if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) { return PR_FAILURE; } if (PR_Write(fmap->fd, "", 1) != 1) { return PR_FAILURE; } } } if (fmap->prot == PR_PROT_READONLY) { fmap->md.prot = PROT_READ; #if defined(DARWIN) || defined(ANDROID) /* * This is needed on OS X because its implementation of * POSIX shared memory returns an error for MAP_PRIVATE, even * when the mapping is read-only. * * And this is needed on Android, because mapping ashmem with * MAP_PRIVATE creates a mapping of zeroed memory instead of * the shm contents. */ fmap->md.flags = MAP_SHARED; #else fmap->md.flags = MAP_PRIVATE; #endif } else if (fmap->prot == PR_PROT_READWRITE) { fmap->md.prot = PROT_READ | PROT_WRITE; fmap->md.flags = MAP_SHARED; } else { PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); fmap->md.prot = PROT_READ | PROT_WRITE; fmap->md.flags = MAP_PRIVATE; } return PR_SUCCESS; } void * _MD_MemMap( PRFileMap *fmap, PRInt64 offset, PRUint32 len) { PRInt32 off; void *addr; LL_L2I(off, offset); if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags, fmap->fd->secret->md.osfd, off)) == (void *) -1) { _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); addr = NULL; } return addr; } PRStatus _MD_MemUnmap(void *addr, PRUint32 len) { if (munmap(addr, len) == 0) { return PR_SUCCESS; } _PR_MD_MAP_DEFAULT_ERROR(errno); return PR_FAILURE; } PRStatus _MD_CloseFileMap(PRFileMap *fmap) { if ( PR_TRUE == fmap->md.isAnonFM ) { PRStatus rc = PR_Close( fmap->fd ); if ( PR_FAILURE == rc ) { PR_LOG( _pr_io_lm, PR_LOG_DEBUG, ("_MD_CloseFileMap(): error closing anonymnous file map osfd")); return PR_FAILURE; } } PR_DELETE(fmap); return PR_SUCCESS; } PRStatus _MD_SyncMemMap( PRFileDesc *fd, void *addr, PRUint32 len) { /* msync(..., MS_SYNC) alone is sufficient to flush modified data to disk * synchronously. It is not necessary to call fsync. */ if (msync(addr, len, MS_SYNC) == 0) { return PR_SUCCESS; } _PR_MD_MAP_DEFAULT_ERROR(errno); return PR_FAILURE; } #if defined(_PR_NEED_FAKE_POLL) /* * Some platforms don't have poll(). For easier porting of code * that calls poll(), we emulate poll() using select(). */ int poll(struct pollfd *filedes, unsigned long nfds, int timeout) { int i; int rv; int maxfd; fd_set rd, wr, ex; struct timeval tv, *tvp; if (timeout < 0 && timeout != -1) { errno = EINVAL; return -1; } if (timeout == -1) { tvp = NULL; } else { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; tvp = &tv; } maxfd = -1; FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); for (i = 0; i < nfds; i++) { int osfd = filedes[i].fd; int events = filedes[i].events; PRBool fdHasEvent = PR_FALSE; if (osfd < 0) { continue; /* Skip this osfd. */ } /* * Map the poll events to the select fd_sets. * POLLIN, POLLRDNORM ===> readable * POLLOUT, POLLWRNORM ===> writable * POLLPRI, POLLRDBAND ===> exception * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) * are ignored. * * The output events POLLERR and POLLHUP are never turned on. * POLLNVAL may be turned on. */ if (events & (POLLIN | POLLRDNORM)) { FD_SET(osfd, &rd); fdHasEvent = PR_TRUE; } if (events & (POLLOUT | POLLWRNORM)) { FD_SET(osfd, &wr); fdHasEvent = PR_TRUE; } if (events & (POLLPRI | POLLRDBAND)) { FD_SET(osfd, &ex); fdHasEvent = PR_TRUE; } if (fdHasEvent && osfd > maxfd) { maxfd = osfd; } } rv = select(maxfd + 1, &rd, &wr, &ex, tvp); /* Compute poll results */ if (rv > 0) { rv = 0; for (i = 0; i < nfds; i++) { PRBool fdHasEvent = PR_FALSE; filedes[i].revents = 0; if (filedes[i].fd < 0) { continue; } if (FD_ISSET(filedes[i].fd, &rd)) { if (filedes[i].events & POLLIN) { filedes[i].revents |= POLLIN; } if (filedes[i].events & POLLRDNORM) { filedes[i].revents |= POLLRDNORM; } fdHasEvent = PR_TRUE; } if (FD_ISSET(filedes[i].fd, &wr)) { if (filedes[i].events & POLLOUT) { filedes[i].revents |= POLLOUT; } if (filedes[i].events & POLLWRNORM) { filedes[i].revents |= POLLWRNORM; } fdHasEvent = PR_TRUE; } if (FD_ISSET(filedes[i].fd, &ex)) { if (filedes[i].events & POLLPRI) { filedes[i].revents |= POLLPRI; } if (filedes[i].events & POLLRDBAND) { filedes[i].revents |= POLLRDBAND; } fdHasEvent = PR_TRUE; } if (fdHasEvent) { rv++; } } PR_ASSERT(rv > 0); } else if (rv == -1 && errno == EBADF) { rv = 0; for (i = 0; i < nfds; i++) { filedes[i].revents = 0; if (filedes[i].fd < 0) { continue; } if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) { filedes[i].revents = POLLNVAL; rv++; } } PR_ASSERT(rv > 0); } PR_ASSERT(-1 != timeout || rv != 0); return rv; } #endif /* _PR_NEED_FAKE_POLL */