diff --git a/usr.bin/script/script.1 b/usr.bin/script/script.1 --- a/usr.bin/script/script.1 +++ b/usr.bin/script/script.1 @@ -33,7 +33,7 @@ .Nd make typescript of terminal session .Sh SYNOPSIS .Nm -.Op Fl aeFfkqr +.Op Fl aeFfkqrw .Op Fl t Ar time .Op Ar file Op Ar command ... .Nm @@ -131,6 +131,9 @@ which is useful for both tools and humans to read, should be used. Note that time-stamps will only be output when different from the previous one. +.It Fl w +Forward terminal size changes on +.Dv SIGWINCH . .El .Pp The script ends when the forked shell (or command) exits (a diff --git a/usr.bin/script/script.c b/usr.bin/script/script.c --- a/usr.bin/script/script.c +++ b/usr.bin/script/script.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ static int fflg, qflg, ttyflg; static int usesleep, rawout, showexit; static TAILQ_HEAD(, buf_elm) obuf_list = TAILQ_HEAD_INITIALIZER(obuf_list); +static volatile sig_atomic_t doresize; static struct termios tt; @@ -93,40 +95,54 @@ static void consume(FILE *, off_t, char *, int); static void playback(FILE *) __dead2; static void usage(void) __dead2; +static void resizeit(int); int main(int argc, char *argv[]) { struct termios rtt, stt; struct winsize win; - struct timeval tv, *tvp; + struct timespec tv, *tvp; time_t tvec, start; char obuf[BUFSIZ]; char ibuf[BUFSIZ]; + sigset_t *pselmask, selmask; fd_set rfd, wfd; struct buf_elm *be; ssize_t cc; - int aflg, Fflg, kflg, pflg, ch, k, n, fcm; + int aflg, Fflg, kflg, pflg, wflg, ch, k, n, fcm; int flushtime, readstdin; int fm_fd, fm_log; - aflg = Fflg = kflg = pflg = 0; + aflg = Fflg = kflg = pflg = wflg = 0; + doresize = 0; usesleep = 1; rawout = 0; flushtime = 30; - fm_fd = -1; /* Shut up stupid "may be used uninitialized" GCC - warning. (not needed w/clang) */ + fm_fd = -1; showexit = 0; - while ((ch = getopt(argc, argv, "adeFfkpqrT:t:")) != -1) - switch(ch) { + /* + * For normal operation, we'll leave pselmask == NULL so that pselect(2) + * leaves the signal mask alone. If -w is specified, we'll restore the + * process signal mask upon entry with SIGWINCH unblocked so that we can + * forward resize events properly. + */ + sigemptyset(&selmask); + pselmask = NULL; + + while ((ch = getopt(argc, argv, "adeFfkpqrT:t:w")) != -1) + switch (ch) { case 'a': aflg = 1; break; case 'd': usesleep = 0; break; - case 'e': /* Default behavior, accepted for linux compat */ + /* + * Default behavior, accepted for linux compat + */ + case 'e': break; case 'F': Fflg = 1; @@ -156,6 +172,9 @@ if (strchr(optarg, '%')) tstamp_fmt = optarg; break; + case 'w': + wflg = 1; + break; case '?': default: usage(); @@ -239,6 +258,11 @@ (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); } + if (fflg) + assert(fm_fd >= 0); + else + assert(fm_fd == -1); + child = fork(); if (child < 0) { warn("fork"); @@ -257,6 +281,23 @@ } close(slave); + if (wflg) { + struct sigaction sa = { .sa_handler = resizeit }; + sigset_t smask; + + sigaction(SIGWINCH, &sa, NULL); + + sigemptyset(&smask); + sigaddset(&smask, SIGWINCH); + + if (sigprocmask(SIG_BLOCK, &smask, &selmask) != 0) + err(1, "Failed to block SIGWINCH"); + + /* Just in case SIGWINCH was blocked before we came in. */ + sigdelset(&selmask, SIGWINCH); + pselmask = &selmask; + } + start = tvec = time(0); readstdin = 1; for (;;) { @@ -269,19 +310,27 @@ FD_SET(master, &wfd); if (!readstdin && ttyflg) { tv.tv_sec = 1; - tv.tv_usec = 0; + tv.tv_nsec = 0; tvp = &tv; readstdin = 1; } else if (flushtime > 0) { tv.tv_sec = flushtime - (tvec - start); - tv.tv_usec = 0; + tv.tv_nsec = 0; tvp = &tv; } else { tvp = NULL; } - n = select(master + 1, &rfd, &wfd, NULL, tvp); + n = pselect(master + 1, &rfd, &wfd, NULL, tvp, pselmask); if (n < 0 && errno != EINTR) break; + + if (doresize) { + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) != -1) { + ioctl(master, TIOCSWINSZ, &win); + } + doresize = 0; + } + if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) { cc = read(STDIN_FILENO, ibuf, BUFSIZ); if (cc < 0) @@ -331,7 +380,7 @@ } } if (n > 0 && FD_ISSET(master, &rfd)) { - cc = read(master, obuf, sizeof (obuf)); + cc = read(master, obuf, sizeof(obuf)); if (cc <= 0) break; (void)write(STDOUT_FILENO, obuf, cc); @@ -356,7 +405,7 @@ usage(void) { (void)fprintf(stderr, - "usage: script [-aeFfkpqr] [-t time] [file [command ...]]\n"); + "usage: script [-aeFfkpqrw] [-t time] [file [command ...]]\n"); (void)fprintf(stderr, " script -p [-deq] [-T fmt] [file]\n"); exit(1); @@ -417,7 +466,7 @@ if (showexit) (void)fprintf(fscript, "\nCommand exit status:" " %d", eno); - (void)fprintf(fscript,"\nScript done on %s", + (void)fprintf(fscript, "\nScript done on %s", ctime(&tvec)); } (void)printf("\nScript done, output file is %s\n", fname); @@ -459,8 +508,7 @@ if (reg) { if (fseeko(fp, len, SEEK_CUR) == -1) err(1, NULL); - } - else { + } else { while (len > 0) { l = MIN(DEF_BUF, len); if (fread(buf, sizeof(char), l, fp) != l) @@ -603,3 +651,9 @@ (void)fclose(fp); exit(0); } + +static void +resizeit(int signo __unused) +{ + doresize = 1; +}