This is uwterm.c in view mode; [Download] [Up]
/* * uwterm * * 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/ioctl.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/wait.h> #include <netinet/in.h> #include <signal.h> #include <strings.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include "openpty.h" #include "uwlib.h" #ifndef UWTERM #define UWTERM "uwterm" #endif #define CTL(c) ((c)&037) #ifndef FD_SET /* 4.2 retrofit: better definitions for these are in 4.3BSD's <sys/types.h> */ #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) #define FD_SETSIZE (NBBY*sizeof(long)) #endif extern int optind; extern char *optarg; extern char *getenv(); extern char *malloc(); extern deadkid(); extern int errno; #ifndef htons /* These should have been defined in <netinet/in.h>, but weren't (in 4.2BSD) */ extern unsigned short htons(), ntohs(); extern unsigned long htonl(), ntohl(); #endif char *argv0; main(argc, argv) int argc; char **argv; { register char *cp; register int c; int wflag; uwtype_t wtype; char *term, *title, *server, *login; struct sockaddr_in sa, *sin; char hostname[32]; /* * If called with no arguments, create a new window using the * current shell according to the SHELL environment variable * (or "/bin/sh" if that doesn't exist). * * Options which are recognized directly are: * * -ninet connect to server at address "inet" * -wtype create window with emulation "type" * -ttitle label window with "title" * -llogin use login name "login" on remote machine * * If no explicit title is specified, the command name is used. */ argv0 = argv[0]; sin = (struct sockaddr_in *)0; server = (char *)0; login = (char *)0; title = (char *)0; wflag = 0; term = (char *)0; while ((c = getopt(argc, argv, "l:n:t:w:")) != EOF) { switch (c) { case 'l': if (optarg[0] == '\0') { fprintf(stderr, "%s: \"-l\" requires user name\n", argv0); } else login = optarg; break; case 'n': server = optarg; sa.sin_family = AF_INET; sa.sin_addr.s_addr = 0; sa.sin_port = 0; bzero(sa.sin_zero, sizeof sa.sin_zero); for (cp=optarg; isxdigit(c = *cp); cp++) { /* Pyramid compiler botch */ /* sa.sin_addr.s_addr *= 16; */ sa.sin_addr.s_addr <<= 4; if (isdigit(c)) sa.sin_addr.s_addr += c - '0'; else if (islower(c)) sa.sin_addr.s_addr += c-'a' + 10; else sa.sin_addr.s_addr += c-'A' + 10; } if (c == '.') for (cp++; isdigit(c = *cp); cp++) sa.sin_port = sa.sin_port*10 + c-'0'; if (sa.sin_addr.s_addr == 0 || sa.sin_port == 0) { fprintf(stderr, "%s: bad Internet address: %s\n", argv0, optarg); return(1); } sa.sin_addr.s_addr = htonl(sa.sin_addr.s_addr); sa.sin_port = htons(sa.sin_port); sin = &sa; break; case 'w': wflag++; term = optarg; wtype = uw_ttype(optarg); break; case 't': title = optarg; break; } } gethostname(hostname, sizeof hostname); if (title == (char *)0) { /* * If there was no "-t" argument, then "title" will still * be NULL. In this case we use the host name. */ if (optind == argc) title = hostname; else title = argv[optind]; } if (!term) { /* * If there was no "-w" argument, fetch the window * type from the environment. If that fails, use * a default. */ if ((term=getenv("TERM")) != (char *)0) wtype = uw_ttype(term); else wtype = UWT_ADM31; } if (optind == argc-1) { /* * The remaining argument is the host name. Fork an "rsh" * to execute this on the remote machine. */ return(doremote(argv[optind], server, title, term, login)); } else if (optind == argc) { /* * There are no other arguments. Set up the connection * to this machine. */ return(dolocal(sin, title, wtype, term)); } else { fprintf(stderr, "Usage: \"%s [-ttitle] [-wtype] [-naddr] [-llogin] host\"\n", argv0); return(1); } } doremote(host, server, title, term, login) char *host; char *server; char *title; char *term; char *login; { register int fd, i, pid; register char *cp; char *av[16]; /* * Invoke a remote "uwterm" via "rsh". */ i = 0; av[i++] = "rsh"; av[i++] = host; av[i++] = "-n"; if (login != NULL) { av[i++] = "-l"; av[i++] = login; } av[i++] = UWTERM; if (server == (char *)0) { if ((server = getenv("UW_INET")) == (char *)0) { fprintf(stderr,"%s: Can't find window server\n",argv0); return(1); } } if ((cp = malloc(3+strlen(server))) == (char *)0) { fprintf(stderr, "%s: out of memory\n", argv0); return(1); } (void)strcat(strcpy(cp, "-n"), server); av[i++] = cp; if (title != (char *)0) { if ((cp = malloc(3+strlen(title))) == (char *)0) { fprintf(stderr, "%s: out of memory\n", argv0); return(1); } (void)strcat(strcpy(cp, "-t"), title); av[i++] = cp; } if (term != (char *)0) { if ((cp = malloc(3+strlen(term))) == (char *)0) { fprintf(stderr, "%s: out of memory\n", argv0); return(1); } (void)strcat(strcpy(cp, "-w"), term); av[i++] = cp; } av[i] = (char *)0; for (fd=getdtablesize()-1; fd > 2; fd--) (void)fcntl(fd, F_SETFD, 1); (void)execvp(av[0], av); (void)execv("/usr/ucb/rsh", av); /* last-ditch try */ perror(av[0]); return(1); } dolocal(sin, title, wtype, term) struct sockaddr_in *sin; char *title; uwtype_t wtype; char *term; { register UWIN uwin; register int fd; register int s; struct ptydesc pt; /* * Create and initialize a pseudo-terminal. */ if (openpty(&pt) < 0) { fprintf(stderr, "No pseudo-terminals are available\n"); return(1); } ttyinit(pt.pt_tfd); /* * Make fd's 0 and 1 be "/dev/null". We'd like to force a known * definition for fd 2 at this point, but we may need it for * uw_perror() if uw_new() fails. */ if ((fd = open("/dev/null", O_RDWR)) >= 0) { /* should be zero */ if (fd != 0 && pt.pt_tfd != 0 && pt.pt_pfd != 0) dup2(fd, 0); if (fd != 1 && pt.pt_tfd != 1 && pt.pt_pfd != 1) dup2(fd, 1); if (fd > 2) (void)close(fd); } /* * Create and title the window. Make it visible. */ if ((uwin = uw_new(wtype, sin)) == (UWIN)0) { uw_perror(argv0, uwerrno, errno); return(1); } (void)uw_stitle(uwin, title); (void)uw_svis(uwin, 1); /* * We no longer have use for fd 2, so make it "/dev/null" (the * same as fd 0. */ (void)dup2(0, 2); /* * Adjust the environment to contain the correct values of TERM, * UW_ID, and UW_INET. These will be inherited by the child * we will create next. */ adjenv(term, sin, UW_ID(uwin)); /* * Create a process to execute the command connected to the pty. */ runcmd(pt.pt_tfd, pt.pt_tname); /* * Ignore signals that might cause us trouble. We do NOT ignore * SIGTSTP so that the user can move us from the foreground into * the background if desired. */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGCHLD, deadkid); #if defined(TIOCSWINSZ) || defined(TIOCSSIZE) /* * Install an option handling routine to catch window size * changes from the Mac and make the appropriate changes to * the pseudo-terminal. */ setresize(uwin, pt.pt_pfd); #endif /* * Close the slave side of the pty. Copy data between the pty * and the window. The return value from copy() is the exit * status. */ (void)close(pt.pt_tfd); s = copy(pt.pt_pfd, UW_DATAFD(uwin)); uw_kill(uwin); return(s); } ttyinit(ptyfd) register int ptyfd; { register int ttyfd; struct sgttyb sg; struct tchars tc; struct ltchars ltc; int ldisc; int lmode; /* * Initialize the modes of the terminal whose file descriptor * is "ptyfd" to the same modes as the current terminal. If there * isn't a "current terminal" handy, then use hardcoded defaults. */ for (ttyfd=0; ttyfd < 3 && ioctl(ttyfd, TIOCGETD, &ldisc) < 0; ttyfd++) ; if (ttyfd < 3) { (void)ioctl(ttyfd, TIOCGETP, &sg); (void)ioctl(ttyfd, TIOCGETC, &tc); (void)ioctl(ttyfd, TIOCGLTC, <c); (void)ioctl(ttyfd, TIOCLGET, &lmode); } else { ldisc = NTTYDISC; sg.sg_ispeed = sg.sg_ospeed = 13; /* doesn't really matter */ sg.sg_erase = 0177; /* ugh */ sg.sg_kill = CTL('u'); /* ugh */ sg.sg_flags = ECHO|CRMOD|ANYP; tc.t_intrc = CTL('c'); /* yuck, should be 0177 */ tc.t_quitc = CTL('\\'); tc.t_startc = CTL('q'); tc.t_stopc = CTL('s'); tc.t_eofc = CTL('d'); tc.t_brkc = -1; ltc.t_suspc = CTL('z'); ltc.t_dsuspc = CTL('y'); ltc.t_rprntc = CTL('r'); ltc.t_flushc = CTL('o'); ltc.t_werasc = CTL('w'); ltc.t_lnextc = CTL('v'); lmode = LCRTBS|LCRTERA|LCRTKIL|LCTLECH; } (void)ioctl(ptyfd, TIOCSETD, &ldisc); (void)ioctl(ptyfd, TIOCSETP, &sg); (void)ioctl(ptyfd, TIOCSETC, &tc); (void)ioctl(ptyfd, TIOCSLTC, <c); (void)ioctl(ptyfd, TIOCLSET, &lmode); } adjenv(term, sin, wid) char *term; struct sockaddr_in *sin; uwid_t wid; { char *env[4]; static char ttype[sizeof "TERM=" + 16]; static char inet[sizeof INET_ENV + 16]; static char idstr[sizeof "UW_ID=" + 20]; /* * Redefine the environment variable UW_ID. Redefine UW_INET * if "sin" is non-NULL. Redefine TERM. */ (void)sprintf(ttype, "TERM=%.15s", term); env[0] = ttype; (void)sprintf(idstr, "UW_ID=%ld", wid); env[1] = idstr; if (sin != NULL) { (void)sprintf(inet, "%s=%08lx.%d", INET_ENV, ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port)); env[2] = inet; env[3] = (char *)0; } else env[2] = (char *)0; env_set(env); } runcmd(fd, tname) int fd; char *tname; { register int pid; register char *shell; /* * Figure out the name of the user's shell. If unknown, * use a default. */ if ((shell = getenv("SHELL")) == (char *)0) shell = "/bin/sh"; /* * Fork a new process and attach "fd" to fd's 0, 1, and 2 of * that new process. Disassociate the current controlling * terminal and attach the new one (whose name is "tname"). */ while ((pid = fork()) < 0) sleep(5); if (pid == 0) { if (fd != 0) dup2(fd, 0); if (fd != 1) dup2(fd, 1); if (fd != 2) dup2(fd, 2); if ((fd = open("/dev/tty", O_RDWR)) >= 0) { (void)ioctl(fd, TIOCNOTTY, (char *)0); (void)close(fd); } else setpgrp(0, 0); (void)open(tname, O_RDWR); for (fd=getdtablesize()-1; fd > 2; fd--) (void)fcntl(fd, F_SETFD, 1); execlp(shell, "-", (char *)0); execl(shell, "-", (char *)0); _exit(1); } } copy(fd1, fd2) int fd1, fd2; { struct fdinfo { int fi_fd; /* associated file descriptor */ int fi_size; /* amount of data in buffer */ char *fi_ptr; /* pointer to data in fi_buf */ char fi_buf[1024]; }; register struct fdinfo *fi, *fo; register int n, nfds, len; struct fdinfo fdinfo[2]; struct fd_set rdmask[2], wtmask[2], exmask[2]; struct timeval tv; /* * Copy data between file descriptors fd1 and fd2. Return when an * EOF is read or an I/O error (other than an interrupted system * call or non-blocking I/O message) is encountered. */ FD_ZERO(&rdmask[1]); FD_ZERO(&wtmask[1]); FD_ZERO(&exmask[1]); fdinfo[0].fi_fd = fd1; fdinfo[0].fi_size = 0; fdinfo[1].fi_fd = fd2; fdinfo[1].fi_size = 0; FD_SET(fd1, &rdmask[1]); FD_SET(fd2, &rdmask[1]); (void)fcntl(fd1, F_SETFL, FNDELAY); (void)fcntl(fd2, F_SETFL, FNDELAY); nfds = ((fd1 > fd2) ? fd1 : fd2) + 1; while (1) { rdmask[0] = rdmask[1]; wtmask[0] = wtmask[1]; exmask[0] = exmask[1]; errno = 0; if (fdinfo[0].fi_size != 0 || fdinfo[1].fi_size != 0) { /* * Select does not work correctly for writes on * some machines, so we must fake it. If a write * is pending, we time out after 1/50 second and * pretend that select told us that writes could * now be performed. The code below will do the * correct thing if the write would still block. */ tv.tv_sec = 0; tv.tv_usec = 1000000 / 50; n = select(nfds, rdmask, wtmask, exmask, &tv); wtmask[0] = wtmask[1]; } else n = select(nfds, rdmask, wtmask, exmask, (struct timeval *)0); if (n < 0 && errno == EINTR) { continue; } else if (n <= 0) { perror("select"); return(1); } for (fi=fdinfo; fi < fdinfo+2; fi++) { fo = fdinfo + !(fi - fdinfo); if (FD_ISSET(fi->fi_fd, rdmask)) { /* data available for reading */ len = read(fi->fi_fd, fi->fi_buf, sizeof fi->fi_buf); if (len > 0) { fi->fi_size = len; fi->fi_ptr = fi->fi_buf; FD_CLR(fi->fi_fd, &rdmask[1]); FD_SET(fo->fi_fd, &wtmask[1]); FD_SET(fo->fi_fd, &wtmask[0]); } else if (len == 0) { /* EOF, exit */ return(0); } else if (errno != EWOULDBLOCK && errno != EINTR) { /* error, exit */ return(1); } } if (FD_ISSET(fo->fi_fd, wtmask)) { /* data ready for writing */ errno = 0; len = write(fo->fi_fd, fi->fi_ptr, fi->fi_size); if (len > 0) { fi->fi_ptr += len; fi->fi_size -= len; if (fi->fi_size == 0) { FD_SET(fi->fi_fd, &rdmask[1]); FD_CLR(fo->fi_fd, &wtmask[1]); } } else if (errno != EWOULDBLOCK && errno != EINTR) { /* error, exit */ return(1); } } } } } deadkid() { register int pid; /* * Collect dead children. Don't bother with their exit status * or resource usage. */ while ((pid = wait3((union wait *)0, WNOHANG, (struct rusage *)0)) > 0) ; } #if defined(TIOCSWINSZ) || defined(TIOCSSIZE) static int ptyfd; #ifdef TIOCSWINSZ static struct winsize winsz; void doresize(uwin, optnum, optcmd, uwoptval) UWIN uwin; uwopt_t optnum; uwoptcmd_t optcmd; union uwoptval *uwoptval; { uwtype_t wtype; /* * 4.3BSD-style window resizing */ if (uw_gtype(uwin, &wtype) < 0) wtype = UWT_ADM31; /* probably wrong to do this here */ if (optcmd == UWOC_SET) { switch (optnum) { case UWOP_WSIZE: winsz.ws_ypixel = uwoptval->uwov_point.v; winsz.ws_xpixel = uwoptval->uwov_point.h; break; case UWOP_TSIZE: if (wtype <= UWT_ANSI) { winsz.ws_row = uwoptval->uwov_point.v; winsz.ws_col = uwoptval->uwov_point.h; } break; } if (wtype <= UWT_ANSI && (optnum == UWOP_WSIZE || optnum == UWOP_TSIZE)) (void)ioctl(ptyfd, TIOCSWINSZ, &winsz); } } setresize(uwin, fd) UWIN uwin; int fd; { struct uwpoint pt; uwtype_t wtype; /* * Set up the option-handling routine "doresize". */ ptyfd = fd; uw_optfn(uwin, UWOP_TSIZE, doresize); uw_optfn(uwin, UWOP_WSIZE, doresize); winsz.ws_row = 24; /* default to standard terminal size */ winsz.ws_col = 80; if (uw_gwsize(uwin, &pt) == 0) { winsz.ws_ypixel = pt.uwp_v; winsz.ws_xpixel = pt.uwp_h; } else { /* make up something plausible */ winsz.ws_ypixel = 8 * winsz.ws_row; winsz.ws_xpixel = 8 * winsz.ws_col; } if (uw_gtype(uwin, &wtype) == 0 && wtype <= UWT_ANSI) (void)uw_optcmd(uwin, UWOP_TSIZE, UWOC_DO, (union uwoptval *)0); } #else #ifdef TIOCSSIZE void doresize(uwin, optnum, optcmd, uwoptval) UWIN uwin; uwopt_t optnum; uwoptcmd_t optcmd; union uwoptval *uwoptval; { struct ttysize ts; uwtype_t wtype; /* * Sun-style window resizing */ if (uw_gtype(uwin, &wtype) < 0) wtype = UWT_ADM31; /* probably wrong to do this here */ if (wtype <= UWT_ANSI && optnum == UWOP_TSIZE && optcmd == UWOC_SET) { ts.ts_lines = uwoptval->uwov_point.v; ts.ts_cols = uwoptval->uwov_point.h; (void)ioctl(ptyfd, TIOCSSIZE, &ts); } } setresize(uwin, fd) UWIN uwin; int fd; { uwtype_t wtype; /* * Set up the option-handling routine "doresize". */ ptyfd = fd; uw_optfn(uwin, UWOP_TSIZE, doresize); if (uw_gtype(uwin, &wtype) == 0 && wtype <= UWT_ANSI) (void)uw_optcmd(uwin, UWOP_TSIZE, UWOC_DO, (union uwoptval *)0); } #endif #endif #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.