Milán Major

netscape-revival

1 branch
Code

nspr/src/prnetio.c

#include <stdarg.h>
#include <memory.h>
#include "prfile.h"
#include "prmon.h"

#ifdef SW_THREADS
#include "swkern.h"
#endif

#if !defined(XP_STRLEN)
#define XP_STRLEN(str)          strlen(str)
#endif

#if defined(IRIX)
#include <signal.h>
extern sigset_t _pr_holdALRM;
#endif

#ifdef SOLARIS
#include <sys/filio.h>
#endif

#if defined(SCO)
#include <sys/socket.h>
#endif

/* XXX into a header file somewhere */
#if defined(XP_UNIX)
#include <sys/time.h>
#define bzero(a,b) memset(a,0,b)

#define _OS_ERROR_EAGAIN        (-EAGAIN)
#define _OS_ERROR_EINTR         (-EINTR)
#define _OS_ERROR_EINPROGRESS	(-EINPROGRESS)
#define _OS_ERROR_EALREADY	(-EALREADY)
#define _OS_ERROR_ECONNREFUSED	(-ECONNREFUSED)
#define _OS_ERROR_EISCONN	(-EISCONN)

#define PR_ERROR_INVALID_FD     (-EBADF)
#define PR_ERROR_WOULD_BLOCK    (-EWOULDBLOCK)

#elif defined(XP_PC)
write me (use wsa error codes);
#elif defined(XP_MAC)
write me;
#endif

PRIOMethods _pr_net_methods;

extern PRFileHandle _PR_InitFileHandle(PROSFD osfd, PRIOMethods *m);

/*
** Import an existing OS socket to NSPR. Under UNIX, useful for importing
** pipes and socketpairs.
*/
PRFileHandle PR_ImportSocket(PROSFD osfd)
{
    PRFileHandle hdl = -1;

    _PR_InitIO();
    /* Make socket non-blocking */
#if defined(XP_UNIX)
    {
	int flgs;
	if ((flgs = _OS_FCNTL(osfd, F_GETFL, 0)) == -1) {
	    /* We are hosed */
	    (void) _OS_CLOSE_SOCKET((PROSFD) osfd);
	    PR_SetError(flgs);
	    goto done;
	}
	_OS_FCNTL(osfd, F_SETFL, flgs | O_NONBLOCK);
    }
#elif defined(XP_PC)
    write me;
#elif defined(XP_MAC)
    write me;
#endif

    /* the os-file-handle is valid */
    hdl = _PR_InitFileHandle((PROSFD) osfd, &_pr_net_methods);
    if( !hdl ) {
        /*
	** Unable to get a slot in the FD table. Ignore error from
	** OS_CLOSE so we don't hide table allocation failure.
	*/
        (void) _OS_CLOSE_SOCKET((PROSFD) osfd);
        hdl = -1;
    }

   done:
    return hdl;
}


/*
** Create a socket. Uses PR_ImportSocket to introduce OS socket to NSPR.
*/
PRFileHandle PR_CreateSocket(int domain, int type, int protocol)
{
    int osfd;

    _PR_InitIO();

    osfd = _OS_SOCKET(domain, type, protocol);
    if (osfd < 0) {
        /* unable to open the file. */
        PR_SetError(osfd);
        return -1;
    } 
    return PR_ImportSocket(osfd);
}

/*
 * On SunOS 2.3 the connect routine (really a library call) can
 * sometimes return an error and errno == EPROTO.  in this case it
 * is pointless to restart the connect call as it will just fail
 * again with EPROTO.  I don't know what causes this but it seem
 * like for now that the only way to fix the problem is to shut the
 * socket down and open it up again.  We report the error to Java and
 * it can try to restart the connection.  Also, if the connection is
 * refused, it seems that select() returns 1, as if everything is
 * OK, but there's no way to find out that the connection failed
 * until you try something!  So, now we try something immediately,
 * and see what we get.  What this boils down to is socket "system
 * calls" do not work when the socket is in nonblocking I/O mode.
 */
