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.