ftp.nice.ch/pub/next/unix/macintosh/uw.4.2.N.bs.tar.gz#/uw/server/uw_ipc.c

This is uw_ipc.c in view mode; [Download] [Up]

/*
 *	uw IPC
 *
 * Copyright 1986 by John D. Bruner.  All rights reserved.  Permission to
 * copy this program is given provided that the copy is not sold and that
 * this copyright notice is included.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <strings.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>

#include "uw_param.h"
#include "uw_err.h"
#include "uw_opt.h"
#include "uw_win.h"
#include "uw_fd.h"
#include "uw_pcl.h"
#include "uw_ipc.h"

#ifndef ntohs
/* declaring these as one-element arrays or as NULL pointers is a HACK */
extern unsigned long ntohl(), htonl();
extern unsigned short ntohs(), htons();
static struct netadj na_ntoh[1] = {
	(short (*)())ntohs, (long (*)())ntohl, ntohs, ntohl
};
static struct netadj na_hton[1] = {
	(short (*)())htons, (long (*)())htonl, htons, htonl
};
#else
static struct netadj *na_ntoh = NULL;
static struct netadj *na_hton = NULL;
#endif

static int have_udport;
static char uipc_port[] = "/tmp/uwXXXXXX";

static int inet_sd;
static struct ipcmsg {
	int		im_len;
	struct uwipc	im_msg;
} *inet_buf;

extern int errno;

ipc_init(use_uipc)
{
	ipc_isinit();
	if (use_uipc)
		ipc_udinit();
}


/*
 * UNIX-domain
 */

