yost@esquire.UUCP (David A. Yost) (08/20/90)
I'd like to fork and exec an xterm that has no shell running in it and whose stdin and stdout are hooked up to two file descriptors in my process. Anyone done this? --dave yost yost@dpw.com or uunet!esquire!yost Please ignore the From or Reply-To fields above, if different.
richard@aiai.ed.ac.uk (Richard Tobin) (08/21/90)
In article <2247@esquire.UUCP> yost@esquire.UUCP (David A. Yost) writes: >I'd like to fork and exec an xterm that has no shell >running in it and whose stdin and stdout are hooked up >to two file descriptors in my process. Anyone done this? Yes, but you have to do it through a pseudo-terminal. Here's a program that illustrates it. -- Richard /* slavewindow.c * * Copyright Richard Tobin / AIAI 1990. * May be freely distributed if this notice remains intact. * * Creates a window (xterm) and accepts commands to display text, * move cursor, etc. Also returns keys typed in the window. * Commands are: * * clear clear the screen * move r c move to row r, col c * string ... display the string (the rest of the line) * char c display the character with ascii code c * format ... set the format for returning characters typed * raw set raw mode (characters returned immediately) * cooked set cooked mode (characters not returned until return typed) * echo set echo mode * noecho set noecho mode (useful with raw) * * The arguments to string and format may contain C style escapes (\n etc). * * The default format for returning characters is * char(%d).\n * In cooked mode, typing an end-of-file causes -1 to be returned. * * Closing the pipe to the process will cause the window to exit. * */ #include <errno.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> extern int errno; void process_command(), process_input(), return_char(), copy_term(), escape(); int create_window(); int win; char format[80] = "char(%d).\n"; main() { win = create_window(); while(1) { int stat, bits; char buf[500]; bits = ((1 << 0) | (1 << win)); stat = select(win+1, &bits, (int *)0, (int *)0, (struct timeval *)0); if(stat == -1 && errno == EINTR) continue; if(stat == -1) { perror("slavewindow: select"); exit(1); } if(bits & (1 << win)) { switch(read(win, buf, 1)) { case 0: /* eof typed */ return_char(-1); break; case 1: return_char(((int)buf[0] & 255)); break; default: if(errno == EINTR) break; perror("slavewindow: window read"); exit(1); } } if(bits & (1 << 0)) { stat = read(0, buf, sizeof(buf)); if(stat == 0) /* eof means exit */ exit(0); if(stat == -1) { if(errno == EINTR) continue; perror("slavewindow: pipe read"); exit(1); } process_input(buf, stat); } } } void process_input(data, dlen) char *data; int dlen; { static char buf[500]; static int len = 0; while(dlen > 0 && len < sizeof(buf)-1) { buf[len++] = *data++; dlen--; if(buf[len-1] == '\n') { buf[len] = '\0'; process_command(buf); len = 0; } } } #define Prefix(pre, str) (strncmp(pre, str, sizeof(pre)-1) == 0) #define CLEAR "\033[H\033[2J" #define MOVE "\033[%d;%dH" void process_command(command) char *command; { int row, col, c; char buf[100], *s; struct sgttyb sgtty; if(Prefix("clear", command)) write(win, CLEAR, sizeof(CLEAR)-1); else if(Prefix("move", command)) { sscanf(command, "move %d %d", &row, &col); sprintf(buf, MOVE, row+1, col+1); write(win, buf, strlen(buf)); } else if(Prefix("string ", command)) { s = command+7; escape(s); write(win, s, strlen(s)-1); } else if(Prefix("char", command)) { sscanf(command, "char %d", &c); buf[0] = c; write(win, buf, 1); } else if(Prefix("raw", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags |= RAW; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("cooked", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags &= ~RAW; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("echo", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags |= ECHO; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("noecho", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags &= ~ECHO; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("format ", command)) { s = command+7; escape(s); strncpy(format, s, sizeof(format)); format[strlen(format)-1] = '\0'; /* get rid of the linefeed */ } else fprintf(stderr, "slavewindow: unknown command %s\n", command); } void escape(s) /* interpret \n etc */ char *s; { char *p = s; int escaped = 0; while(*s) { if(escaped) { char *a = "aenrt\\", *b = "\007\033\n\r\t\\"; char *i = strchr(a, *s); *p++ = i ? b[i-a] : *s; escaped = 0; } else { if(*s == '\\') escaped = 1; else *p++ = *s; } s++; } *p = '\0'; } void return_char(c) { char buf[100]; sprintf(buf, format, c); write(1, buf, strlen(buf)); } #define MASTER "/dev/ptyXY" #define SLAVE "/dev/ttyXY" int create_window() { static char master_path[] = MASTER, slave_path[] = SLAVE, buf[100]; int i, master, slave, oldtty; strcpy(master_path, MASTER); strcpy(slave_path, SLAVE ); for(i=0; i <= ('s'-'p') * 16; ++i) { master_path[strlen(MASTER)-2] = i / 16 + 'p'; master_path[strlen(MASTER)-1] = "0123456789abcdef"[i & 15]; master = open(master_path,O_RDWR); if(master >= 0) break; } if(master < 0) { fprintf(stderr, "slavewindow: can't get a pty\n"); exit(1); } slave_path[strlen(SLAVE)-2] = master_path[strlen(MASTER)-2]; slave_path[strlen(SLAVE)-1] = master_path[strlen(MASTER)-1]; sprintf(buf, "-S%s%d", &slave_path[strlen(slave_path)-2], master); if(vfork() == 0) { setpgrp(0, getpid()); execlp("xterm", "xterm", buf, 0); fprintf(stderr, "xterm exec failed\n"); _exit(1); } close(master); slave = open(slave_path, O_RDWR, 0); read(slave, buf, sizeof(buf)); /* suck xterm junk before echo on */ oldtty = open("/dev/tty", O_RDWR, 0); copy_term(oldtty, slave); close(oldtty); return slave; } void copy_term(from, to) int from, to; { int err; struct sgttyb sb; struct tchars tc; struct ltchars ltc; int bits; err = ioctl(from, TIOCGETP, &sb); err |= ioctl(to, TIOCSETP, &sb); err |= ioctl(from, TIOCGETC, &tc); err |= ioctl(to, TIOCSETC, &tc); err |= ioctl(from, TIOCLGET, &bits); err |= ioctl(to, TIOCLSET, &bits); err |= ioctl(from, TIOCGLTC, <c); err |= ioctl(to, TIOCSLTC, <c); if(err != 0) perror("can't copy terminal modes"); } -- Richard Tobin, JANET: R.Tobin@uk.ac.ed AI Applications Institute, ARPA: R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!R.Tobin
yost@DPW.COM (David A. Yost) (09/27/90)
In article <2247@esquire.UUCP> I wrote: >I'd like to fork and exec an xterm that has no shell >running in it and whose stdin and stdout are hooked up >to two file descriptors in my process. Anyone done this? Here's a way to do it: xterm -e sockio -il xtermsocket & This runs sockio in the xterm window instead of a shell. The -i option tells sockio to run in interactive (bidirectional copy) mode, and the l option tells it to listen and accept a connection on "xtermsocket", which is a unix domain socket. Then to talk to the xterm window, you do this: sockio -i xtermsocket I will post sockio to comp.unix.sources sometime soon. Here's what it does: Usage: sockio <options> [ -l [ -c ] ] <name> or: sockio <options> -d inet <socket> [ <host> ] or: sockio <options> -d inet -l [ -c ] [ <socket> ] Options: [ -k ] [ -i ] or [ -r [ -a ] ] [ -v ] [ -s <size> ] Copies between unix or inet stream socket and standard input and/or output in one of three modes: By default, copies from stdin to socket (write mode). -r Read mode: copies from socket to stdout. -i Interactive mode: copies from socket to stdout and from stdin to socket. -l Sockio listens on the socket and accepts a connection. If unix domain, removes the socket file when it exits or is killed. -c Sockio continually listens for and accepts new connections. Otherwise, when the other end closes the connection, sockio exits. -k When the -l option is not given, by default sockio expects the socket to already exist, and exits with status 1 if it doesn't exist. If the -k option is given, it keeps trying to connect. -s <size> How many bytes or lines to copy. A numeric size is a number of bytes, a number followed by an 'l' is a number of lines. -a Used only with -r, the -a option tells sockio to read all it can (up to the given size, if any) until the socket becomes empty, then exit. -v Verbosity: notify on connects and accepts. -d inet Additional arguments: A <host> name or numeric address in dot notation, default is this host. A <socket> argument which can be a number or a service name. In the -l case, <socket> can be unspecified, in which case a socket number is assigned by the system, and the number reported to stderr. If no -d option is given, then the <name> given is taken as a filename representing a unix domain socket. Examples: sockio -rlc logs | tee logfile & date | sockio logs date | sockio logs or: awk < /dev/null 'BEGIN { for (;;) print ++n }' | sockio -lc -s 1l numbers & sockio -r numbers sockio -r numbers or: xterm -e sockio -il xtermsocket & sockio -i xtermsocket Bug: if the writer is invoked with -l and -c, output data stranded in the socket when the reader closes the socket is lost. --dave yost yost@dpw.com or uunet!esquire!yost Please don't use other mangled forms you may see in the From, Reply-To, or CC fields above.