diff options
-rw-r--r-- | include/applets.h | 1 | ||||
-rw-r--r-- | include/usage.h | 10 | ||||
-rw-r--r-- | util-linux/Config.in | 7 | ||||
-rw-r--r-- | util-linux/Kbuild | 1 | ||||
-rw-r--r-- | util-linux/script.c | 59 | ||||
-rw-r--r-- | util-linux/scriptreplay.c | 38 |
6 files changed, 95 insertions, 21 deletions
diff --git a/include/applets.h b/include/applets.h index 359440def..7838757f5 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -326,6 +326,7 @@ IF_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | |||
326 | IF_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 326 | IF_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
327 | IF_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 327 | IF_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
328 | IF_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 328 | IF_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
329 | IF_SCRIPTREPLAY(APPLET(scriptreplay, _BB_DIR_BIN, _BB_SUID_NEVER)) | ||
329 | IF_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) | 330 | IF_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) |
330 | IF_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | 331 | IF_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) |
331 | IF_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | 332 | IF_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) |
diff --git a/include/usage.h b/include/usage.h index 123462a02..1e327fb97 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -3512,6 +3512,11 @@ | |||
3512 | "\n -g Process group id(s)" \ | 3512 | "\n -g Process group id(s)" \ |
3513 | "\n -u Process user name(s) and/or id(s)" \ | 3513 | "\n -u Process user name(s) and/or id(s)" \ |
3514 | 3514 | ||
3515 | #define scriptreplay_trivial_usage \ | ||
3516 | "timingfile [typescript [divisor]]" | ||
3517 | #define scriptreplay_full_usage "\n\n" \ | ||
3518 | "Play back typescripts, using timing information" | ||
3519 | |||
3515 | #define reset_trivial_usage \ | 3520 | #define reset_trivial_usage \ |
3516 | "" | 3521 | "" |
3517 | #define reset_full_usage "\n\n" \ | 3522 | #define reset_full_usage "\n\n" \ |
@@ -3706,13 +3711,16 @@ | |||
3706 | "$ rx /tmp/foo\n" | 3711 | "$ rx /tmp/foo\n" |
3707 | 3712 | ||
3708 | #define script_trivial_usage \ | 3713 | #define script_trivial_usage \ |
3709 | "[-afq] [-c COMMAND] [OUTFILE]" | 3714 | "[-afq" IF_SCRIPTREPLAY("t") "] [-c COMMAND] [OUTFILE]" |
3710 | #define script_full_usage "\n\n" \ | 3715 | #define script_full_usage "\n\n" \ |
3711 | "Options:" \ | 3716 | "Options:" \ |
3712 | "\n -a Append output" \ | 3717 | "\n -a Append output" \ |
3713 | "\n -c Run COMMAND, not shell" \ | 3718 | "\n -c Run COMMAND, not shell" \ |
3714 | "\n -f Flush output after each write" \ | 3719 | "\n -f Flush output after each write" \ |
3715 | "\n -q Quiet" \ | 3720 | "\n -q Quiet" \ |
3721 | IF_SCRIPTREPLAY( \ | ||
3722 | "\n -t Send timing to stderr" \ | ||
3723 | ) | ||
3716 | 3724 | ||
3717 | #define sed_trivial_usage \ | 3725 | #define sed_trivial_usage \ |
3718 | "[-efinr] pattern [files...]" | 3726 | "[-efinr] pattern [files...]" |
diff --git a/util-linux/Config.in b/util-linux/Config.in index e5c053fcf..024550172 100644 --- a/util-linux/Config.in +++ b/util-linux/Config.in | |||
@@ -764,6 +764,13 @@ config SCRIPT | |||
764 | help | 764 | help |
765 | The script makes typescript of terminal session. | 765 | The script makes typescript of terminal session. |
766 | 766 | ||
767 | config SCRIPTREPLAY | ||
768 | bool "scriptreplay" | ||
769 | default n | ||
770 | help | ||
771 | This program replays a typescript, using timing information | ||
772 | given by script -t. | ||
773 | |||
767 | config SETARCH | 774 | config SETARCH |
768 | bool "setarch" | 775 | bool "setarch" |
769 | default n | 776 | default n |
diff --git a/util-linux/Kbuild b/util-linux/Kbuild index ac071c620..eaad3319d 100644 --- a/util-linux/Kbuild +++ b/util-linux/Kbuild | |||
@@ -33,6 +33,7 @@ lib-$(CONFIG_RDEV) += rdev.o | |||
33 | lib-$(CONFIG_READPROFILE) += readprofile.o | 33 | lib-$(CONFIG_READPROFILE) += readprofile.o |
34 | lib-$(CONFIG_RTCWAKE) += rtcwake.o | 34 | lib-$(CONFIG_RTCWAKE) += rtcwake.o |
35 | lib-$(CONFIG_SCRIPT) += script.o | 35 | lib-$(CONFIG_SCRIPT) += script.o |
36 | lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o | ||
36 | lib-$(CONFIG_SETARCH) += setarch.o | 37 | lib-$(CONFIG_SETARCH) += setarch.o |
37 | lib-$(CONFIG_SWAPONOFF) += swaponoff.o | 38 | lib-$(CONFIG_SWAPONOFF) += swaponoff.o |
38 | lib-$(CONFIG_SWITCH_ROOT) += switch_root.o | 39 | lib-$(CONFIG_SWITCH_ROOT) += switch_root.o |
diff --git a/util-linux/script.c b/util-linux/script.c index a9f24b10e..d16a2914a 100644 --- a/util-linux/script.c +++ b/util-linux/script.c | |||
@@ -10,16 +10,8 @@ | |||
10 | * | 10 | * |
11 | * Licensed under GPLv2 or later, see file License in this tarball for details. | 11 | * Licensed under GPLv2 or later, see file License in this tarball for details. |
12 | */ | 12 | */ |
13 | |||
14 | #include "libbb.h" | 13 | #include "libbb.h" |
15 | 14 | ||
16 | static smallint fd_count = 2; | ||
17 | |||
18 | static void handle_sigchld(int sig UNUSED_PARAM) | ||
19 | { | ||
20 | fd_count = 0; | ||
21 | } | ||
22 | |||
23 | int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 15 | int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
24 | int script_main(int argc UNUSED_PARAM, char **argv) | 16 | int script_main(int argc UNUSED_PARAM, char **argv) |
25 | { | 17 | { |
@@ -36,6 +28,15 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
36 | const char *shell; | 28 | const char *shell; |
37 | char shell_opt[] = "-i"; | 29 | char shell_opt[] = "-i"; |
38 | char *shell_arg = NULL; | 30 | 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 | #if ENABLE_SCRIPTREPLAY | ||
37 | OPT_t = (1 << 4), | ||
38 | #endif | ||
39 | }; | ||
39 | 40 | ||
40 | #if ENABLE_GETOPT_LONG | 41 | #if ENABLE_GETOPT_LONG |
41 | static const char getopt_longopts[] ALIGN1 = | 42 | static const char getopt_longopts[] ALIGN1 = |
@@ -43,25 +44,28 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
43 | "command\0" Required_argument "c" | 44 | "command\0" Required_argument "c" |
44 | "flush\0" No_argument "f" | 45 | "flush\0" No_argument "f" |
45 | "quiet\0" No_argument "q" | 46 | "quiet\0" No_argument "q" |
47 | # if ENABLE_SCRIPTREPLAY | ||
48 | "timing\0" No_argument "t" | ||
49 | # endif | ||
46 | ; | 50 | ; |
47 | 51 | ||
48 | applet_long_options = getopt_longopts; | 52 | applet_long_options = getopt_longopts; |
49 | #endif | 53 | #endif |
50 | opt_complementary = "?1"; /* max one arg */ | 54 | opt_complementary = "?1"; /* max one arg */ |
51 | opt = getopt32(argv, "ac:fq", &shell_arg); | 55 | opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg); |
52 | //argc -= optind; | 56 | //argc -= optind; |
53 | argv += optind; | 57 | argv += optind; |
54 | if (argv[0]) { | 58 | if (argv[0]) { |
55 | fname = argv[0]; | 59 | fname = argv[0]; |
56 | } | 60 | } |
57 | mode = O_CREAT|O_TRUNC|O_WRONLY; | 61 | mode = O_CREAT|O_TRUNC|O_WRONLY; |
58 | if (opt & 1) { | 62 | if (opt & OPT_a) { |
59 | mode = O_CREAT|O_APPEND|O_WRONLY; | 63 | mode = O_CREAT|O_APPEND|O_WRONLY; |
60 | } | 64 | } |
61 | if (opt & 2) { | 65 | if (opt & OPT_c) { |
62 | shell_opt[1] = 'c'; | 66 | shell_opt[1] = 'c'; |
63 | } | 67 | } |
64 | if (!(opt & 8)) { /* not -q */ | 68 | if (!(opt & OPT_q)) { |
65 | printf("Script started, file is %s\n", fname); | 69 | printf("Script started, file is %s\n", fname); |
66 | } | 70 | } |
67 | shell = getenv("SHELL"); | 71 | shell = getenv("SHELL"); |
@@ -83,7 +87,7 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
83 | /* "script" from util-linux exits when child exits, | 87 | /* "script" from util-linux exits when child exits, |
84 | * we wouldn't wait for EOF from slave pty | 88 | * we wouldn't wait for EOF from slave pty |
85 | * (output may be produced by grandchildren of child) */ | 89 | * (output may be produced by grandchildren of child) */ |
86 | signal(SIGCHLD, handle_sigchld); | 90 | signal(SIGCHLD, record_signo); |
87 | 91 | ||
88 | /* TODO: SIGWINCH? pass window size changes down to slave? */ | 92 | /* TODO: SIGWINCH? pass window size changes down to slave? */ |
89 | 93 | ||
@@ -97,19 +101,23 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
97 | #define buf bb_common_bufsiz1 | 101 | #define buf bb_common_bufsiz1 |
98 | struct pollfd pfd[2]; | 102 | struct pollfd pfd[2]; |
99 | int outfd, count, loop; | 103 | int outfd, count, loop; |
104 | #if ENABLE_SCRIPTREPLAY | ||
105 | double oldtime = time(NULL); | ||
106 | #endif | ||
107 | smallint fd_count = 2; | ||
100 | 108 | ||
101 | outfd = xopen(fname, mode); | 109 | outfd = xopen(fname, mode); |
102 | pfd[0].fd = pty; | 110 | pfd[0].fd = pty; |
103 | pfd[0].events = POLLIN; | 111 | pfd[0].events = POLLIN; |
104 | pfd[1].fd = 0; | 112 | pfd[1].fd = STDIN_FILENO; |
105 | pfd[1].events = POLLIN; | 113 | pfd[1].events = POLLIN; |
106 | ndelay_on(pty); /* this descriptor is not shared, can do this */ | 114 | ndelay_on(pty); /* this descriptor is not shared, can do this */ |
107 | /* ndelay_on(0); - NO, stdin can be shared! Pity :( */ | 115 | /* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */ |
108 | 116 | ||
109 | /* copy stdin to pty master input, | 117 | /* copy stdin to pty master input, |
110 | * copy pty master output to stdout and file */ | 118 | * copy pty master output to stdout and file */ |
111 | /* TODO: don't use full_write's, use proper write buffering */ | 119 | /* TODO: don't use full_write's, use proper write buffering */ |
112 | while (fd_count) { | 120 | while (fd_count && !bb_got_signal) { |
113 | /* not safe_poll! we want SIGCHLD to EINTR poll */ | 121 | /* not safe_poll! we want SIGCHLD to EINTR poll */ |
114 | if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) { | 122 | if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) { |
115 | /* If child exits too quickly, we may get EIO: | 123 | /* If child exits too quickly, we may get EIO: |
@@ -124,9 +132,20 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
124 | goto restore; | 132 | goto restore; |
125 | } | 133 | } |
126 | if (count > 0) { | 134 | if (count > 0) { |
135 | #if ENABLE_SCRIPTREPLAY | ||
136 | if (opt & OPT_t) { | ||
137 | struct timeval tv; | ||
138 | double newtime; | ||
139 | |||
140 | gettimeofday(&tv, NULL); | ||
141 | newtime = tv.tv_sec + (double) tv.tv_usec / 1000000; | ||
142 | fprintf(stderr, "%f %u\n", newtime - oldtime, count); | ||
143 | oldtime = newtime; | ||
144 | } | ||
145 | #endif | ||
127 | full_write(STDOUT_FILENO, buf, count); | 146 | full_write(STDOUT_FILENO, buf, count); |
128 | full_write(outfd, buf, count); | 147 | full_write(outfd, buf, count); |
129 | if (opt & 4) { /* -f */ | 148 | if (opt & OPT_f) { |
130 | fsync(outfd); | 149 | fsync(outfd); |
131 | } | 150 | } |
132 | } | 151 | } |
@@ -142,8 +161,8 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
142 | } | 161 | } |
143 | } | 162 | } |
144 | } | 163 | } |
145 | /* If loop was exited because SIGCHLD handler set fd_count to 0, | 164 | /* If loop was exited because SIGCHLD handler set bb_got_signal, |
146 | * there still can be some buffered output. But not loop forever: | 165 | * there still can be some buffered output. But dont loop forever: |
147 | * we won't pump orphaned grandchildren's output indefinitely. | 166 | * we won't pump orphaned grandchildren's output indefinitely. |
148 | * Testcase: running this in script: | 167 | * Testcase: running this in script: |
149 | * exec dd if=/dev/zero bs=1M count=1 | 168 | * exec dd if=/dev/zero bs=1M count=1 |
@@ -158,7 +177,7 @@ int script_main(int argc UNUSED_PARAM, char **argv) | |||
158 | restore: | 177 | restore: |
159 | if (attr_ok == 0) | 178 | if (attr_ok == 0) |
160 | tcsetattr(0, TCSAFLUSH, &tt); | 179 | tcsetattr(0, TCSAFLUSH, &tt); |
161 | if (!(opt & 8)) /* not -q */ | 180 | if (!(opt & OPT_q)) |
162 | printf("Script done, file is %s\n", fname); | 181 | printf("Script done, file is %s\n", fname); |
163 | return EXIT_SUCCESS; | 182 | return EXIT_SUCCESS; |
164 | } | 183 | } |
diff --git a/util-linux/scriptreplay.c b/util-linux/scriptreplay.c new file mode 100644 index 000000000..038dbdfe1 --- /dev/null +++ b/util-linux/scriptreplay.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * scriptreplay - play back typescripts, using timing information | ||
4 | * | ||
5 | * pascal.bellard@ads-lu.com | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file License in this tarball for details. | ||
8 | * | ||
9 | */ | ||
10 | #include "libbb.h" | ||
11 | |||
12 | int scriptreplay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
13 | int scriptreplay_main(int argc UNUSED_PARAM, char **argv) | ||
14 | { | ||
15 | const char *script = "typescript"; | ||
16 | double delay, factor = 1000000.0; | ||
17 | int fd; | ||
18 | unsigned long count; | ||
19 | FILE *tfp; | ||
20 | |||
21 | if (argv[2]) { | ||
22 | script = argv[2]; | ||
23 | if (argv[3]) | ||
24 | factor /= atof(argv[3]); | ||
25 | } | ||
26 | |||
27 | tfp = xfopen_for_read(argv[1]); | ||
28 | fd = xopen(script, O_RDONLY); | ||
29 | while (fscanf(tfp, "%lf %lu\n", &delay, &count) == 2) { | ||
30 | usleep(delay * factor); | ||
31 | bb_copyfd_exact_size(fd, STDOUT_FILENO, count); | ||
32 | } | ||
33 | #if ENABLE_FEATURE_CLEAN_UP | ||
34 | close(fd); | ||
35 | fclose(tfp); | ||
36 | #endif | ||
37 | return EXIT_SUCCESS; | ||
38 | } | ||