]>
git.saurik.com Git - apple/shell_cmds.git/blob - script/script.c
2 * Copyright (c) 2010, 2012 David E. O'Brien
3 * Copyright (c) 1980, 1992, 1993
4 * The Regents of the University of California. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/param.h>
32 __FBSDID("$FreeBSD$");
34 static const char copyright
[] =
35 "@(#) Copyright (c) 1980, 1992, 1993\n\
36 The Regents of the University of California. All rights reserved.\n";
39 static const char sccsid
[] = "@(#)script.c 8.1 (Berkeley) 6/6/93";
44 #include <sys/ioctl.h>
48 #include <libkern/OSByteOrder.h>
50 #include <sys/endian.h>
53 #include <dev/filemon/filemon.h>
54 #endif /* ENABLE_FILEMON */
75 uint64_t scr_len
; /* amount of data */
76 uint64_t scr_sec
; /* time it arrived in seconds... */
77 uint32_t scr_usec
; /* ...and microseconds */
78 uint32_t scr_direction
; /* 'i', 'o', etc (also indicates endianness) */
82 static int master
, slave
;
84 static const char *fname
;
87 static int fflg
, qflg
, ttyflg
;
88 #else /* !ENABLE_FILEMON */
89 static int qflg
, ttyflg
;
90 #endif /* ENABLE_FILEMON */
91 static int usesleep
, rawout
;
93 static struct termios tt
;
95 static void done(int) __dead2
;
96 static void doshell(char **);
97 static void fail(void);
98 static void finish(void);
99 static void record(FILE *, char *, size_t, int);
100 static void consume(FILE *, off_t
, char *, int);
101 static void playback(FILE *) __dead2
;
102 static void usage(void);
105 main(int argc
, char *argv
[])
108 struct termios rtt
, stt
;
110 struct timeval tv
, *tvp
;
115 int aflg
, Fflg
, kflg
, pflg
, ch
, k
, n
;
116 int flushtime
, readstdin
;
117 #ifdef ENABLE_FILEMON
119 #endif /* ENABLE_FILEMON */
121 aflg
= Fflg
= kflg
= pflg
= 0;
125 #ifdef ENABLE_FILEMON
126 fm_fd
= -1; /* Shut up stupid "may be used uninitialized" GCC
127 warning. (not needed w/clang) */
128 #endif /* ENABLE_FILEMON */
130 #ifdef ENABLE_FILEMON
131 while ((ch
= getopt(argc
, argv
, "adFfkpqrt:")) != -1)
132 #else /* !ENABLE_FILEMON */
133 while ((ch
= getopt(argc
, argv
, "adFkpqrt:")) != -1)
134 #endif /* ENABLE_FILEMON */
145 #ifdef ENABLE_FILEMON
149 #endif /* ENABLE_FILEMON */
163 flushtime
= atoi(optarg
);
165 err(1, "invalid flush time %d", flushtime
);
179 fname
= "typescript";
181 if ((fscript
= fopen(fname
, pflg
? "r" : aflg
? "a" : "w")) == NULL
)
184 #ifdef ENABLE_FILEMON
186 asprintf(&fmfname
, "%s.filemon", fname
);
188 err(1, "%s.filemon", fname
);
189 if ((fm_fd
= open("/dev/filemon", O_RDWR
)) == -1)
190 err(1, "open(\"/dev/filemon\", O_RDWR)");
191 if ((fm_log
= open(fmfname
, O_WRONLY
| O_CREAT
| O_TRUNC
,
192 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
)) == -1)
193 err(1, "open(%s)", fmfname
);
194 if (ioctl(fm_fd
, FILEMON_SET_FD
, &fm_log
) < 0)
195 err(1, "Cannot set filemon log file descriptor");
197 /* Set up these two fd's to close on exec. */
198 (void)fcntl(fm_fd
, F_SETFD
, FD_CLOEXEC
);
199 (void)fcntl(fm_log
, F_SETFD
, FD_CLOEXEC
);
201 #endif /* ENABLE_FILEMON */
206 if ((ttyflg
= isatty(STDIN_FILENO
)) != 0) {
207 if (tcgetattr(STDIN_FILENO
, &tt
) == -1)
209 if (ioctl(STDIN_FILENO
, TIOCGWINSZ
, &win
) == -1)
211 if (openpty(&master
, &slave
, NULL
, &tt
, &win
) == -1)
214 if (openpty(&master
, &slave
, NULL
, NULL
, NULL
) == -1)
219 record(fscript
, NULL
, 0, 's');
223 (void)printf("Script started, output file is %s\n", fname
);
225 (void)fprintf(fscript
, "Script started on %s",
228 fprintf(fscript
, "command: ");
229 for (k
= 0 ; argv
[k
] ; ++k
)
230 fprintf(fscript
, "%s%s", k
? " " : "",
232 fprintf(fscript
, "\n");
236 #ifdef ENABLE_FILEMON
238 (void)printf("Filemon started, output file is %s\n",
241 #endif /* ENABLE_FILEMON */
246 rtt
.c_lflag
&= ~ECHO
;
247 (void)tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &rtt
);
259 #ifdef ENABLE_FILEMON
260 if (fflg
&& ioctl(fm_fd
, FILEMON_SET_PID
, &child
) < 0)
261 err(1, "Cannot set filemon PID");
262 #endif /* ENABLE_FILEMON */
264 start
= tvec
= time(0);
268 FD_SET(master
, &rfd
);
270 FD_SET(STDIN_FILENO
, &rfd
);
271 if (!readstdin
&& ttyflg
) {
276 } else if (flushtime
> 0) {
277 tv
.tv_sec
= flushtime
- (tvec
- start
);
283 n
= select(master
+ 1, &rfd
, 0, 0, tvp
);
284 if (n
< 0 && errno
!= EINTR
)
286 if (n
> 0 && FD_ISSET(STDIN_FILENO
, &rfd
)) {
287 cc
= read(STDIN_FILENO
, ibuf
, BUFSIZ
);
291 if (tcgetattr(master
, &stt
) == 0 &&
292 (stt
.c_lflag
& ICANON
) != 0) {
293 (void)write(master
, &stt
.c_cc
[VEOF
], 1);
299 record(fscript
, ibuf
, cc
, 'i');
300 (void)write(master
, ibuf
, cc
);
301 if (kflg
&& tcgetattr(master
, &stt
) >= 0 &&
302 ((stt
.c_lflag
& ECHO
) == 0)) {
303 (void)fwrite(ibuf
, 1, cc
, fscript
);
307 if (n
> 0 && FD_ISSET(master
, &rfd
)) {
308 cc
= read(master
, obuf
, sizeof (obuf
));
311 (void)write(STDOUT_FILENO
, obuf
, cc
);
313 record(fscript
, obuf
, cc
, 'o');
315 (void)fwrite(obuf
, 1, cc
, fscript
);
318 if (tvec
- start
>= flushtime
) {
332 (void)fprintf(stderr
,
333 #ifdef ENABLE_FILEMON
334 "usage: script [-adfkpqr] [-t time] [file [command ...]]\n");
335 #else /* !ENABLE_FILEMON */
336 "usage: script [-adkpqr] [-t time] [file [command ...]]\n");
337 #endif /* ENABLE_FILEMON */
346 if (waitpid(child
, &status
, 0) == child
) {
347 if (WIFEXITED(status
))
348 e
= WEXITSTATUS(status
);
349 else if (WIFSIGNALED(status
))
350 e
= WTERMSIG(status
);
351 else /* can't happen */
362 shell
= getenv("SHELL");
364 shell
= _PATH_BSHELL
;
367 (void)fclose(fscript
);
370 setenv("SCRIPT", fname
, 1);
375 execl(shell
, shell
, "-i", (char *)NULL
);
384 (void)kill(0, SIGTERM
);
394 (void)tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tt
);
397 record(fscript
, NULL
, 0, 'e');
400 (void)fprintf(fscript
,"\nScript done on %s",
402 (void)printf("\nScript done, output file is %s\n", fname
);
403 #ifdef ENABLE_FILEMON
405 (void)printf("Filemon done, output file is %s\n",
408 #endif /* ENABLE_FILEMON */
410 (void)fclose(fscript
);
416 record(FILE *fp
, char *buf
, size_t cc
, int direction
)
422 (void)gettimeofday(&tv
, NULL
);
424 stamp
.scr_sec
= tv
.tv_sec
;
425 stamp
.scr_usec
= tv
.tv_usec
;
426 stamp
.scr_direction
= direction
;
427 iov
[0].iov_len
= sizeof(stamp
);
428 iov
[0].iov_base
= &stamp
;
430 iov
[1].iov_base
= buf
;
431 if (writev(fileno(fp
), &iov
[0], 2) == -1)
436 consume(FILE *fp
, off_t len
, char *buf
, int reg
)
441 if (fseeko(fp
, len
, SEEK_CUR
) == -1)
446 l
= MIN(DEF_BUF
, len
);
447 if (fread(buf
, sizeof(char), l
, fp
) != l
)
448 err(1, "cannot read buffer");
455 #define bswap32 OSSwapInt32
456 #define bswap64 OSSwapInt64
457 #endif /* __APPLE__ */
458 #define swapstamp(stamp) do { \
459 if (stamp.scr_direction > 0xff) { \
460 stamp.scr_len = bswap64(stamp.scr_len); \
461 stamp.scr_sec = bswap64(stamp.scr_sec); \
462 stamp.scr_usec = bswap32(stamp.scr_usec); \
463 stamp.scr_direction = bswap32(stamp.scr_direction); \
465 } while (0/*CONSTCOND*/)
470 struct timespec tsi
, tso
;
474 off_t nread
, save_len
;
479 if (fstat(fileno(fp
), &pst
) == -1)
480 err(1, "fstat failed");
482 reg
= S_ISREG(pst
.st_mode
);
484 for (nread
= 0; !reg
|| nread
< pst
.st_size
; nread
+= save_len
) {
485 if (fread(&stamp
, sizeof(stamp
), 1, fp
) != 1) {
487 err(1, "reading playback header");
492 save_len
= sizeof(stamp
);
494 if (reg
&& stamp
.scr_len
>
495 (uint64_t)(pst
.st_size
- save_len
) - nread
)
496 errx(1, "invalid stamp");
498 save_len
+= stamp
.scr_len
;
499 tclock
= stamp
.scr_sec
;
500 tso
.tv_sec
= stamp
.scr_sec
;
501 tso
.tv_nsec
= stamp
.scr_usec
* 1000;
503 switch (stamp
.scr_direction
) {
506 (void)printf("Script started on %s",
509 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
513 (void)printf("\nScript done on %s",
515 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
518 /* throw input away */
519 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
522 tsi
.tv_sec
= tso
.tv_sec
- tsi
.tv_sec
;
523 tsi
.tv_nsec
= tso
.tv_nsec
- tsi
.tv_nsec
;
524 if (tsi
.tv_nsec
< 0) {
526 tsi
.tv_nsec
+= 1000000000;
529 (void)nanosleep(&tsi
, NULL
);
531 while (stamp
.scr_len
> 0) {
532 l
= MIN(DEF_BUF
, stamp
.scr_len
);
533 if (fread(buf
, sizeof(char), l
, fp
) != l
)
534 err(1, "cannot read buffer");
536 (void)write(STDOUT_FILENO
, buf
, l
);
541 errx(1, "invalid direction");