Help, please, with pseudo tty

Larry McVoy lm at snafu.Sun.COM
Tue Nov 29 10:38:18 AEST 1988


In article <926 at dlhpedg.co.uk> cl at 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 at sun.com)



More information about the Comp.unix.wizards mailing list