10 |
* |
* |
11 |
* Licensed under GPLv2 or later, see file License in this tarball for details. |
* Licensed under GPLv2 or later, see file License in this tarball for details. |
12 |
*/ |
*/ |
|
|
|
13 |
#include "libbb.h" |
#include "libbb.h" |
14 |
|
|
|
static smallint fd_count = 2; |
|
|
|
|
|
static void handle_sigchld(int sig UNUSED_PARAM) |
|
|
{ |
|
|
fd_count = 0; |
|
|
} |
|
|
|
|
15 |
int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
16 |
int script_main(int argc UNUSED_PARAM, char **argv) |
int script_main(int argc UNUSED_PARAM, char **argv) |
17 |
{ |
{ |
28 |
const char *shell; |
const char *shell; |
29 |
char shell_opt[] = "-i"; |
char shell_opt[] = "-i"; |
30 |
char *shell_arg = NULL; |
char *shell_arg = NULL; |
31 |
|
enum { |
32 |
|
OPT_a = (1 << 0), |
33 |
|
OPT_c = (1 << 1), |
34 |
|
OPT_f = (1 << 2), |
35 |
|
OPT_q = (1 << 3), |
36 |
|
OPT_t = (1 << 4), |
37 |
|
}; |
38 |
|
|
39 |
#if ENABLE_GETOPT_LONG |
#if ENABLE_LONG_OPTS |
40 |
static const char getopt_longopts[] ALIGN1 = |
static const char getopt_longopts[] ALIGN1 = |
41 |
"append\0" No_argument "a" |
"append\0" No_argument "a" |
42 |
"command\0" Required_argument "c" |
"command\0" Required_argument "c" |
43 |
"flush\0" No_argument "f" |
"flush\0" No_argument "f" |
44 |
"quiet\0" No_argument "q" |
"quiet\0" No_argument "q" |
45 |
|
IF_SCRIPTREPLAY("timing\0" No_argument "t") |
46 |
; |
; |
47 |
|
|
48 |
applet_long_options = getopt_longopts; |
applet_long_options = getopt_longopts; |
49 |
#endif |
#endif |
50 |
|
|
51 |
opt_complementary = "?1"; /* max one arg */ |
opt_complementary = "?1"; /* max one arg */ |
52 |
opt = getopt32(argv, "ac:fq", &shell_arg); |
opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg); |
53 |
//argc -= optind; |
//argc -= optind; |
54 |
argv += optind; |
argv += optind; |
55 |
if (argv[0]) { |
if (argv[0]) { |
56 |
fname = argv[0]; |
fname = argv[0]; |
57 |
} |
} |
58 |
mode = O_CREAT|O_TRUNC|O_WRONLY; |
mode = O_CREAT|O_TRUNC|O_WRONLY; |
59 |
if (opt & 1) { |
if (opt & OPT_a) { |
60 |
mode = O_CREAT|O_APPEND|O_WRONLY; |
mode = O_CREAT|O_APPEND|O_WRONLY; |
61 |
} |
} |
62 |
if (opt & 2) { |
if (opt & OPT_c) { |
63 |
shell_opt[1] = 'c'; |
shell_opt[1] = 'c'; |
64 |
} |
} |
65 |
if (!(opt & 8)) { /* not -q */ |
if (!(opt & OPT_q)) { |
66 |
printf("Script started, file is %s\n", fname); |
printf("Script started, file is %s\n", fname); |
67 |
} |
} |
68 |
shell = getenv("SHELL"); |
shell = getenv("SHELL"); |
84 |
/* "script" from util-linux exits when child exits, |
/* "script" from util-linux exits when child exits, |
85 |
* we wouldn't wait for EOF from slave pty |
* we wouldn't wait for EOF from slave pty |
86 |
* (output may be produced by grandchildren of child) */ |
* (output may be produced by grandchildren of child) */ |
87 |
signal(SIGCHLD, handle_sigchld); |
signal(SIGCHLD, record_signo); |
88 |
|
|
89 |
/* TODO: SIGWINCH? pass window size changes down to slave? */ |
/* TODO: SIGWINCH? pass window size changes down to slave? */ |
90 |
|
|
98 |
#define buf bb_common_bufsiz1 |
#define buf bb_common_bufsiz1 |
99 |
struct pollfd pfd[2]; |
struct pollfd pfd[2]; |
100 |
int outfd, count, loop; |
int outfd, count, loop; |
101 |
|
double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0; |
102 |
|
smallint fd_count = 2; |
103 |
|
|
104 |
outfd = xopen(fname, mode); |
outfd = xopen(fname, mode); |
105 |
pfd[0].fd = pty; |
pfd[0].fd = pty; |
106 |
pfd[0].events = POLLIN; |
pfd[0].events = POLLIN; |
107 |
pfd[1].fd = 0; |
pfd[1].fd = STDIN_FILENO; |
108 |
pfd[1].events = POLLIN; |
pfd[1].events = POLLIN; |
109 |
ndelay_on(pty); /* this descriptor is not shared, can do this */ |
ndelay_on(pty); /* this descriptor is not shared, can do this */ |
110 |
/* ndelay_on(0); - NO, stdin can be shared! Pity :( */ |
/* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */ |
111 |
|
|
112 |
/* copy stdin to pty master input, |
/* copy stdin to pty master input, |
113 |
* copy pty master output to stdout and file */ |
* copy pty master output to stdout and file */ |
114 |
/* TODO: don't use full_write's, use proper write buffering */ |
/* TODO: don't use full_write's, use proper write buffering */ |
115 |
while (fd_count) { |
while (fd_count && !bb_got_signal) { |
116 |
/* not safe_poll! we want SIGCHLD to EINTR poll */ |
/* not safe_poll! we want SIGCHLD to EINTR poll */ |
117 |
if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) { |
if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) { |
118 |
/* If child exits too quickly, we may get EIO: |
/* If child exits too quickly, we may get EIO: |
127 |
goto restore; |
goto restore; |
128 |
} |
} |
129 |
if (count > 0) { |
if (count > 0) { |
130 |
|
if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) { |
131 |
|
struct timeval tv; |
132 |
|
double newtime; |
133 |
|
|
134 |
|
gettimeofday(&tv, NULL); |
135 |
|
newtime = tv.tv_sec + (double) tv.tv_usec / 1000000; |
136 |
|
fprintf(stderr, "%f %u\n", newtime - oldtime, count); |
137 |
|
oldtime = newtime; |
138 |
|
} |
139 |
full_write(STDOUT_FILENO, buf, count); |
full_write(STDOUT_FILENO, buf, count); |
140 |
full_write(outfd, buf, count); |
full_write(outfd, buf, count); |
141 |
if (opt & 4) { /* -f */ |
if (opt & OPT_f) { |
142 |
fsync(outfd); |
fsync(outfd); |
143 |
} |
} |
144 |
} |
} |
154 |
} |
} |
155 |
} |
} |
156 |
} |
} |
157 |
/* If loop was exited because SIGCHLD handler set fd_count to 0, |
/* If loop was exited because SIGCHLD handler set bb_got_signal, |
158 |
* there still can be some buffered output. But not loop forever: |
* there still can be some buffered output. But dont loop forever: |
159 |
* we won't pump orphaned grandchildren's output indefinitely. |
* we won't pump orphaned grandchildren's output indefinitely. |
160 |
* Testcase: running this in script: |
* Testcase: running this in script: |
161 |
* exec dd if=/dev/zero bs=1M count=1 |
* exec dd if=/dev/zero bs=1M count=1 |
170 |
restore: |
restore: |
171 |
if (attr_ok == 0) |
if (attr_ok == 0) |
172 |
tcsetattr(0, TCSAFLUSH, &tt); |
tcsetattr(0, TCSAFLUSH, &tt); |
173 |
if (!(opt & 8)) /* not -q */ |
if (!(opt & OPT_q)) |
174 |
printf("Script done, file is %s\n", fname); |
printf("Script done, file is %s\n", fname); |
175 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
176 |
} |
} |
193 |
|
|
194 |
/* Non-ignored signals revert to SIG_DFL on exec anyway */ |
/* Non-ignored signals revert to SIG_DFL on exec anyway */ |
195 |
/*signal(SIGCHLD, SIG_DFL);*/ |
/*signal(SIGCHLD, SIG_DFL);*/ |
196 |
execl(shell, shell, shell_opt, shell_arg, NULL); |
execl(shell, shell, shell_opt, shell_arg, (char *) NULL); |
197 |
bb_simple_perror_msg_and_die(shell); |
bb_simple_perror_msg_and_die(shell); |
198 |
} |
} |