#ifndef	lint
static char sccsid[] = "@(#)server_rpc.c 1.9 88/12/21";
#endif	/*lint*/

#include "cbratp.h"
#include "romfs.h"
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>

#define WIRETAPPORT	9473	/* Arbitrary 'WIRE' port number */

extern int vsdev;
extern int errno;
extern char *host;

fd_set	non_blocking_fds;
fd_set	stopped_fds;
fd_set	lseek_fds;

#ifndef	WIRETAP
rpc_open(path, flags, mode)
    register char *path;
    int flags;
    int mode;
{
    u_char *buffer;
    register u_char *p;

    if (!pkt_mode)
	return -1;

    p = buffer = (u_char *) malloc((unsigned) strlen(path) + 9);
    while (*path)
	*p++ = *path++;
    *p++ = '\0';
    p = encode_long(p, (long) flags);
    p = encode_long(p, (long) mode);
    enqueue(OPEN_CHN, buffer, p - buffer);
    free((char *) buffer);
    wait_for(OPEN_CHN);

    if (flags & O_WRONLY)
	return return_int2;

    return return_int;
}
#endif	/*WIRETAP*/

rpc_close(fd)
    int	fd;
{
    u_char buffer[4];
    register u_char *p = buffer;

    if (!pkt_mode)
	return -1;

    *p++ = LOCAL_TO_RPC(fd);
    enqueue(CLOSE_CHN, buffer, p - buffer);
    wait_for(CLOSE_CHN);

    if (return_int == 0) {
	FD_CLR(fd, &read_fds);
	FD_CLR(fd, &non_blocking_fds);
	FD_CLR(fd, &stopped_fds);
	FD_CLR(fd, &lseek_fds);
	if (rd_queue[fd] != NULL) {
	    cq_free(rd_queue[fd]);
	    rd_queue[fd] = NULL;
	}
	if (rd_chan[fd]) {
	    free_channel(rd_chan[fd]);
	    rd_chan[fd] = NULL;
	}
	if (wr_chan[fd]) {
	    free_channel(wr_chan[fd]);
	    wr_chan[fd] = NULL;
	}
    }

    return 0;
}

rpc_read(fd, buf, size)
    int	fd;
    register char *buf;
    register int size;
{
    struct cqueue *r;
    register u_char *p;
    extern u_char bgetc();

    if (!pkt_mode)
	return -1;

    if ((r = rd_queue[fd]) == NULL)
	return -1;

    if (r->size == 0) {
	if (FD_ISSET(fd, &read_fds))
		return 0;
	
	if (FD_ISSET(fd, &non_blocking_fds)) {
	    errno = EWOULDBLOCK;
	    return -1;
	} 
	
	if (FD_ISSET(fd, &lseek_fds)) {
	    u_char buffer[4];

	    p = encode_long(buffer, (long) size);
	    enqueue(rd_chan[fd], buffer, p - buffer);
	}

	wait_for(rd_chan[fd]);
    }

    if (r->size < size)
	size = r->size;

    p = (u_char *) buf;
    while (--size >= 0)
	*p++ = bgetc(r);

    if (r->size == 0) {
	FD_CLR(fd, &read_fds);
    }

    if (r->size < 1024 && FD_ISSET(fd, &stopped_fds)) {
	enqueue(rd_chan[fd], (u_char *) 0, 0);
	FD_CLR(fd, &stopped_fds);
    }

    return((char *) p - buf);
}

#ifndef	WIRETAP
rpc_nbwrite(fd, buf, size)
    int fd;
    char *buf;
    int size;
{
    return rpc_write(fd, buf, size);
} /*### all writes are almost non-blocking */
#endif	/*WIRETAP*/

rpc_write(fd, buf, size)
    int fd;
    register char *buf;
    register int size;
{
    if (!pkt_mode)
	return size;

    if (wr_chan[fd]) {
	enqueue(wr_chan[fd], (u_char *) buf, size);
	return size;
    }

    return -1;
}

rpc_poll(width, readfds, writefds, exceptfds, timeout)
    register int width;
    register fd_set *readfds;
    register fd_set *writefds;
    fd_set *exceptfds;
    struct timeval *timeout;
{
    register int i;
    register int n = 0;
    register fd_set *rpc_fds = &read_fds;

    if (width > RPC_FDMAX + 1)
	width = RPC_FDMAX + 1;