static
ipc_udinit()
{
	register int len;
	register char *cp;
	register fildes_t sd;
	auto struct sockaddr_un sa;
	auto char *env[2];
	extern char *mktemp();

	len = strlen(UIPC_ENV) + sizeof uipc_port + 1;
	if ((cp = malloc(len)) != NULL) {
		(void)sprintf(cp, "%s=%s", UIPC_ENV, mktemp(uipc_port));
		env[0] = cp;
		env[1] = (char *)0;
		env_set(env);

		sa.sun_family = AF_UNIX;
		(void)strncpy(sa.sun_path, uipc_port, sizeof sa.sun_path-1);
		sa.sun_path[sizeof sa.sun_path-1] = '\0';
		if ((sd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0 &&
		    bind(sd,&sa,sizeof sa.sun_family+strlen(sa.sun_path)) >= 0){
			have_udport = 1;
			(void)chmod(uipc_port, S_IREAD|S_IWRITE);
			(void)fcntl(sd, F_SETFL, FNDELAY);
			fdmap[sd].f_type = FDT_UDSOCK;
			FD_SET(sd, &selmask[0].sm_rd);
		}
	}
}

ipc_exit()
{
	if (have_udport)
		(void)unlink(uipc_port);
}

ipc_udrecv(sd)
register fildes_t sd;
{
	register struct window *w;
	register int cnt;
	struct msghdr msg;
	auto int fd;
	struct iovec iov;
	struct stat st1, st2;
	union {
		struct uwipc uwip;
		char data[1024];
	} buf;


	/*
	 * main() calls this routine when there is a message waiting on
	 * the UNIX-domain socket.  The message's access rights are
	 * expected to contain the file descriptor for the "master" side
	 * of a pseudo-tty.  The message contains the name of the pty.
	 * The sender is expected to start up a process on the slave side
	 * of the pty.  This allows the host end to create windows which
	 * run something other than the shell.
	 */
	fd = -1;

	iov.iov_base = (caddr_t)buf.data;
	iov.iov_len = sizeof buf - 1;

	msg.msg_name = (caddr_t)0;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_accrights = (caddr_t)&fd;
	msg.msg_accrightslen = sizeof fd;

	if ((cnt=recvmsg(sd, &msg, 0)) < 0 || cnt != buf.uwip.uwip_len)
		return;
	switch (buf.uwip.uwip_cmd) {
	case UWC_NEWT:
		if (msg.msg_accrightslen > 0 && fd >= 0) {
			/*
			 * We can't trust the process which connected to us,
			 * so we verify that it really passed us a pseudo-tty's
			 * file descriptor by checking the device name and its
			 * inode number.  [Of course, if someone else wants to
			 * hand us a terminal session running under their
			 * uid....]
			 */
			if (cnt == sizeof buf)
				cnt--;
			buf.data[cnt] = '\0';
			if (strncmp(buf.uwip.uwip_newt.uwnt_pty,
			    "/dev/pty", sizeof "/dev/pty"-1) ||
			    fstat(fd, &st1) < 0 ||
			    stat(buf.uwip.uwip_newt.uwnt_pty, &st2) < 0 ||
			    st1.st_dev != st2.st_dev ||
			    st1.st_ino != st2.st_ino) {
				(void)close(fd);
				return;
			}
			/*
			 * OK, we believe the sender.  We allocate a window and
			 * tell the Macintosh to create that window on its end.
			 * If we have no free windows, then we close the file
			 * descriptor (which will terminate the slave process).
			 */
			w = PCL_NEWW(0, WC_INTERNAL,
			      buf.uwip.uwip_newt.uwnt_type,
			      (nwin_t)0, buf.uwip.uwip_newt.uwnt_id,
			      fd, (fildes_t)-1);
			if (w != NULL) {
				(void)strncpy(w->w_tty,
				    buf.uwip.uwip_newt.uwnt_pty,
				    sizeof w->w_tty-1);
				w->w_tty[5] = 't'; /* switch to "/dev/ttyp?" */
				utmp_add(w->w_tty);
			} else
				(void)close(fd);
		}
		break;
	case UWC_OPTION:
		w = win_search(buf.uwip.uwip_option.uwop_id,
		    protocol->p_maxwin);
		if (w != NULL) {
			opt_extopt((caddr_t)w, &w->w_optdefn,
			    (woptcmd_t)buf.uwip.uwip_option.uwop_cmd,
			    (woption_t)buf.uwip.uwip_option.uwop_opt,
			    (char *)&buf.uwip.uwip_option.uwop_val,
			    (struct netadj *)0);
		}
		break;
	}
}


/*
 * Internet domain
 */

static
ipc_isinit()
{
	register fildes_t sd;
	register char *cp;
	struct hostent *h;
	struct sockaddr_in sin;
	auto int sinlen;
	char hostname[32];
	char *env[2];

	/*
	 * Allocate enough buffers for each file descriptor to have one.
	 * This is overkill.
	 */
	inet_buf = (struct ipcmsg *)malloc(nfds * sizeof(struct ipcmsg));
	if (inet_buf == NULL)
		return;

	/*
	 * Determine our host name and get an Internet stream socket.
	 * We really should specify the protocol here (rather than 0)
	 * but we "know" that it defaults to TCP.
	 */
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return;
	sin.sin_family = AF_INET;
	sin.sin_port = 0;
	bzero(sin.sin_zero, sizeof sin.sin_zero);
	if (gethostname(hostname, sizeof hostname) < 0 || hostname[0] == '\0')
		(void)strcpy(hostname, "localhost");
	if ((h = gethostbyname(hostname)) != NULL)
		bcopy(h->h_addr, (char *)&sin.sin_addr, h->h_length);
	else
		sin.sin_addr.s_addr = htonl(0x7f000001L); /* 128.0.0.1 (lo0) */
	if (bind(sd, &sin, sizeof sin) < 0) {
		/*
		 * Unable to bind to unspecified port -- try once more with
		 * loopback device.  If we already were using the loopback
		 * device we just suffer the inefficiency of doing this twice.
		 */
		sin.sin_addr.s_addr = htonl(0x7f000001L);
		if (bind(sd, &sin, sizeof sin) < 0) {
			(void)close(sd);
			return;
		}
	}

	/*
	 * Listen for incoming connections
	 */
	if (listen(sd, NWINDOW) < 0) {
		(void)close(sd);
		return;
	}

	/*
	 * Determine our port number and put our address in the environment.
	 */
	sinlen = sizeof sin;
	if (getsockname(sd, (char *)&sin, &sinlen) < 0) {
		/* huh?  Oh well, give up */
		(void)close(sd);
		return;
	}
	if ((cp = malloc(sizeof INET_ENV + 1 + 8 + 1 + 5)) == NULL) {
		/* no memory, give up */
		(void)close(sd);
		return;
	}
	sprintf(cp, "%s=%08lx.%05u", INET_ENV,
	    ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
	env[0] = cp;
	env[1] = (char *)0;
	env_set(env);

	inet_sd = sd;
	fdmap[sd].f_type = FDT_ISSOCK;
	FD_SET(sd, &selmask[0].sm_rd);
}

ipc_isrecv(sd)
register fildes_t sd;
{
	register fildes_t fd;
	register struct uwipc *uwip;
	register struct window *w;
	register uwerr_t uwerr;
	register int len;
	struct sockaddr sin;
	struct uwipc reply;
	auto int sinlen;

	/*
	 * This routine is called when one of two conditions occur.  It is
	 * called when an outside process tries to establish a steam
	 * Internet connection.
	 *
	 * Later, as soon as data is available, this routine will be
	 * called again to handle the external message (which must be
	 * a "new window" command).
	 */
	if (sd == inet_sd) {
		sinlen = sizeof sin;
		if ((fd = accept(sd, &sin, &sinlen)) >= 0) {
			(void)fcntl(fd, F_SETFL, FNDELAY);
			fdmap[fd].f_type = FDT_ISSOCK;
			fdmap[fd].f_win = (struct window *)0;
			FD_SET(fd, &selmask[0].sm_rd);
			inet_buf[fd].im_len = 0;
		}
	} else {
		switch (ipc_getmsg(sd, inet_buf + sd)) {
		case -1:
			(void)close(sd);
			fdmap[sd].f_type = FDT_NONE;
			FD_CLR(sd, &selmask[0].sm_rd);
			FD_CLR(sd, &selmask[0].sm_wt);
			FD_CLR(sd, &selmask[0].sm_ex);
			break;
		case 1:
			uwip = &inet_buf[sd].im_msg;
			uwerr = UWE_NONE;
			if ((uwip->uwip_len < sizeof(struct uwneww) + 
			    ((char *)&uwip->uwip_neww - (char *)uwip)) ||
			    uwip->uwip_cmd != UWC_NEWW) {
				uwerr = UWE_NXTYPE;
			} else {
				fd = ipc_ctlopen(sd,
				    (unsigned)uwip->uwip_neww.uwnw_ctlport);
				w = PCL_NEWW(0, WC_EXTERNAL,
				      ntohs(uwip->uwip_neww.uwnw_type),
				      (nwin_t)0, ntohl(uwip->uwip_neww.uwnw_id),
				      sd, fd);
				if (w == (struct window *)0)
					uwerr = UWE_NXTYPE;	/* for now */
				else
					uwerr = UWE_NONE;
			}
			len = sizeof(struct uwstatus) +
			    ((char *)&reply.uwip_status - (char *)&reply);
			reply.uwip_len = htons(len);
			reply.uwip_cmd = htons(UWC_STATUS);
			reply.uwip_status.uwst_err = htons(uwerr);
			reply.uwip_status.uwst_errno = htons(errno);
			if (uwerr == UWE_NONE)
				reply.uwip_status.uwst_id = htonl(w->w_id);
			else
				reply.uwip_status.uwst_id = 0;
			(void)write(sd, (char *)&reply, len);
			if (uwerr != UWE_NONE) {
				(void)close(sd);
				fdmap[sd].f_type = FDT_NONE;
				FD_CLR(sd, &selmask[0].sm_rd);
				FD_CLR(sd, &selmask[0].sm_wt);
				FD_CLR(sd, &selmask[0].sm_ex);
			}
			inet_buf[sd].im_len = 0;
		}
	}
}

ipc_ctlopen(sd, port)
fildes_t sd;
unsigned port;
{
	register int fd;
	auto struct sockaddr_in sin;
	auto int sinlen;

	/*
	 * Create a control socket and connect it to the same host as
	 * "sd" on the specified port.
	 */
	sinlen = sizeof sin;
	if (port == 0 ||
	    getpeername(sd, (struct sockaddr *)&sin, &sinlen) < 0 ||
	    (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		return(-1);
	} else {
		sin.sin_port = port;
		(void)fcntl(fd, F_SETFL, FNDELAY);
		if (connect(fd, &sin, sinlen) < 0 && errno != EINPROGRESS) {
			(void)close(fd);
			return(-1);
		} else
			return(fd);
	}
}

void
ipc_optmsg(win, optcmd, optnum, data, datalen)
caddr_t win;
woptcmd_t optcmd;
woption_t optnum;
char *data;
unsigned datalen;
{
	register struct window *w;
	register int len;
	struct uwipc uwip;

	/*
	 * Propagate a window option message (WILL, WONT, SET) from the Mac
	 * to the remote process (external windows only).
	 */
	if ((w = (struct window *)win) != NULL && w->w_alloc &&
	    w->w_class == WC_EXTERNAL && w->w_ctlfd >= 0 &&
	    optnum <= WONUM_MAX && (optcmd == WOC_WILL || optcmd == WOC_WONT ||
	     (optcmd == WOC_SET && data != NULL))) {
		len = datalen +
		    ((char *)&uwip.uwip_option.uwop_val - (char *)&uwip);
		uwip.uwip_len = htons(len);
		uwip.uwip_cmd = htons(UWC_OPTION);
		uwip.uwip_option.uwop_id = htonl(w->w_id);
		uwip.uwip_option.uwop_opt = htons(optnum);
		uwip.uwip_option.uwop_cmd = htons(optcmd);
		if (optcmd == WOC_SET) {
			bcopy(data, (char *)&uwip.uwip_option.uwop_val,
			    (int)datalen);
			opt_netadj(w->w_optdefn.wod_optlst[optnum].wol_argdefn,
			    (char *)&uwip.uwip_option.uwop_val, na_hton);
		}
		(void)write(w->w_ctlfd, (char *)&uwip, len);
	}
}

ipc_ctlrecv(mfd, sd, win)
fildes_t mfd;
register fildes_t sd;
register struct window *win;
{
	register struct window *w;
	register struct uwipc *uwip;

	switch (ipc_getmsg(sd, inet_buf + sd)) {
	case -1:
		(void)close(sd);
		fdmap[sd].f_type = FDT_NONE;
		FD_CLR(sd, &selmask[0].sm_rd);
		FD_CLR(sd, &selmask[0].sm_wt);
		FD_CLR(sd, &selmask[0].sm_ex);
		break;
	case 1:
		uwip = &inet_buf[sd].im_msg;
		switch (uwip->uwip_cmd) {
		case UWC_KILLW:
			if ((uwip->uwip_len == sizeof(struct uwkillw) + 
			    ((char *)&uwip->uwip_killw - (char *)uwip))) {
				w = win_search(ntohl(uwip->uwip_killw.uwkw_id),
				    protocol->p_maxwin);
				if (w == win)
					PCL_KILLW(mfd, w);
			}
			break;
		case UWC_OPTION:
			/* hope the message is long enough... sigh */
			if (uwip->uwip_len > 
			 ((char *)&uwip->uwip_option.uwop_val - (char *)uwip)) {
				w = win_search(ntohl(uwip->uwip_option.uwop_id),
				    protocol->p_maxwin);
				if (w == win) {
					opt_extopt((caddr_t)w, &w->w_optdefn,
					    (woptcmd_t)ntohs(uwip->uwip_option.uwop_cmd),
					    (woption_t)ntohs(uwip->uwip_option.uwop_opt),
					    (char *)&uwip->uwip_option.uwop_val,
					    na_ntoh);
				}
			}
			break;
		}
		inet_buf[sd].im_len = 0;
	}
}

ipc_getmsg(sd, im)
register fildes_t sd;
register struct ipcmsg *im;
{
	register int len;
	register char *cp;

	/*
	 * Read some more bytes from socket "sd" into the message buffer
	 * contained in "im".  Return 1 if the message is now complete,
	 * -1 if an EOF was reached, or 0 otherwise.  Before returning 1,
	 * the byte order of the common parameters (command, length) is
	 * changed from network to host order.
	 *
	 * This routine expects the socket to use non-blocking I/O (which
	 * is enabled by ipc_isrecv() when the connection is accepted).
	 */
	cp = (char *)&im->im_msg + im->im_len;
	if (im->im_len < sizeof(im->im_msg.uwip_len)) {
		len = read(sd, cp, sizeof im->im_msg.uwip_len - im->im_len);
		if (len == 0 || (len < 0 && errno != EWOULDBLOCK))
			return(-1);
		if ((im->im_len += len) < sizeof im->im_msg.uwip_len)
			return(0);
		im->im_msg.uwip_len = ntohs(im->im_msg.uwip_len);
		if (im->im_msg.uwip_len == sizeof im->im_msg.uwip_len)
			return(1);
		cp += len;
	}
	if (im->im_msg.uwip_len > sizeof(struct ipcmsg))
		im->im_msg.uwip_len = sizeof(struct ipcmsg);
	len = read(sd, cp, im->im_msg.uwip_len - im->im_len);
	if (len == 0)
		return(-1);
	if (len < 0)
		return((errno==EWOULDBLOCK) ? 0 : -1);
	if ((im->im_len += len) == im->im_msg.uwip_len) {
		im->im_msg.uwip_cmd = ntohs(im->im_msg.uwip_cmd);
		return(1);
	} else
		return(0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.