xref: /onnv-gate/usr/src/cmd/script/script.c (revision 334)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
70Sstevel@tonic-gate /*	  All Rights Reserved  	*/
80Sstevel@tonic-gate 
90Sstevel@tonic-gate 
100Sstevel@tonic-gate /*
110Sstevel@tonic-gate  * Copyright (c) 1980 Regents of the University of California.
120Sstevel@tonic-gate  * All rights reserved.  The Berkeley software License Agreement
130Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
140Sstevel@tonic-gate  */
150Sstevel@tonic-gate 
160Sstevel@tonic-gate /*	Portions Copyright(c) 1988, Sun Microsystems, Inc.	*/
170Sstevel@tonic-gate /*	All Rights Reserved.					*/
180Sstevel@tonic-gate 
190Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
200Sstevel@tonic-gate 
210Sstevel@tonic-gate /*
22*334Sdp  * script: Produce a record of a terminal session.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate #include <stdio.h>
25*334Sdp #include <stdlib.h>
26*334Sdp #include <unistd.h>
270Sstevel@tonic-gate #include <signal.h>
280Sstevel@tonic-gate #include <fcntl.h>
290Sstevel@tonic-gate #include <locale.h>
300Sstevel@tonic-gate #include <time.h>
310Sstevel@tonic-gate #include <sys/stropts.h>
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/stat.h>
340Sstevel@tonic-gate #include <sys/termios.h>
350Sstevel@tonic-gate #include <sys/file.h>
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate int 	grantpt();
390Sstevel@tonic-gate int 	unlockpt();
400Sstevel@tonic-gate char	*ptsname();
41*334Sdp void	doinput() __NORETURN;
42*334Sdp void	dooutput();
43*334Sdp void	doshell();
44*334Sdp void	fixtty();
45*334Sdp void	fail();
46*334Sdp void	done() __NORETURN;
47*334Sdp void	getmaster();
48*334Sdp void	getslave();
490Sstevel@tonic-gate 
500Sstevel@tonic-gate char	*shell;
510Sstevel@tonic-gate FILE	*fscript;
520Sstevel@tonic-gate int	master;			/* file descriptor for master pseudo-tty */
530Sstevel@tonic-gate int	slave;			/* file descriptor for slave pseudo-tty */
540Sstevel@tonic-gate int	child;
550Sstevel@tonic-gate int	subchild;
560Sstevel@tonic-gate char	*fname = "typescript";
570Sstevel@tonic-gate void	sigwinch();
580Sstevel@tonic-gate void	finish();
590Sstevel@tonic-gate 
600Sstevel@tonic-gate struct	termios b;
610Sstevel@tonic-gate struct	winsize size;
620Sstevel@tonic-gate int	lb;
630Sstevel@tonic-gate int	l;
640Sstevel@tonic-gate char	*mptname = "/dev/ptmx";	/* master pseudo-tty device */
650Sstevel@tonic-gate 
660Sstevel@tonic-gate int	aflg;
670Sstevel@tonic-gate 
68*334Sdp int
69*334Sdp main(int argc, char *argv[])
700Sstevel@tonic-gate {
710Sstevel@tonic-gate 	uid_t ruidt;
720Sstevel@tonic-gate 	gid_t gidt;
730Sstevel@tonic-gate 
740Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
750Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
760Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
770Sstevel@tonic-gate #endif
780Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	shell = getenv("SHELL");
81*334Sdp 	if (shell == NULL)
820Sstevel@tonic-gate 		shell = "/bin/sh";
830Sstevel@tonic-gate 	argc--, argv++;
840Sstevel@tonic-gate 	while (argc > 0 && argv[0][0] == '-') {
850Sstevel@tonic-gate 		switch (argv[0][1]) {
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 		case 'a':
880Sstevel@tonic-gate 			aflg++;
890Sstevel@tonic-gate 			break;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 		default:
920Sstevel@tonic-gate 			fprintf(stderr,
930Sstevel@tonic-gate 			    gettext("usage: script [ -a ] [ typescript ]\n"));
940Sstevel@tonic-gate 			exit(1);
950Sstevel@tonic-gate 		}
960Sstevel@tonic-gate 		argc--, argv++;
970Sstevel@tonic-gate 	}
980Sstevel@tonic-gate 	if (argc > 0)
990Sstevel@tonic-gate 		fname = argv[0];
1000Sstevel@tonic-gate 	ruidt = getuid();
1010Sstevel@tonic-gate 	gidt = getgid();
1020Sstevel@tonic-gate 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
1030Sstevel@tonic-gate 		perror(fname);
1040Sstevel@tonic-gate 		fail();
1050Sstevel@tonic-gate 	}
1060Sstevel@tonic-gate 	chown(fname, ruidt, gidt);
1070Sstevel@tonic-gate 	getmaster();
1080Sstevel@tonic-gate 	printf(gettext("Script started, file is %s\n"), fname);
1090Sstevel@tonic-gate 	fixtty();
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	(void) signal(SIGCHLD, finish);
1120Sstevel@tonic-gate 	child = fork();
1130Sstevel@tonic-gate 	if (child < 0) {
1140Sstevel@tonic-gate 		perror("fork");
1150Sstevel@tonic-gate 		fail();
1160Sstevel@tonic-gate 	}
1170Sstevel@tonic-gate 	if (child == 0) {
1180Sstevel@tonic-gate 		subchild = child = fork();
1190Sstevel@tonic-gate 		if (child < 0) {
1200Sstevel@tonic-gate 			perror("fork");
1210Sstevel@tonic-gate 			fail();
1220Sstevel@tonic-gate 		}
1230Sstevel@tonic-gate 		if (child)
1240Sstevel@tonic-gate 			dooutput();
1250Sstevel@tonic-gate 		else
1260Sstevel@tonic-gate 			doshell();
1270Sstevel@tonic-gate 	}
1280Sstevel@tonic-gate 	doinput();
129*334Sdp 	/* NOTREACHED */
130*334Sdp 	return (0);
1310Sstevel@tonic-gate }
1320Sstevel@tonic-gate 
133*334Sdp void
1340Sstevel@tonic-gate doinput()
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate 	char ibuf[BUFSIZ];
1370Sstevel@tonic-gate 	int cc;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	(void) fclose(fscript);
1400Sstevel@tonic-gate 	sigset(SIGWINCH, sigwinch);
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
1430Sstevel@tonic-gate 		if (cc == -1) {
1440Sstevel@tonic-gate 			if (errno == EINTR) {   /* SIGWINCH probably */
1450Sstevel@tonic-gate 				continue;
1460Sstevel@tonic-gate 			} else {
1470Sstevel@tonic-gate 				break;
1480Sstevel@tonic-gate 			}
1490Sstevel@tonic-gate 		}
1500Sstevel@tonic-gate 		(void) write(master, ibuf, cc);
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 	done();
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate void
1560Sstevel@tonic-gate sigwinch()
1570Sstevel@tonic-gate {
1580Sstevel@tonic-gate 	struct winsize ws;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	if (ioctl(0, TIOCGWINSZ, &ws) == 0)
1610Sstevel@tonic-gate 		(void) ioctl(master, TIOCSWINSZ, &ws);
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate #include <sys/wait.h>
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate void
1670Sstevel@tonic-gate finish()
1680Sstevel@tonic-gate {
1690Sstevel@tonic-gate 	int status;
1700Sstevel@tonic-gate 	register int pid;
1710Sstevel@tonic-gate 	register int die = 0;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	while ((pid = wait(&status)) > 0)
1740Sstevel@tonic-gate 		if (pid == child)
1750Sstevel@tonic-gate 			die = 1;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	if (die)
1780Sstevel@tonic-gate 		done();
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate 
181*334Sdp void
1820Sstevel@tonic-gate dooutput()
1830Sstevel@tonic-gate {
1840Sstevel@tonic-gate 	time_t tvec;
1850Sstevel@tonic-gate 	char obuf[BUFSIZ];
1860Sstevel@tonic-gate 	char tbuf[BUFSIZ];
1870Sstevel@tonic-gate 	int cc;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	(void) close(0);
1900Sstevel@tonic-gate 	tvec = time((time_t *)0);
1910Sstevel@tonic-gate 	strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
1920Sstevel@tonic-gate 	fprintf(fscript, gettext("Script started on %s\n"), tbuf);
1930Sstevel@tonic-gate 	for (;;) {
1940Sstevel@tonic-gate 		cc = read(master, obuf, sizeof (obuf));
1950Sstevel@tonic-gate 		if (cc <= 0)
1960Sstevel@tonic-gate 			break;
1970Sstevel@tonic-gate 		(void) write(1, obuf, cc);
1980Sstevel@tonic-gate 		(void) fwrite(obuf, 1, cc, fscript);
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate 	done();
2010Sstevel@tonic-gate }
2020Sstevel@tonic-gate 
203*334Sdp void
2040Sstevel@tonic-gate doshell()
2050Sstevel@tonic-gate {
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	setpgrp();	/* relinquish control terminal */
2080Sstevel@tonic-gate 	getslave();
2090Sstevel@tonic-gate 	(void) close(master);
2100Sstevel@tonic-gate 	(void) fclose(fscript);
2110Sstevel@tonic-gate 	(void) dup2(slave, 0);
2120Sstevel@tonic-gate 	(void) dup2(slave, 1);
2130Sstevel@tonic-gate 	(void) dup2(slave, 2);
2140Sstevel@tonic-gate 	(void) close(slave);
2150Sstevel@tonic-gate 	execl(shell, shell, "-i", (char *)0);
2160Sstevel@tonic-gate 	perror(shell);
2170Sstevel@tonic-gate 	fail();
2180Sstevel@tonic-gate }
2190Sstevel@tonic-gate 
220*334Sdp void
2210Sstevel@tonic-gate fixtty()
2220Sstevel@tonic-gate {
2230Sstevel@tonic-gate 	struct termios sbuf;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	sbuf = b;
2260Sstevel@tonic-gate 	sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
2270Sstevel@tonic-gate 	sbuf.c_oflag &= ~OPOST;
2280Sstevel@tonic-gate 	sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
2290Sstevel@tonic-gate 	sbuf.c_cc[VMIN] = 1;
2300Sstevel@tonic-gate 	sbuf.c_cc[VTIME] = 0;
2310Sstevel@tonic-gate 	(void) ioctl(0, TCSETSF, (char *)&sbuf);
2320Sstevel@tonic-gate }
2330Sstevel@tonic-gate 
234*334Sdp void
2350Sstevel@tonic-gate fail()
2360Sstevel@tonic-gate {
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	(void) kill(0, SIGTERM);
2390Sstevel@tonic-gate 	done();
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
242*334Sdp void
2430Sstevel@tonic-gate done()
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate 	time_t tvec;
2460Sstevel@tonic-gate 	char tbuf[BUFSIZ];
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	if (subchild) {
2490Sstevel@tonic-gate 		tvec = time((time_t *)0);
2500Sstevel@tonic-gate 		strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
2510Sstevel@tonic-gate 		fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
2520Sstevel@tonic-gate 		(void) fclose(fscript);
2530Sstevel@tonic-gate 		(void) close(master);
2540Sstevel@tonic-gate 	} else {
2550Sstevel@tonic-gate 		(void) ioctl(0, TCSETSW, (char *)&b);
2560Sstevel@tonic-gate 		printf(gettext("Script done, file is %s\n"), fname);
2570Sstevel@tonic-gate 	}
2580Sstevel@tonic-gate 	exit(0);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
261*334Sdp void
2620Sstevel@tonic-gate getmaster()
2630Sstevel@tonic-gate {
2640Sstevel@tonic-gate 	struct stat stb;
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
2670Sstevel@tonic-gate 		(void) ioctl(0, TCGETS, (char *)&b);
2680Sstevel@tonic-gate 		(void) ioctl(0, TIOCGWINSZ, (char *)&size);
2690Sstevel@tonic-gate 		return;
2700Sstevel@tonic-gate 	} else {				/* out of pseudo-tty's */
2710Sstevel@tonic-gate 		perror(mptname);
2720Sstevel@tonic-gate 		fprintf(stderr, gettext("Out of pseudo-tty's\n"));
2730Sstevel@tonic-gate 		fail();
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate 
277*334Sdp void
2780Sstevel@tonic-gate getslave()
2790Sstevel@tonic-gate {
2800Sstevel@tonic-gate 	char *slavename;	/* name of slave pseudo-tty */
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	grantpt(master);		/* change permissions of slave */
2830Sstevel@tonic-gate 	unlockpt(master);			/* unlock slave */
2840Sstevel@tonic-gate 	slavename = ptsname(master);		/* get name of slave */
2850Sstevel@tonic-gate 	slave = open(slavename, O_RDWR);	/* open slave */
2860Sstevel@tonic-gate 	if (slave < 0) {			/* error on opening slave */
2870Sstevel@tonic-gate 		perror(slavename);
2880Sstevel@tonic-gate 		fail();
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 	ioctl(slave, I_PUSH, "ptem");	/* push pt hw emulation module */
2910Sstevel@tonic-gate 	ioctl(slave, I_PUSH, "ldterm");		/* push line discipline */
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	(void) ioctl(slave, TCSETSF, (char *)&b);
2940Sstevel@tonic-gate 	(void) ioctl(slave, TIOCSWINSZ, (char *)&size);
2950Sstevel@tonic-gate }
296