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