aboutsummaryrefslogtreecommitdiff
path: root/util-linux/script.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/script.c')
-rw-r--r--util-linux/script.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/util-linux/script.c b/util-linux/script.c
new file mode 100644
index 000000000..fda726ed9
--- /dev/null
+++ b/util-linux/script.c
@@ -0,0 +1,195 @@
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 tarball for details.
12 */
13
14#include "libbb.h"
15
16struct globals {
17 int child_pid;
18 int attr_ok; /* NB: 0: ok */
19 struct termios tt;
20 const char *fname;
21};
22#define G (*ptr_to_globals)
23#define child_pid (G.child_pid)
24#define attr_ok (G.attr_ok )
25#define tt (G.tt )
26#define fname (G.fname )
27#define INIT_G() do { \
28 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
29 fname = "typescript"; \
30} while (0)
31
32static void done(void)
33{
34 if (child_pid) { /* we are parent */
35 if (attr_ok == 0)
36 tcsetattr(0, TCSAFLUSH, &tt);
37 if (!(option_mask32 & 8)) /* not -q */
38 printf("Script done, file is %s\n", fname);
39 }
40 exit(0);
41}
42
43#ifdef UNUSED
44static void handle_sigchld(int sig)
45{
46 /* wait for the exited child and exit */
47 while (wait_any_nohang(&sig) > 0)
48 continue;
49 done();
50}
51#endif
52
53#if ENABLE_GETOPT_LONG
54static const char getopt_longopts[] ALIGN1 =
55 "append\0" No_argument "a"
56 "command\0" Required_argument "c"
57 "flush\0" No_argument "f"
58 "quiet\0" No_argument "q"
59 ;
60#endif
61
62int script_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
63int script_main(int argc, char *argv[])
64{
65 int opt, pty;
66 int winsz_ok;
67 int mode;
68 struct termios rtt;
69 struct winsize win;
70 char line[32];
71 const char *shell;
72 char shell_opt[] = "-i";
73 char *shell_arg = NULL;
74
75 INIT_G();
76#if ENABLE_GETOPT_LONG
77 applet_long_options = getopt_longopts;
78#endif
79 opt_complementary = "?1"; /* max one arg */
80 opt = getopt32(argv, "ac:fq", &shell_arg);
81 //argc -= optind;
82 argv += optind;
83 if (argv[0]) {
84 fname = argv[0];
85 }
86 mode = O_CREAT|O_TRUNC|O_WRONLY;
87 if (opt & 1) {
88 mode = O_CREAT|O_APPEND|O_WRONLY;
89 }
90 if (opt & 2) {
91 shell_opt[1] = 'c';
92 }
93 if (!(opt & 8)) { /* not -q */
94 printf("Script started, file is %s\n", fname);
95 }
96 shell = getenv("SHELL");
97 if (shell == NULL) {
98 shell = _PATH_BSHELL;
99 }
100
101 pty = getpty(line, sizeof(line));
102 if (pty < 0) {
103 bb_perror_msg_and_die("can't get pty");
104 }
105
106 /* get current stdin's tty params */
107 attr_ok = tcgetattr(0, &tt);
108 winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);
109
110 rtt = tt;
111 cfmakeraw(&rtt);
112 rtt.c_lflag &= ~ECHO;
113 tcsetattr(0, TCSAFLUSH, &rtt);
114
115 /* We exit as soon as child exits */
116 //signal(SIGCHLD, handle_sigchld);
117 signal(SIGCHLD, (void (*)(int)) done);
118
119 child_pid = vfork();
120 if (child_pid < 0) {
121 bb_perror_msg_and_die("vfork");
122 }
123
124 if (child_pid) {
125 /* parent */
126 char buf[256];
127 struct pollfd pfd[2];
128 int outfd;
129 int fd_count = 2;
130 struct pollfd *ppfd = pfd;
131
132 outfd = xopen(fname, mode);
133 pfd[0].fd = 0;
134 pfd[0].events = POLLIN;
135 pfd[1].fd = pty;
136 pfd[1].events = POLLIN;
137 ndelay_on(pty); /* this descriptor is not shared, can do this */
138 /* ndelay_on(0); - NO, stdin can be shared! */
139
140 /* copy stdin to pty master input,
141 * copy pty master output to stdout and file */
142 /* TODO: don't use full_write's, use proper write buffering */
143 while (fd_count && safe_poll(ppfd, fd_count, -1) > 0) {
144 if (pfd[0].revents) {
145 int count = safe_read(0, buf, sizeof(buf));
146 if (count <= 0) {
147 /* err/eof: don't read anymore */
148 pfd[0].revents = 0;
149 ppfd++;
150 fd_count--;
151 } else {
152 full_write(pty, buf, count);
153 }
154 }
155 if (pfd[1].revents) {
156 int count;
157 errno = 0;
158 count = safe_read(pty, buf, sizeof(buf));
159 if (count <= 0 && errno != EAGAIN) {
160 /* err/eof: don't read anymore */
161 pfd[1].revents = 0;
162 fd_count--;
163 }
164 if (count > 0) {
165 full_write(1, buf, count);
166 full_write(outfd, buf, count);
167 if (opt & 4) { /* -f */
168 fsync(outfd);
169 }
170 }
171 }
172 }
173 done(); /* does not return */
174 }
175
176 /* child: make pty slave to be input, output, error; run shell */
177 close(pty); /* close pty master */
178 /* open pty slave to fd 0,1,2 */
179 close(0);
180 xopen(line, O_RDWR); /* uses fd 0 */
181 xdup2(0, 1);
182 xdup2(0, 2);
183 /* copy our original stdin tty's parameters to pty */
184 if (attr_ok == 0)
185 tcsetattr(0, TCSAFLUSH, &tt);
186 if (winsz_ok == 0)
187 ioctl(0, TIOCSWINSZ, (char *)&win);
188 /* set pty as a controlling tty */
189 setsid();
190 ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
191
192 /* signal(SIGCHLD, SIG_DFL); - exec does this for us */
193 execl(shell, shell, shell_opt, shell_arg, NULL);
194 bb_simple_perror_msg_and_die(shell);
195}