#ifdef SCO
/*
 * Portions Copyright (C) 1994 The Santa Cruz Operation, Inc.
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software
 * for any purpose is hereby granted without fee, provided that the 
 * above copyright notice and this notice appear in all copies
 * and that both the copyright notice and this notice appear in
 * supporting documentation.  SCO makes no representations about
 * the suitability of this software for any purpose.  It is provided
 * "AS IS" without express or implied warranty.
 * 
 * SCO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 
 * INCLUDING ALL IMPLIED WARRANTIES OR MERCHANTABILITY AND FITNESS.  
 * IN NO EVENT SHALL SCO BE LIABLE FOR ANY SPECIAL, INDIRECT, 
 * PUNITIVE, CONSEQUENTIAL OR INCIDENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, LOSS OF DATA OR LOSS OF
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
 * PERFORMANCE OF THIS SOFTWARE.
 *
 *	THIS COPYRIGHT NOTICE DOES NOT APPLY TO THE FUNCTION
 *	spipe() WHICH WAS OBTAINED FROM:
 *
 *	Stevens, W. Richards.  UNIX Network Programming, Prentice-Hall,
 *	1990.  [If you program on UNIX, you want this book.  Really.
 *	One of the best. (tom@sco.com)]
 *		
 */

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/socket.h>
#include <stropts.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

#define SPX_DEVICE	"/dev/spx"

/*
 *	Create an un-named stream pipe to replace a socketpair.
 *	From Stevens: UNIX Network Programming.  This is not
 *	covered by SCO copyright.
 */

int
s_pipe(int fd[2])
{
	struct strfdinsert ins;
	queue_t	*pointer;

	/* open clone device /dev/spx twice */

	if ((fd[0] = open(SPX_DEVICE, O_RDWR)) < 0)
		return -1;
	if ((fd[1] = open(SPX_DEVICE, O_RDWR)) < 0) {
		close(fd[0]);
		return -1;
	}

	/* link streams together with I_FDINSERT */

	ins.ctlbuf.buf = (char *) &pointer;
	ins.ctlbuf.maxlen = sizeof(queue_t *);
	ins.ctlbuf.len = sizeof(queue_t *);

	ins.databuf.buf = (char *) 0;
	ins.databuf.len = -1;	/* magic -1 for streams pipes */
	ins.databuf.maxlen = 0;

	ins.fildes = fd[1];	/* the fd to connect */
	ins.flags = 0;
	ins.offset = 0;

	if (ioctl(fd[0], I_FDINSERT, (char *) &ins) < 0) {
		close(fd[0]);
		close(fd[1]);
		return -1;
	}
	return 0;
}

/*
 *	Code adapted from SCO X-server to do IPC over
 *	streams pipes.  Thanks SCO.
 */

/*
 *	This is the server side code
 */


#define X_UNIX_PATH	"/dev/X"

/* 
 *	This is executed by a server to establish a streams pipe
 *	on which connection requests can be made.
 *
 *	The parameter tells which device to use.  For X, it
 *	is the display number.  When term is configured for
 *	streams pipes, we "borrow" one of the X devices ...
 *
 */

int
open_stream_pipe (int number)
{
	struct flock mylock;		/* Used for fcntl F_SETLK  S012 */
	struct strfdinsert fdins;	/* Used in FDINSERT ioctl's */
	int connmaster;			/* Master pipe for making connections */
	int connother;			/* Other end of connmaster */
	char mybuf[sizeof (struct file *)]; /* Buffer for FDINSERT message */
	char strnamebuf[sizeof X_UNIX_PATH + 5];
  
  	/*
  	 * The server creates both ends of a stream pipe on two special
	 * minor devices of the stream pipe driver.  One of these will be the
	 * master connection, which is the value we return.  The other we
	 * simply forget;  we will hold it open as long as the server runs.
	 *
	 * The name of the special minor device is "/dev/X<n>[RS]", where
	 * <n> is the input parameter.  R is the request device (which is
	 * written to open a connection);  S is the server's end of the
	 * request device.
	 */

	sprintf(strnamebuf,"%s%dS", X_UNIX_PATH, number);

	connmaster = open (strnamebuf, O_RDWR | O_NDELAY);
	if (connmaster < 0) {
	    fprintf(stderr, "Can't open %s\n", strnamebuf);
	    return -1;
	}

	/* S012
	 * Lock the master connection device.
	 * This is the atomic operation that resolves
	 * duplicate server startup races.
	 * The lock will automagically disappear
	 * whenever the master is closed.
	 */
	mylock.l_type   = F_WRLCK;
	mylock.l_whence = 0;
	mylock.l_start  = 0;	/* lock entire "file" */
	mylock.l_len    = 0;
	if (fcntl (connmaster, F_SETLK, &mylock) < 0) {
	    if (errno == EACCES) {
		fprintf(stderr, "streams pipe %s is busy\n", strnamebuf);
	    } else {
		fprintf(stderr, "cannot lock %s\n", strnamebuf);
	    }
	    close (connmaster);
	    return -1;
	}

	sprintf(strnamebuf,"%s%dR", X_UNIX_PATH, number);

	connother = open (strnamebuf, O_RDWR | O_NDELAY);
	if (connother < 0) {
	    fprintf(stderr, "Can't open %s\n", strnamebuf);
	    close (connmaster);
	    return -1;
	}
	fdins.ctlbuf.maxlen = sizeof mybuf;
	fdins.ctlbuf.len = sizeof mybuf;
	fdins.ctlbuf.buf = mybuf;
	fdins.databuf.maxlen = 0;
	fdins.databuf.len = -1;
	fdins.databuf.buf = NULL;
	fdins.fildes = connother;
	fdins.offset = 0;
	fdins.flags = 0;

	if (ioctl (connmaster, I_FDINSERT, &fdins) < 0) {
	    fprintf(stderr, "cannot pass file descriptor\n");
	    close (connmaster);
	    close (connother);
	    return -1;
	}
	return connmaster;
}

