]>
git.cameronkatri.com Git - apple_cmds.git/blob - shell_cmds/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 */
72 #include <libiosexec.h>
77 uint64_t scr_len
; /* amount of data */
78 uint64_t scr_sec
; /* time it arrived in seconds... */
79 uint32_t scr_usec
; /* ...and microseconds */
80 uint32_t scr_direction
; /* 'i', 'o', etc (also indicates endianness) */
84 static int master
, slave
;
86 static const char *fname
;
89 static int fflg
, qflg
, ttyflg
;
90 #else /* !ENABLE_FILEMON */
91 static int qflg
, ttyflg
;
92 #endif /* ENABLE_FILEMON */
93 static int usesleep
, rawout
;
95 static struct termios tt
;
97 static void done(int) __dead2
;
98 static void doshell(char **);
99 static void fail(void);
100 static void finish(void);
101 static void record(FILE *, char *, size_t, int);
102 static void consume(FILE *, off_t
, char *, int);
103 static void playback(FILE *) __dead2
;
104 static void usage(void);
107 main(int argc
, char *argv
[])
110 struct termios rtt
, stt
;
112 struct timeval tv
, *tvp
;
117 int aflg
, Fflg
, kflg
, pflg
, ch
, k
, n
;
118 int flushtime
, readstdin
;
119 #ifdef ENABLE_FILEMON
121 #endif /* ENABLE_FILEMON */
123 aflg
= Fflg
= kflg
= pflg
= 0;
127 #ifdef ENABLE_FILEMON
128 fm_fd
= -1; /* Shut up stupid "may be used uninitialized" GCC
129 warning. (not needed w/clang) */
130 #endif /* ENABLE_FILEMON */
132 #ifdef ENABLE_FILEMON
133 while ((ch
= getopt(argc
, argv
, "adFfkpqrt:")) != -1)
134 #else /* !ENABLE_FILEMON */
135 while ((ch
= getopt(argc
, argv
, "adFkpqrt:")) != -1)
136 #endif /* ENABLE_FILEMON */
147 #ifdef ENABLE_FILEMON
151 #endif /* ENABLE_FILEMON */
165 flushtime
= atoi(optarg
);
167 err(1, "invalid flush time %d", flushtime
);
181 fname
= "typescript";
183 if ((fscript
= fopen(fname
, pflg
? "r" : aflg
? "a" : "w")) == NULL
)
186 #ifdef ENABLE_FILEMON
188 asprintf(&fmfname
, "%s.filemon", fname
);
190 err(1, "%s.filemon", fname
);
191 if ((fm_fd
= open("/dev/filemon", O_RDWR
)) == -1)
192 err(1, "open(\"/dev/filemon\", O_RDWR)");
193 if ((fm_log
= open(fmfname
, O_WRONLY
| O_CREAT
| O_TRUNC
,
194 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
)) == -1)
195 err(1, "open(%s)", fmfname
);
196 if (ioctl(fm_fd
, FILEMON_SET_FD
, &fm_log
) < 0)
197 err(1, "Cannot set filemon log file descriptor");
199 /* Set up these two fd's to close on exec. */
200 (void)fcntl(fm_fd
, F_SETFD
, FD_CLOEXEC
);
201 (void)fcntl(fm_log
, F_SETFD
, FD_CLOEXEC
);
203 #endif /* ENABLE_FILEMON */
208 if ((ttyflg
= isatty(STDIN_FILENO
)) != 0) {
209 if (tcgetattr(STDIN_FILENO
, &tt
) == -1)
211 if (ioctl(STDIN_FILENO
, TIOCGWINSZ
, &win
) == -1)
213 if (openpty(&master
, &slave
, NULL
, &tt
, &win
) == -1)
216 if (openpty(&master
, &slave
, NULL
, NULL
, NULL
) == -1)
221 record(fscript
, NULL
, 0, 's');
225 (void)printf("Script started, output file is %s\n", fname
);
227 (void)fprintf(fscript
, "Script started on %s",
230 fprintf(fscript
, "command: ");
231 for (k
= 0 ; argv
[k
] ; ++k
)
232 fprintf(fscript
, "%s%s", k
? " " : "",
234 fprintf(fscript
, "\n");
238 #ifdef ENABLE_FILEMON
240 (void)printf("Filemon started, output file is %s\n",
243 #endif /* ENABLE_FILEMON */
248 rtt
.c_lflag
&= ~ECHO
;
249 (void)tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &rtt
);
261 #ifdef ENABLE_FILEMON
262 if (fflg
&& ioctl(fm_fd
, FILEMON_SET_PID
, &child
) < 0)
263 err(1, "Cannot set filemon PID");
264 #endif /* ENABLE_FILEMON */
266 start
= tvec
= time(0);
270 FD_SET(master
, &rfd
);
272 FD_SET(STDIN_FILENO
, &rfd
);
273 if (!readstdin
&& ttyflg
) {
278 } else if (flushtime
> 0) {
279 tv
.tv_sec
= flushtime
- (tvec
- start
);
285 n
= select(master
+ 1, &rfd
, 0, 0, tvp
);
286 if (n
< 0 && errno
!= EINTR
)
288 if (n
> 0 && FD_ISSET(STDIN_FILENO
, &rfd
)) {
289 cc
= read(STDIN_FILENO
, ibuf
, BUFSIZ
);
293 if (tcgetattr(master
, &stt
) == 0 &&
294 (stt
.c_lflag
& ICANON
) != 0) {
295 (void)write(master
, &stt
.c_cc
[VEOF
], 1);
301 record(fscript
, ibuf
, cc
, 'i');
302 (void)write(master
, ibuf
, cc
);
303 if (kflg
&& tcgetattr(master
, &stt
) >= 0 &&
304 ((stt
.c_lflag
& ECHO
) == 0)) {
305 (void)fwrite(ibuf
, 1, cc
, fscript
);
309 if (n
> 0 && FD_ISSET(master
, &rfd
)) {
310 cc
= read(master
, obuf
, sizeof (obuf
));
313 (void)write(STDOUT_FILENO
, obuf
, cc
);
315 record(fscript
, obuf
, cc
, 'o');
317 (void)fwrite(obuf
, 1, cc
, fscript
);
320 if (tvec
- start
>= flushtime
) {
334 (void)fprintf(stderr
,
335 #ifdef ENABLE_FILEMON
336 "usage: script [-adfkpqr] [-t time] [file [command ...]]\n");
337 #else /* !ENABLE_FILEMON */
338 "usage: script [-adkpqr] [-t time] [file [command ...]]\n");
339 #endif /* ENABLE_FILEMON */
348 if (waitpid(child
, &status
, 0) == child
) {
349 if (WIFEXITED(status
))
350 e
= WEXITSTATUS(status
);
351 else if (WIFSIGNALED(status
))
352 e
= WTERMSIG(status
);
353 else /* can't happen */
364 shell
= getenv("SHELL");
366 shell
= _PATH_BSHELL
;
369 (void)fclose(fscript
);
372 setenv("SCRIPT", fname
, 1);
377 execl(shell
, shell
, "-i", (char *)NULL
);
386 (void)kill(0, SIGTERM
);
396 (void)tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tt
);
399 record(fscript
, NULL
, 0, 'e');
402 (void)fprintf(fscript
,"\nScript done on %s",
404 (void)printf("\nScript done, output file is %s\n", fname
);
405 #ifdef ENABLE_FILEMON
407 (void)printf("Filemon done, output file is %s\n",
410 #endif /* ENABLE_FILEMON */
412 (void)fclose(fscript
);
418 record(FILE *fp
, char *buf
, size_t cc
, int direction
)
424 (void)gettimeofday(&tv
, NULL
);
426 stamp
.scr_sec
= tv
.tv_sec
;
427 stamp
.scr_usec
= tv
.tv_usec
;
428 stamp
.scr_direction
= direction
;
429 iov
[0].iov_len
= sizeof(stamp
);
430 iov
[0].iov_base
= &stamp
;
432 iov
[1].iov_base
= buf
;
433 if (writev(fileno(fp
), &iov
[0], 2) == -1)
438 consume(FILE *fp
, off_t len
, char *buf
, int reg
)
443 if (fseeko(fp
, len
, SEEK_CUR
) == -1)
448 l
= MIN(DEF_BUF
, len
);
449 if (fread(buf
, sizeof(char), l
, fp
) != l
)
450 err(1, "cannot read buffer");
457 #define bswap32 OSSwapInt32
458 #define bswap64 OSSwapInt64
459 #endif /* __APPLE__ */
460 #define swapstamp(stamp) do { \
461 if (stamp.scr_direction > 0xff) { \
462 stamp.scr_len = bswap64(stamp.scr_len); \
463 stamp.scr_sec = bswap64(stamp.scr_sec); \
464 stamp.scr_usec = bswap32(stamp.scr_usec); \
465 stamp.scr_direction = bswap32(stamp.scr_direction); \
467 } while (0/*CONSTCOND*/)
472 struct timespec tsi
, tso
;
476 off_t nread
, save_len
;
481 if (fstat(fileno(fp
), &pst
) == -1)
482 err(1, "fstat failed");
484 reg
= S_ISREG(pst
.st_mode
);
486 for (nread
= 0; !reg
|| nread
< pst
.st_size
; nread
+= save_len
) {
487 if (fread(&stamp
, sizeof(stamp
), 1, fp
) != 1) {
489 err(1, "reading playback header");
494 save_len
= sizeof(stamp
);
496 if (reg
&& stamp
.scr_len
>
497 (uint64_t)(pst
.st_size
- save_len
) - nread
)
498 errx(1, "invalid stamp");
500 save_len
+= stamp
.scr_len
;
501 tclock
= stamp
.scr_sec
;
502 tso
.tv_sec
= stamp
.scr_sec
;
503 tso
.tv_nsec
= stamp
.scr_usec
* 1000;
505 switch (stamp
.scr_direction
) {
508 (void)printf("Script started on %s",
511 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
515 (void)printf("\nScript done on %s",
517 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
520 /* throw input away */
521 (void)consume(fp
, stamp
.scr_len
, buf
, reg
);
524 tsi
.tv_sec
= tso
.tv_sec
- tsi
.tv_sec
;
525 tsi
.tv_nsec
= tso
.tv_nsec
- tsi
.tv_nsec
;
526 if (tsi
.tv_nsec
< 0) {
528 tsi
.tv_nsec
+= 1000000000;
531 (void)nanosleep(&tsi
, NULL
);
533 while (stamp
.scr_len
> 0) {
534 l
= MIN(DEF_BUF
, stamp
.scr_len
);
535 if (fread(buf
, sizeof(char), l
, fp
) != l
)
536 err(1, "cannot read buffer");
538 (void)write(STDOUT_FILENO
, buf
, l
);
543 errx(1, "invalid direction");