    if (readfds)
	for (i=RPC_FDBASE; i<width; i++)
	    if (FD_ISSET(i, readfds)) {
	    readagain:
		if (FD_ISSET(i, rpc_fds))
		    n++;
		else {
		    if (timeout)	/* don't wait */
			FD_CLR(i, readfds);
		    else {	/* loop until data is ready for this fd */
			wakeup_io();
			goto readagain;
		    }
		}
	    }

    if (writefds)
	for (i=RPC_FDBASE; i< width; i++)
	    if (FD_ISSET(i, writefds))
		n++;

    if (exceptfds)
	for (i=RPC_FDBASE; i< width; i++)
	    FD_CLR(i, exceptfds);

    return n;
}

rpc_openconnection(name)
    char *name;
{
    if (!pkt_mode)
	return -1;

    enqueue(OPENCON_CHN, (u_char *) name, strlen(name) + 1);
    wait_for(OPENCON_CHN);

    return return_int;
}

rpc_acceptconnection(fd)
    int fd;
{
    u_char buffer[4];
    register u_char *p = buffer;

    if (!pkt_mode)
	return -1;

    *p++ = LOCAL_TO_RPC(fd);
    enqueue(ACCEPTCON_CHN, buffer, p - buffer);
    wait_for(ACCEPTCON_CHN); 
    FD_CLR(fd, &read_fds);

    if (return_int >= 0)
	FD_SET(return_int, &non_blocking_fds);

    return return_int;
}

rpc_spawn(buf)
    register char *buf;
{
    if(!pkt_mode)
	return -1;

    enqueue(SPAWN_CHN, (u_char *) buf, strlen(buf) + 1);
    return 0;
}

rpc_putenv(name, value)
    register char *name;
    register char *value;
{
    int namelen;
    int valuelen;
    char *buffer;

    valuelen = strlen(value);

    if (strcmp(name, "XNEWSHOME") == 0) {
#ifdef	notdef
	if (xnewshome)
	    free(xnewshome);
#endif	/*notdef*/
	if ((xnewshome = malloc((unsigned) (valuelen + 1))) == 0)
	    abort();
	(void) strcpy(xnewshome, value);
    }

    if (pkt_mode) {
	namelen = strlen(name);
	buffer = malloc((unsigned) (namelen + valuelen + 2));
	bcopy(name, buffer, namelen);
	buffer[namelen] = '=';
	bcopy(value, &buffer[namelen + 1], valuelen);
	buffer[namelen + valuelen + 1] = '\0';
	enqueue(PUTENV_CHN, (u_char *) buffer, namelen + valuelen + 2);
	free(buffer);
    }

    (void) setenv(name, value);
    return 0;
}

rpc_serveraddress(fd, adr, len)
    int	fd;
    char *adr;
    int	len;
{
    u_char buffer[4];
    register u_char *p = buffer;

    if (!pkt_mode)
	return -1;

    *p++ = LOCAL_TO_RPC(fd);
    enqueue(SERVERADDR_CHN, buffer, p - buffer);
    wait_for(SERVERADDR_CHN);

    bcopy(return_ptr, adr, len);
    free(return_ptr);

    return 0;
}

#ifndef	WIRETAP
/*
 * Does a getpeername() on peer connected to socket s.
 * Stores the name at adr and returns the actual length.
 */
rpc_clientaddress(fd, adr, len)
    int fd;
    char *adr;
    int len;
{
    u_char buffer[4];
    register u_char *p = buffer;

    if (!pkt_mode)
	return -1;

    *p++ = LOCAL_TO_RPC(fd);
    enqueue(CLIENTADDR_CHN, buffer, p - buffer);
    wait_for(CLIENTADDR_CHN);
    bcopy(return_ptr, adr, len);
    free(return_ptr);

    return 0;
}
#endif	/*WIRETAP*/

rpc_servername(adr, len)
    char *adr;
    int len;
{
    if(!pkt_mode)
	return -1;

    enqueue(SERVERNAME_CHN, (u_char *) 0, 0);
    wait_for(SERVERNAME_CHN);
    bcopy(return_ptr, adr, len);
    free(return_ptr);

    return 0;
}

