ain@j.cc.purdue.edu (Patrick White) (01/25/88)
Program Name: uw (unix window server) Submitted By: I snarfed it from simtel20 and posted it. Summary: unix (server) end of the unix windows protocol. Poster Boy: Pat White (ain@j.cc.purdue.edu) Tested. NOTES: I know a lot of people are going to want this, so I've posted it.. this is the version on simtel20 with the 110K of macintosh stuff removed. It is suggested that the "env" array in uw.c be changed so the server dosen't force the termcap to a adm31 as the Amiga dosen't use the adm31 termcap. -- Pat White (co-moderator comp.sources/binaries.amiga) UUCP: j.cc.purdue.edu!ain BITNET: PATWHITE@PURCCVM PHONE: (317) 743-8421 U.S. Mail: 320 Brown St. apt. 406, West Lafayette, IN 47906 ======================================== # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # README # Makefile # openpty.c # uw.c # uwtool.c # openpty.h # uw.h # This archive created: Mon Jan 25 09:35:02 1988 # By: Patrick White (PUCC Land, USA) echo shar: extracting README '(4555 characters)' cat << \SHAR_EOF > README The program "uw" is a multiple-window terminal emulator for the Macintosh(tm) computer. It is designed for use with a 4.2BSD UNIX(tm) system. Up to seven independent windows may be created, each of which is connected to a pseudo-terminal on the UNIX machine. A server program which runs on the UNIX host multiplexes the input and output for these terminal sessions onto a single RS-232 serial line. UW requires the following: a Macintosh a 4.2BSD UNIX host the "uw" program for the Macintosh the "uw" server for the UNIX host the "uwtool" program for the UNIX host (optional) the RAM serial driver (resource SERD, ID=1) (optional) At the present time, UW emulates a Lear Siegler ADM-31 terminal, a DEC VT52, and a Tektronix 4010. All portions of UW are copyrighted 1985 by John D. Bruner. Permission to copy is given provided that the copy is not sold and the copyright notices are included. UW was designed to use the RAM serial driver from the Apple software supplement. Since this driver is proprietary Apple software, it is not distributed with UW. For this reason, UW will use the ROM serial driver if the RAM serial driver is not present, and it will attempt to provide flow control itself. The flow control provided in this fashion is not as effective as that which the RAM serial driver provides, so use of the RAM driver is recommended if it is available. The distribution includes: README - this file Makefile - to be used to compile the 4.2BSD programs uw.h - include file (describes the simple serial protocol) uw.c - source for the 4.2BSD server program uwtool.c - source for a 4.2BSD "tool" program openpty.c - source for a utility routine to find and open pty's openpty.h - include file (describes a structure used by openpty()) macmouse.ml - mlisp functions for use with (Unipress) Emacs uw.hqx - Binhex 4.0 binary for the Macintosh uw.doc.hqx - Binhex 4.0 MacWrite documentation Changes to UW ------------- This is UW version 2.10. The last distributed version was 1.6. A number of changes have been made to the Macintosh portion of UW. Some of the more significant ones are 1) Configuration files: A number of parameters such as the baud rate, parity, number of stop bits, keyboard mapping, etc. can be saved to and reloaded from a configuration file. 2) Terminal emulations: In addition to an ADM31 emulation, UW also can emulate the VT52 and Tektronix 4010. (The VT52 emulation is provisional and may be replaced by a VT100 emulation in a future version of UW.) The terminal emulation is selectable on a per-window basis. 3) Clipboard: Desk accessories may use all of the standard clipboard functions. Text may be copied from a UW window onto the clipboard, and text in the clipboard may be pasted into a window. 4) Mouse clicks: As an alternative to the clipboard, mouse (up/down) events within a window may be translated into an escape sequence and transmitted as input data to the host. This provides a rudimentary facility for (e.g.) EMACS to use mouse input. The use of mouse-to-host or clipboard copying is selectable on a per-window basis. 5) Multiple fonts: In addition to the standard 9-point Monaco font, UW will also use a 7-point font. The font size is selectable on a per-window basis. 6) Inverse video: Inverse video was not implemented in the previous version of UW. It is now supported for the ADM31. 7) Window renaming: Window titles may be changed (on the Macintosh). In conjunction with (4) above, Chris Kent at Purdue (cak@purdue) has written mlisp code for Goslings Emacs which interprets the mouse-event encoding that UW can optionally provide. He has given his permission for his file "macmouse.ml" to be included in this distribution. There are no changes in the Mac-host interface (although some plans are being made in this area). Thus, one of the most-requested features, file transfer, is not implemented (yet). Of course, bugs in the previous version have been fixed (most notably some that dealt with scrolling). Almost certainly new bugs have been introduced to take the place of those which were expunged. Since some of the changes are rather significant (and a couple, like the method of selecting text to copy to the clipboard, are slightly non-standard), the updated documentation is recommended reading for all UW users. UW is much larger than it was before. Extensive use of the Tektronix 4010 emulation will almost certainly exhaust memory on a 128K Macintosh. SHAR_EOF if test 4555 -ne "`wc -c README`" then echo shar: error transmitting README '(should have been 4555 characters)' fi echo shar: extracting Makefile '(156 characters)' cat << \SHAR_EOF > Makefile CC = /bin/cc CFLAGS = -O all: uw uwtool uw: uw.o openpty.o $(CC) -o uw uw.o openpty.o uwtool: uwtool.o openpty.o $(CC) -o uwtool uwtool.o openpty.o SHAR_EOF if test 156 -ne "`wc -c Makefile`" then echo shar: error transmitting Makefile '(should have been 156 characters)' fi echo shar: extracting openpty.c '(3914 characters)' cat << \SHAR_EOF > openpty.c /* * openpty - open a pseudo-terminal * * The first time that the routine is called, the device directory is * searched and a list of all candidate pseudo-terminals is compiled. * Candidates are defined to be those entries in "/dev" whose names * (1) are the same length as PTY_PROTO and (2) start with the * initial string PTY_PREFIX. Further, the master and slave sides * must both exist. * * openpty() attempts to find an unused pseudo-terminal from the list * of candidates. If one is found, the master and slave sides are * opened and the file descriptors and names of these two devices are * returned in a "ptydesc" structure. (The address of this structure * is supplied by the caller. Zero is returned if openpty() was * successful, -1 is returned if no pty could be found. */ #include <sys/types.h> #include <sys/dir.h> #include <fcntl.h> #include <strings.h> #include "openpty.h" #define DEV_DIR "/dev" /* directory where devices live */ #define PT_INDEX (sizeof DEV_DIR) /* location of 'p' in "pty" */ #define PTY_PROTO "ptyp0" /* prototype for pty names */ #define PTY_PREFIX "pty" /* prefix required for name of pty */ struct ptyinfo { struct ptyinfo *pi_next; char *pi_pty; char *pi_tty; }; static struct ptyinfo *ptylist; extern char *malloc(); static char * devname(name) char *name; { register char *fullname; /* * Construct the full name of a device in DEV_DIR. Returns * NULL if it failed (because malloc() failed). */ fullname = malloc((unsigned)(sizeof DEV_DIR + 1 + strlen(name))); if (fullname != NULL) { (void)strcpy(fullname, DEV_DIR); (void)strcat(fullname, "/"); (void)strcat(fullname, name); } return(fullname); } static isapty(dp) struct direct *dp; { static struct ptyinfo *pi; /* * We don't care about the gory details of the directory entry. * Instead, what we really want is an array of pointers to * device names (with DEV_DIR prepended). Therefore, we create * this array ourselves and tell scandir() to ignore every * directory entry. * * If malloc() fails, the current directory entry is ignored. */ if (pi == NULL && (pi = (struct ptyinfo *)malloc((unsigned)sizeof *pi)) == NULL) return(0); if (strlen(dp->d_name) == sizeof PTY_PROTO - 1 && strncmp(dp->d_name, PTY_PREFIX, sizeof PTY_PREFIX - 1) == 0) { pi->pi_pty = devname(dp->d_name); if (pi->pi_pty == NULL) return(0); pi->pi_tty = malloc((unsigned)(strlen(pi->pi_pty) + 1)); if (pi->pi_tty == NULL) { free(pi->pi_pty); return(0); } (void)strcpy(pi->pi_tty, pi->pi_pty); pi->pi_tty[PT_INDEX] = 't'; if (access(pi->pi_pty, 0) == 0 && access(pi->pi_tty, 0) == 0) { pi->pi_next = ptylist; ptylist = pi; pi = NULL; } else { free(pi->pi_pty); free(pi->pi_tty); } } return(0); } openpty(pt) struct ptydesc *pt; { register struct ptyinfo *pi; static int fail; auto struct direct **dirlist; extern char *re_comp(); extern int alphasort(); /* * If the regular expression PTY_PROTO is bad, scandir() fails, or * no possible pty's are found, then "fail" is set non-zero. If * "fail" is non-zero then the routine bombs out immediately. * Otherwise, the list of candidates is examined starting with the * entry following the last one chosen. */ if (fail) return(-1); if (!ptylist) { /* first time */ if (scandir(DEV_DIR, &dirlist, isapty, alphasort) < 0 || ptylist == NULL) { fail = 1; return(-1); } for (pi=ptylist; pi->pi_next; pi=pi->pi_next) ; pi->pi_next = ptylist; /* make the list circular */ } pi = ptylist; do { if ((pt->pt_pfd = open(pi->pi_pty, O_RDWR)) >= 0) { if ((pt->pt_tfd = open(pi->pi_tty, O_RDWR)) >= 0) { ptylist = pi->pi_next; pt->pt_pname = pi->pi_pty; pt->pt_tname = pi->pi_tty; return(0); } else (void)close(pt->pt_pfd); } pi = pi->pi_next; } while (pi != ptylist); return(-1); } SHAR_EOF if test 3914 -ne "`wc -c openpty.c`" then echo shar: error transmitting openpty.c '(should have been 3914 characters)' fi echo shar: extracting uw.c '(17598 characters)' cat << \SHAR_EOF > uw.c # /* * uw - UNIX windows program for the Macintosh (VAX end) * * Copyright 1985 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/stat.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/uio.h> #include <signal.h> #include <setjmp.h> #include <errno.h> #include <fcntl.h> #include <strings.h> #include <stdio.h> #include "uw.h" #include "openpty.h" #define MAXENV 128 /* maximum environment size */ #define W_IN 0 #define W_OUT 1 #define XON 0021 /* ASCII XON */ #define XOFF 0023 /* ASCII XOFF */ #define RUB 0177 /* ASCII RUBOUT */ #define META 0200 /* "meta" bit for whatever it's worth */ #define RCV_OK (-1) /* see recvcmd() */ #define RCV_META (-2) /* see recvcmd() */ #ifndef FD_SET #define FD_SET(n,p) ((p)->fds_bits[0] |= (1 << (n))) #define FD_CLR(n,p) ((p)->fds_bits[0] &= ~(1 << (n))) #define FD_ISSET(n,p) ((p)->fds_bits[0] & (1 << (n))) #define FD_ZERO(p) ((p)->fds_bits[0] = 0) #endif typedef int fildes_t; /* file descriptor data type */ typedef int nwin_t; /* window index data type */ struct window { fildes_t w_fd; char w_tty[32]; }; struct window window[NWINDOW]; /* window data structures */ struct window **fdmap; /* mapping from fd's to windows */ struct window *curwin[2]; /* current input and output windows */ fildes_t nfds; /* number of file descriptors */ char portal[] = "/tmp/uwXXXXXX"; /* UNIX-domain network address */ /* The following are added to the environment of all child processes */ char env_uwin[64] = "UWIN="; char *env[] = { env_uwin, "TERM=adm31", "TERMCAP=adm31:cr=^M:do=^J:nl=^J:al=\\EE:am:le=^H:bs:ce=\\ET:cm=\\E=%+ %+ :cl=^Z:cd=\\EY:co#80:dc=\\EW:dl=\\ER:ei=\\Er:ho=^^:im=\\Eq:li#24:mi:nd=^L:up=^K:MT:km:so=\\EG1:se=\\EG0:", NULL }; int ctlch[] = { -1, IAC, XON, XOFF, -1, -1, -1, -1 }; /* CTL char mapping */ struct selmask { struct fd_set sm_rd; struct fd_set sm_wt; struct fd_set sm_ex; } selmask[2]; extern char *mktemp(); extern char *getenv(); extern char *malloc(); extern done(), cwait(); extern int errno; main(argc, argv) char **argv; { register fildes_t fd, sd; register struct window *w; struct sockaddr sa; /* * Make sure we don't accidentally try to run this inside itself. */ if (getenv("UWIN")) { fprintf(stderr, "%s is already running\n", *argv); exit(1); } /* * Close all file descriptors except 0, 1, and 2. */ nfds = getdtablesize(); for (fd=3; fd < nfds; fd++) (void)close(fd); /* * Allocate "fdmap". We can't do this at compile time because * we get the number of file descriptors from getdtablesize(). */ if (!(fdmap=(struct window **)malloc(nfds*sizeof(struct window *)))) { fprintf(stderr, "cannot allocate 'fdmap' array\n"); exit(1); } /* * Mark all windows closed. */ for (w=window; w < window+NWINDOW; w++) w->w_fd = -1; /* * Create a UNIX-domain network address, and put its name into * the environment so that descendents can contact us with new * window requests. */ (void)strncat(env_uwin, mktemp(portal), sizeof env_uwin - 1); setenv(env); if ((sd=socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } sa.sa_family = AF_UNIX; (void)strncpy(sa.sa_data, portal, sizeof sa.sa_data-1); sa.sa_data[sizeof sa.sa_data-1] = '\0'; if (bind(sd, &sa, sizeof sa.sa_family + strlen(sa.sa_data)) < 0) { perror("bind"); exit(1); } if (fcntl(sd, F_SETFL, FNDELAY)) perror("fcntl(sd, F_SETFL, FNDELAY)"); /* * Ignore interrupts, quits, and terminal stops. Clean up and exit * if a hangup or termination is received. Also catch changes in * child status (so that we can wait for them). Set up the terminal * modes. */ (void)signal(SIGHUP, done); (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGTERM, done); (void)signal(SIGTSTP, SIG_IGN); (void)signal(SIGCHLD, cwait); tmode(1); /* * Tell the Macintosh to initialize. Initialize the current input * and output windows to NULL. */ xmitcmd(CB_FN_MAINT|CB_MF_ENTRY); curwin[W_IN] = curwin[W_OUT] = NULL; /* * Initialize the "select" masks, create window 1 (to start * things off) and wait for input. When input is available, * process it. */ FD_ZERO(&selmask[0].sm_rd); FD_ZERO(&selmask[0].sm_wt); FD_ZERO(&selmask[0].sm_ex); FD_SET(0, &selmask[0].sm_rd); FD_SET(sd, &selmask[0].sm_rd); if (newwind(window) == 0) xmitcmd(CB_FN_NEWW|1); while (1) { selmask[1] = selmask[0]; if (select(nfds, &selmask[1].sm_rd, &selmask[1].sm_wt, &selmask[1].sm_ex, (struct timeval *)0) < 0) { if (errno == EINTR) continue; perror("select"); done(1); /* for now -- fix this! */ } for (fd=0; fd < nfds; fd++) { if (FD_ISSET(fd, &selmask[1].sm_rd)) { if (fd < 2) mrecv(); else if (fd == sd) netrecv(sd); else mxmit(fd); } } } } mrecv() { register int len; register char *cp, *cq; auto int nready; char ibuf[512], obuf[512]; static int seen_iac, seen_meta; /* * The received bytestream is examined. Non-command bytes are * written to the file descriptor corresponding to the current * "input" window (relative to the Macintosh -- the window the * user types input to). */ if (ioctl(0, (int)FIONREAD, (char *)&nready) < 0) { perror("FIONREAD"); return; } cq = obuf; while (nready > 0) { if (nready > sizeof ibuf) len = read(0, ibuf, sizeof ibuf); else len = read(0, ibuf, nready); if (len <= 0) { perror("read"); return; } for (cp=ibuf; cp < ibuf+len; cp++) { if (seen_iac) { if ((*cp&CB_DIR) == CB_DIR_MTOH) { if (cq > obuf) { (void)write(curwin[W_IN]->w_fd, obuf, cq-obuf); cq = obuf; } switch (*cq = recvcmd(*cp)) { case RCV_OK: break; case RCV_META: seen_meta = 1; break; default: if (seen_meta) { seen_meta = 0; *cq |= META; } if (curwin[W_IN]) cq++; break; } } seen_iac = 0; } else if (*cp == IAC) seen_iac++; else if (curwin[W_IN]) { if (seen_meta) { seen_meta = 0; *cq++ = *cp|META; } else *cq++ = *cp; if (cq >= obuf+sizeof obuf) { (void)write(curwin[W_IN]->w_fd, obuf, cq-obuf); cq = obuf; } } } nready -= len; } if (cq > obuf) (void)write(curwin[W_IN]->w_fd, obuf, cq-obuf); } recvcmd(cmd) char cmd; { register int nwin, fn; register struct window *w; /* * Perform the function the Mac is asking for. There are three * broad categories of these functions: RCV_META, which tells * the caller that the next character is a "meta" character; * an ASCII data character, which is passed back to the caller * for proper handling; and RCV_OK, which means that this routine * has done everything which was required to process the command. */ fn = cmd&CB_FN; switch (fn) { case CB_FN_NEWW: case CB_FN_KILLW: case CB_FN_ISELW: nwin = cmd&CB_WINDOW; if (!nwin) break; w = &window[nwin-1]; switch (fn) { case CB_FN_NEWW: if (w->w_fd < 0 && newwind(w) < 0) xmitcmd(CB_FN_KILLW|nwin); break; case CB_FN_KILLW: killwind(w, 0); break; case CB_FN_ISELW: if (w->w_fd >= 0) curwin[W_IN] = w; break; } break; case CB_FN_META: return(RCV_META); case CB_FN_CTLCH: return(ctlch[cmd&CB_CC]); case CB_FN_MAINT: if ((cmd&CB_MF) == CB_MF_EXIT) done(0); /*NOTREACHED*/ } return(RCV_OK); } xmitcmd(cmd) char cmd; { static char cmdbuf[2] = { IAC, '\0' }; /* * Transmit the command "cmd" to the Macintosh. The byte is ORed * with the host-to-Mac direction indicator. */ cmdbuf[1] = cmd|CB_DIR_HTOM; (void)write(1, cmdbuf, sizeof cmdbuf); } netrecv(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; static int unity = 1; char buf[256]; /* * 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; 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) perror("recvmsg"); 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....] */ buf[cnt] = 0; if (strncmp(buf, "/dev/pty", sizeof "/dev/pty" - 1) || fstat(fd, &st1) < 0 || stat(buf, &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. */ buf[5] = 't'; /* switch to "/dev/ttyp?" */ for (w=window; w < window+NWINDOW; w++) { if (w->w_fd < 0) { w->w_fd = fd; fdmap[fd] = w; FD_SET(fd, &selmask[0].sm_rd); (void)strncpy(w->w_tty, buf, sizeof w->w_tty-1); xmitcmd(CB_FN_NEWW|(w-window+1)); break; } } /* * If we have no free windows, then we close the file * descriptor (which will terminate the slave process). */ if (w == window+NWINDOW) (void)close(fd); /* * Set non-blocking I/O on the master side. */ (void)ioctl(fd, (int)FIONBIO, (char *)&unity); } } mxmit(fd) register fildes_t fd; { register char *cp, *cq, i; register int len; char ibuf[32], obuf[32]; /* * Copy input from file "fd" to the Macintosh. Be sure to convert * any embedded IAC characters. * * Note that the input/output buffers should NOT be very large. * It is undesirable to perform large reads and effectively * "lock out" all other file descriptors. The chosen size * should preserve a reasonable amount of efficiency. */ if (fdmap[fd]) { curwin[W_OUT] = fdmap[fd]; xmitcmd(CB_FN_OSELW|(fdmap[fd]-window+1)); cq = obuf; if ((len = read(fd, ibuf, sizeof ibuf)) < 0 && errno != EWOULDBLOCK) { killwind(fdmap[fd], 1); return; } for (cp=ibuf; cp < ibuf+len; cp++) { if (*cp&META) { if (cq > obuf) { (void)write(1, obuf, cq-obuf); cq = obuf; } xmitcmd(CB_FN_META); *cp &= ~META; } i = -1; if (*cp == RUB || *cp < ' ') { i = sizeof ctlch; while (i >= 0 && ctlch[i] != *cp) i--; } if (i >= 0) { if (cq > obuf) { (void)write(1, obuf, cq-obuf); cq = obuf; } xmitcmd(CB_FN_CTLCH|i); } else { *cq++ = *cp; if (cq >= obuf+sizeof obuf) { (void)write(1, obuf, cq-obuf); cq = obuf; } } } } else (void)read(fd, ibuf, sizeof ibuf); if (cq > obuf) (void)write(1, obuf, cq-obuf); } killwind(w, notify) register struct window *w; { /* * Kill window "w". Notify the Macintosh that it is gone if * "notify" is nonzero. */ FD_CLR(w->w_fd, &selmask[0].sm_rd); FD_CLR(w->w_fd, &selmask[0].sm_wt); FD_CLR(w->w_fd, &selmask[0].sm_ex); (void)close(w->w_fd); fdmap[w->w_fd] = NULL; w->w_fd = -1; if (curwin[W_IN] == w) curwin[W_IN] = NULL; if (curwin[W_OUT] == w) curwin[W_OUT] = NULL; if (notify) xmitcmd(CB_FN_KILLW|(w-window+1)); } newwind(w) register struct window *w; { register int pid; register char *shell; register int fd; auto struct ptydesc pt; static int unity = 1; extern char *getenv(); /* * Create a new window using "w". */ if (openpty(&pt) < 0) return(-1); /* better recovery is needed */ (void)ioctl(pt.pt_pfd, (int)FIONBIO, (char *)&unity); fdmap[pt.pt_pfd] = w; w->w_fd = pt.pt_pfd; FD_SET(pt.pt_pfd, &selmask[0].sm_rd); (void)strncpy(w->w_tty, pt.pt_tname, sizeof w->w_tty); /* * We must spawn a new process with fork(), not vfork(), because * some implementations (e.g. Sun UNIX) manipulate static * variables within the signal() subroutine. If we vfork() * the parent will die when the first SIGCHLD arrives. The * data+bss size is fairly small, so this isn't too expensive. */ while ((pid=fork()) < 0) sleep(5); if (!pid) { (void)signal(SIGHUP, SIG_DFL); (void)signal(SIGINT, SIG_DFL); (void)signal(SIGQUIT, SIG_DFL); (void)signal(SIGTERM, SIG_DFL); (void)signal(SIGTSTP, SIG_IGN); (void)signal(SIGCHLD, SIG_DFL); (void)ioctl(open("/dev/tty",O_RDWR), (int)TIOCNOTTY, (char *)0); (void)close(open(pt.pt_tname, O_RDONLY)); /* set new ctrl tty */ (void)setuid(getuid()); /* shouldn't need this */ if (!(shell = getenv("SHELL"))) shell = "/bin/sh"; (void)dup2(pt.pt_tfd, 0); (void)dup2(0, 1); (void)dup2(0, 2); for (fd=3; fd < nfds; fd++) (void)close(fd); tmode(0); /* HACK! */ execl(shell, shell, (char *)0); _exit(1); } (void)close(pt.pt_tfd); return(0); } tmode(f) { static struct sgttyb ostty, nstty; static struct tchars otchars, ntchars; static struct ltchars oltchars, nltchars; static int olmode, nlmode; static saved; /* * This routine either saves the current terminal modes and then * sets up the terminal line or resets the terminal modes (depending * upon the value of "f"). The terminal line is used in "cbreak" * mode with all special characters except XON/XOFF disabled. The * hated (by me) LDECCTQ mode is required for the Macintosh to * handle flow control properly. */ if (f == 1) { if (ioctl(0, (int)TIOCGETP, (char *)&ostty) < 0) { perror("ioctl((int)TIOCGETP)"); done(1); } if (ioctl(0, (int)TIOCGETC, (char *)&otchars) < 0) { perror("ioctl((int)TIOCGETC)"); done(1); } if (ioctl(0, (int)TIOCGLTC, (char *)&oltchars) < 0) { perror("ioctl((int)TIOCGLTC)"); done(1); } if (ioctl(0, (int)TIOCLGET, (char *)&olmode) < 0) { perror("ioctl((int)TIOCLGET)"); done(1); } nstty = ostty; nstty.sg_erase = nstty.sg_kill = -1; nstty.sg_flags |= CBREAK|TANDEM; nstty.sg_flags &= ~(RAW|CRMOD|ECHO|LCASE|XTABS); ntchars.t_intrc = ntchars.t_quitc = -1; ntchars.t_eofc = ntchars.t_brkc = -1; ntchars.t_startc = XON; ntchars.t_stopc = XOFF; nltchars.t_suspc = nltchars.t_dsuspc = -1; nltchars.t_rprntc = nltchars.t_flushc = -1; nltchars.t_werasc = nltchars.t_lnextc = -1; nlmode = olmode | LDECCTQ; if (ioctl(0, (int)TIOCSETN, (char *)&nstty) < 0) { perror("ioctl((int)TIOCSETN)"); done(1); } if (ioctl(0, (int)TIOCSETC, (char *)&ntchars) < 0) { perror("ioctl((int)TIOCSETC"); done(1); } if (ioctl(0, (int)TIOCSLTC, (char *)&nltchars) < 0) { perror("ioctl((int)TIOCSLTC"); done(1); } if (ioctl(0, (int)TIOCLSET, (char *)&nlmode) < 0) { perror("ioctl((int)TIOCLSET)"); done(1); } saved = 1; } else if (saved) { (void)ioctl(0, (int)TIOCSETP, (char *)&ostty); (void)ioctl(0, (int)TIOCSETC, (char *)&otchars); (void)ioctl(0, (int)TIOCSLTC, (char *)&oltchars); (void)ioctl(0, (int)TIOCLSET, (char *)&olmode); } } done(s) { register fildes_t fd; /* * Clean up and exit. It is overkill to close all of the file * descriptors, but it causes no harm. After we are sure that * our UNIX-domain network connection is closed we remove the * entry that it created (as a side effect) in the filesystem. * * We also restore the terminal modes. */ /*xmitcmd(CB_FN_MAINT|CB_MF_EXIT);*/ for (fd=3; fd < nfds; fd++) (void)close(fd); (void)unlink(portal); tmode(0); exit(s); } cwait() { union wait status; struct rusage rusage; /* * Collect dead children. We don't use the information that * wait3() returns. (Someday we might.) */ while (wait3(&status, WNOHANG, &rusage) > 0) ; } static char *earray[MAXENV+1]; setenv(env) char **env; { register char **ep1, **ep2, *cp; char **ep3; extern char **environ; /* * Merge the set of environment strings in "env" into the * environment. */ /* * The first time through, copy the environment from its * original location to the array "earray". This makes it a * little easier to change things. */ if (environ != earray){ ep1=environ; ep2=earray; while(*ep1 && ep2 <= earray+MAXENV) *ep2++ = *ep1++; *ep2++ = NULL; environ = earray; } /* * If "env" is non-NULL, it points to a list of new items to * be added to the environment. These replace existing items * with the same name. */ if (env){ for(ep1=env; *ep1; ep1++){ for(ep2=environ; *ep2; ep2++) if (!envcmp(*ep1, *ep2)) break; if (ep2 < earray+MAXENV) { if (!*ep2) ep2[1] = NULL; *ep2 = *ep1; } } } /* Finally, use an insertion sort to put things in order. */ for(ep1=environ+1; cp = *ep1; ep1++){ for(ep2=environ; ep2 < ep1; ep2++) if (envcmp(*ep1, *ep2) < 0) break; ep3 = ep2; for(ep2=ep1; ep2 > ep3; ep2--) ep2[0] = ep2[-1]; *ep2 = cp; } } static envcmp(e1, e2) register char *e1, *e2; { register d; do { if (d = *e1 - *e2++) return(d); } while(*e1 && *e1++ != '='); return(0); } SHAR_EOF if test 17598 -ne "`wc -c uw.c`" then echo shar: error transmitting uw.c '(should have been 17598 characters)' fi echo shar: extracting uwtool.c '(3073 characters)' cat << \SHAR_EOF > uwtool.c # /* * uwtool * * Copyright 1985 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/socket.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/uio.h> #include <strings.h> #include <signal.h> #include <stdio.h> #include "openpty.h" #define NFDS 20 /* max number of file descriptors */ typedef int fildes_t; extern char *getenv(); main(argc, argv) char **argv; { register int pid; register fildes_t sd; auto fildes_t fd; char *portal, *shell; int lmode; struct sgttyb sg; struct tchars tc; struct ltchars ltc; struct iovec iov; struct msghdr msg; struct sockaddr sa; auto struct ptydesc pt; /* * Close all file descriptors except 0, 1, and 2. */ for (fd=3; fd < NFDS; fd++) (void)close(fd); /* * Get the terminal configuration for this tty. */ (void)ioctl(0, (int)TIOCGETP, (char *)&sg); (void)ioctl(0, (int)TIOCGETC, (char *)&tc); (void)ioctl(0, (int)TIOCGLTC, (char *)<c); (void)ioctl(0, (int)TIOCLGET, (char *)&lmode); /* * Create a UNIX-domain socket. */ if (!(portal=getenv("UWIN"))) { fprintf(stderr, "You must run %s under the window manager\n", *argv); exit(1); } if ((sd=socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } sa.sa_family = AF_UNIX; (void)strncpy(sa.sa_data, portal, sizeof sa.sa_data-1); sa.sa_data[sizeof sa.sa_data-1] = '\0'; /* * Obtain a pseudo-tty. */ if (openpty(&pt) < 0) { fprintf(stderr, "Can't obtain a pseudo-tty for a new window\n"); exit(1); } /* * Fork a child process using this pseudo-tty. Initialize the * terminal modes on the pseudo-tty to match those of the parent * tty. */ while ((pid=vfork()) < 0) sleep(5); if (!pid) { (void)setuid(getuid());/* in case it's setuid-root by mistake */ (void)signal(SIGTSTP, SIG_IGN); (void)ioctl(open("/dev/tty", 2), (int)TIOCNOTTY, (char *)0); (void)close(open(pt.pt_tname, 0)); /*set new ctrl tty */ (void)dup2(pt.pt_tfd, 0); (void)dup2(0, 1); (void)dup2(0, 2); for (fd=3; fd < NFDS; fd++) (void)close(fd); (void)ioctl(0, (int)TIOCSETN, (char *)&sg); (void)ioctl(0, (int)TIOCSETC, (char *)&tc); (void)ioctl(0, (int)TIOCSLTC, (char *)<c); (void)ioctl(0, (int)TIOCLSET, (char *)&lmode); if (argc == 1) { if (!(shell=getenv("SHELL"))) shell = "/bin/sh"; execl(shell, shell, (char *)0); perror(shell); } else { execvp(argv[1], argv+1); perror(argv[1]); } _exit(1); } /* * Pass the file descriptor to the window server and exit. */ iov.iov_base = pt.pt_pname; iov.iov_len = strlen(pt.pt_pname); msg.msg_name = (caddr_t)&sa; msg.msg_namelen = sizeof sa.sa_family + strlen(sa.sa_data); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_accrights = (caddr_t)&pt.pt_pfd; msg.msg_accrightslen = sizeof pt.pt_pfd; if (sendmsg(sd, &msg, 0) < 0) { perror("sendmsg"); exit(1); } exit(0); } SHAR_EOF if test 3073 -ne "`wc -c uwtool.c`" then echo shar: error transmitting uwtool.c '(should have been 3073 characters)' fi echo shar: extracting openpty.h '(308 characters)' cat << \SHAR_EOF > openpty.h /* * This file defines the "ptydesc" structure which is returned * by the routine "openpty". */ struct ptydesc { int pt_pfd; /* file descriptor of master side */ int pt_tfd; /* file descriptor of slave side */ char *pt_pname; /* master device name */ char *pt_tname; /* slave device name */ }; SHAR_EOF if test 308 -ne "`wc -c openpty.h`" then echo shar: error transmitting openpty.h '(should have been 308 characters)' fi echo shar: extracting uw.h '(2697 characters)' cat << \SHAR_EOF > uw.h /* * uw command bytes * * Copyright 1985 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. * * * Two types of information are exchanged through the 7-bit serial line: * ordinary data and command bytes. Command bytes are preceeded by * an IAC byte. IAC bytes and literal XON/XOFF characters (those which * are not used for flow control) are sent by a CB_FN_CTLCH command. * Characters with the eighth bit set (the "meta" bit) are prefixed with * a CB_FN_META function. * * The next most-significant bit in the byte specifies the sender and * recipient of the command. If this bit is clear (0), the command byte * was sent from the host computer to the Macintosh; if it is set (1) * the command byte was sent from the Macintosh to the host computer. * This prevents confusion in the event that the host computer * (incorrectly) echos a command back to the Macintosh. * * The remaining six bits are partitioned into two fields. The low-order * three bits specify a window number from 1-7 (window 0 is reserved for * other uses) or another type of command-dependent parameter. The next * three bits specify the operation to be performed by the recipient of * the command byte. * * Note that the choice of command bytes prevents the ASCII XON (021) and * XOFF (023) characters from being sent as commands. CB_FN_ISELW commands * are only sent by the Macintosh (and thus are tagged with the CB_DIR_MTOH * bit). Since XON and XOFF data characters are handled via CB_FN_CTLCH, * this allows them to be used for flow control purposes. */ #define IAC 0001 /* interpret as command */ #define CB_DIR 0100 /* command direction: */ #define CB_DIR_HTOM 0000 /* from host to Mac */ #define CB_DIR_MTOH 0100 /* from Mac to host */ #define CB_FN 0070 /* function code: */ #define CB_FN_NEWW 0000 /* new window */ #define CB_FN_KILLW 0010 /* kill (delete) window */ #define CB_FN_ISELW 0020 /* select window for input */ #define CB_FN_OSELW 0030 /* select window for output */ #define CB_FN_META 0050 /* add meta to next data char */ #define CB_FN_CTLCH 0060 /* low 3 bits specify char */ #define CB_FN_MAINT 0070 /* maintenance functions */ #define CB_WINDOW 0007 /* window number mask */ #define CB_CC 0007 /* control character specifier: */ #define CB_CC_IAC 1 /* IAC */ #define CB_CC_XON 2 /* XON */ #define CB_CC_XOFF 3 /* XOFF */ #define CB_MF 0007 /* maintenance functions: */ #define CB_MF_ENTRY 0 /* beginning execution */ #define CB_MF_EXIT 7 /* execution terminating */ #define NWINDOW 7 /* maximum number of windows */ SHAR_EOF if test 2697 -ne "`wc -c uw.h`" then echo shar: error transmitting uw.h '(should have been 2697 characters)' fi # End of shell archive exit 0