xref: /illumos-gate/usr/src/cmd/script/script.c (revision 1fa2a664)
17c478bd9Sstevel@tonic-gate /*
2179c3dacSRitwik Ghoshal  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
6*1fa2a664SJoshua M. Clulow /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7*1fa2a664SJoshua M. Clulow /*	  All Rights Reserved */
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate /*
117c478bd9Sstevel@tonic-gate  * Copyright (c) 1980 Regents of the University of California.
127c478bd9Sstevel@tonic-gate  * All rights reserved.  The Berkeley software License Agreement
137c478bd9Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
147c478bd9Sstevel@tonic-gate  */
157c478bd9Sstevel@tonic-gate 
167c478bd9Sstevel@tonic-gate /*	Portions Copyright(c) 1988, Sun Microsystems, Inc.	*/
177c478bd9Sstevel@tonic-gate /*	All Rights Reserved.					*/
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate /*
2034e48580Sdp  * script: Produce a record of a terminal session.
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate #include <stdio.h>
2334e48580Sdp #include <stdlib.h>
2434e48580Sdp #include <unistd.h>
257c478bd9Sstevel@tonic-gate #include <signal.h>
267c478bd9Sstevel@tonic-gate #include <fcntl.h>
277c478bd9Sstevel@tonic-gate #include <locale.h>
287c478bd9Sstevel@tonic-gate #include <time.h>
297c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/stat.h>
327c478bd9Sstevel@tonic-gate #include <sys/termios.h>
337c478bd9Sstevel@tonic-gate #include <sys/file.h>
347c478bd9Sstevel@tonic-gate #include <errno.h>
357c478bd9Sstevel@tonic-gate 
36*1fa2a664SJoshua M. Clulow int	grantpt();
37*1fa2a664SJoshua M. Clulow int	unlockpt();
387c478bd9Sstevel@tonic-gate char	*ptsname();
3934e48580Sdp void	doinput() __NORETURN;
4034e48580Sdp void	dooutput();
4134e48580Sdp void	doshell();
4234e48580Sdp void	fixtty();
4334e48580Sdp void	fail();
4434e48580Sdp void	done() __NORETURN;
45*1fa2a664SJoshua M. Clulow void	getmanager();
46*1fa2a664SJoshua M. Clulow void	getsubsid();
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate char	*shell;
497c478bd9Sstevel@tonic-gate FILE	*fscript;
50*1fa2a664SJoshua M. Clulow int	manager;	/* file descriptor for manager pseudo-tty */
51*1fa2a664SJoshua M. Clulow int	subsid;		/* file descriptor for subsidiary pseudo-tty */
527c478bd9Sstevel@tonic-gate int	child;
537c478bd9Sstevel@tonic-gate int	subchild;
547c478bd9Sstevel@tonic-gate char	*fname = "typescript";
557c478bd9Sstevel@tonic-gate void	sigwinch();
567c478bd9Sstevel@tonic-gate void	finish();
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate struct	termios b;
597c478bd9Sstevel@tonic-gate struct	winsize size;
607c478bd9Sstevel@tonic-gate int	lb;
617c478bd9Sstevel@tonic-gate int	l;
62*1fa2a664SJoshua M. Clulow char	*mptname = "/dev/ptmx";	/* manager pseudo-tty device */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate int	aflg;
657c478bd9Sstevel@tonic-gate 
6634e48580Sdp int
main(int argc,char * argv[])6734e48580Sdp main(int argc, char *argv[])
687c478bd9Sstevel@tonic-gate {
697c478bd9Sstevel@tonic-gate 	uid_t ruidt;
707c478bd9Sstevel@tonic-gate 	gid_t gidt;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
737c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
747c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
757c478bd9Sstevel@tonic-gate #endif
767c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	shell = getenv("SHELL");
7934e48580Sdp 	if (shell == NULL)
807c478bd9Sstevel@tonic-gate 		shell = "/bin/sh";
817c478bd9Sstevel@tonic-gate 	argc--, argv++;
827c478bd9Sstevel@tonic-gate 	while (argc > 0 && argv[0][0] == '-') {
837c478bd9Sstevel@tonic-gate 		switch (argv[0][1]) {
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 		case 'a':
867c478bd9Sstevel@tonic-gate 			aflg++;
877c478bd9Sstevel@tonic-gate 			break;
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 		default:
907c478bd9Sstevel@tonic-gate 			fprintf(stderr,
917c478bd9Sstevel@tonic-gate 			    gettext("usage: script [ -a ] [ typescript ]\n"));
927c478bd9Sstevel@tonic-gate 			exit(1);
937c478bd9Sstevel@tonic-gate 		}
947c478bd9Sstevel@tonic-gate 		argc--, argv++;
957c478bd9Sstevel@tonic-gate 	}
967c478bd9Sstevel@tonic-gate 	if (argc > 0)
977c478bd9Sstevel@tonic-gate 		fname = argv[0];
987c478bd9Sstevel@tonic-gate 	ruidt = getuid();
997c478bd9Sstevel@tonic-gate 	gidt = getgid();
1007c478bd9Sstevel@tonic-gate 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
1017c478bd9Sstevel@tonic-gate 		perror(fname);
1027c478bd9Sstevel@tonic-gate 		fail();
1037c478bd9Sstevel@tonic-gate 	}
104179c3dacSRitwik Ghoshal 	setbuf(fscript, NULL);
1057c478bd9Sstevel@tonic-gate 	chown(fname, ruidt, gidt);
106*1fa2a664SJoshua M. Clulow 	getmanager();
1077c478bd9Sstevel@tonic-gate 	printf(gettext("Script started, file is %s\n"), fname);
1087c478bd9Sstevel@tonic-gate 	fixtty();
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, finish);
1117c478bd9Sstevel@tonic-gate 	child = fork();
1127c478bd9Sstevel@tonic-gate 	if (child < 0) {
1137c478bd9Sstevel@tonic-gate 		perror("fork");
1147c478bd9Sstevel@tonic-gate 		fail();
1157c478bd9Sstevel@tonic-gate 	}
1167c478bd9Sstevel@tonic-gate 	if (child == 0) {
1177c478bd9Sstevel@tonic-gate 		subchild = child = fork();
1187c478bd9Sstevel@tonic-gate 		if (child < 0) {
1197c478bd9Sstevel@tonic-gate 			perror("fork");
1207c478bd9Sstevel@tonic-gate 			fail();
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 		if (child)
1237c478bd9Sstevel@tonic-gate 			dooutput();
1247c478bd9Sstevel@tonic-gate 		else
1257c478bd9Sstevel@tonic-gate 			doshell();
1267c478bd9Sstevel@tonic-gate 	}
1277c478bd9Sstevel@tonic-gate 	doinput();
12834e48580Sdp 	/* NOTREACHED */
12934e48580Sdp 	return (0);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate 
13234e48580Sdp void
doinput()1337c478bd9Sstevel@tonic-gate doinput()
1347c478bd9Sstevel@tonic-gate {
1357c478bd9Sstevel@tonic-gate 	char ibuf[BUFSIZ];
1367c478bd9Sstevel@tonic-gate 	int cc;
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	(void) fclose(fscript);
1397c478bd9Sstevel@tonic-gate 	sigset(SIGWINCH, sigwinch);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
1427c478bd9Sstevel@tonic-gate 		if (cc == -1) {
1437c478bd9Sstevel@tonic-gate 			if (errno == EINTR) {   /* SIGWINCH probably */
1447c478bd9Sstevel@tonic-gate 				continue;
1457c478bd9Sstevel@tonic-gate 			} else {
1467c478bd9Sstevel@tonic-gate 				break;
1477c478bd9Sstevel@tonic-gate 			}
1487c478bd9Sstevel@tonic-gate 		}
149*1fa2a664SJoshua M. Clulow 		(void) write(manager, ibuf, cc);
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	done();
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate void
sigwinch()1557c478bd9Sstevel@tonic-gate sigwinch()
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	struct winsize ws;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	if (ioctl(0, TIOCGWINSZ, &ws) == 0)
160*1fa2a664SJoshua M. Clulow 		(void) ioctl(manager, TIOCSWINSZ, &ws);
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate #include <sys/wait.h>
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate void
finish()1667c478bd9Sstevel@tonic-gate finish()
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate 	int status;
1697c478bd9Sstevel@tonic-gate 	register int pid;
1707c478bd9Sstevel@tonic-gate 	register int die = 0;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	while ((pid = wait(&status)) > 0)
1737c478bd9Sstevel@tonic-gate 		if (pid == child)
1747c478bd9Sstevel@tonic-gate 			die = 1;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	if (die)
1777c478bd9Sstevel@tonic-gate 		done();
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
18034e48580Sdp void
dooutput()1817c478bd9Sstevel@tonic-gate dooutput()
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	time_t tvec;
1847c478bd9Sstevel@tonic-gate 	char obuf[BUFSIZ];
1857c478bd9Sstevel@tonic-gate 	char tbuf[BUFSIZ];
1867c478bd9Sstevel@tonic-gate 	int cc;
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	(void) close(0);
1897c478bd9Sstevel@tonic-gate 	tvec = time((time_t *)0);
1907c478bd9Sstevel@tonic-gate 	strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
1917c478bd9Sstevel@tonic-gate 	fprintf(fscript, gettext("Script started on %s\n"), tbuf);
1927c478bd9Sstevel@tonic-gate 	for (;;) {
193*1fa2a664SJoshua M. Clulow 		cc = read(manager, obuf, sizeof (obuf));
1947c478bd9Sstevel@tonic-gate 		if (cc <= 0)
1957c478bd9Sstevel@tonic-gate 			break;
1967c478bd9Sstevel@tonic-gate 		(void) write(1, obuf, cc);
1977c478bd9Sstevel@tonic-gate 		(void) fwrite(obuf, 1, cc, fscript);
1987c478bd9Sstevel@tonic-gate 	}
1997c478bd9Sstevel@tonic-gate 	done();
2007c478bd9Sstevel@tonic-gate }
2017c478bd9Sstevel@tonic-gate 
20234e48580Sdp void
doshell()2037c478bd9Sstevel@tonic-gate doshell()
2047c478bd9Sstevel@tonic-gate {
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	setpgrp();	/* relinquish control terminal */
207*1fa2a664SJoshua M. Clulow 	getsubsid();
208*1fa2a664SJoshua M. Clulow 	(void) close(manager);
2097c478bd9Sstevel@tonic-gate 	(void) fclose(fscript);
210*1fa2a664SJoshua M. Clulow 	(void) dup2(subsid, 0);
211*1fa2a664SJoshua M. Clulow 	(void) dup2(subsid, 1);
212*1fa2a664SJoshua M. Clulow 	(void) dup2(subsid, 2);
213*1fa2a664SJoshua M. Clulow 	(void) close(subsid);
2147c478bd9Sstevel@tonic-gate 	execl(shell, shell, "-i", (char *)0);
2157c478bd9Sstevel@tonic-gate 	perror(shell);
2167c478bd9Sstevel@tonic-gate 	fail();
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate 
21934e48580Sdp void
fixtty()2207c478bd9Sstevel@tonic-gate fixtty()
2217c478bd9Sstevel@tonic-gate {
2227c478bd9Sstevel@tonic-gate 	struct termios sbuf;
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	sbuf = b;
2257c478bd9Sstevel@tonic-gate 	sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
2267c478bd9Sstevel@tonic-gate 	sbuf.c_oflag &= ~OPOST;
2277c478bd9Sstevel@tonic-gate 	sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
2287c478bd9Sstevel@tonic-gate 	sbuf.c_cc[VMIN] = 1;
2297c478bd9Sstevel@tonic-gate 	sbuf.c_cc[VTIME] = 0;
2307c478bd9Sstevel@tonic-gate 	(void) ioctl(0, TCSETSF, (char *)&sbuf);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
23334e48580Sdp void
fail()2347c478bd9Sstevel@tonic-gate fail()
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	(void) kill(0, SIGTERM);
2387c478bd9Sstevel@tonic-gate 	done();
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate 
24134e48580Sdp void
done()2427c478bd9Sstevel@tonic-gate done()
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate 	time_t tvec;
2457c478bd9Sstevel@tonic-gate 	char tbuf[BUFSIZ];
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	if (subchild) {
2487c478bd9Sstevel@tonic-gate 		tvec = time((time_t *)0);
2497c478bd9Sstevel@tonic-gate 		strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
2507c478bd9Sstevel@tonic-gate 		fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
2517c478bd9Sstevel@tonic-gate 		(void) fclose(fscript);
252*1fa2a664SJoshua M. Clulow 		(void) close(manager);
2537c478bd9Sstevel@tonic-gate 	} else {
2547c478bd9Sstevel@tonic-gate 		(void) ioctl(0, TCSETSW, (char *)&b);
2557c478bd9Sstevel@tonic-gate 		printf(gettext("Script done, file is %s\n"), fname);
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 	exit(0);
2587c478bd9Sstevel@tonic-gate }
2597c478bd9Sstevel@tonic-gate 
26034e48580Sdp void
getmanager()261*1fa2a664SJoshua M. Clulow getmanager()
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate 	struct stat stb;
2647c478bd9Sstevel@tonic-gate 
265*1fa2a664SJoshua M. Clulow 	if ((manager = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
2667c478bd9Sstevel@tonic-gate 		(void) ioctl(0, TCGETS, (char *)&b);
2677c478bd9Sstevel@tonic-gate 		(void) ioctl(0, TIOCGWINSZ, (char *)&size);
2687c478bd9Sstevel@tonic-gate 		return;
2697c478bd9Sstevel@tonic-gate 	} else {				/* out of pseudo-tty's */
2707c478bd9Sstevel@tonic-gate 		perror(mptname);
2717c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext("Out of pseudo-tty's\n"));
2727c478bd9Sstevel@tonic-gate 		fail();
2737c478bd9Sstevel@tonic-gate 	}
2747c478bd9Sstevel@tonic-gate }
2757c478bd9Sstevel@tonic-gate 
27634e48580Sdp void
getsubsid()277*1fa2a664SJoshua M. Clulow getsubsid()
2787c478bd9Sstevel@tonic-gate {
279*1fa2a664SJoshua M. Clulow 	char *subsidname;	/* name of subsidiary pseudo-tty */
280*1fa2a664SJoshua M. Clulow 
281*1fa2a664SJoshua M. Clulow 	grantpt(manager);		/* change permissions of subsidiary */
282*1fa2a664SJoshua M. Clulow 	unlockpt(manager);			/* unlock subsidiary */
283*1fa2a664SJoshua M. Clulow 	subsidname = ptsname(manager);		/* get name of subsidiary */
284*1fa2a664SJoshua M. Clulow 	subsid = open(subsidname, O_RDWR);	/* open subsidiary */
285*1fa2a664SJoshua M. Clulow 	if (subsid < 0) {			/* error opening subsidiary */
286*1fa2a664SJoshua M. Clulow 		perror(subsidname);
2877c478bd9Sstevel@tonic-gate 		fail();
2887c478bd9Sstevel@tonic-gate 	}
289*1fa2a664SJoshua M. Clulow 	ioctl(subsid, I_PUSH, "ptem");	/* push pt hw emulation module */
290*1fa2a664SJoshua M. Clulow 	ioctl(subsid, I_PUSH, "ldterm");	/* push line discipline */
2917c478bd9Sstevel@tonic-gate 
292*1fa2a664SJoshua M. Clulow 	(void) ioctl(subsid, TCSETSF, (char *)&b);
293*1fa2a664SJoshua M. Clulow 	(void) ioctl(subsid, TIOCSWINSZ, (char *)&size);
2947c478bd9Sstevel@tonic-gate }
295