script: handle terminal resize on SIGWINCH

Add a -w flag to forward terminal resize events on to the child, which
can be useful in some circumstances to avoid terminal corruption.

Reviewed by:	des
Co-authored-by:	Xavier Beaudouin <xavier.beaudouin@klarasystems.com>
Sponsored by:	Modirum MDPay
Sponsored by:	Klara, Inc.
Differential Revision:	https://reviews.freebsd.org/D44167
This commit is contained in:
Kyle Evans 2024-04-26 11:12:00 -05:00
parent 4459abe334
commit 8ceac8e13d
2 changed files with 58 additions and 9 deletions

View File

@ -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 @@ characters, it indicates the default format:
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

View File

@ -77,6 +77,7 @@ static char *fmfname;
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;
@ -94,31 +95,43 @@ static void record(FILE *, char *, size_t, int);
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;
showexit = 0;
while ((ch = getopt(argc, argv, "adeFfkpqrT:t:")) != -1)
/*
* 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;
@ -157,6 +170,9 @@ main(int argc, char *argv[])
if (strchr(optarg, '%'))
tstamp_fmt = optarg;
break;
case 'w':
wflg = 1;
break;
case '?':
default:
usage();
@ -260,6 +276,23 @@ main(int argc, char *argv[])
}
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 (;;) {
@ -272,19 +305,26 @@ main(int argc, char *argv[])
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)
@ -359,7 +399,7 @@ static void
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);
@ -605,3 +645,9 @@ playback(FILE *fp)
(void)fclose(fp);
exit(0);
}
static void
resizeit(int signo __unused)
{
doresize = 1;
}