int32 PR_ConnectSocket(PRFileDesc *fd, void *addr, int addrlen)
{
    int oserror;
    fd_set write_fds;
    struct timeval poll;

    _PR_InitIO();

    FD_ZERO(&write_fds);
    FD_SET(fd->osfd, &write_fds);
    poll.tv_sec = poll.tv_usec = 0;

#if defined(IRIX)
    /*
    ** Block out timer interrupts. On IRIX taking an interrupt during a
    ** connect sometimes causes an EADDRINUSE
    */
    sigprocmask(SIG_BLOCK, &_pr_holdALRM, 0);
#endif

    oserror = _OS_CONNECT(fd->osfd, addr, addrlen);

#if defined(IRIX)
    /*
    ** Release timer interrupts. On IRIX taking an interrupt during a
    ** connect sometimes causes an EADDRINUSE
    */
    sigprocmask(SIG_UNBLOCK, &_pr_holdALRM, 0);
#endif

    while ((oserror < 0) && !_PR_PENDING_EXCEPTION()) {
	if ((oserror == _OS_ERROR_EINPROGRESS) ||
	    (oserror == _OS_ERROR_EALREADY)) {
#ifdef SOLARIS
	    int	cnt = _OS_SELECT(fd->osfd+1, 0, &write_fds, 0, &poll);
	    FD_SET(fd->osfd, &write_fds);
	    if (cnt == 1) {
		int bytes;
		int ioc;

		if ((ioc =_OS_IOCTL(fd->osfd, FIONREAD, (int)&bytes)) < 0) {
		    /* If this fails with EPIPE, then we assume
		       connection is refused - who knows what's
		       right?  Not I. */
		    if (ioc == -EPIPE) {
			oserror = _OS_ERROR_ECONNREFUSED;
		    }
		    PR_SetError(oserror);
		    break;
		}
		oserror = 0;
		break;
	    }
#endif
            _PR_WaitForFD(fd, 0);

	    /* Try again... */
	    oserror = _OS_CONNECT(fd->osfd, addr, addrlen);
	} else if (oserror == _OS_ERROR_EISCONN) {
	    oserror = 0;
	} else {
	    break;
	}
    }
    return oserror;
}

/*
** Read data from a socket object into buffer. This allows for async
** socket i/o.
*/
int32 _PR_ReadSocket(PRFileDesc *fd, void *buf, int32 amount)
{
    int32 oserror;

    if (!fd || _OS_INVALID_FD(fd)) {
        PR_SetError(PR_ERROR_INVALID_FD);
        return -1;
    }

    oserror = _OS_RECV(fd->osfd, buf, amount, 0);
    while ((oserror < 0) && !_PR_PENDING_EXCEPTION() &&
           (oserror == _OS_ERROR_EAGAIN || oserror == _OS_ERROR_EINTR)) {
        if (oserror == _OS_ERROR_EAGAIN) {
            if (fd->flags & _PR_USER_ASYNC) {
                oserror = PR_ERROR_WOULD_BLOCK;
                PR_SetError(oserror);
                return oserror;
            }
            _PR_WaitForFD(fd, 1);
        }
        oserror = _OS_RECV(fd->osfd, buf, amount, 0);
    }
    return oserror;
}

#if 0
/*
** Scatter gather data from a file object into buffer. This allows for
** async file i/o in case either the underlying system supports it or if
** (for example) files are used to represent devices (e.g. unix).
*/
int32 _PR_ReadvSocket(PRFileDesc *fd, PRIOVec *iov, int iovcnt)
{
    int32 oserror;

    if (!fd || _OS_INVALID_FD(fd)) {
        PR_SetError(PR_ERROR_INVALID_FD);
        return -1;
    }

    oserror = _OS_READV(fd->osfd, iov, iovcnt);
    while ((oserror < 0) && !_PR_PENDING_EXCEPTION() &&
           ((oserror == _OS_ERROR_EAGAIN) || (oserrno == _OS_ERROR_EINTR))) {
        if (oserror == _OS_ERROR_EAGAIN) {
            if (fd->flags & _PR_USER_ASYNC) {
                oserror = PR_ERROR_WOULD_BLOCK;
                PR_SetError(oserror);
                return oserror;
            }
            _PR_WaitForFD(fd, 1);
        }
        oserror = _OS_READV(fd->osfd, iov, iovcnt);
    }
    return oserror;
}
#endif

