]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - usr.bin/script/script.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / usr.bin / script / script.c
1 /*
2  * Copyright (c) 1980, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31
32 __FBSDID("$FreeBSD$");
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1992, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif
39
40 #ifndef lint
41 static const char sccsid[] = "@(#)script.c      8.1 (Berkeley) 6/6/93";
42 #endif
43
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <libutil.h>
54 #include <paths.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <termios.h>
60 #include <unistd.h>
61
62 static FILE *fscript;
63 static int master, slave;
64 static int child;
65 static const char *fname;
66 static int qflg, ttyflg;
67
68 static struct termios tt;
69
70 static void done(int) __dead2;
71 static void doshell(char **);
72 static void fail(void);
73 static void finish(void);
74 static void usage(void);
75
76 int
77 main(int argc, char *argv[])
78 {
79         int cc;
80         struct termios rtt, stt;
81         struct winsize win;
82         int aflg, kflg, ch, n;
83         struct timeval tv, *tvp;
84         time_t tvec, start;
85         char obuf[BUFSIZ];
86         char ibuf[BUFSIZ];
87         fd_set rfd;
88         int flushtime = 30;
89         int readstdin;
90
91         aflg = kflg = 0;
92         while ((ch = getopt(argc, argv, "aqkt:")) != -1)
93                 switch(ch) {
94                 case 'a':
95                         aflg = 1;
96                         break;
97                 case 'q':
98                         qflg = 1;
99                         break;
100                 case 'k':
101                         kflg = 1;
102                         break;
103                 case 't':
104                         flushtime = atoi(optarg);
105                         if (flushtime < 0)
106                                 err(1, "invalid flush time %d", flushtime);
107                         break;
108                 case '?':
109                 default:
110                         usage();
111                 }
112         argc -= optind;
113         argv += optind;
114
115         if (argc > 0) {
116                 fname = argv[0];
117                 argv++;
118                 argc--;
119         } else
120                 fname = "typescript";
121
122         if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
123                 err(1, "%s", fname);
124
125         if ((ttyflg = isatty(STDIN_FILENO)) != 0) {
126                 if (tcgetattr(STDIN_FILENO, &tt) == -1)
127                         err(1, "tcgetattr");
128                 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1)
129                         err(1, "ioctl");
130                 if (openpty(&master, &slave, NULL, &tt, &win) == -1)
131                         err(1, "openpty");
132         } else {
133                 if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
134                         err(1, "openpty");
135         }
136
137         if (!qflg) {
138                 tvec = time(NULL);
139                 (void)printf("Script started, output file is %s\n", fname);
140                 (void)fprintf(fscript, "Script started on %s", ctime(&tvec));
141                 fflush(fscript);
142         }
143         if (ttyflg) {
144                 rtt = tt;
145                 cfmakeraw(&rtt);
146                 rtt.c_lflag &= ~ECHO;
147                 (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
148         }
149
150         child = fork();
151         if (child < 0) {
152                 warn("fork");
153                 done(1);
154         }
155         if (child == 0)
156                 doshell(argv);
157         close(slave);
158
159         start = tvec = time(0);
160         readstdin = 1;
161         for (;;) {
162                 FD_ZERO(&rfd);
163                 FD_SET(master, &rfd);
164                 if (readstdin)
165                         FD_SET(STDIN_FILENO, &rfd);
166                 if (!readstdin && ttyflg) {
167                         tv.tv_sec = 1;
168                         tv.tv_usec = 0;
169                         tvp = &tv;
170                         readstdin = 1;
171                 } else if (flushtime > 0) {
172                         tv.tv_sec = flushtime - (tvec - start);
173                         tv.tv_usec = 0;
174                         tvp = &tv;
175                 } else {
176                         tvp = NULL;
177                 }
178                 n = select(master + 1, &rfd, 0, 0, tvp);
179                 if (n < 0 && errno != EINTR)
180                         break;
181                 if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
182                         cc = read(STDIN_FILENO, ibuf, BUFSIZ);
183                         if (cc < 0)
184                                 break;
185                         if (cc == 0) {
186                                 if (tcgetattr(master, &stt) == 0 &&
187                                     (stt.c_lflag & ICANON) != 0) {
188                                         (void)write(master, &stt.c_cc[VEOF], 1);
189                                 }
190                                 readstdin = 0;
191                         }
192                         if (cc > 0) {
193                                 (void)write(master, ibuf, cc);
194                                 if (kflg && tcgetattr(master, &stt) >= 0 &&
195                                     ((stt.c_lflag & ECHO) == 0)) {
196                                         (void)fwrite(ibuf, 1, cc, fscript);
197                                 }
198                         }
199                 }
200                 if (n > 0 && FD_ISSET(master, &rfd)) {
201                         cc = read(master, obuf, sizeof (obuf));
202                         if (cc <= 0)
203                                 break;
204                         (void)write(STDOUT_FILENO, obuf, cc);
205                         (void)fwrite(obuf, 1, cc, fscript);
206                 }
207                 tvec = time(0);
208                 if (tvec - start >= flushtime) {
209                         fflush(fscript);
210                         start = tvec;
211                 }
212         }
213         finish();
214         done(0);
215 }
216
217 static void
218 usage(void)
219 {
220         (void)fprintf(stderr,
221             "usage: script [-akq] [-t time] [file [command ...]]\n");
222         exit(1);
223 }
224
225 static void
226 finish(void)
227 {
228         int e, status;
229
230         if (waitpid(child, &status, 0) == child) {
231                 if (WIFEXITED(status))
232                         e = WEXITSTATUS(status);
233                 else if (WIFSIGNALED(status))
234                         e = WTERMSIG(status);
235                 else /* can't happen */
236                         e = 1;
237                 done(e);
238         }
239 }
240
241 static void
242 doshell(char **av)
243 {
244         const char *shell;
245         int k;
246
247         shell = getenv("SHELL");
248         if (shell == NULL)
249                 shell = _PATH_BSHELL;
250
251         if (av[0])
252                 for (k = 0 ; av[k] ; ++k)
253                         fprintf(fscript, "%s%s", k ? " " : "", av[k]);
254                 fprintf(fscript, "\r\n");
255
256         (void)close(master);
257         (void)fclose(fscript);
258         login_tty(slave);
259         setenv("SCRIPT", fname, 1);
260         if (av[0]) {
261                 execvp(av[0], av);
262                 warn("%s", av[0]);
263         } else {
264                 execl(shell, shell, "-i", (char *)NULL);
265                 warn("%s", shell);
266         }
267         fail();
268 }
269
270 static void
271 fail(void)
272 {
273         (void)kill(0, SIGTERM);
274         done(1);
275 }
276
277 static void
278 done(int eno)
279 {
280         time_t tvec;
281
282         if (ttyflg)
283                 (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
284         tvec = time(NULL);
285         if (!qflg) {
286                 (void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
287                 (void)printf("\nScript done, output file is %s\n", fname);
288         }
289         (void)fclose(fscript);
290         (void)close(master);
291         exit(eno);
292 }