Path: utzoo!utgpu!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!mailrus!ames!decwrl!sun!snafu!lm From: lm@snafu.Sun.COM (Larry McVoy) Newsgroups: comp.unix.wizards Subject: Re: Help, please, with pseudo tty Message-ID: <79149@sun.uucp> Date: 29 Nov 88 00:38:18 GMT References: <926@dlhpedg.co.uk> Sender: news@sun.uucp Reply-To: lm@sun.UUCP (Larry McVoy) Organization: Sun Microsystems, Mountain View Lines: 401 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# include # include # include # include # 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 # include 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 # include # include # include # include # include /* * 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< 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 #include #include #include #include /* * 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)