/*
** Write data to a socket object from a buffer. This allows for async
** socket i/o.
*/
int32 _PR_WriteSocket(PRFileDesc *fd, const void *buf, int32 amount)
{
    const char *bp = (const char*) buf;
    int32 oserror, total_written;

    if (!fd || _OS_INVALID_FD(fd)) {
        PR_SetError(PR_ERROR_INVALID_FD);
        return -1;
    }
    total_written = 0;
    while ((total_written < amount) && !_PR_PENDING_EXCEPTION()) {
        oserror = _OS_SEND(fd->osfd, bp+total_written, amount-total_written, 0);
        while ((oserror < 0) && !_PR_PENDING_EXCEPTION()) {
            if (oserror == _OS_ERROR_EAGAIN) {
                if (fd->flags & _PR_USER_ASYNC) {
                    if (total_written == 0) {
                        oserror = PR_ERROR_WOULD_BLOCK;
                        PR_SetError(oserror);
                        total_written = oserror;
                    }
                    return total_written;
                }
                _PR_WaitForFD(fd, 0);
            } else if (oserror != _OS_ERROR_EINTR) {
                total_written = oserror;
                return total_written;
            }
            oserror = _OS_SEND(fd->osfd, bp+total_written,
			       amount-total_written, 0);
        }
        total_written += oserror;
    }
    return total_written;
}

#if 0
/*
** Scatter write data to a file object from some buffers. This allows for
** async file i/o in case either the underlying system supports it or if
** (for example) files are used to represent devices (e.g. unix).
*/
int32 _PR_WritevSocket(PRFileDesc *fd, PRIOVec *iov, int iovcnt)
{
    int32 total_written = 0;
    int32 nwrote, ndone = 0;
    int32 oserror;

    if (!fd || _OS_INVALID_FD(fd)) {
        PR_SetError(PR_ERROR_INVALID_FD);
        return -1;
    }

    LOCK(pr_asyncOutput);
    _PR_AsyncFD(fd);

    while ((iovcnt > 0) && !pendingException() ) {
        iov->iov_len -= ndone;
        iov->iov_base += ndone;
        nwrote = OS_WRITEV_FILE(fd->osfd, iov, iovcnt, &oserror);
        while (oserror) {
            if (errno == EAGAIN) {
                if (fd->flags & FD_ASYNC) {
                    if (total_written == 0) {
                        total_written = -1;
                    }
                    goto unlock;
                }
                _PR_WaitForFD(fd, 0);
            } else if (errno != EINTR) {
                total_written = -1;
                goto unlock;
            }
            nwrote = OS_WRITEV_FILE(fd->osfd, iov, iovcnt, &oserror);
        }
        total_written += nwrote;
        iov->iov_len += ndone;
        iov->iov_base -= ndone;
        ndone += nwrote;
        for (; (iovcnt > 0) && (ndone >= iov->iov_len) ; iovcnt--, iov++) {
            ndone -= iov->iov_len;
        }
    }

unlock:
    UNLOCK(pr_asyncOutput);
    return total_written;
}
#endif

int32 _PR_CloseSocket(PRFileDesc *fd)
{
    int oserror = PR_ERROR_INVALID_FD;

    if (fd) {
        oserror = _OS_CLOSE_SOCKET(fd->osfd);
        fd->osfd = _OS_INVALID_FD_VALUE;
    }
    PR_SetError(oserror);
    return oserror;
}

PRIOMethods _pr_net_methods = {
    0,
    _PR_CloseSocket,
    _PR_ReadSocket,
    _PR_WriteSocket,
};