Annotation of src/usr.bin/script/script.c, Revision 1.10
1.10 ! wiz 1: /* $NetBSD: script.c,v 1.9 2003/08/07 11:15:48 agc Exp $ */
1.3 jtc 2:
1.1 cgd 3: /*
1.3 jtc 4: * Copyright (c) 1980, 1992, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
1.9 agc 15: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29: * SUCH DAMAGE.
30: */
31:
1.5 lukem 32: #include <sys/cdefs.h>
1.1 cgd 33: #ifndef lint
1.5 lukem 34: __COPYRIGHT("@(#) Copyright (c) 1980, 1992, 1993\n\
35: The Regents of the University of California. All rights reserved.\n");
1.1 cgd 36: #endif /* not lint */
37:
38: #ifndef lint
1.3 jtc 39: #if 0
40: static char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93";
41: #endif
1.10 ! wiz 42: __RCSID("$NetBSD: script.c,v 1.9 2003/08/07 11:15:48 agc Exp $");
1.1 cgd 43: #endif /* not lint */
44:
45: #include <sys/types.h>
1.3 jtc 46: #include <sys/wait.h>
1.1 cgd 47: #include <sys/stat.h>
48: #include <sys/ioctl.h>
49: #include <sys/time.h>
1.8 atatat 50: #include <sys/uio.h>
1.3 jtc 51:
1.5 lukem 52: #include <err.h>
1.3 jtc 53: #include <errno.h>
54: #include <fcntl.h>
55: #include <paths.h>
56: #include <signal.h>
1.1 cgd 57: #include <stdio.h>
1.3 jtc 58: #include <stdlib.h>
59: #include <string.h>
60: #include <termios.h>
1.6 kleink 61: #include <time.h>
1.3 jtc 62: #include <tzfile.h>
63: #include <unistd.h>
1.5 lukem 64: #include <util.h>
1.1 cgd 65:
1.8 atatat 66: struct stamp {
67: uint64_t scr_len; /* amount of data */
68: uint64_t scr_sec; /* time it arrived in seconds... */
69: uint32_t scr_usec; /* ...and microseconds */
70: uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */
71: };
72:
1.1 cgd 73: FILE *fscript;
1.3 jtc 74: int master, slave;
75: int child, subchild;
76: int outcc;
1.8 atatat 77: int usesleep, rawout;
1.1 cgd 78: char *fname;
79:
80: struct termios tt;
81:
1.5 lukem 82: void done __P((void));
83: void dooutput __P((void));
84: void doshell __P((void));
85: void fail __P((void));
86: void finish __P((int));
87: int main __P((int, char **));
88: void scriptflush __P((int));
1.8 atatat 89: void record __P((FILE *, char *, size_t, int));
90: void playback __P((FILE *));
1.3 jtc 91:
92: int
1.1 cgd 93: main(argc, argv)
94: int argc;
95: char *argv[];
96: {
1.5 lukem 97: int cc;
1.3 jtc 98: struct termios rtt;
99: struct winsize win;
1.8 atatat 100: int aflg, pflg, ch;
1.3 jtc 101: char ibuf[BUFSIZ];
1.1 cgd 102:
1.3 jtc 103: aflg = 0;
1.8 atatat 104: pflg = 0;
105: usesleep = 1;
106: rawout = 0;
107: while ((ch = getopt(argc, argv, "adpr")) != -1)
1.3 jtc 108: switch(ch) {
1.1 cgd 109: case 'a':
1.3 jtc 110: aflg = 1;
1.1 cgd 111: break;
1.8 atatat 112: case 'd':
113: usesleep = 0;
114: break;
115: case 'p':
116: pflg = 1;
117: break;
118: case 'r':
119: rawout = 1;
120: break;
1.1 cgd 121: case '?':
122: default:
1.10 ! wiz 123: (void)fprintf(stderr, "usage: %s [-adpr] [file]\n",
! 124: getprogname());
1.1 cgd 125: exit(1);
126: }
127: argc -= optind;
128: argv += optind;
129:
130: if (argc > 0)
131: fname = argv[0];
132: else
133: fname = "typescript";
134:
1.8 atatat 135: if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL)
1.5 lukem 136: err(1, "fopen %s", fname);
1.3 jtc 137:
1.8 atatat 138: if (pflg)
139: playback(fscript);
140:
1.3 jtc 141: (void)tcgetattr(STDIN_FILENO, &tt);
142: (void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
143: if (openpty(&master, &slave, NULL, &tt, &win) == -1)
1.5 lukem 144: err(1, "openpty");
1.1 cgd 145:
1.3 jtc 146: (void)printf("Script started, output file is %s\n", fname);
147: rtt = tt;
148: cfmakeraw(&rtt);
149: rtt.c_lflag &= ~ECHO;
150: (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
1.1 cgd 151:
1.3 jtc 152: (void)signal(SIGCHLD, finish);
1.1 cgd 153: child = fork();
154: if (child < 0) {
1.5 lukem 155: warn("fork");
1.1 cgd 156: fail();
157: }
158: if (child == 0) {
159: subchild = child = fork();
160: if (child < 0) {
1.5 lukem 161: warn("fork");
1.1 cgd 162: fail();
163: }
164: if (child)
165: dooutput();
166: else
167: doshell();
168: }
169:
1.8 atatat 170: if (!rawout)
171: (void)fclose(fscript);
172: while ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
173: if (rawout)
174: record(fscript, ibuf, cc, 'i');
1.3 jtc 175: (void)write(master, ibuf, cc);
1.8 atatat 176: }
1.1 cgd 177: done();
1.5 lukem 178: /* NOTREACHED */
179: return (0);
1.1 cgd 180: }
181:
182: void
1.3 jtc 183: finish(signo)
184: int signo;
1.1 cgd 185: {
1.7 christos 186: int die, pid, status;
1.1 cgd 187:
1.3 jtc 188: die = 0;
1.7 christos 189: while ((pid = wait3(&status, WNOHANG, 0)) > 0)
1.1 cgd 190: if (pid == child)
191: die = 1;
192:
193: if (die)
194: done();
195: }
196:
1.3 jtc 197: void
1.1 cgd 198: dooutput()
199: {
1.3 jtc 200: struct itimerval value;
1.5 lukem 201: int cc;
1.3 jtc 202: time_t tvec;
203: char obuf[BUFSIZ];
1.1 cgd 204:
1.3 jtc 205: (void)close(STDIN_FILENO);
206: tvec = time(NULL);
1.8 atatat 207: if (rawout)
208: record(fscript, NULL, 0, 's');
209: else
210: (void)fprintf(fscript, "Script started on %s", ctime(&tvec));
1.3 jtc 211:
212: (void)signal(SIGALRM, scriptflush);
213: value.it_interval.tv_sec = SECSPERMIN / 2;
214: value.it_interval.tv_usec = 0;
215: value.it_value = value.it_interval;
216: (void)setitimer(ITIMER_REAL, &value, NULL);
1.1 cgd 217: for (;;) {
218: cc = read(master, obuf, sizeof (obuf));
219: if (cc <= 0)
220: break;
1.3 jtc 221: (void)write(1, obuf, cc);
1.8 atatat 222: if (rawout)
223: record(fscript, obuf, cc, 'o');
224: else
225: (void)fwrite(obuf, 1, cc, fscript);
1.3 jtc 226: outcc += cc;
1.1 cgd 227: }
228: done();
229: }
230:
1.3 jtc 231: void
232: scriptflush(signo)
233: int signo;
1.1 cgd 234: {
1.3 jtc 235: if (outcc) {
236: (void)fflush(fscript);
237: outcc = 0;
1.1 cgd 238: }
239: }
240:
1.3 jtc 241: void
242: doshell()
1.1 cgd 243: {
1.3 jtc 244: char *shell;
245:
246: shell = getenv("SHELL");
247: if (shell == NULL)
248: shell = _PATH_BSHELL;
1.1 cgd 249:
1.3 jtc 250: (void)close(master);
251: (void)fclose(fscript);
252: login_tty(slave);
1.4 mikel 253: execl(shell, shell, "-i", NULL);
1.5 lukem 254: warn("execl %s", shell);
1.3 jtc 255: fail();
1.1 cgd 256: }
257:
1.3 jtc 258: void
1.1 cgd 259: fail()
260: {
261:
1.3 jtc 262: (void)kill(0, SIGTERM);
1.1 cgd 263: done();
264: }
265:
1.3 jtc 266: void
1.1 cgd 267: done()
268: {
1.3 jtc 269: time_t tvec;
1.1 cgd 270:
271: if (subchild) {
1.3 jtc 272: tvec = time(NULL);
1.8 atatat 273: if (rawout)
274: record(fscript, NULL, 0, 'e');
275: else
276: (void)fprintf(fscript,"\nScript done on %s",
277: ctime(&tvec));
1.3 jtc 278: (void)fclose(fscript);
279: (void)close(master);
1.1 cgd 280: } else {
1.3 jtc 281: (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
282: (void)printf("Script done, output file is %s\n", fname);
1.1 cgd 283: }
1.8 atatat 284: exit(0);
285: }
286:
287: void
288: record(fscript, buf, cc, direction)
289: FILE *fscript;
290: char *buf;
291: size_t cc;
292: int direction;
293: {
294: struct iovec iov[2];
295: struct stamp stamp;
296: struct timeval tv;
297:
298: (void)gettimeofday(&tv, NULL);
299: stamp.scr_len = cc;
300: stamp.scr_sec = tv.tv_sec;
301: stamp.scr_usec = tv.tv_usec;
302: stamp.scr_direction = direction;
303: iov[0].iov_len = sizeof(stamp);
304: iov[0].iov_base = &stamp;
305: iov[1].iov_len = cc;
306: iov[1].iov_base = buf;
307: if (writev(fileno(fscript), &iov[0], 2) == -1)
308: err(1, "writev");
309: }
310:
311: #define swapstamp(stamp) do { \
312: if (stamp.scr_direction > 0xff) { \
313: stamp.scr_len = bswap64(stamp.scr_len); \
314: stamp.scr_sec = bswap64(stamp.scr_sec); \
315: stamp.scr_usec = bswap32(stamp.scr_usec); \
316: stamp.scr_direction = bswap32(stamp.scr_direction); \
317: } \
318: } while (0/*CONSTCOND*/)
319:
320: void
321: playback(fscript)
322: FILE *fscript;
323: {
324: struct timespec tsi, tso;
325: struct stamp stamp;
326: char buf[BUFSIZ];
327: size_t l;
328: time_t clock;
329:
330: do {
331: if (fread(&stamp, sizeof(stamp), 1, fscript) != 1)
332: err(1, "reading playback header");
333:
334: swapstamp(stamp);
335: l = fread(buf, 1, stamp.scr_len, fscript);
336: clock = stamp.scr_sec;
337: tso.tv_sec = stamp.scr_sec;
338: tso.tv_nsec = stamp.scr_usec * 1000;
339:
340: switch (stamp.scr_direction) {
341: case 's':
342: (void)printf("Script started on %s", ctime(&clock));
343: tsi = tso;
344: break;
345: case 'e':
346: (void)printf("\nScript done on %s", ctime(&clock));
347: break;
348: case 'i':
349: /* throw input away */
350: break;
351: case 'o':
352: tsi.tv_sec = tso.tv_sec - tsi.tv_sec;
353: tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec;
354: if (tsi.tv_nsec < 0) {
355: tsi.tv_sec -= 1;
356: tsi.tv_nsec += 1000000000;
357: }
358: if (usesleep)
359: (void)nanosleep(&tsi, NULL);
360: tsi = tso;
361: (void)write(STDOUT_FILENO, buf, l);
362: break;
363: }
364: } while (stamp.scr_direction != 'e');
365:
366: (void)fclose(fscript);
1.1 cgd 367: exit(0);
368: }
CVSweb <webmaster@jp.NetBSD.org>