xref: /openbsd-src/usr.bin/script/script.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: script.c,v 1.24 2005/12/12 20:10:53 deraadt Exp $	*/
2 /*	$NetBSD: script.c,v 1.3 1994/12/21 08:55:43 jtc Exp $	*/
3 
4 /*
5  * Copyright (c) 2001 Theo de Raadt
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Copyright (c) 1980, 1992, 1993
31  *	The Regents of the University of California.  All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  * 3. Neither the name of the University nor the names of its contributors
42  *    may be used to endorse or promote products derived from this software
43  *    without specific prior written permission.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 #ifndef lint
59 static const char copyright[] =
60 "@(#) Copyright (c) 1980, 1992, 1993\n\
61 	The Regents of the University of California.  All rights reserved.\n";
62 #endif /* not lint */
63 
64 #ifndef lint
65 #if 0
66 static const char sccsid[] = "@(#)script.c	8.1 (Berkeley) 6/6/93";
67 #endif
68 static const char rcsid[] = "$OpenBSD: script.c,v 1.24 2005/12/12 20:10:53 deraadt Exp $";
69 #endif /* not lint */
70 
71 #include <sys/types.h>
72 #include <sys/wait.h>
73 #include <sys/stat.h>
74 #include <sys/ioctl.h>
75 #include <sys/time.h>
76 
77 #include <errno.h>
78 #include <fcntl.h>
79 #include <paths.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <termios.h>
85 #include <tzfile.h>
86 #include <unistd.h>
87 
88 #include <util.h>
89 #include <err.h>
90 
91 FILE	*fscript;
92 int	master, slave;
93 volatile sig_atomic_t child;
94 pid_t	subchild;
95 char	*fname;
96 
97 volatile sig_atomic_t dead;
98 volatile sig_atomic_t sigdeadstatus;
99 volatile sig_atomic_t flush;
100 
101 struct	termios tt;
102 
103 __dead void done(int);
104 void dooutput(void);
105 void doshell(void);
106 void fail(void);
107 void finish(int);
108 void scriptflush(int);
109 void handlesigwinch(int);
110 
111 int
112 main(int argc, char *argv[])
113 {
114 	extern char *__progname;
115 	struct sigaction sa;
116 	struct termios rtt;
117 	struct winsize win;
118 	char ibuf[BUFSIZ];
119 	ssize_t cc, off;
120 	int aflg, ch;
121 
122 	aflg = 0;
123 	while ((ch = getopt(argc, argv, "a")) != -1)
124 		switch(ch) {
125 		case 'a':
126 			aflg = 1;
127 			break;
128 		default:
129 			fprintf(stderr, "usage: %s [-a] [file]\n", __progname);
130 			exit(1);
131 		}
132 	argc -= optind;
133 	argv += optind;
134 
135 	if (argc > 0)
136 		fname = argv[0];
137 	else
138 		fname = "typescript";
139 
140 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
141 		err(1, "%s", fname);
142 
143 	(void)tcgetattr(STDIN_FILENO, &tt);
144 	(void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
145 	if (openpty(&master, &slave, NULL, &tt, &win) == -1)
146 		err(1, "openpty");
147 
148 	(void)printf("Script started, output file is %s\n", fname);
149 	rtt = tt;
150 	cfmakeraw(&rtt);
151 	rtt.c_lflag &= ~ECHO;
152 	(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
153 
154 	bzero(&sa, sizeof sa);
155 	sigemptyset(&sa.sa_mask);
156 	sa.sa_handler = finish;
157 	(void)sigaction(SIGCHLD, &sa, NULL);
158 
159 	sa.sa_handler = handlesigwinch;
160 	sa.sa_flags = SA_RESTART;
161 	(void)sigaction(SIGWINCH, &sa, NULL);
162 
163 	child = fork();
164 	if (child < 0) {
165 		warn("fork");
166 		fail();
167 	}
168 	if (child == 0) {
169 		subchild = child = fork();
170 		if (child < 0) {
171 			warn("fork");
172 			fail();
173 		}
174 		if (child)
175 			dooutput();
176 		else
177 			doshell();
178 	}
179 
180 	(void)fclose(fscript);
181 	while (1) {
182 		if (dead)
183 			break;
184 		cc = read(STDIN_FILENO, ibuf, BUFSIZ);
185 		if (cc == -1 && errno == EINTR)
186 			continue;
187 		if (cc <= 0)
188 			break;
189 		for (off = 0; off < cc; ) {
190 			ssize_t n = write(master, ibuf + off, cc - off);
191 			if (n == -1 && errno != EAGAIN)
192 				break;
193 			if (n == 0)
194 				break;	/* skip writing */
195 			if (n > 0)
196 				off += n;
197 		}
198 	}
199 	done(sigdeadstatus);
200 }
201 
202 /* ARGSUSED */
203 void
204 finish(int signo)
205 {
206 	int save_errno = errno;
207 	int status, e = 1;
208 	pid_t pid;
209 
210 	while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
211 		if (pid == (pid_t)child) {
212 			if (WIFEXITED(status))
213 				e = WEXITSTATUS(status);
214 		}
215 	}
216 	dead = 1;
217 	sigdeadstatus = e;
218 	errno = save_errno;
219 }
220 
221 /* ARGSUSED */
222 void
223 handlesigwinch(int signo)
224 {
225 	int save_errno = errno;
226 	struct winsize win;
227 	pid_t pgrp;
228 
229 	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) != -1) {
230 		ioctl(slave, TIOCSWINSZ, &win);
231 		if (ioctl(slave, TIOCGPGRP, &pgrp) != -1)
232 			killpg(pgrp, SIGWINCH);
233 	}
234 	errno = save_errno;
235 }
236 
237 void
238 dooutput(void)
239 {
240 	struct sigaction sa;
241 	struct itimerval value;
242 	sigset_t blkalrm;
243 	char obuf[BUFSIZ];
244 	time_t tvec;
245 	ssize_t outcc = 0, cc, off;
246 
247 	(void)close(STDIN_FILENO);
248 	tvec = time(NULL);
249 	(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
250 
251 	sigemptyset(&blkalrm);
252 	sigaddset(&blkalrm, SIGALRM);
253 	bzero(&sa, sizeof sa);
254 	sigemptyset(&sa.sa_mask);
255 	sa.sa_handler = scriptflush;
256 	(void)sigaction(SIGALRM, &sa, NULL);
257 
258 	value.it_interval.tv_sec = SECSPERMIN / 2;
259 	value.it_interval.tv_usec = 0;
260 	value.it_value = value.it_interval;
261 	(void)setitimer(ITIMER_REAL, &value, NULL);
262 	for (;;) {
263 		if (flush) {
264 			if (outcc) {
265 				(void)fflush(fscript);
266 				outcc = 0;
267 			}
268 			flush = 0;
269 		}
270 		cc = read(master, obuf, sizeof (obuf));
271 		if (cc == -1 && errno == EINTR)
272 			continue;
273 		if (cc <= 0)
274 			break;
275 		sigprocmask(SIG_BLOCK, &blkalrm, NULL);
276 		for (off = 0; off < cc; ) {
277 			ssize_t n = write(STDOUT_FILENO, obuf + off, cc - off);
278 			if (n == -1 && errno != EAGAIN)
279 				break;
280 			if (n == 0)
281 				break;	/* skip writing */
282 			if (n > 0)
283 				off += n;
284 		}
285 		(void)fwrite(obuf, 1, cc, fscript);
286 		outcc += cc;
287 		sigprocmask(SIG_UNBLOCK, &blkalrm, NULL);
288 	}
289 	done(0);
290 }
291 
292 /* ARGSUSED */
293 void
294 scriptflush(int signo)
295 {
296 	flush = 1;
297 }
298 
299 void
300 doshell(void)
301 {
302 	char *shell;
303 
304 	shell = getenv("SHELL");
305 	if (shell == NULL)
306 		shell = _PATH_BSHELL;
307 
308 	(void)close(master);
309 	(void)fclose(fscript);
310 	login_tty(slave);
311 	execl(shell, shell, "-i", (char *)NULL);
312 	warn("%s", shell);
313 	fail();
314 }
315 
316 void
317 fail(void)
318 {
319 
320 	(void)kill(0, SIGTERM);
321 	done(1);
322 }
323 
324 void
325 done(int eval)
326 {
327 	time_t tvec;
328 
329 	if (subchild) {
330 		tvec = time(NULL);
331 		(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
332 		(void)fclose(fscript);
333 		(void)close(master);
334 	} else {
335 		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
336 		(void)printf("Script done, output file is %s\n", fname);
337 	}
338 	exit(eval);
339 }
340