/*
 *	This code (in the server) adds a connection to a new client.
 *	The client has already sent a byte on the "R" pipe, which the
 *	server has noticed.
 */

addclient (int connmaster)			/* Add connection to new slave */
{
	struct strfdinsert	fdins;	/* Used for FDINSERT on master */
	int			fd;	/* FD of new connection */
	char			mybuf[sizeof (struct file *)];

	if (read (connmaster, &fd, 1) != 1)	/* Read the dummy byte */
	    return -1;
	fd = open (SPX_DEVICE, O_RDWR);
	if (fd < 0) {
	    /* send zero-length msg to client to signal error */
	    fdins.ctlbuf.buf = mybuf;
	    fdins.ctlbuf.len = 0;
	    putmsg (connmaster, &fdins.ctlbuf, NULL, 0);
	    return -1;
	}

	fdins.ctlbuf.maxlen = sizeof mybuf;
	fdins.ctlbuf.len = sizeof mybuf;
	fdins.ctlbuf.buf = mybuf;
	fdins.databuf.maxlen = 0;
	fdins.databuf.len = -1;
	fdins.databuf.buf = NULL;
	fdins.fildes = fd;
	fdins.offset = 0;
	fdins.flags = 0;

	if (ioctl (connmaster, I_FDINSERT, &fdins) < 0) {
	    close (fd);
	    return -1;
	}
	return fd;
}


/*
 *	This is the client side code.  "number" is the X
 *	display number to use (or borrow).
 */

int MakeStreamPipeConnection (int number)
{
	int errsave;			/* Place to save errno if trouble */
	int flags;			/* Flags to getmsg call */
	int mfd;			/* Fd to talk to master */
	char mybuf[sizeof (struct file *)]; /* Buffer for linkup message */
	struct strbuf myctlbuf;		/* Control reception buffer */
	int retfd;			/* Resulting fd to talk on */
	static char strnamebuf[sizeof X_UNIX_PATH + 5];

	/*
	 * The server creates both ends of a stream pipe on two special
	 * minor devices of the stream pipe driver.  One of these is the
	 * connection we will make our request on.
	 */

	sprintf(strnamebuf,"%s%dR",X_UNIX_PATH,number);

	mfd = open (strnamebuf, O_RDWR);
	if (mfd < 0) {
	    perror(strnamebuf);
	    return -1;
	}

	retfd = open (SPX_DEVICE, O_RDWR);
	if (retfd < 0) {
	    perror(SPX_DEVICE);
	    errsave = errno;
	    close (mfd);
	    errno = errsave;
	    return -1;
	}

	if (write (mfd, (char *)&mfd, 1) != 1) { /* Ask for a connection */
	    errsave = errno;
	    close (retfd);
	    close (mfd);
	    errno = errsave;
	    return -1;
	}

	myctlbuf.maxlen = sizeof (mybuf);
	myctlbuf.buf = mybuf;
	flags = 0;

	/*
	 *	Get the file descriptor sent by the server
	 */

	if (getmsg (mfd, &myctlbuf, (struct strbuf *) NULL, &flags) < 0) {
	    errsave = errno;
	    close (retfd);
	    close (mfd);
	    errno = errsave;
	    return -1;
	}

	/*
	 *	Connect our endpoint to the server's endpoint
	 */

	if (putmsg (retfd, &myctlbuf, (struct strbuf *) NULL, 0) < 0) {
	    errsave = errno;
	    close (retfd);
	    close (mfd);
	    errno = errsave;
	    return -1;
	}
	close (mfd);
	return retfd;
}

int
CheckClientConnection(int socket)
{
	char dumb;
	int s;

	if (ioctl(socket, FIONREAD, &dumb) == -1) {
		if ((s = addclient(socket)) < 0) {
			return -1;	/* couldn't add client */
		} else
			return s;
	} else
		return -2;		/* not a streams pipe */
}

#endif
