cl@datlog.co.uk (Charles Lambert) (11/24/88)
I'm struggling with my first attempt to use a pseudo-tty on AIX 2.2. What I want is a daemon that opens the controller side then waits forever, processing data as and when something writes to the server side. Processes may come and go on the server side but the daemon goes on forever. I've got the controller side open; now, how do I get the deamon to hang pending a write to the server side? I can't find any explanation of how reads are satisfied on the controller side. Charlie cl@datlog.co.uk
lm@snafu.Sun.COM (Larry McVoy) (11/29/88)
In article <926@dlhpedg.co.uk> cl@datlog.co.uk (Charles Lambert) writes: > >I'm struggling with my first attempt to use a pseudo-tty on AIX 2.2. [ Someone else wanted this as well, enjoy ] This is some code that basically gives you a shell (or an su, I don't remember) and logs all the commands that you type. Last I remember, it more or less worked - no promises. At any rate, it certainly gives all those pty hackers a running start. This source was generated on and for Sun machines. # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # Makefile log.awk log.c mode.c pty.c ptypair.c echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' CFLAGS = -DLOGFILE=\"./log\" O = pty.o ptypair.o log.o pty: $O cc $(CFLAGS) $O -o pty clobber: /bin/rm -f $O pty log //E*O*F Makefile// echo x - log.awk cat > "log.awk" << '//E*O*F log.awk//' BEGIN { FS=":" } NF == 3 && $2 == "END" { IN=0; } NF == 6 && $1 == "START" { IN = 1; printf("user=%s time=%s\n", $3, $2); getline; } IN == 1 { print; } //E*O*F log.awk// echo x - log.c cat > "log.c" << '//E*O*F log.c//' # include <stdio.h> # include <sys/file.h> # include <sys/types.h> # include <sys/stat.h> # include <pwd.h> # ifndef LOGFILE # define LOGFILE "/usr/adm/sulog" # endif static logfd; static opened; static char *tmp = "/tmp/ReXXXXXX"; logstartup() { struct stat st; struct passwd *p; struct passwd *getpwuid(); struct passwd *getpwnam(); char *date(); char *getwd(); char *getlogin(); char buf[200]; char wd[100]; if (stat(LOGFILE, &st)) close(creat(LOGFILE, 0600)); logfd = open(mktemp(tmp), O_RDWR | O_CREAT, 0600); opened = 1; setpwent(); if (!(p = getpwnam(getlogin())) && !(p = getpwuid(getuid()))) { printf("Who are you?\n"); exit(1); } endpwent(); sprintf(buf, "START:%s:%s:%d:%d:%s\n", date(), p->pw_name, getuid(), getgid(), getwd(wd)); logadd(buf, strlen(buf)); } char * date() { long t; register char *s; char *ctime(); time(&t); s = ctime(&t); s[13] = '.'; s[16] = 0; return s; } logadd(buf, n) char *buf; { register i; if (!opened) { logstartup(); opened = 1; } for (i = 0; i < n; i++) if (buf[i] == '\r') buf[i] = '\n'; write(logfd, buf, n); } logclose() { register trys = 0, nread, log; char buf[BUFSIZ]; while (trys++ < 4 && (log = open(LOGFILE, O_RDWR | O_EXCL | O_APPEND)) < 0) sleep(1); logadd(":END:\n", 8); close(logfd); logfd = open(tmp, O_RDONLY); sleep(1); while ((nread = read(logfd, buf, sizeof(buf))) > 0) write(log, buf, nread); close(logfd); close(log); unlink(tmp); } //E*O*F log.c// echo x - mode.c cat > "mode.c" << '//E*O*F mode.c//' # include <stdio.h> # include <sgtty.h> static struct sgttyb old; static struct sgttyb new; static int done; m_init() { ioctl(fileno(stdin), TIOCGETP, &old); new = old; } m_normal(fd) { if (!done++) m_init(); ioctl(fd, TIOCSETP, &old); } m_raw(fd) { if (!done++) m_init(); new.sg_flags = RAW; ioctl(fd, TIOCSETP, &new); } //E*O*F mode.c// echo x - pty.c cat > "pty.c" << '//E*O*F pty.c//' # include <stdio.h> # include <signal.h> # include <errno.h> # include <sgtty.h> # include <assert.h> # include <sys/types.h> /* * pty filter - trap all keyboard input in a logfile */ # define debug(x) /* fprintf x /* */ # define BSIZ 256 # define BAD_READ 1 # define BAD_SELECT 2 int cleanup(); /* called to quit */ int kidid; /* child pid & std{*} */ struct sgttyb restore; /* tty modes of stdin */ main(ac, av, ev) char **av; char **ev; { int fds[2]; /* master/slave */ /* tidy cleanup, thank you. */ signal(SIGHUP, cleanup); signal(SIGCLD, cleanup); /* save the modes for exit... */ ioctl(0, TIOCGETP, &restore); if (ptypair(fds) == -1) perror("ptypair"); if (!(kidid = fork())) { close(fds[0]); kid(fds[1], av, ev); } close(fds[1]); pass(fds[0]); /* NOTREACHED */ } /* kid - exec a sh */ kid(fd, av, ev) char **av; char **ev; { int pgrp; /* * dup the slave side to std{in,out,err}, make a new process group, grab * the pty, set the pty modes to be "normal" for a shell, and exec. */ dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); ioctl(fd, TIOCSETP, &restore); setpgrp(0, pgrp = getpid()); ioctl(fd, TIOCSPGRP, &pgrp); av[0] = "/bin/sh"; execve(av[0], av, ev); syserr("exec"); /* NOTREACHED */ } /* * pass - pass through filter * * take any input from stdin and pass to fd * take any output from fd and pass to stdout */ # define STDIN 1 # define SLAVE (1<<fd) pass(fd) { extern errno; struct sgttyb p; static u_char buf[BSIZ]; u_long orgmask, mask; register nread; /* input is raw (no signal processing & fast */ ioctl(0, TIOCGETP, &p); p.sg_flags = RAW; ioctl(0, TIOCSETP, &p); orgmask = STDIN | SLAVE; logstartup(); for (;;) { mask = orgmask; /* * could check for EINTR except we're in RAW mode so no signals, and * there are no itimers either... pretty safe. */ if (select(32, &mask, 0, 0, 0) == -1) cleanup(BAD_SELECT); debug((stderr, "mask=%#x\n", mask)); /* * input from the "keyboard" */ if (mask & STDIN) { if ((nread = read(0, buf, sizeof(buf))) <= 0) cleanup(BAD_READ); debug((stderr, "Line: %d bytes\n\r", nread)); write(fd, buf, nread); logadd(buf, nread); } /* * output from the process */ if (mask & SLAVE) { if ((nread = read(fd, buf, sizeof(buf))) <= 0) cleanup(BAD_READ); debug((stderr, "pty: %d bytes\n\r", nread)); write(1, buf, nread); } } /* NOTREACHED */ } syserr(msg) char *msg; { extern int errno; psyserr(msg); exit(errno ? errno : 100); } psyserr(msg) register char *msg; { extern int errno, sys_nerr; extern char *sys_errlist[]; fprintf(stderr, "ERROR: %s (%d", msg, errno); if (errno > 0 && errno < sys_nerr) fprintf(stderr, "; %s)\n", sys_errlist[errno]); else fprintf(stderr, ")\n"); } cleanup(sig) { kill(kid, SIGHUP); ioctl(0, TIOCSETP, &restore); logclose(); exit(0); } //E*O*F pty.c// echo x - ptypair.c cat > "ptypair.c" << '//E*O*F ptypair.c//' #include <sys/types.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/errno.h> #include <sys/ioctl.h> /* * ptypair * works like pipe() or socketpair(), but returns a master/slave * pty pair. The master is fd[0], the slave is fd[1]. The slave * end is the end that actually looks like a tty. The pty is * returned in RAW/NO-PARITY mode and both ends are open read/write. * * returns 0 if it found a pty, -1 if not. */ int ptypair(fd) int fd[2]; { register i; register char *c, *line; struct stat stb; struct sgttyb sb; extern int errno; char ptymask[10]; #define PTYMASK_LEN 10 strcpy(ptymask, "/dev/ptyXX"); for (c = "pqrs"; *c != 0; c++) { line = ptymask; line[PTYMASK_LEN - 2] = *c; line[PTYMASK_LEN - 1] = '0'; if (stat(line, &stb) < 0) /* see if the block of ptys exists */ break; for (i = 0; i < 16; i++) { line[PTYMASK_LEN - 1] = "0123456789abcdef"[i]; fd[0] = open(line, O_RDWR); if (fd[0] > 0) { line[PTYMASK_LEN - 5] = 't'; fd[1] = open(line, O_RDWR); if (fd[1] < 0) {/* if tty open fails, try another */ (void) close(fd[0]); continue; } /* now, make it sane */ (void) ioctl(fd[1], (int) TIOCGETP, (char *) &sb); sb.sg_ispeed = EXTA; sb.sg_ospeed = EXTA; sb.sg_flags = RAW | ANYP; (void) ioctl(fd[1], (int) TIOCSETP, (char *) &sb); return 0; } } } errno = ENOSPC; /* no space left on device -- it's close */ return -1; } //E*O*F ptypair.c// echo Possible errors detected by \'wc\' [hopefully none]: temp=/tmp/shar$$ trap "rm -f $temp; exit" 0 1 2 3 15 cat > $temp <<\!!! 8 21 121 Makefile 7 37 156 log.awk 85 205 1741 log.c 29 47 363 mode.c 150 447 3210 pty.c 63 238 1524 ptypair.c 342 995 7115 total !!! wc Makefile log.awk log.c mode.c pty.c ptypair.c | sed 's=[^ ]*/==' | diff -b $temp - exit 0 Larry McVoy (lm%snafu@sun.com)