source: MondoRescue/branches/3.3/mindi-busybox/util-linux/script.c@ 3625

Last change on this file since 3625 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 5.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * script implementation for busybox
4 *
5 * pascal.bellard@ads-lu.com
6 *
7 * Based on code from util-linux v 2.12r
8 * Copyright (c) 1980
9 * The Regents of the University of California. All rights reserved.
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 */
13
14//usage:#define script_trivial_usage
15//usage: "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]"
16//usage:#define script_full_usage "\n\n"
17//usage: " -a Append output"
18//usage: "\n -c PROG Run PROG, not shell"
19//usage: "\n -f Flush output after each write"
20//usage: "\n -q Quiet"
21//usage: IF_SCRIPTREPLAY(
22//usage: "\n -t Send timing to stderr"
23//usage: )
24
25#include "libbb.h"
26#include "common_bufsiz.h"
27
28int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
29int script_main(int argc UNUSED_PARAM, char **argv)
30{
31 int opt;
32 int mode;
33 int child_pid;
34 int attr_ok; /* NB: 0: ok */
35 int winsz_ok;
36 int pty;
37 char pty_line[GETPTY_BUFSIZE];
38 struct termios tt, rtt;
39 struct winsize win;
40 const char *fname = "typescript";
41 const char *shell;
42 char shell_opt[] = "-i";
43 char *shell_arg = NULL;
44 enum {
45 OPT_a = (1 << 0),
46 OPT_c = (1 << 1),
47 OPT_f = (1 << 2),
48 OPT_q = (1 << 3),
49 OPT_t = (1 << 4),
50 };
51
52#if ENABLE_LONG_OPTS
53 static const char getopt_longopts[] ALIGN1 =
54 "append\0" No_argument "a"
55 "command\0" Required_argument "c"
56 "flush\0" No_argument "f"
57 "quiet\0" No_argument "q"
58 IF_SCRIPTREPLAY("timing\0" No_argument "t")
59 ;
60
61 applet_long_options = getopt_longopts;
62#endif
63
64 opt_complementary = "?1"; /* max one arg */
65 opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg);
66 //argc -= optind;
67 argv += optind;
68 if (argv[0]) {
69 fname = argv[0];
70 }
71 mode = O_CREAT|O_TRUNC|O_WRONLY;
72 if (opt & OPT_a) {
73 mode = O_CREAT|O_APPEND|O_WRONLY;
74 }
75 if (opt & OPT_c) {
76 shell_opt[1] = 'c';
77 }
78 if (!(opt & OPT_q)) {
79 printf("Script started, file is %s\n", fname);
80 }
81
82 shell = get_shell_name();
83
84 /* Some people run "script ... 0>&-".
85 * Our code assumes that STDIN_FILENO != pty.
86 * Ensure STDIN_FILENO is not closed:
87 */
88 bb_sanitize_stdio();
89
90 pty = xgetpty(pty_line);
91
92 /* get current stdin's tty params */
93 attr_ok = tcgetattr(0, &tt);
94 winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);
95
96 rtt = tt;
97 cfmakeraw(&rtt);
98 rtt.c_lflag &= ~ECHO;
99 tcsetattr(0, TCSAFLUSH, &rtt);
100
101 /* "script" from util-linux exits when child exits,
102 * we wouldn't wait for EOF from slave pty
103 * (output may be produced by grandchildren of child) */
104 signal(SIGCHLD, record_signo);
105
106 /* TODO: SIGWINCH? pass window size changes down to slave? */
107
108 child_pid = xvfork();
109
110 if (child_pid) {
111 /* parent */
112 struct pollfd pfd[2];
113 int outfd, count, loop;
114 double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0;
115 smallint fd_count = 2;
116#define buf bb_common_bufsiz1
117 setup_common_bufsiz();
118
119 outfd = xopen(fname, mode);
120 pfd[0].fd = pty;
121 pfd[0].events = POLLIN;
122 pfd[1].fd = STDIN_FILENO;
123 pfd[1].events = POLLIN;
124 ndelay_on(pty); /* this descriptor is not shared, can do this */
125 /* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */
126
127 /* copy stdin to pty master input,
128 * copy pty master output to stdout and file */
129 /* TODO: don't use full_write's, use proper write buffering */
130 while (fd_count && !bb_got_signal) {
131 /* not safe_poll! we want SIGCHLD to EINTR poll */
132 if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) {
133 /* If child exits too quickly, we may get EIO:
134 * for example, try "script -c true" */
135 break;
136 }
137 if (pfd[0].revents) {
138 errno = 0;
139 count = safe_read(pty, buf, COMMON_BUFSIZE);
140 if (count <= 0 && errno != EAGAIN) {
141 /* err/eof from pty: exit */
142 goto restore;
143 }
144 if (count > 0) {
145 if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) {
146 struct timeval tv;
147 double newtime;
148
149 gettimeofday(&tv, NULL);
150 newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
151 fprintf(stderr, "%f %u\n", newtime - oldtime, count);
152 oldtime = newtime;
153 }
154 full_write(STDOUT_FILENO, buf, count);
155 full_write(outfd, buf, count);
156 if (opt & OPT_f) {
157 fsync(outfd);
158 }
159 }
160 }
161 if (pfd[1].revents) {
162 count = safe_read(STDIN_FILENO, buf, COMMON_BUFSIZE);
163 if (count <= 0) {
164 /* err/eof from stdin: don't read stdin anymore */
165 pfd[1].revents = 0;
166 fd_count--;
167 } else {
168 full_write(pty, buf, count);
169 }
170 }
171 }
172 /* If loop was exited because SIGCHLD handler set bb_got_signal,
173 * there still can be some buffered output. But dont loop forever:
174 * we won't pump orphaned grandchildren's output indefinitely.
175 * Testcase: running this in script:
176 * exec dd if=/dev/zero bs=1M count=1
177 * must have "1+0 records in, 1+0 records out" captured too.
178 * (util-linux's script doesn't do this. buggy :) */
179 loop = 999;
180 /* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
181 while (--loop && (count = safe_read(pty, buf, COMMON_BUFSIZE)) > 0) {
182 full_write(STDOUT_FILENO, buf, count);
183 full_write(outfd, buf, count);
184 }
185 restore:
186 if (attr_ok == 0)
187 tcsetattr(0, TCSAFLUSH, &tt);
188 if (!(opt & OPT_q))
189 printf("Script done, file is %s\n", fname);
190 return EXIT_SUCCESS;
191 }
192
193 /* child: make pty slave to be input, output, error; run shell */
194 close(pty); /* close pty master */
195 /* open pty slave to fd 0,1,2 */
196 close(0);
197 xopen(pty_line, O_RDWR); /* uses fd 0 */
198 xdup2(0, 1);
199 xdup2(0, 2);
200 /* copy our original stdin tty's parameters to pty */
201 if (attr_ok == 0)
202 tcsetattr(0, TCSAFLUSH, &tt);
203 if (winsz_ok == 0)
204 ioctl(0, TIOCSWINSZ, (char *)&win);
205 /* set pty as a controlling tty */
206 setsid();
207 ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
208
209 /* Non-ignored signals revert to SIG_DFL on exec anyway */
210 /*signal(SIGCHLD, SIG_DFL);*/
211 execl(shell, shell, shell_opt, shell_arg, (char *) NULL);
212 bb_simple_perror_msg_and_die(shell);
213}
Note: See TracBrowser for help on using the repository browser.