[comp.sources.misc] System V session recorder

eric@ms.uky.edu@mandrill.CWRU.Edu (Eric Herrin) (09/24/87)

Here's a cute little utility I had to write to allow students to 
record terminal sessions.  It works with the System V pty driver I 
posted earlier for the UnixPC, but should work on any System V box
with ptys.  Hope it can be useful to someone else.

			eric


|									      | 
|    Eric Herrin II				     	cbosgd!ukma!eric      |
|    "'tis better to be silent                         	eric@UKMA.BITNET      |
|     and be THOUGHT a fool, than to open              	eric@ms.uky.csnet     |
|     one's mouth and remove all doubt."                eric@ms.uky.edu       |

______________________ cut here _________________________
#! /bin/sh
: This is a shell archive, meaning:
: 1. Remove everything above the '#! /bin/sh' line.
: 2. Save the resulting text in a file.
: 3. Execute the file with /bin/sh '(not csh)' to create the files:
:	'session.c'
:	'session.mk'
: This archive created: 'Sun Sep 20 12:02:40 1987
'
export PATH; PATH=/bin:$PATH
echo shar: extracting "'session.c'" '(4397 characters)'
if test -f 'session.c'
then
	echo shar: will not over-write existing file "'session.c'"
else
sed 's/^X//'  >'session.c' <<'SHAR_EOF'
X/* Copyright (c) 1987 University of Kentucky Mathematical Sciences
X * Eric H. Herrin II
X * eric@ms.uky.edu, eric@ms.uky.csnet, !cbosgd!ukma!eric
X * 
X * Permission is hereby granted to modify and redistribute
X * this program for non-commercial use, provided this copyright
X * notice is retained in all versions.
X */
X
X/* session.c - record a terminal session into a file for UNIX System V.
X * 
X * Usage: session [filename]
X */
X#include <stdio.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <signal.h>
X#include <termio.h>
X#include <sys/ttold.h>
X
X#define SHELL 		"/bin/ksh"
X#define SHELLARG 	"ksh"
X#define BYTES		1
X
XFILE		*recordfile, *fopen();
Xint		pty, tty;
Xstruct sgttyb	savesgbuf;
Xchar		*slave="/dev/ttyXX", *master="/dev/ptyXX";
X
Xmain(argc, argv)
X	int		argc;
X	char		*argv[];
X{
X	int		p, s;
X	int		bytes;
X	char		buf[BYTES];
X	char		*filename;
X	struct sgttyb	sgbuf;
X	void		cleanup();
X
X	if (argc > 2) {
X		fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
X		exit(1);
X	} else if (argc == 2)
X		filename = argv[1];
X	else
X		filename = "session_file";
X
X	if ((recordfile = fopen(filename, "w")) == NULL) {
X		fprintf(stderr, "%s: cannot open %s.\n", argv[0], filename);
X		exit(1);
X	}
X	signal(SIGTERM, cleanup);
X	OpenPtys(slave, master, &tty, &pty);
X	/* fork off a process to handle the setup of the tty for the
X	 * shell and execing the shell.
X	 */
X	if (fork() == 0) {
X		struct termio	tio;
X
X		close(pty);
X		/* setpgrp to associate the process with the pty.
X		 */
X		setpgrp();
X		if ((tty = open(slave, O_RDWR)) < 0) {
X			printf("ERROR: open slave: errno = %d\n", errno);
X			exit(1);
X		}
X		/* set up the tty end of the pty to be the std[in,out,err]
X		 * of the new shell.
X		 */
X		close(0); dup(tty);
X		close(1); dup(tty);
X		close(2); dup(tty);
X		/* set up some reasonable parameters for the pty, ie.
X		 * let the shell have a normal environment.
X		 */
X        	ioctl(0, TCGETA, &tio);
X        	tio.c_cc[VINTR] = '';
X		tio.c_cc[VERASE] = '';
X        	tio.c_cc[VKILL] = '';
X        	tio.c_cc[VEOF] = '';
X        	tio.c_iflag &= ~(IGNBRK | PARMRK | INPCK | INLCR | IGNCR | IUCLC);
X		tio.c_iflag |= (BRKINT | IGNPAR | ISTRIP | ICRNL | IXON);
X        	tio.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL);
X        	tio.c_oflag |= (OPOST);
X        	tio.c_oflag |= (ONLCR); /*?*/
X        	tio.c_lflag |= (ISIG | ICANON | ECHO | ECHOK);
X        	tio.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARODD | HUPCL | CLOCAL);
X        	tio.c_cflag |= (B9600 | CS7 | CREAD | PARENB);
X        	ioctl(0, TCSETAW, &tio);
X		/* exec the shell
X		 */
X		execl(SHELL, SHELLARG, "-i", 0);
X		fprintf(stderr, "exec of /bin/sh failed\n");
X		exit(1);
X	}
X	close(tty);
X	/* master process, device end of tty 
X	 */
X	ioctl(0, TIOCGETP, &savesgbuf);
X	sgbuf = savesgbuf;
X	sgbuf.sg_flags |= O_RAW;
X	sgbuf.sg_flags &= ~O_ECHO;
X	ioctl(0, TIOCSETP, &sgbuf);
X	/* fork off a reader of the pty master end.
X	 */
X	if (fork() ==  0) {
X		while ((bytes=read(pty, buf, BYTES)) > 0) {
X			fwrite(buf, 1, bytes, stdout);
X			fwrite(buf, 1, bytes, recordfile);
X			fflush(stdout);
X		}
X		exit(0);
X	}
X	/* fork off a writer of the pty master end.
X	 */
X	if (fork() == 0) {
X		while ((bytes=read(0, buf, BYTES)) > 0)
X			write(pty, buf, bytes);
X		exit(0);
X	}
X	/* The shell is the only process which will exit on its own.
X	 * wait for it.
X	 */
X	wait((int *)0); 
X	/* terminate last line, ^D from shell won't do it.
X	 */
X	fprintf(recordfile, "\n");
X	/* shell is already dead, so kill everybody else after cleanup.
X	 */
X	kill(0, SIGTERM);
X	close(pty);
X	fclose(recordfile);
X}
X
Xvoid cleanup()
X{
X	fclose(recordfile);
X	close(tty);
X	close(pty);
X	ioctl(0, TIOCSETP, &savesgbuf);
X	exit(0);
X}
X
XOpenPtys(slave, master, tty, pty)
X	char		*slave, *master;
X	int		*tty, *pty;
X{
X	int		i, j;
X	int		tmaster;
X	int		letcnt=0, numcnt=0;
X	static char	*letters = "pqrstuvwxyz",
X			*numbers = "0123456789abcdef";
X	static int	letmax, nummax;
X
X	letmax=strlen(letters)-1, nummax=strlen(numbers)-1;
X	do {
X		master[strlen("/dev/pty")] = letters[letcnt];
X		master[strlen("/dev/ptyX")] = numbers[numcnt];
X		if (letcnt > letmax) {
X			fprintf(stderr, "ERROR: all ptys in use\n");
X			exit(1);
X		} else if (++numcnt > nummax) {
X			letcnt++;
X			numcnt = 0;
X		} 
X	} while ((*pty=open(master, O_RDWR)) < 0);
X	/* got a free pty. 
X	 */
X	slave[8] = master[8];
X	slave[9] = master[9];
X	if ((*tty = open(slave, O_RDWR)) < 0) {
X		fprintf(stderr, "ERROR: opening slave: errno = %d\n", errno);
X		exit(1);
X	}
X}
SHAR_EOF
echo shar: 4 control characters may be missing from "'session.c'"
if test 4397 -ne "`wc -c < 'session.c'`"
then
	echo shar: error transmitting "'session.c'" '(should have been 4397 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'session.mk'" '(642 characters)'
