GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/script/script.c Lines: 0 147 0.0 %
Date: 2017-11-07 Branches: 0 86 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: script.c,v 1.33 2017/04/12 14:49:05 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
#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
int		istty;
89
90
__dead void done(int);
91
void dooutput(void);
92
void doshell(void);
93
void fail(void);
94
void finish(int);
95
void scriptflush(int);
96
void handlesigwinch(int);
97
98
int
99
main(int argc, char *argv[])
100
{
101
	extern char *__progname;
102
	struct sigaction sa;
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
	if (isatty(0)) {
130
		if (tcgetattr(STDIN_FILENO, &tt) == 0 &&
131
		    ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == 0)
132
			istty = 1;
133
	}
134
	if (openpty(&master, &slave, NULL, &tt, &win) == -1)
135
		err(1, "openpty");
136
137
	(void)printf("Script started, output file is %s\n", fname);
138
	if (istty) {
139
		struct termios rtt = tt;
140
141
		cfmakeraw(&rtt);
142
		rtt.c_lflag &= ~ECHO;
143
		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
144
	}
145
146
	bzero(&sa, sizeof sa);
147
	sigemptyset(&sa.sa_mask);
148
	sa.sa_handler = handlesigwinch;
149
	sa.sa_flags = SA_RESTART;
150
	(void)sigaction(SIGWINCH, &sa, NULL);
151
152
	child = fork();
153
	if (child < 0) {
154
		warn("fork");
155
		fail();
156
	}
157
	if (child == 0) {
158
		subchild = child = fork();
159
		if (child < 0) {
160
			warn("fork");
161
			fail();
162
		}
163
		if (child)
164
			dooutput();
165
		else
166
			doshell();
167
	}
168
169
	bzero(&sa, sizeof sa);
170
	sigemptyset(&sa.sa_mask);
171
	sa.sa_handler = finish;
172
	(void)sigaction(SIGCHLD, &sa, NULL);
173
174
	if (pledge("stdio proc tty flock rpath cpath wpath", NULL) == -1)
175
		err(1, "pledge");
176
177
	(void)fclose(fscript);
178
	while (1) {
179
		if (dead)
180
			break;
181
		cc = read(STDIN_FILENO, ibuf, BUFSIZ);
182
		if (cc == -1 && errno == EINTR)
183
			continue;
184
		if (cc <= 0)
185
			break;
186
		for (off = 0; off < cc; ) {
187
			ssize_t n = write(master, ibuf + off, cc - off);
188
			if (n == -1 && errno != EAGAIN)
189
				break;
190
			if (n == 0)
191
				break;	/* skip writing */
192
			if (n > 0)
193
				off += n;
194
		}
195
	}
196
	done(sigdeadstatus);
197
}
198
199
/* ARGSUSED */
200
void
201
finish(int signo)
202
{
203
	int save_errno = errno;
204
	int status, e = 1;
205
	pid_t pid;
206
207
	while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
208
		if (pid == (pid_t)child) {
209
			if (WIFEXITED(status))
210
				e = WEXITSTATUS(status);
211
		}
212
	}
213
	dead = 1;
214
	sigdeadstatus = e;
215
	errno = save_errno;
216
}
217
218
/* ARGSUSED */
219
void
220
handlesigwinch(int signo)
221
{
222
	int save_errno = errno;
223
	struct winsize win;
224
	pid_t pgrp;
225
226
	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) != -1) {
227
		ioctl(slave, TIOCSWINSZ, &win);
228
		if (ioctl(slave, TIOCGPGRP, &pgrp) != -1)
229
			killpg(pgrp, SIGWINCH);
230
	}
231
	errno = save_errno;
232
}
233
234
void
235
dooutput(void)
236
{
237
	struct sigaction sa;
238
	struct itimerval value;
239
	sigset_t blkalrm;
240
	char obuf[BUFSIZ];
241
	time_t tvec;
242
	ssize_t outcc = 0, cc, off;
243
244
	(void)close(STDIN_FILENO);
245
	tvec = time(NULL);
246
	(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
247
248
	sigemptyset(&blkalrm);
249
	sigaddset(&blkalrm, SIGALRM);
250
	bzero(&sa, sizeof sa);
251
	sigemptyset(&sa.sa_mask);
252
	sa.sa_handler = scriptflush;
253
	(void)sigaction(SIGALRM, &sa, NULL);
254
255
	bzero(&sa, sizeof sa);
256
	sigemptyset(&sa.sa_mask);
257
	sa.sa_handler = SIG_IGN;
258
	(void)sigaction(SIGCHLD, &sa, NULL);
259
260
	if (pledge("stdio proc flock rpath cpath wpath", NULL) == -1)
261
		err(1, "pledge");
262
263
	value.it_interval.tv_sec = 30;
264
	value.it_interval.tv_usec = 0;
265
	value.it_value = value.it_interval;
266
	(void)setitimer(ITIMER_REAL, &value, NULL);
267
	for (;;) {
268
		if (flush) {
269
			if (outcc) {
270
				(void)fflush(fscript);
271
				outcc = 0;
272
			}
273
			flush = 0;
274
		}
275
		cc = read(master, obuf, sizeof (obuf));
276
		if (cc == -1 && errno == EINTR)
277
			continue;
278
		if (cc <= 0)
279
			break;
280
		sigprocmask(SIG_BLOCK, &blkalrm, NULL);
281
		for (off = 0; off < cc; ) {
282
			ssize_t n = write(STDOUT_FILENO, obuf + off, cc - off);
283
			if (n == -1 && errno != EAGAIN)
284
				break;
285
			if (n == 0)
286
				break;	/* skip writing */
287
			if (n > 0)
288
				off += n;
289
		}
290
		(void)fwrite(obuf, 1, cc, fscript);
291
		outcc += cc;
292
		sigprocmask(SIG_UNBLOCK, &blkalrm, NULL);
293
	}
294
	done(0);
295
}
296
297
/* ARGSUSED */
298
void
299
scriptflush(int signo)
300
{
301
	flush = 1;
302
}
303
304
void
305
doshell(void)
306
{
307
	char *shell;
308
309
	shell = getenv("SHELL");
310
	if (shell == NULL)
311
		shell = _PATH_BSHELL;
312
313
	(void)close(master);
314
	(void)fclose(fscript);
315
	login_tty(slave);
316
	execl(shell, shell, "-i", (char *)NULL);
317
	warn("%s", shell);
318
	fail();
319
}
320
321
void
322
fail(void)
323
{
324
325
	(void)kill(0, SIGTERM);
326
	done(1);
327
}
328
329
void
330
done(int eval)
331
{
332
	time_t tvec;
333
334
	if (subchild) {
335
		tvec = time(NULL);
336
		(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
337
		(void)fclose(fscript);
338
		(void)close(master);
339
	} else {
340
		if (istty)
341
			(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
342
		(void)printf("Script done, output file is %s\n", fname);
343
	}
344
	exit(eval);
345
}