script: correctly handle buffered "tail" of output. +35 bytes.

This commit is contained in:
Denis Vlasenko 2008-02-28 10:10:10 +00:00
parent 93e1a215af
commit 32fd76c1d5

View File

@ -13,67 +13,38 @@
#include "libbb.h"
struct globals {
int child_pid;
int attr_ok; /* NB: 0: ok */
struct termios tt;
const char *fname;
};
#define G (*ptr_to_globals)
#define child_pid (G.child_pid)
#define attr_ok (G.attr_ok )
#define tt (G.tt )
#define fname (G.fname )
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
fname = "typescript"; \
} while (0)
static smallint fd_count = 2;
static void done(void)
{
if (child_pid) { /* we are parent */
if (attr_ok == 0)
tcsetattr(0, TCSAFLUSH, &tt);
if (!(option_mask32 & 8)) /* not -q */
printf("Script done, file is %s\n", fname);
}
exit(0);
}
#ifdef UNUSED
static void handle_sigchld(int sig)
{
/* wait for the exited child and exit */
while (wait_any_nohang(&sig) > 0)
continue;
done();
fd_count = 0;
}
#endif
#if ENABLE_GETOPT_LONG
static const char getopt_longopts[] ALIGN1 =
"append\0" No_argument "a"
"command\0" Required_argument "c"
"flush\0" No_argument "f"
"quiet\0" No_argument "q"
;
#endif
int script_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
int script_main(int argc, char *argv[])
{
int opt, pty;
int winsz_ok;
int opt;
int mode;
struct termios rtt;
int child_pid;
int attr_ok; /* NB: 0: ok */
int winsz_ok;
int pty;
char pty_line[32];
struct termios tt, rtt;
struct winsize win;
char line[32];
const char *fname = "typescript";
const char *shell;
char shell_opt[] = "-i";
char *shell_arg = NULL;
INIT_G();
#if ENABLE_GETOPT_LONG
static const char getopt_longopts[] ALIGN1 =
"append\0" No_argument "a"
"command\0" Required_argument "c"
"flush\0" No_argument "f"
"quiet\0" No_argument "q"
;
applet_long_options = getopt_longopts;
#endif
opt_complementary = "?1"; /* max one arg */
@ -95,10 +66,10 @@ int script_main(int argc, char *argv[])
}
shell = getenv("SHELL");
if (shell == NULL) {
shell = _PATH_BSHELL;
shell = DEFAULT_SHELL;
}
pty = getpty(line, sizeof(line));
pty = getpty(pty_line, sizeof(pty_line));
if (pty < 0) {
bb_perror_msg_and_die("can't get pty");
}
@ -112,9 +83,12 @@ int script_main(int argc, char *argv[])
rtt.c_lflag &= ~ECHO;
tcsetattr(0, TCSAFLUSH, &rtt);
/* We exit as soon as child exits */
//signal(SIGCHLD, handle_sigchld);
signal(SIGCHLD, (void (*)(int)) done);
/* "script" from util-linux exits when child exits,
* we wouldn't wait for EOF from slave pty
* (output may be produced by grandchildren of child) */
signal(SIGCHLD, handle_sigchld);
/* TODO: SIGWINCH? pass window size changes down to slave? */
child_pid = vfork();
if (child_pid < 0) {
@ -123,11 +97,10 @@ int script_main(int argc, char *argv[])
if (child_pid) {
/* parent */
char buf[256];
#define buf bb_common_bufsiz1
struct pollfd pfd[2];
int outfd;
int fd_count = 2;
struct pollfd *ppfd = pfd;
int outfd, count, loop;
outfd = xopen(fname, mode);
pfd[0].fd = 0;
@ -135,14 +108,16 @@ int script_main(int argc, char *argv[])
pfd[1].fd = pty;
pfd[1].events = POLLIN;
ndelay_on(pty); /* this descriptor is not shared, can do this */
/* ndelay_on(0); - NO, stdin can be shared! */
/* ndelay_on(0); - NO, stdin can be shared! Pity :( */
/* copy stdin to pty master input,
* copy pty master output to stdout and file */
/* TODO: don't use full_write's, use proper write buffering */
while (fd_count && safe_poll(ppfd, fd_count, -1) > 0) {
while (fd_count) {
/* not safe_poll! we want SIGCHLD to EINTR poll */
poll(ppfd, fd_count, -1);
if (pfd[0].revents) {
int count = safe_read(0, buf, sizeof(buf));
count = safe_read(0, buf, sizeof(buf));
if (count <= 0) {
/* err/eof: don't read anymore */
pfd[0].revents = 0;
@ -153,7 +128,6 @@ int script_main(int argc, char *argv[])
}
}
if (pfd[1].revents) {
int count;
errno = 0;
count = safe_read(pty, buf, sizeof(buf));
if (count <= 0 && errno != EAGAIN) {
@ -170,14 +144,32 @@ int script_main(int argc, char *argv[])
}
}
}
done(); /* does not return */
/* If loop was exited because SIGCHLD handler set fd_count to 0,
* there still can be some buffered output. But not loop forever:
* we won't pump orphaned grandchildren's output indefinitely.
* Testcase: running this in script:
* exec dd if=/dev/zero bs=1M count=1
* must have "1+0 records in, 1+0 records out" captured too.
* (util-linux's script doesn't do this. buggy :) */
loop = 999;
/* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) {
full_write(1, buf, count);
full_write(outfd, buf, count);
}
if (attr_ok == 0)
tcsetattr(0, TCSAFLUSH, &tt);
if (!(opt & 8)) /* not -q */
printf("Script done, file is %s\n", fname);
return EXIT_SUCCESS;
}
/* child: make pty slave to be input, output, error; run shell */
close(pty); /* close pty master */
/* open pty slave to fd 0,1,2 */
close(0);
xopen(line, O_RDWR); /* uses fd 0 */
xopen(pty_line, O_RDWR); /* uses fd 0 */
xdup2(0, 1);
xdup2(0, 2);
/* copy our original stdin tty's parameters to pty */