if test -f 'session.mk'
then
	echo shar: will not over-write existing file "'session.mk'"
else
sed 's/^X//'  >'session.mk' <<'SHAR_EOF'
X# session.mk
X# Copyright (c) 1987 University of Kentucky Mathematical Sciences
X# Eric H. Herrin II
X# eric@ms.uky.edu, eric@ms.uky.csnet, !cbosgd!ukma!eric
X# 
X# Permission is hereby granted to modify and redistribute
X# this program for non-commercial use, provided this copyright
X# notice is retained in all versions.
X#
X
XSRCS = session.c
XOBJS = session.o
XINSDIR = /usr/local/bin
XCFLAGS = -O -c
X
X.c.o:
X	${CC} ${CFLAGS} $<
X
Xsession: session.o
X	${CC} ${OBJS} -o session
X
Xinstall: session
X	cp session ${INSDIR}
X	chown bin ${INSDIR}/session
X	chgrp bin ${INSDIR}/session	
X	chmod 555 ${INSDIR}/session
X
Xclean:
X	rm -f *.o
X
Xclobber:
X	rm -f *.o session
SHAR_EOF
if test 642 -ne "`wc -c < 'session.mk'`"
then
	echo shar: error transmitting "'session.mk'" '(should have been 642 characters)'
fi
fi # end of overwriting check
:	End of shell archive
a iness