xref: /onnv-gate/usr/src/cmd/script/script.c (revision 9258:aa2ec2ce7f29)
10Sstevel@tonic-gate /*
2*9258SRitwik.Ghoshal@Sun.COM  * Copyright 2009 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 /*
20334Sdp  * script: Produce a record of a terminal session.
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate #include <stdio.h>
23334Sdp #include <stdlib.h>
24334Sdp #include <unistd.h>
250Sstevel@tonic-gate #include <signal.h>
260Sstevel@tonic-gate #include <fcntl.h>
270Sstevel@tonic-gate #include <locale.h>
280Sstevel@tonic-gate #include <time.h>
290Sstevel@tonic-gate #include <sys/stropts.h>
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/stat.h>
320Sstevel@tonic-gate #include <sys/termios.h>
330Sstevel@tonic-gate #include <sys/file.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate int 	grantpt();
370Sstevel@tonic-gate int 	unlockpt();
380Sstevel@tonic-gate char	*ptsname();
39334Sdp void	doinput() __NORETURN;
40334Sdp void	dooutput();
41334Sdp void	doshell();
42334Sdp void	fixtty();
43334Sdp void	fail();
44334Sdp void	done() __NORETURN;
45334Sdp void	getmaster();
46334Sdp void	getslave();
470Sstevel@tonic-gate 
480Sstevel@tonic-gate char	*shell;
490Sstevel@tonic-gate FILE	*fscript;
500Sstevel@tonic-gate int	master;			/* file descriptor for master pseudo-tty */
510Sstevel@tonic-gate int	slave;			/* file descriptor for slave pseudo-tty */
520Sstevel@tonic-gate int	child;
530Sstevel@tonic-gate int	subchild;
540Sstevel@tonic-gate char	*fname = "typescript";
550Sstevel@tonic-gate void	sigwinch();
560Sstevel@tonic-gate void	finish();
570Sstevel@tonic-gate 
580Sstevel@tonic-gate struct	termios b;
590Sstevel@tonic-gate struct	winsize size;
600Sstevel@tonic-gate int	lb;
610Sstevel@tonic-gate int	l;
620Sstevel@tonic-gate char	*mptname = "/dev/ptmx";	/* master pseudo-tty device */
630Sstevel@tonic-gate 
640Sstevel@tonic-gate int	aflg;
650Sstevel@tonic-gate 
66334Sdp int
main(int argc,char * argv[])67334Sdp main(int argc, char *argv[])
680Sstevel@tonic-gate {
690Sstevel@tonic-gate 	uid_t ruidt;
700Sstevel@tonic-gate 	gid_t gidt;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
730Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
740Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
750Sstevel@tonic-gate #endif
760Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	shell = getenv("SHELL");
79334Sdp 	if (shell == NULL)
800Sstevel@tonic-gate 		shell = "/bin/sh";
810Sstevel@tonic-gate 	argc--, argv++;
820Sstevel@tonic-gate 	while (argc > 0 && argv[0][0] == '-') {
830Sstevel@tonic-gate 		switch (argv[0][1]) {
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 		case 'a':
860Sstevel@tonic-gate 			aflg++;
870Sstevel@tonic-gate 			break;
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 		default:
900Sstevel@tonic-gate 			fprintf(stderr,
910Sstevel@tonic-gate 			    gettext("usage: script [ -a ] [ typescript ]\n"));
920Sstevel@tonic-gate 			exit(1);
930Sstevel@tonic-gate 		}
940Sstevel@tonic-gate 		argc--, argv++;
950Sstevel@tonic-gate 	}
960Sstevel@tonic-gate 	if (argc > 0)
970Sstevel@tonic-gate 		fname = argv[0];
980Sstevel@tonic-gate 	ruidt = getuid();
990Sstevel@tonic-gate 	gidt = getgid();
1000Sstevel@tonic-gate 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
1010Sstevel@tonic-gate 		perror(fname);
1020Sstevel@tonic-gate 		fail();
1030Sstevel@tonic-gate 	}
104*9258SRitwik.Ghoshal@Sun.COM 	setbuf(fscript, NULL);
1050Sstevel@tonic-gate 	chown(fname, ruidt, gidt);
1060Sstevel@tonic-gate 	getmaster();
1070Sstevel@tonic-gate 	printf(gettext("Script started, file is %s\n"), fname);
1080Sstevel@tonic-gate 	fixtty();
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	(void) signal(SIGCHLD, finish);
1110Sstevel@tonic-gate 	child = fork();
1120Sstevel@tonic-gate 	if (child < 0) {
1130Sstevel@tonic-gate 		perror("fork");
1140Sstevel@tonic-gate 		fail();
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate 	if (child == 0) {
1170Sstevel@tonic-gate 		subchild = child = fork();
1180Sstevel@tonic-gate 		if (child < 0) {
1190Sstevel@tonic-gate 			perror("fork");
1200Sstevel@tonic-gate 			fail();
1210Sstevel@tonic-gate 		}
1220Sstevel@tonic-gate 		if (child)
1230Sstevel@tonic-gate 			dooutput();
1240Sstevel@tonic-gate 		else
1250Sstevel@tonic-gate 			doshell();
1260Sstevel@tonic-gate 	}
1270Sstevel@tonic-gate 	doinput();
128334Sdp 	/* NOTREACHED */
129334Sdp 	return (0);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
132334Sdp void
doinput()1330Sstevel@tonic-gate doinput()
1340Sstevel@tonic-gate {
1350Sstevel@tonic-gate 	char ibuf[BUFSIZ];
1360Sstevel@tonic-gate 	int cc;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	(void) fclose(fscript);
1390Sstevel@tonic-gate 	sigset(SIGWINCH, sigwinch);
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
1420Sstevel@tonic-gate 		if (cc == -1) {
1430Sstevel@tonic-gate 			if (errno == EINTR) {   /* SIGWINCH probably */
1440Sstevel@tonic-gate 				continue;
1450Sstevel@tonic-gate 			} else {
1460Sstevel@tonic-gate 				break;
1470Sstevel@tonic-gate 			}
1480Sstevel@tonic-gate 		}
1490Sstevel@tonic-gate 		(void) write(master, ibuf, cc);
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate 	done();
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate void
sigwinch()1550Sstevel@tonic-gate sigwinch()
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate 	struct winsize ws;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	if (ioctl(0, TIOCGWINSZ, &ws) == 0)
1600Sstevel@tonic-gate 		(void) ioctl(master, TIOCSWINSZ, &ws);
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate #include <sys/wait.h>
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate void
finish()1660Sstevel@tonic-gate finish()
1670Sstevel@tonic-gate {
1680Sstevel@tonic-gate 	int status;
1690Sstevel@tonic-gate 	register int pid;
1700Sstevel@tonic-gate 	register int die = 0;
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	while ((pid = wait(&status)) > 0)
1730Sstevel@tonic-gate 		if (pid == child)
1740Sstevel@tonic-gate 			die = 1;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	if (die)
1770Sstevel@tonic-gate 		done();
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate 
180334Sdp void
dooutput()1810Sstevel@tonic-gate dooutput()
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate 	time_t tvec;
1840Sstevel@tonic-gate 	char obuf[BUFSIZ];
1850Sstevel@tonic-gate 	char tbuf[BUFSIZ];
1860Sstevel@tonic-gate 	int cc;
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	(void) close(0);
1890Sstevel@tonic-gate 	tvec = time((time_t *)0);
1900Sstevel@tonic-gate 	strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
1910Sstevel@tonic-gate 	fprintf(fscript, gettext("Script started on %s\n"), tbuf);
1920Sstevel@tonic-gate 	for (;;) {
1930Sstevel@tonic-gate 		cc = read(master, obuf, sizeof (obuf));
1940Sstevel@tonic-gate 		if (cc <= 0)
1950Sstevel@tonic-gate 			break;
1960Sstevel@tonic-gate 		(void) write(1, obuf, cc);
1970Sstevel@tonic-gate 		(void) fwrite(obuf, 1, cc, fscript);
1980Sstevel@tonic-gate 	}
1990Sstevel@tonic-gate 	done();
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
202334Sdp void
doshell()2030Sstevel@tonic-gate doshell()
2040Sstevel@tonic-gate {
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	setpgrp();	/* relinquish control terminal */
2070Sstevel@tonic-gate 	getslave();
2080Sstevel@tonic-gate 	(void) close(master);
2090Sstevel@tonic-gate 	(void) fclose(fscript);
2100Sstevel@tonic-gate 	(void) dup2(slave, 0);
2110Sstevel@tonic-gate 	(void) dup2(slave, 1);
2120Sstevel@tonic-gate 	(void) dup2(slave, 2);
2130Sstevel@tonic-gate 	(void) close(slave);
2140Sstevel@tonic-gate 	execl(shell, shell, "-i", (char *)0);
2150Sstevel@tonic-gate 	perror(shell);
2160Sstevel@tonic-gate 	fail();
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate 
219334Sdp void
fixtty()2200Sstevel@tonic-gate fixtty()
2210Sstevel@tonic-gate {
2220Sstevel@tonic-gate 	struct termios sbuf;
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	sbuf = b;
2250Sstevel@tonic-gate 	sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
2260Sstevel@tonic-gate 	sbuf.c_oflag &= ~OPOST;
2270Sstevel@tonic-gate 	sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
2280Sstevel@tonic-gate 	sbuf.c_cc[VMIN] = 1;
2290Sstevel@tonic-gate 	sbuf.c_cc[VTIME] = 0;
2300Sstevel@tonic-gate 	(void) ioctl(0, TCSETSF, (char *)&sbuf);
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate 
233334Sdp void
fail()2340Sstevel@tonic-gate fail()
2350Sstevel@tonic-gate {
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	(void) kill(0, SIGTERM);
2380Sstevel@tonic-gate 	done();
2390Sstevel@tonic-gate }
2400Sstevel@tonic-gate 
241334Sdp void
done()2420Sstevel@tonic-gate done()
2430Sstevel@tonic-gate {
2440Sstevel@tonic-gate 	time_t tvec;
2450Sstevel@tonic-gate 	char tbuf[BUFSIZ];
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	if (subchild) {
2480Sstevel@tonic-gate 		tvec = time((time_t *)0);
2490Sstevel@tonic-gate 		strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
2500Sstevel@tonic-gate 		fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
2510Sstevel@tonic-gate 		(void) fclose(fscript);
2520Sstevel@tonic-gate 		(void) close(master);
2530Sstevel@tonic-gate 	} else {
2540Sstevel@tonic-gate 		(void) ioctl(0, TCSETSW, (char *)&b);
2550Sstevel@tonic-gate 		printf(gettext("Script done, file is %s\n"), fname);
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 	exit(0);
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate 
260334Sdp void
getmaster()2610Sstevel@tonic-gate getmaster()
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	struct stat stb;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
2660Sstevel@tonic-gate 		(void) ioctl(0, TCGETS, (char *)&b);
2670Sstevel@tonic-gate 		(void) ioctl(0, TIOCGWINSZ, (char *)&size);
2680Sstevel@tonic-gate 		return;
2690Sstevel@tonic-gate 	} else {				/* out of pseudo-tty's */
2700Sstevel@tonic-gate 		perror(mptname);
2710Sstevel@tonic-gate 		fprintf(stderr, gettext("Out of pseudo-tty's\n"));
2720Sstevel@tonic-gate 		fail();
2730Sstevel@tonic-gate 	}
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate 
276334Sdp void
getslave()2770Sstevel@tonic-gate getslave()
2780Sstevel@tonic-gate {
2790Sstevel@tonic-gate 	char *slavename;	/* name of slave pseudo-tty */
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	grantpt(master);		/* change permissions of slave */
2820Sstevel@tonic-gate 	unlockpt(master);			/* unlock slave */
2830Sstevel@tonic-gate 	slavename = ptsname(master);		/* get name of slave */
2840Sstevel@tonic-gate 	slave = open(slavename, O_RDWR);	/* open slave */
2850Sstevel@tonic-gate 	if (slave < 0) {			/* error on opening slave */
2860Sstevel@tonic-gate 		perror(slavename);
2870Sstevel@tonic-gate 		fail();
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 	ioctl(slave, I_PUSH, "ptem");	/* push pt hw emulation module */
2900Sstevel@tonic-gate 	ioctl(slave, I_PUSH, "ldterm");		/* push line discipline */
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	(void) ioctl(slave, TCSETSF, (char *)&b);
2930Sstevel@tonic-gate 	(void) ioctl(slave, TIOCSWINSZ, (char *)&size);
2940Sstevel@tonic-gate }
295