1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7/*	  All Rights Reserved  	*/
8
9
10/*
11 * Copyright (c) 1980 Regents of the University of California.
12 * All rights reserved.  The Berkeley software License Agreement
13 * specifies the terms and conditions for redistribution.
14 */
15
16/*	Portions Copyright(c) 1988, Sun Microsystems, Inc.	*/
17/*	All Rights Reserved.					*/
18
19/*
20 * script: Produce a record of a terminal session.
21 */
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <signal.h>
26#include <fcntl.h>
27#include <locale.h>
28#include <time.h>
29#include <sys/stropts.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/termios.h>
33#include <sys/file.h>
34#include <errno.h>
35
36int 	grantpt();
37int 	unlockpt();
38char	*ptsname();
39void	doinput() __NORETURN;
40void	dooutput();
41void	doshell();
42void	fixtty();
43void	fail();
44void	done() __NORETURN;
45void	getmaster();
46void	getslave();
47
48char	*shell;
49FILE	*fscript;
50int	master;			/* file descriptor for master pseudo-tty */
51int	slave;			/* file descriptor for slave pseudo-tty */
52int	child;
53int	subchild;
54char	*fname = "typescript";
55void	sigwinch();
56void	finish();
57
58struct	termios b;
59struct	winsize size;
60int	lb;
61int	l;
62char	*mptname = "/dev/ptmx";	/* master pseudo-tty device */
63
64int	aflg;
65
66int
67main(int argc, char *argv[])
68{
69	uid_t ruidt;
70	gid_t gidt;
71
72	(void) setlocale(LC_ALL, "");
73#if !defined(TEXT_DOMAIN)
74#define	TEXT_DOMAIN	"SYS_TEST"
75#endif
76	(void) textdomain(TEXT_DOMAIN);
77
78	shell = getenv("SHELL");
79	if (shell == NULL)
80		shell = "/bin/sh";
81	argc--, argv++;
82	while (argc > 0 && argv[0][0] == '-') {
83		switch (argv[0][1]) {
84
85		case 'a':
86			aflg++;
87			break;
88
89		default:
90			fprintf(stderr,
91			    gettext("usage: script [ -a ] [ typescript ]\n"));
92			exit(1);
93		}
94		argc--, argv++;
95	}
96	if (argc > 0)
97		fname = argv[0];
98	ruidt = getuid();
99	gidt = getgid();
100	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
101		perror(fname);
102		fail();
103	}
104	setbuf(fscript, NULL);
105	chown(fname, ruidt, gidt);
106	getmaster();
107	printf(gettext("Script started, file is %s\n"), fname);
108	fixtty();
109
110	(void) signal(SIGCHLD, finish);
111	child = fork();
112	if (child < 0) {
113		perror("fork");
114		fail();
115	}
116	if (child == 0) {
117		subchild = child = fork();
118		if (child < 0) {
119			perror("fork");
120			fail();
121		}
122		if (child)
123			dooutput();
124		else
125			doshell();
126	}
127	doinput();
128	/* NOTREACHED */
129	return (0);
130}
131
132void
133doinput()
134{
135	char ibuf[BUFSIZ];
136	int cc;
137
138	(void) fclose(fscript);
139	sigset(SIGWINCH, sigwinch);
140
141	while ((cc = read(0, ibuf, BUFSIZ)) != 0) {
142		if (cc == -1) {
143			if (errno == EINTR) {   /* SIGWINCH probably */
144				continue;
145			} else {
146				break;
147			}
148		}
149		(void) write(master, ibuf, cc);
150	}
151	done();
152}
153
154void
155sigwinch()
156{
157	struct winsize ws;
158
159	if (ioctl(0, TIOCGWINSZ, &ws) == 0)
160		(void) ioctl(master, TIOCSWINSZ, &ws);
161}
162
163#include <sys/wait.h>
164
165void
166finish()
167{
168	int status;
169	register int pid;
170	register int die = 0;
171
172	while ((pid = wait(&status)) > 0)
173		if (pid == child)
174			die = 1;
175
176	if (die)
177		done();
178}
179
180void
181dooutput()
182{
183	time_t tvec;
184	char obuf[BUFSIZ];
185	char tbuf[BUFSIZ];
186	int cc;
187
188	(void) close(0);
189	tvec = time((time_t *)0);
190	strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
191	fprintf(fscript, gettext("Script started on %s\n"), tbuf);
192	for (;;) {
193		cc = read(master, obuf, sizeof (obuf));
194		if (cc <= 0)
195			break;
196		(void) write(1, obuf, cc);
197		(void) fwrite(obuf, 1, cc, fscript);
198	}
199	done();
200}
201
202void
203doshell()
204{
205
206	setpgrp();	/* relinquish control terminal */
207	getslave();
208	(void) close(master);
209	(void) fclose(fscript);
210	(void) dup2(slave, 0);
211	(void) dup2(slave, 1);
212	(void) dup2(slave, 2);
213	(void) close(slave);
214	execl(shell, shell, "-i", (char *)0);
215	perror(shell);
216	fail();
217}
218
219void
220fixtty()
221{
222	struct termios sbuf;
223
224	sbuf = b;
225	sbuf.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON);
226	sbuf.c_oflag &= ~OPOST;
227	sbuf.c_lflag &= ~(ICANON|ISIG|ECHO);
228	sbuf.c_cc[VMIN] = 1;
229	sbuf.c_cc[VTIME] = 0;
230	(void) ioctl(0, TCSETSF, (char *)&sbuf);
231}
232
233void
234fail()
235{
236
237	(void) kill(0, SIGTERM);
238	done();
239}
240
241void
242done()
243{
244	time_t tvec;
245	char tbuf[BUFSIZ];
246
247	if (subchild) {
248		tvec = time((time_t *)0);
249		strftime(tbuf, BUFSIZ, "%c", localtime(&tvec));
250		fprintf(fscript, gettext("\nscript done on %s\n"), tbuf);
251		(void) fclose(fscript);
252		(void) close(master);
253	} else {
254		(void) ioctl(0, TCSETSW, (char *)&b);
255		printf(gettext("Script done, file is %s\n"), fname);
256	}
257	exit(0);
258}
259
260void
261getmaster()
262{
263	struct stat stb;
264
265	if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
266		(void) ioctl(0, TCGETS, (char *)&b);
267		(void) ioctl(0, TIOCGWINSZ, (char *)&size);
268		return;
269	} else {				/* out of pseudo-tty's */
270		perror(mptname);
271		fprintf(stderr, gettext("Out of pseudo-tty's\n"));
272		fail();
273	}
274}
275
276void
277getslave()
278{
279	char *slavename;	/* name of slave pseudo-tty */
280
281	grantpt(master);		/* change permissions of slave */
282	unlockpt(master);			/* unlock slave */
283	slavename = ptsname(master);		/* get name of slave */
284	slave = open(slavename, O_RDWR);	/* open slave */
285	if (slave < 0) {			/* error on opening slave */
286		perror(slavename);
287		fail();
288	}
289	ioctl(slave, I_PUSH, "ptem");	/* push pt hw emulation module */
290	ioctl(slave, I_PUSH, "ldterm");		/* push line discipline */
291
292	(void) ioctl(slave, TCSETSF, (char *)&b);
293	(void) ioctl(slave, TIOCSWINSZ, (char *)&size);
294}
295