xref: /openbsd-src/usr.bin/script/script.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: script.c,v 1.32 2015/10/20 14:55:21 semarie 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 #include <sys/types.h>
59 #include <sys/wait.h>
60 #include <sys/stat.h>
61 #include <sys/ioctl.h>
62 #include <sys/time.h>
63 
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <paths.h>
67 #include <signal.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <termios.h>
72 #include <unistd.h>
73 
74 #include <util.h>
75 #include <err.h>
76 
77 FILE	*fscript;
78 int	master, slave;
79 volatile sig_atomic_t child;
80 pid_t	subchild;
81 char	*fname;
82 
83 volatile sig_atomic_t dead;
84 volatile sig_atomic_t sigdeadstatus;
85 volatile sig_atomic_t flush;
86 
87 struct	termios tt;
88 
89 __dead void done(int);
90 void dooutput(void);
91 void doshell(void);
92 void fail(void);
93 void finish(int);
94 void scriptflush(int);
95 void handlesigwinch(int);
96 
97 int
98 main(int argc, char *argv[])
99 {
100 	extern char *__progname;
101 	struct sigaction sa;
102 	struct termios rtt;
103 	struct winsize win;
104 	char ibuf[BUFSIZ];
105 	ssize_t cc, off;
106 	int aflg, ch;
107 
108 	aflg = 0;
109 	while ((ch = getopt(argc, argv, "a")) != -1)
110 		switch(ch) {
111 		case 'a':
112 			aflg = 1;
113 			break;
114 		default:
115 			fprintf(stderr, "usage: %s [-a] [file]\n", __progname);
116 			exit(1);
117 		}
118 	argc -= optind;
119 	argv += optind;
120 
121 	if (argc > 0)
122 		fname = argv[0];
123 	else
124 		fname = "typescript";
125 
126 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
127 		err(1, "%s", fname);
128 
129 	(void)tcgetattr(STDIN_FILENO, &tt);
130 	(void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
131 	if (openpty(&master, &slave, NULL, &tt, &win) == -1)
132 		err(1, "openpty");
133 
134 	(void)printf("Script started, output file is %s\n", fname);
135 	rtt = tt;
136 	cfmakeraw(&rtt);
137 	rtt.c_lflag &= ~ECHO;
138 	(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
139 
140 	bzero(&sa, sizeof sa);
141 	sigemptyset(&sa.sa_mask);
142 	sa.sa_handler = handlesigwinch;
143 	sa.sa_flags = SA_RESTART;
144 	(void)sigaction(SIGWINCH, &sa, NULL);
145 
146 	child = fork();
147 	if (child < 0) {
148 		warn("fork");
149 		fail();
150 	}
151 	if (child == 0) {
152 		subchild = child = fork();
153 		if (child < 0) {
154 			warn("fork");
155 			fail();
156 		}
157 		if (child)
158 			dooutput();
159 		else
160 			doshell();
161 	}
162 
163 	bzero(&sa, sizeof sa);
164 	sigemptyset(&sa.sa_mask);
165 	sa.sa_handler = finish;
166 	(void)sigaction(SIGCHLD, &sa, NULL);
167 
168 	if (pledge("stdio proc tty", NULL) == -1)
169 		err(1, "pledge");
170 
171 	(void)fclose(fscript);
172 	while (1) {
173 		if (dead)
174 			break;
175 		cc = read(STDIN_FILENO, ibuf, BUFSIZ);
176 		if (cc == -1 && errno == EINTR)
177 			continue;
178 		if (cc <= 0)
179 			break;
180 		for (off = 0; off < cc; ) {
181 			ssize_t n = write(master, ibuf + off, cc - off);
182 			if (n == -1 && errno != EAGAIN)
183 				break;
184 			if (n == 0)
185 				break;	/* skip writing */
186 			if (n > 0)
187 				off += n;
188 		}
189 	}
190 	done(sigdeadstatus);
191 }
192 
193 /* ARGSUSED */
194 void
195 finish(int signo)
196 {
197 	int save_errno = errno;
198 	int status, e = 1;
199 	pid_t pid;
200 
201 	while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
202 		if (pid == (pid_t)child) {
203 			if (WIFEXITED(status))
204 				e = WEXITSTATUS(status);
205 		}
206 	}
207 	dead = 1;
208 	sigdeadstatus = e;
209 	errno = save_errno;
210 }
211 
212 /* ARGSUSED */
213 void
214 handlesigwinch(int signo)
215 {
216 	int save_errno = errno;
217 	struct winsize win;
218 	pid_t pgrp;
219 
220 	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) != -1) {
221 		ioctl(slave, TIOCSWINSZ, &win);
222 		if (ioctl(slave, TIOCGPGRP, &pgrp) != -1)
223 			killpg(pgrp, SIGWINCH);
224 	}
225 	errno = save_errno;
226 }
227 
228 void
229 dooutput(void)
230 {
231 	struct sigaction sa;
232 	struct itimerval value;
233 	sigset_t blkalrm;
234 	char obuf[BUFSIZ];
235 	time_t tvec;
236 	ssize_t outcc = 0, cc, off;
237 
238 	(void)close(STDIN_FILENO);
239 	tvec = time(NULL);
240 	(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
241 
242 	sigemptyset(&blkalrm);
243 	sigaddset(&blkalrm, SIGALRM);
244 	bzero(&sa, sizeof sa);
245 	sigemptyset(&sa.sa_mask);
246 	sa.sa_handler = scriptflush;
247 	(void)sigaction(SIGALRM, &sa, NULL);
248 
249 	bzero(&sa, sizeof sa);
250 	sigemptyset(&sa.sa_mask);
251 	sa.sa_handler = SIG_IGN;
252 	(void)sigaction(SIGCHLD, &sa, NULL);
253 
254 	if (pledge("stdio proc", NULL) == -1)
255 		err(1, "pledge");
256 
257 	value.it_interval.tv_sec = 30;
258 	value.it_interval.tv_usec = 0;
259 	value.it_value = value.it_interval;
260 	(void)setitimer(ITIMER_REAL, &value, NULL);
261 	for (;;) {
262 		if (flush) {
263 			if (outcc) {
264 				(void)fflush(fscript);
265 				outcc = 0;
266 			}
267 			flush = 0;
268 		}
269 		cc = read(master, obuf, sizeof (obuf));
270 		if (cc == -1 && errno == EINTR)
271 			continue;
272 		if (cc <= 0)
273 			break;
274 		sigprocmask(SIG_BLOCK, &blkalrm, NULL);
275 		for (off = 0; off < cc; ) {
276 			ssize_t n = write(STDOUT_FILENO, obuf + off, cc - off);
277 			if (n == -1 && errno != EAGAIN)
278 				break;
279 			if (n == 0)
280 				break;	/* skip writing */
281 			if (n > 0)
282 				off += n;
283 		}
284 		(void)fwrite(obuf, 1, cc, fscript);
285 		outcc += cc;
286 		sigprocmask(SIG_UNBLOCK, &blkalrm, NULL);
287 	}
288 	done(0);
289 }
290 
291 /* ARGSUSED */
292 void
293 scriptflush(int signo)
294 {
295 	flush = 1;
296 }
297 
298 void
299 doshell(void)
300 {
301 	char *shell;
302 
303 	shell = getenv("SHELL");
304 	if (shell == NULL)
305 		shell = _PATH_BSHELL;
306 
307 	(void)close(master);
308 	(void)fclose(fscript);
309 	login_tty(slave);
310 	execl(shell, shell, "-i", (char *)NULL);
311 	warn("%s", shell);
312 	fail();
313 }
314 
315 void
316 fail(void)
317 {
318 
319 	(void)kill(0, SIGTERM);
320 	done(1);
321 }
322 
323 void
324 done(int eval)
325 {
326 	time_t tvec;
327 
328 	if (subchild) {
329 		tvec = time(NULL);
330 		(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
331 		(void)fclose(fscript);
332 		(void)close(master);
333 	} else {
334 		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
335 		(void)printf("Script done, output file is %s\n", fname);
336 	}
337 	exit(eval);
338 }
339