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