#ifndef	WIRETAP
char *
rpc_getenv(name)
    register char *name;
{
    if (pkt_mode) {
	enqueue(GETENV_CHN, (u_char *) name, strlen(name) + 1);
	wait_for(GETENV_CHN);

	if (return_int) {
	    setenv(name, return_ptr);
	    free(return_ptr);
	}
	else {
	    free(return_ptr);
	    return (char *)0;
	}
    }

    return(nut_getenv(name));
}

off_t
rpc_lseek(fd, offset, whence)
    int	fd;
    off_t offset;
    int whence;
{
    u_char buffer[6];
    u_char tmp = whence;
    register u_char *p = buffer;

    if (!pkt_mode)
	return((off_t) -1);

    *p++ = LOCAL_TO_RPC(fd);
    *p++ = tmp;
    p = encode_long(p, offset);
    enqueue(LSEEK_CHN, buffer, p - buffer);
    wait_for(LSEEK_CHN);

    FD_SET(fd, &lseek_fds);
    if (return_long >= 0 && rd_queue[fd] != 0) {
	if (whence == L_INCR && offset == 0) {
	    /* just checking current position, adjust for buffering */
	    return_long -= rd_queue[fd]->size;
	}
	else {
	    /* reset all data pointers, we're seeking some place else */
	    FD_CLR(fd, &read_fds);
	    rd_queue[fd]->size = 0;
	    rd_queue[fd]->tail = rd_queue[fd]->buf;
	    rd_queue[fd]->head = rd_queue[fd]->buf;
	}
    }

    return((off_t) return_long);
}

char *
rpc_gethostname(family, length, pAddr)
    int family;
    int length;
    u_char *pAddr;
{
    u_char buffer[100];
    register u_char *p = buffer;

    if (!pkt_mode)
	return NULL;

    p = encode_long(p, (long) family);
    bcopy((char *) pAddr, (char *) p, length);
    p += length;
    enqueue(GETHOSTNAME_CHN, buffer, p - buffer);
    wait_for(GETHOSTNAME_CHN);

    return return_ptr;
}

rpc_gethostaddr(name, family, length, pAddr)
    char *name;
    int *family;
    int *length;
    char **pAddr;
{
    if (!pkt_mode)
	return 0;

    enqueue(GETHOSTADDR_CHN, (u_char *) name, strlen(name) + 1);
    wait_for(GETHOSTADDR_CHN);

    if (return_int) {
	*length = return_int;
	*family = (int)return_long;
	*pAddr = return_ptr;
	return 1;
    }

    return 0;
}

char *
rpc_homedir(username)
    char *username;
{
    if(!pkt_mode)
	return 0;

    enqueue(HOMEDIR_CHN, (u_char *) username, strlen(username) + 1);
    wait_for(HOMEDIR_CHN);

    return((char *) return_ptr);
}

char *
rpc_cwd()
{
    if(!pkt_mode)
	return 0;

    enqueue(CWD_CHN, (u_char *) 0, 0);
    wait_for(CWD_CHN);

    return((char *) return_ptr);
}

off_t 
rpc_filesize(fd)
    int	fd;
{
    u_char	c;

    if(!pkt_mode)
	return 0;

    c = LOCAL_TO_RPC(fd);
    enqueue(FILESIZE_CHN, &c, 1);
    wait_for(FILESIZE_CHN);

    return((off_t) return_long);
}
#endif	/*WIRETAP*/

rpc_quit()
{
    u_char buffer[5];
    register u_char *b = buffer;

    if (!pkt_mode)
	return;

    *b++ = QUIT_CMD;
    enqueue(CONTROL_CHN, buffer, b - buffer);
    wait_for(CONTROL_CHN);
    enqueue(CONTROL_CHN, (u_char *) 0, 0);
    drain();
}

#ifndef	WIRETAP
rpc_setbaudrate(r)
    int	r;
{
    u_char buffer[5];
    register u_char *p = buffer;

    if(!pkt_mode)
	return 1;

    *p++ = SPEED_CMD;
    p = encode_long(p, (long) r);
    enqueue(CONTROL_CHN, buffer, p - buffer);
    wait_for(CONTROL_CHN);
    enqueue(CONTROL_CHN, (u_char *) 0, 0);
    drain();

    return 0;
}

