]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - usr.bin/script/script.c
Copy head to stable/9 as part of 9.0-RELEASE release cycle.
[FreeBSD/stable/9.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
90         aflg = kflg = 0;
91         while ((ch = getopt(argc, argv, "aqkt:")) != -1)
92                 switch(ch) {
93                 case 'a':
94                         aflg = 1;
95                         break;
96                 case 'q':
97                         qflg = 1;
98                         break;
99                 case 'k':
100                         kflg = 1;
101                         break;
102                 case 't':
103                         flushtime = atoi(optarg);
104                         if (flushtime < 0)
105                                 err(1, "invalid flush time %d", flushtime);
106                         break;
107                 case '?':
108                 default:
109                         usage();
110                 }
111         argc -= optind;
112         argv += optind;
113
114         if (argc > 0) {
115                 fname = argv[0];
116                 argv++;
117                 argc--;
118         } else
119                 fname = "typescript";
120
121         if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
122                 err(1, "%s", fname);
123
124         if ((ttyflg = isatty(STDIN_FILENO)) != 0) {
125                 if (tcgetattr(STDIN_FILENO, &tt) == -1)
126                         err(1, "tcgetattr");
127                 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1)
128                         err(1, "ioctl");
129                 if (openpty(&master, &slave, NULL, &tt, &win) == -1)
130                         err(1, "openpty");
131         } else {
132                 if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
133                         err(1, "openpty");
134         }
135
136         if (!qflg) {
137                 tvec = time(NULL);
138                 (void)printf("Script started, output file is %s\n", fname);
139                 (void)fprintf(fscript, "Script started on %s", ctime(&tvec));
140                 fflush(fscript);
141         }
142         if (ttyflg) {
143                 rtt = tt;
144                 cfmakeraw(&rtt);
145                 rtt.c_lflag &= ~ECHO;
146                 (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
147         }
148
149         child = fork();
150         if (child < 0) {
151                 warn("fork");
152                 done(1);
153         }
154         if (child == 0)
155                 doshell(argv);
156         close(slave);
157
158         if (flushtime > 0)
159                 tvp = &tv;
160         else
161                 tvp = NULL;
162
163         start = time(0);
164         FD_ZERO(&rfd);
165         for (;;) {
166                 FD_SET(master, &rfd);
167                 FD_SET(STDIN_FILENO, &rfd);
168                 if (flushtime > 0) {
169                         tv.tv_sec = flushtime;
170                         tv.tv_usec = 0;
171                 }
172                 n = select(master + 1, &rfd, 0, 0, tvp);
173                 if (n < 0 && errno != EINTR)
174                         break;
175                 if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
176                         cc = read(STDIN_FILENO, ibuf, BUFSIZ);
177                         if (cc < 0)
178                                 break;
179                         if (cc == 0)
180                                 (void)write(master, ibuf, 0);
181                         if (cc > 0) {
182                                 (void)write(master, ibuf, cc);
183                                 if (kflg && tcgetattr(master, &stt) >= 0 &&
184                                     ((stt.c_lflag & ECHO) == 0)) {
185                                         (void)fwrite(ibuf, 1, cc, fscript);
186                                 }
187                         }
188                 }
189                 if (n > 0 && FD_ISSET(master, &rfd)) {
190                         cc = read(master, obuf, sizeof (obuf));
191                         if (cc <= 0)
192                                 break;
193                         (void)write(STDOUT_FILENO, obuf, cc);
194                         (void)fwrite(obuf, 1, cc, fscript);
195                 }
196                 tvec = time(0);
197                 if (tvec - start >= flushtime) {
198                         fflush(fscript);
199                         start = tvec;
200                 }
201         }
202         finish();
203         done(0);
204 }
205
206 static void
207 usage(void)
208 {
209         (void)fprintf(stderr,
210             "usage: script [-akq] [-t time] [file [command ...]]\n");
211         exit(1);
212 }
213
214 static void
215 finish(void)
216 {
217         int e, status;
218
219         if (waitpid(child, &status, 0) == child) {
220                 if (WIFEXITED(status))
221                         e = WEXITSTATUS(status);
222                 else if (WIFSIGNALED(status))
223                         e = WTERMSIG(status);
224                 else /* can't happen */
225                         e = 1;
226                 done(e);
227         }
228 }
229
230 static void
231 doshell(char **av)
232 {
233         const char *shell;
234         int k;
235
236         shell = getenv("SHELL");
237         if (shell == NULL)
238                 shell = _PATH_BSHELL;
239
240         if (av[0])
241                 for (k = 0 ; av[k] ; ++k)
242                         fprintf(fscript, "%s%s", k ? " " : "", av[k]);
243                 fprintf(fscript, "\r\n");
244
245         (void)close(master);
246         (void)fclose(fscript);
247         login_tty(slave);
248         setenv("SCRIPT", fname, 1);
249         if (av[0]) {
250                 execvp(av[0], av);
251                 warn("%s", av[0]);
252         } else {
253                 execl(shell, shell, "-i", (char *)NULL);
254                 warn("%s", shell);
255         }
256         fail();
257 }
258
259 static void
260 fail(void)
261 {
262         (void)kill(0, SIGTERM);
263         done(1);
264 }
265
266 static void
267 done(int eno)
268 {
269         time_t tvec;
270
271         if (ttyflg)
272                 (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
273         tvec = time(NULL);
274         if (!qflg) {
275                 (void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
276                 (void)printf("\nScript done, output file is %s\n", fname);
277         }
278         (void)fclose(fscript);
279         (void)close(master);
280         exit(eno);
281 }