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)