rpc_crconoff(c)
    int	c;
{
    u_char buffer[4];
    register u_char *b = buffer;

    if(!pkt_mode)
	return;

    *b++ = CRC_CMD;
    *b++ = c;
    enqueue(CONTROL_CHN, buffer, b - buffer);
    wait_for(CONTROL_CHN);
    enqueue(CONTROL_CHN, (u_char *) 0, 0);
    drain();

    crc_mode = c;
}
#endif	/*WIRETAP*/


VERSION *
rpc_version(vers)
    int vers;
{
    u_char buffer[4];
    register u_char *p = buffer;
    static VERSION version;

    if(!pkt_mode)
	return 0;

    if (version.version == 0) {
	p = encode_short(p, vers);
	enqueue(VERSION_CHN, buffer, p - buffer);
	wait_for(VERSION_CHN);

	version.version = decode_short((u_char *) return_ptr);
	version.firstfd = decode_short((u_char *) return_ptr + 2);
	version.res1 = decode_long((u_char *) return_ptr + 4);
	version.res2 = decode_long((u_char *) return_ptr + 8);
	version.res3 = decode_long((u_char *) return_ptr + 12);
	free(return_ptr);
    }

    return &version;
}

#ifndef	WIRETAP
rpc_pipe(name, ifd, ofd, flags)
    char *name;
    int *ifd;
    int *ofd;
    int flags;
{
    register u_char *buffer;
    register int len;
    register char c = flags;

    if(!pkt_mode)
	return -1;

    len = strlen(name);
    buffer = (u_char *) malloc((unsigned) len + 2);	/* null + 1 byte fd */
    if (buffer == (u_char *) 0)
	return -1;

    *buffer = c;
    bcopy(name, (char *) buffer + 1, len + 1);
    enqueue(PIPE_CHN, buffer, len + 2);
    wait_for(PIPE_CHN);
    free((char *) buffer);

    *ifd = return_int;
    *ofd = return_int2;

    if (flags & O_WRONLY)
	return return_int2;

    return return_int;
}

/*ARGSUSED*/
rpc_ioctl(fd, request, arg)
    int fd;
    int request;
    char *arg;
{
    return 0;
}

/*ARGSUSED*/
rpc_fcntl(fd, cmd, arg)
    int fd;
    int cmd;
    int arg;
{
    return 0;
}
#endif	/*WIRETAP*/


tcp_init(inited)
int inited;
{
#ifdef WIRETAP
    return (iconnect(host, WIRETAPPORT));
#endif WIRETAP
#ifdef HELPER
    char   myname[MAXHOSTNAME+1];
    int    s;
    u_short portnum = 2048;
    struct sockaddr_in sa;
    struct hostent *hp;
    struct servent *sp;

    if (inited) 
	return(wait_for_connection);

    bzero(&sa,sizeof(struct sockaddr_in));      /* clear our address */
    gethostname(myname,MAXHOSTNAME);            /* who are we? */
    hp= gethostbyname(myname);                  /* get our address info */
    if (hp == NULL)                             /* we don't exist !? */
        return(-1);
    sa.sin_family= hp->h_addrtype;              /* this is our host address */
    sa.sin_port= htons(WIRETAPPORT); 		/* this is our port number */
    if ((s= socket(AF_INET,SOCK_STREAM,0)) < 0) /* create socket */
        return(-1);
    if (bind(s,&sa,sizeof sa,0) < 0) {
        close(s);
        return(-1);                               /* bind address to socket */
    }
    listen(s, 1);                               /* max # of queued connects */
    return(wait_for_connection(s));
}

int wait_for_connection(s)
int s;
{
    fd_set readfd;
    int t;

    FD_ZERO(&readfd);
    FD_SET(s, &readfd);

#ifdef DEBUG
    printf("Waiting for a connection\n");
#endif DEBUG

    while (1) {
	select(s+1, &readfd, (fd_set *)0, (fd_set *)0, NULL);
	if ((t = get_connection(s)) < 0) {
	    if (errno == EINTR)
		continue;
	    perror("accept");
	    exit(1);
	}
	return(t);
    }
}

int get_connection(s)
int s;                    /* socket created with establish() */
{
    struct sockaddr_in isa; /* address of socket */
    int i;                  /* size of address */
    int t;                  /* socket of connection */

    i = sizeof(isa);                   /* find socket's address */
    getsockname(s,&isa,&i);            /* for accept() */

    if ((t = accept(s,&isa,&i)) < 0)   /* accept connection if there is one */
        return(-1);
    return(t);
#endif HELPER
}
