aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-11-02 10:19:18 +0000
committerRon Yorston <rmy@pobox.com>2016-11-02 10:19:18 +0000
commite45ff573dc9707b1ea71f490ef16cd9d08feaaf2 (patch)
tree09980b02b9c8efc8d772c7379abdcd4eef4ddad8
parent7bc3efa4f0c7608723d301bda5ee006a0f6141fe (diff)
parent2e6af549715f5d7b4c2ab204e46c8b8f6f057045 (diff)
downloadbusybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.tar.gz
busybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.tar.bz2
busybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.zip
Merge branch 'busybox' into merge
-rw-r--r--libbb/xfuncs.c47
-rw-r--r--miscutils/man.c105
-rw-r--r--shell/ash.c122
-rw-r--r--shell/ash_test/ash-vars/readonly1.right2
-rwxr-xr-xshell/ash_test/ash-vars/readonly1.tests7
-rw-r--r--shell/hush.c418
-rw-r--r--shell/hush_test/hush-misc/wait1.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rw-r--r--shell/hush_test/hush-misc/wait2.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests4
-rw-r--r--shell/hush_test/hush-misc/wait3.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
12 files changed, 466 insertions, 251 deletions
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 3f9a84ad4..45650edba 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -237,16 +237,27 @@ ssize_t FAST_FUNC full_write2_str(const char *str)
237 237
238static int wh_helper(int value, int def_val, const char *env_name, int *err) 238static int wh_helper(int value, int def_val, const char *env_name, int *err)
239{ 239{
240 if (value == 0) { 240 /* Envvars override even if "value" from ioctl is valid (>0).
241 char *s = getenv(env_name); 241 * Rationale: it's impossible to guess what user wants.
242 if (s) { 242 * For example: "man CMD | ...": should "man" format output
243 value = atoi(s); 243 * to stdout's width? stdin's width? /dev/tty's width? 80 chars?
244 /* If LINES/COLUMNS are set, pretend that there is 244 * We _cant_ know it. If "..." saves text for e.g. email,
245 * no error getting w/h, this prevents some ugly 245 * then it's probably 80 chars.
246 * cursor tricks by our callers */ 246 * If "..." is, say, "grep -v DISCARD | $PAGER", then user
247 *err = 0; 247 * would prefer his tty's width to be used!
248 } 248 *
249 * Since we don't know, at least allow user to do this:
250 * "COLUMNS=80 man CMD | ..."
251 */
252 char *s = getenv(env_name);
253 if (s) {
254 value = atoi(s);
255 /* If LINES/COLUMNS are set, pretend that there is
256 * no error getting w/h, this prevents some ugly
257 * cursor tricks by our callers */
258 *err = 0;
249 } 259 }
260
250 if (value <= 1 || value >= 30000) 261 if (value <= 1 || value >= 30000)
251 value = def_val; 262 value = def_val;
252 return value; 263 return value;
@@ -258,6 +269,20 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
258{ 269{
259 struct winsize win; 270 struct winsize win;
260 int err; 271 int err;
272 int close_me = -1;
273
274 if (fd == -1) {
275 if (isatty(STDOUT_FILENO))
276 fd = STDOUT_FILENO;
277 else
278 if (isatty(STDERR_FILENO))
279 fd = STDERR_FILENO;
280 else
281 if (isatty(STDIN_FILENO))
282 fd = STDIN_FILENO;
283 else
284 close_me = fd = open("/dev/tty", O_RDONLY);
285 }
261 286
262 win.ws_row = 0; 287 win.ws_row = 0;
263 win.ws_col = 0; 288 win.ws_col = 0;
@@ -268,6 +293,10 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
268 *height = wh_helper(win.ws_row, 24, "LINES", &err); 293 *height = wh_helper(win.ws_row, 24, "LINES", &err);
269 if (width) 294 if (width)
270 *width = wh_helper(win.ws_col, 80, "COLUMNS", &err); 295 *width = wh_helper(win.ws_col, 80, "COLUMNS", &err);
296
297 if (close_me >= 0)
298 close(close_me);
299
271 return err; 300 return err;
272} 301}
273int FAST_FUNC get_terminal_width(int fd) 302int FAST_FUNC get_terminal_width(int fd)
diff --git a/miscutils/man.c b/miscutils/man.c
index 6d32890d1..a8c2d4047 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -9,8 +9,11 @@
9//usage: "Format and display manual page\n" 9//usage: "Format and display manual page\n"
10//usage: "\n -a Display all pages" 10//usage: "\n -a Display all pages"
11//usage: "\n -w Show page locations" 11//usage: "\n -w Show page locations"
12//usage: "\n"
13//usage: "\n$COLUMNS overrides output width"
12 14
13#include "libbb.h" 15#include "libbb.h"
16#include "common_bufsiz.h"
14 17
15enum { 18enum {
16 OPT_a = 1, /* all */ 19 OPT_a = 1, /* all */
@@ -18,7 +21,6 @@ enum {
18}; 21};
19 22
20/* This is what I see on my desktop system being executed: 23/* This is what I see on my desktop system being executed:
21
22( 24(
23echo ".ll 12.4i" 25echo ".ll 12.4i"
24echo ".nr LL 12.4i" 26echo ".nr LL 12.4i"
@@ -28,11 +30,41 @@ echo ".\\\""
28echo ".pl \n(nlu+10" 30echo ".pl \n(nlu+10"
29) | gtbl | nroff -Tlatin1 -mandoc | less 31) | gtbl | nroff -Tlatin1 -mandoc | less
30 32
31*/ 33Some systems use -Tascii.
34
35On another system I see this:
36
37... | tbl | nroff -mandoc -rLL=<NNN>n -rLT=<NNN>n -Tutf8 | less
32 38
33static int show_manpage(const char *pager, char *man_filename, int man, int level); 39where <NNN> is screen width minus 5.
40Replacing "DEFINE nroff nroff -mandoc" in /etc/man_db.conf
41changes "nroff -mandoc" part; -rLL=<NNN>n, -rLT=<NNN>n and -Tutf8 parts are
42appended to the user-specified command.
34 43
35static int run_pipe(const char *pager, char *man_filename, int man, int level) 44Redirecting to a pipe or file sets GROFF_NO_SGR=1 to prevent color escapes,
45and uses "col -b -p -x" instead of pager, this filters out backspace
46and underscore tricks.
47*/
48
49struct globals {
50 const char *col;
51 const char *tbl;
52 const char *nroff;
53 const char *pager;
54} FIX_ALIASING;
55#define G (*(struct globals*)bb_common_bufsiz1)
56#define INIT_G() do { \
57 setup_common_bufsiz(); \
58 G.col = "col"; \
59 G.tbl = "tbl"; \
60 /* Removed -Tlatin1. Assuming system nroff has suitable default */ \
61 G.nroff = "nroff -mandoc"; \
62 G.pager = ENABLE_LESS ? "less" : "more"; \
63} while (0)
64
65static int show_manpage(char *man_filename, int man, int level);
66
67static int run_pipe(char *man_filename, int man, int level)
36{ 68{
37 char *cmd; 69 char *cmd;
38 70
@@ -95,7 +127,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
95 man_filename = xasprintf("%s/%s", man_filename, linkname); 127 man_filename = xasprintf("%s/%s", man_filename, linkname);
96 free(line); 128 free(line);
97 /* Note: we leak "new" man_filename string as well... */ 129 /* Note: we leak "new" man_filename string as well... */
98 if (show_manpage(pager, man_filename, man, level + 1)) 130 if (show_manpage(man_filename, man, level + 1))
99 return 1; 131 return 1;
100 /* else: show the link, it's better than nothing */ 132 /* else: show the link, it's better than nothing */
101 } 133 }
@@ -103,20 +135,26 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
103 ordinary_manpage: 135 ordinary_manpage:
104 close(STDIN_FILENO); 136 close(STDIN_FILENO);
105 open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ 137 open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */
106 /* "2>&1" is added so that nroff errors are shown in pager too. 138 if (man) {
107 * Otherwise it may show just empty screen */ 139 int w = get_terminal_width(-1);
108 cmd = xasprintf( 140 if (w > 10)
109 /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */ 141 w -= 2;
110 man ? "gtbl | nroff -Tascii -mandoc 2>&1 | %s" 142 /* "2>&1" is added so that nroff errors are shown in pager too.
111 : "%s", 143 * Otherwise it may show just empty screen.
112 pager); 144 */
145 cmd = xasprintf("%s | %s -rLL=%un -rLT=%un 2>&1 | %s",
146 G.tbl, G.nroff, w, w,
147 G.pager);
148 } else {
149 cmd = xstrdup(G.pager);
150 }
113 system(cmd); 151 system(cmd);
114 free(cmd); 152 free(cmd);
115 return 1; 153 return 1;
116} 154}
117 155
118/* man_filename is of the form "/dir/dir/dir/name.s" */ 156/* man_filename is of the form "/dir/dir/dir/name.s" */
119static int show_manpage(const char *pager, char *man_filename, int man, int level) 157static int show_manpage(char *man_filename, int man, int level)
120{ 158{
121#if SEAMLESS_COMPRESSION 159#if SEAMLESS_COMPRESSION
122 /* We leak this allocation... */ 160 /* We leak this allocation... */
@@ -125,26 +163,26 @@ static int show_manpage(const char *pager, char *man_filename, int man, int leve
125#endif 163#endif
126 164
127#if ENABLE_FEATURE_SEAMLESS_LZMA 165#if ENABLE_FEATURE_SEAMLESS_LZMA
128 if (run_pipe(pager, filename_with_zext, man, level)) 166 if (run_pipe(filename_with_zext, man, level))
129 return 1; 167 return 1;
130#endif 168#endif
131#if ENABLE_FEATURE_SEAMLESS_XZ 169#if ENABLE_FEATURE_SEAMLESS_XZ
132 strcpy(ext, "xz"); 170 strcpy(ext, "xz");
133 if (run_pipe(pager, filename_with_zext, man, level)) 171 if (run_pipe(filename_with_zext, man, level))
134 return 1; 172 return 1;
135#endif 173#endif
136#if ENABLE_FEATURE_SEAMLESS_BZ2 174#if ENABLE_FEATURE_SEAMLESS_BZ2
137 strcpy(ext, "bz2"); 175 strcpy(ext, "bz2");
138 if (run_pipe(pager, filename_with_zext, man, level)) 176 if (run_pipe(filename_with_zext, man, level))
139 return 1; 177 return 1;
140#endif 178#endif
141#if ENABLE_FEATURE_SEAMLESS_GZ 179#if ENABLE_FEATURE_SEAMLESS_GZ
142 strcpy(ext, "gz"); 180 strcpy(ext, "gz");
143 if (run_pipe(pager, filename_with_zext, man, level)) 181 if (run_pipe(filename_with_zext, man, level))
144 return 1; 182 return 1;
145#endif 183#endif
146 184
147 return run_pipe(pager, man_filename, man, level); 185 return run_pipe(man_filename, man, level);
148} 186}
149 187
150static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) 188static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
@@ -186,11 +224,20 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
186 return man_path_list; 224 return man_path_list;
187} 225}
188 226
227static const char *if_redefined(const char *var, const char *key, const char *line)
228{
229 if (!is_prefixed_with(line, key))
230 return var;
231 line += strlen(key);
232 if (!isspace(line[0]))
233 return var;
234 return xstrdup(skip_whitespace(line));
235}
236
189int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 237int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
190int man_main(int argc UNUSED_PARAM, char **argv) 238int man_main(int argc UNUSED_PARAM, char **argv)
191{ 239{
192 parser_t *parser; 240 parser_t *parser;
193 const char *pager = ENABLE_LESS ? "less" : "more";
194 char *sec_list; 241 char *sec_list;
195 char *cur_path, *cur_sect; 242 char *cur_path, *cur_sect;
196 char **man_path_list; 243 char **man_path_list;
@@ -199,6 +246,8 @@ int man_main(int argc UNUSED_PARAM, char **argv)
199 int opt, not_found; 246 int opt, not_found;
200 char *token[2]; 247 char *token[2];
201 248
249 INIT_G();
250
202 opt_complementary = "-1"; /* at least one argument */ 251 opt_complementary = "-1"; /* at least one argument */
203 opt = getopt32(argv, "+aw"); 252 opt = getopt32(argv, "+aw");
204 argv += optind; 253 argv += optind;
@@ -232,9 +281,10 @@ int man_main(int argc UNUSED_PARAM, char **argv)
232 if (!token[1]) 281 if (!token[1])
233 continue; 282 continue;
234 if (strcmp("DEFINE", token[0]) == 0) { 283 if (strcmp("DEFINE", token[0]) == 0) {
235 if (is_prefixed_with(token[1], "pager")) { 284 G.col = if_redefined(G.tbl , "col", token[1]);
236 pager = xstrdup(skip_whitespace(token[1] + 5)); 285 G.tbl = if_redefined(G.tbl , "tbl", token[1]);
237 } 286 G.nroff = if_redefined(G.nroff, "nroff", token[1]);
287 G.pager = if_redefined(G.pager, "pager", token[1]);
238 } else 288 } else
239 if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ 289 if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
240 || strcmp("MANDATORY_MANPATH", token[0]) == 0 290 || strcmp("MANDATORY_MANPATH", token[0]) == 0
@@ -254,7 +304,12 @@ int man_main(int argc UNUSED_PARAM, char **argv)
254 if (!env_pager) 304 if (!env_pager)
255 env_pager = getenv("PAGER"); 305 env_pager = getenv("PAGER");
256 if (env_pager) 306 if (env_pager)
257 pager = env_pager; 307 G.pager = env_pager;
308 }
309
310 if (!isatty(STDOUT_FILENO)) {
311 putenv((char*)"GROFF_NO_SGR=1");
312 G.pager = xasprintf("%s -b -p -x", G.col);
258 } 313 }
259 314
260 not_found = 0; 315 not_found = 0;
@@ -263,7 +318,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
263 cur_mp = 0; 318 cur_mp = 0;
264 319
265 if (strchr(*argv, '/')) { 320 if (strchr(*argv, '/')) {
266 found = show_manpage(pager, *argv, /*man:*/ 1, 0); 321 found = show_manpage(*argv, /*man:*/ 1, 0);
267 goto check_found; 322 goto check_found;
268 } 323 }
269 while ((cur_path = man_path_list[cur_mp++]) != NULL) { 324 while ((cur_path = man_path_list[cur_mp++]) != NULL) {
@@ -284,7 +339,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
284 sect_len, cur_sect, 339 sect_len, cur_sect,
285 *argv, 340 *argv,
286 sect_len, cur_sect); 341 sect_len, cur_sect);
287 found_here = show_manpage(pager, man_filename, cat0man1, 0); 342 found_here = show_manpage(man_filename, cat0man1, 0);
288 found |= found_here; 343 found |= found_here;
289 cat0man1 += found_here + 1; 344 cat0man1 += found_here + 1;
290 free(man_filename); 345 free(man_filename);
diff --git a/shell/ash.c b/shell/ash.c
index dd320355e..a709c3602 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -362,8 +362,7 @@ struct globals_misc {
362 volatile int suppress_int; /* counter */ 362 volatile int suppress_int; /* counter */
363 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 363 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
364 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 364 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
365 /* last pending signal */ 365 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
366 volatile /*sig_atomic_t*/ smallint pending_sig;
367 smallint exception_type; /* kind of exception (0..5) */ 366 smallint exception_type; /* kind of exception (0..5) */
368 /* exceptions */ 367 /* exceptions */
369#define EXINT 0 /* SIGINT received */ 368#define EXINT 0 /* SIGINT received */
@@ -915,13 +914,8 @@ trace_vprintf(const char *fmt, va_list va)
915{ 914{
916 if (debug != 1) 915 if (debug != 1)
917 return; 916 return;
918 if (DEBUG_TIME)
919 fprintf(tracefile, "%u ", (int) time(NULL));
920 if (DEBUG_PID)
921 fprintf(tracefile, "[%u] ", (int) getpid());
922 if (DEBUG_SIG)
923 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
924 vfprintf(tracefile, fmt, va); 917 vfprintf(tracefile, fmt, va);
918 fprintf(tracefile, "\n");
925} 919}
926 920
927static void 921static void
@@ -1314,11 +1308,10 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
1314{ 1308{
1315#if DEBUG 1309#if DEBUG
1316 if (msg) { 1310 if (msg) {
1317 TRACE(("ash_vmsg_and_raise(%d, \"", cond)); 1311 TRACE(("ash_vmsg_and_raise(%d):", cond));
1318 TRACEV((msg, ap)); 1312 TRACEV((msg, ap));
1319 TRACE(("\") pid=%d\n", getpid()));
1320 } else 1313 } else
1321 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid())); 1314 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
1322 if (msg) 1315 if (msg)
1323#endif 1316#endif
1324 ash_vmsg(msg, ap); 1317 ash_vmsg(msg, ap);
@@ -2250,6 +2243,7 @@ setvareq(char *s, int flags)
2250 if (flags & VNOSAVE) 2243 if (flags & VNOSAVE)
2251 free(s); 2244 free(s);
2252 n = vp->var_text; 2245 n = vp->var_text;
2246 exitstatus = 1;
2253 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); 2247 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2254 } 2248 }
2255 2249
@@ -3715,11 +3709,6 @@ setsignal(int signo)
3715#define CUR_RUNNING 1 3709#define CUR_RUNNING 1
3716#define CUR_STOPPED 0 3710#define CUR_STOPPED 0
3717 3711
3718/* mode flags for dowait */
3719#define DOWAIT_NONBLOCK 0
3720#define DOWAIT_BLOCK 1
3721#define DOWAIT_BLOCK_OR_SIG 2
3722
3723#if JOBS 3712#if JOBS
3724/* pgrp of shell on invocation */ 3713/* pgrp of shell on invocation */
3725static int initialpgrp; //references:2 3714static int initialpgrp; //references:2
@@ -4231,24 +4220,30 @@ waitpid_child(int *status, int wait_flags)
4231#else 4220#else
4232 4221
4233static int 4222static int
4234wait_block_or_sig(int *status, int wait_flags) 4223wait_block_or_sig(int *status)
4235{ 4224{
4236 sigset_t mask;
4237 int pid; 4225 int pid;
4238 4226
4239 do { 4227 do {
4240 /* Poll all children for changes in their state */ 4228 /* Poll all children for changes in their state */
4241 got_sigchld = 0; 4229 got_sigchld = 0;
4242 pid = waitpid(-1, status, wait_flags | WNOHANG); 4230 /* if job control is active, accept stopped processes too */
4231 pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG);
4243 if (pid != 0) 4232 if (pid != 0)
4244 break; /* Error (e.g. EINTR) or pid */ 4233 break; /* Error (e.g. EINTR, ECHILD) or pid */
4245 4234
4246 /* No child is ready. Sleep until interesting signal is received */ 4235 /* Children exist, but none are ready. Sleep until interesting signal */
4236#if 0 /* dash does this */
4237 sigset_t mask;
4247 sigfillset(&mask); 4238 sigfillset(&mask);
4248 sigprocmask(SIG_SETMASK, &mask, &mask); 4239 sigprocmask(SIG_SETMASK, &mask, &mask);
4249 while (!got_sigchld && !pending_sig) 4240 while (!got_sigchld && !pending_sig)
4250 sigsuspend(&mask); 4241 sigsuspend(&mask);
4251 sigprocmask(SIG_SETMASK, &mask, NULL); 4242 sigprocmask(SIG_SETMASK, &mask, NULL);
4243#else
4244 while (!got_sigchld && !pending_sig)
4245 pause();
4246#endif
4252 4247
4253 /* If it was SIGCHLD, poll children again */ 4248 /* If it was SIGCHLD, poll children again */
4254 } while (got_sigchld); 4249 } while (got_sigchld);
@@ -4257,11 +4252,13 @@ wait_block_or_sig(int *status, int wait_flags)
4257} 4252}
4258#endif 4253#endif
4259 4254
4255#define DOWAIT_NONBLOCK 0
4256#define DOWAIT_BLOCK 1
4257#define DOWAIT_BLOCK_OR_SIG 2
4260 4258
4261static int 4259static int
4262dowait(int block, struct job *job) 4260dowait(int block, struct job *job)
4263{ 4261{
4264 int wait_flags;
4265 int pid; 4262 int pid;
4266 int status; 4263 int status;
4267 struct job *jp; 4264 struct job *jp;
@@ -4269,13 +4266,6 @@ dowait(int block, struct job *job)
4269 4266
4270 TRACE(("dowait(0x%x) called\n", block)); 4267 TRACE(("dowait(0x%x) called\n", block));
4271 4268
4272 wait_flags = 0;
4273 if (block == DOWAIT_NONBLOCK)
4274 wait_flags = WNOHANG;
4275 /* If job control is compiled in, we accept stopped processes too. */
4276 if (doing_jobctl)
4277 wait_flags |= WUNTRACED;
4278
4279 /* It's wrong to call waitpid() outside of INT_OFF region: 4269 /* It's wrong to call waitpid() outside of INT_OFF region:
4280 * signal can arrive just after syscall return and handler can 4270 * signal can arrive just after syscall return and handler can
4281 * longjmp away, losing stop/exit notification processing. 4271 * longjmp away, losing stop/exit notification processing.
@@ -4286,17 +4276,27 @@ dowait(int block, struct job *job)
4286 * in INT_OFF region: "wait" needs to wait for any running job 4276 * in INT_OFF region: "wait" needs to wait for any running job
4287 * to change state, but should exit on any trap too. 4277 * to change state, but should exit on any trap too.
4288 * In INT_OFF region, a signal just before syscall entry can set 4278 * In INT_OFF region, a signal just before syscall entry can set
4289 * pending_sig valiables, but we can't check them, and we would 4279 * pending_sig variables, but we can't check them, and we would
4290 * either enter a sleeping waitpid() (BUG), or need to busy-loop. 4280 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
4281 *
4291 * Because of this, we run inside INT_OFF, but use a special routine 4282 * Because of this, we run inside INT_OFF, but use a special routine
4292 * which combines waitpid() and sigsuspend(). 4283 * which combines waitpid() and pause().
4284 * This is the reason why we need to have a handler for SIGCHLD:
4285 * SIG_DFL handler does not wake pause().
4293 */ 4286 */
4294 INT_OFF; 4287 INT_OFF;
4295 if (block == DOWAIT_BLOCK_OR_SIG) 4288 if (block == DOWAIT_BLOCK_OR_SIG) {
4296 pid = wait_block_or_sig(&status, wait_flags); 4289 pid = wait_block_or_sig(&status);
4297 else 4290 } else {
4298 /* NB: _not_ safe_waitpid, we need to detect EINTR. */ 4291 int wait_flags = 0;
4292 if (block == DOWAIT_NONBLOCK)
4293 wait_flags = WNOHANG;
4294 /* if job control is active, accept stopped processes too */
4295 if (doing_jobctl)
4296 wait_flags |= WUNTRACED;
4297 /* NB: _not_ safe_waitpid, we need to detect EINTR */
4299 pid = waitpid(-1, &status, wait_flags); 4298 pid = waitpid(-1, &status, wait_flags);
4299 }
4300 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", 4300 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
4301 pid, status, errno, strerror(errno))); 4301 pid, status, errno, strerror(errno)));
4302 if (pid <= 0) 4302 if (pid <= 0)
@@ -7410,6 +7410,21 @@ expandmeta(struct strlist *str /*, int flag*/)
7410 7410
7411 if (fflag) 7411 if (fflag)
7412 goto nometa; 7412 goto nometa;
7413
7414 /* Avoid glob() (and thus, stat() et al) for words like "echo" */
7415 p = str->text;
7416 while (*p) {
7417 if (*p == '*')
7418 goto need_glob;
7419 if (*p == '?')
7420 goto need_glob;
7421 if (*p == '[')
7422 goto need_glob;
7423 p++;
7424 }
7425 goto nometa;
7426
7427 need_glob:
7413 INT_OFF; 7428 INT_OFF;
7414 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 7429 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7415// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match 7430// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
@@ -10064,7 +10079,7 @@ evalcommand(union node *cmd, int flags)
10064 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { 10079 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
10065 if (exception_type == EXERROR && spclbltin <= 0) { 10080 if (exception_type == EXERROR && spclbltin <= 0) {
10066 FORCE_INT_ON; 10081 FORCE_INT_ON;
10067 break; 10082 goto readstatus;
10068 } 10083 }
10069 raise: 10084 raise:
10070 longjmp(exception_handler->loc, 1); 10085 longjmp(exception_handler->loc, 1);
@@ -10584,6 +10599,9 @@ popfile(void)
10584{ 10599{
10585 struct parsefile *pf = g_parsefile; 10600 struct parsefile *pf = g_parsefile;
10586 10601
10602 if (pf == &basepf)
10603 return;
10604
10587 INT_OFF; 10605 INT_OFF;
10588 if (pf->pf_fd >= 0) 10606 if (pf->pf_fd >= 0)
10589 close(pf->pf_fd); 10607 close(pf->pf_fd);
@@ -12747,6 +12765,10 @@ expandstr(const char *ps)
12747static int 12765static int
12748evalstring(char *s, int flags) 12766evalstring(char *s, int flags)
12749{ 12767{
12768 struct jmploc *volatile savehandler;
12769 struct jmploc jmploc;
12770 int ex;
12771
12750 union node *n; 12772 union node *n;
12751 struct stackmark smark; 12773 struct stackmark smark;
12752 int status; 12774 int status;
@@ -12756,6 +12778,19 @@ evalstring(char *s, int flags)
12756 setstackmark(&smark); 12778 setstackmark(&smark);
12757 12779
12758 status = 0; 12780 status = 0;
12781 /* On exception inside execution loop, we must popfile().
12782 * Try interactively:
12783 * readonly a=a
12784 * command eval "a=b" # throws "is read only" error
12785 * "command BLTIN" is not supposed to abort (even in non-interactive use).
12786 * But if we skip popfile(), we hit EOF in eval's string, and exit.
12787 */
12788 savehandler = exception_handler;
12789 ex = setjmp(jmploc.loc);
12790 if (ex)
12791 goto out;
12792 exception_handler = &jmploc;
12793
12759 while ((n = parsecmd(0)) != NODE_EOF) { 12794 while ((n = parsecmd(0)) != NODE_EOF) {
12760 int i; 12795 int i;
12761 12796
@@ -12766,10 +12801,15 @@ evalstring(char *s, int flags)
12766 if (evalskip) 12801 if (evalskip)
12767 break; 12802 break;
12768 } 12803 }
12804 out:
12769 popstackmark(&smark); 12805 popstackmark(&smark);
12770 popfile(); 12806 popfile();
12771 stunalloc(s); 12807 stunalloc(s);
12772 12808
12809 exception_handler = savehandler;
12810 if (ex)
12811 longjmp(exception_handler->loc, ex);
12812
12773 return status; 12813 return status;
12774} 12814}
12775 12815
@@ -13982,11 +14022,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13982 goto state4; 14022 goto state4;
13983 } 14023 }
13984 exception_handler = &jmploc; 14024 exception_handler = &jmploc;
13985#if DEBUG
13986 opentrace();
13987 TRACE(("Shell args: "));
13988 trace_puts_args(argv);
13989#endif
13990 rootpid = getpid(); 14025 rootpid = getpid();
13991 14026
13992 init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0)); 14027 init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0));
@@ -14003,6 +14038,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14003 } 14038 }
14004#endif 14039#endif
14005 procargs(argv); 14040 procargs(argv);
14041#if DEBUG
14042 TRACE(("Shell args: "));
14043 trace_puts_args(argv);
14044#endif
14045
14006#if ENABLE_PLATFORM_MINGW32 14046#if ENABLE_PLATFORM_MINGW32
14007 if ( noconsole ) { 14047 if ( noconsole ) {
14008 DWORD dummy; 14048 DWORD dummy;
diff --git a/shell/ash_test/ash-vars/readonly1.right b/shell/ash_test/ash-vars/readonly1.right
new file mode 100644
index 000000000..2b363e325
--- /dev/null
+++ b/shell/ash_test/ash-vars/readonly1.right
@@ -0,0 +1,2 @@
1One:1
2One:1
diff --git a/shell/ash_test/ash-vars/readonly1.tests b/shell/ash_test/ash-vars/readonly1.tests
new file mode 100755
index 000000000..81b461f5f
--- /dev/null
+++ b/shell/ash_test/ash-vars/readonly1.tests
@@ -0,0 +1,7 @@
1readonly bla=123
2# Bare "eval bla=123" should abort ("eval" is a special builtin):
3(eval bla=123 2>/dev/null; echo BUG)
4echo One:$?
5# "command BLTIN" disables "special-ness", should not abort:
6command eval bla=123 2>/dev/null
7echo One:$?
diff --git a/shell/hush.c b/shell/hush.c
index c80429d5c..7c2f157b8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,6 +44,8 @@
44 * special variables (done: PWD, PPID, RANDOM) 44 * special variables (done: PWD, PPID, RANDOM)
45 * tilde expansion 45 * tilde expansion
46 * aliases 46 * aliases
47 * kill %jobspec
48 * wait %jobspec
47 * follow IFS rules more precisely, including update semantics 49 * follow IFS rules more precisely, including update semantics
48 * builtins mandated by standards we don't support: 50 * builtins mandated by standards we don't support:
49 * [un]alias, command, fc, getopts, newgrp, readonly, times 51 * [un]alias, command, fc, getopts, newgrp, readonly, times
@@ -7041,16 +7043,134 @@ static void delete_finished_bg_job(struct pipe *pi)
7041} 7043}
7042#endif /* JOB */ 7044#endif /* JOB */
7043 7045
7044/* Check to see if any processes have exited -- if they 7046static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
7045 * have, figure out why and see if a job has completed */
7046static int checkjobs(struct pipe *fg_pipe)
7047{ 7047{
7048 int attributes;
7049 int status;
7050#if ENABLE_HUSH_JOB 7048#if ENABLE_HUSH_JOB
7051 struct pipe *pi; 7049 struct pipe *pi;
7052#endif 7050#endif
7053 pid_t childpid; 7051 int i, dead;
7052
7053 dead = WIFEXITED(status) || WIFSIGNALED(status);
7054
7055#if DEBUG_JOBS
7056 if (WIFSTOPPED(status))
7057 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
7058 childpid, WSTOPSIG(status), WEXITSTATUS(status));
7059 if (WIFSIGNALED(status))
7060 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
7061 childpid, WTERMSIG(status), WEXITSTATUS(status));
7062 if (WIFEXITED(status))
7063 debug_printf_jobs("pid %d exited, exitcode %d\n",
7064 childpid, WEXITSTATUS(status));
7065#endif
7066 /* Were we asked to wait for a fg pipe? */
7067 if (fg_pipe) {
7068 i = fg_pipe->num_cmds;
7069 while (--i >= 0) {
7070 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
7071 if (fg_pipe->cmds[i].pid != childpid)
7072 continue;
7073 if (dead) {
7074 int ex;
7075 fg_pipe->cmds[i].pid = 0;
7076 fg_pipe->alive_cmds--;
7077 ex = WEXITSTATUS(status);
7078 /* bash prints killer signal's name for *last*
7079 * process in pipe (prints just newline for SIGINT/SIGPIPE).
7080 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
7081 */
7082 if (WIFSIGNALED(status)) {
7083 int sig = WTERMSIG(status);
7084 if (i == fg_pipe->num_cmds-1)
7085 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
7086 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
7087 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
7088 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
7089 * Maybe we need to use sig | 128? */
7090 ex = sig + 128;
7091 }
7092 fg_pipe->cmds[i].cmd_exitcode = ex;
7093 } else {
7094 fg_pipe->stopped_cmds++;
7095 }
7096 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
7097 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
7098 if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
7099 /* All processes in fg pipe have exited or stopped */
7100 int rcode = 0;
7101 i = fg_pipe->num_cmds;
7102 while (--i >= 0) {
7103 rcode = fg_pipe->cmds[i].cmd_exitcode;
7104 /* usually last process gives overall exitstatus,
7105 * but with "set -o pipefail", last *failed* process does */
7106 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
7107 break;
7108 }
7109 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
7110/* Note: *non-interactive* bash does not continue if all processes in fg pipe
7111 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
7112 * and "killall -STOP cat" */
7113 if (G_interactive_fd) {
7114#if ENABLE_HUSH_JOB
7115 if (fg_pipe->alive_cmds != 0)
7116 insert_bg_job(fg_pipe);
7117#endif
7118 return rcode;
7119 }
7120 if (fg_pipe->alive_cmds == 0)
7121 return rcode;
7122 }
7123 /* There are still running processes in the fg_pipe */
7124 return -1;
7125 }
7126 /* It wasnt in fg_pipe, look for process in bg pipes */
7127 }
7128
7129#if ENABLE_HUSH_JOB
7130 /* We were asked to wait for bg or orphaned children */
7131 /* No need to remember exitcode in this case */
7132 for (pi = G.job_list; pi; pi = pi->next) {
7133 for (i = 0; i < pi->num_cmds; i++) {
7134 if (pi->cmds[i].pid == childpid)
7135 goto found_pi_and_prognum;
7136 }
7137 }
7138 /* Happens when shell is used as init process (init=/bin/sh) */
7139 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
7140 return -1; /* this wasn't a process from fg_pipe */
7141
7142 found_pi_and_prognum:
7143 if (dead) {
7144 /* child exited */
7145 pi->cmds[i].pid = 0;
7146 pi->cmds[i].cmd_exitcode = WEXITSTATUS(status);
7147 if (WIFSIGNALED(status))
7148 pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status);
7149 pi->alive_cmds--;
7150 if (!pi->alive_cmds) {
7151 if (G_interactive_fd)
7152 printf(JOB_STATUS_FORMAT, pi->jobid,
7153 "Done", pi->cmdtext);
7154 delete_finished_bg_job(pi);
7155 }
7156 } else {
7157 /* child stopped */
7158 pi->stopped_cmds++;
7159 }
7160#endif
7161 return -1; /* this wasn't a process from fg_pipe */
7162}
7163
7164/* Check to see if any processes have exited -- if they have,
7165 * figure out why and see if a job has completed.
7166 * Alternatively (fg_pipe == NULL, waitfor_pid != 0),
7167 * wait for a specific pid to complete, return exitcode+1
7168 * (this allows to distinguish zero as "no children exited" result).
7169 */
7170static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
7171{
7172 int attributes;
7173 int status;
7054 int rcode = 0; 7174 int rcode = 0;
7055 7175
7056 debug_printf_jobs("checkjobs %p\n", fg_pipe); 7176 debug_printf_jobs("checkjobs %p\n", fg_pipe);
@@ -7087,12 +7207,10 @@ static int checkjobs(struct pipe *fg_pipe)
7087 * 1 <========== bg pipe is not fully done, but exitcode is already known! 7207 * 1 <========== bg pipe is not fully done, but exitcode is already known!
7088 * [hush 1.14.0: yes we do it right] 7208 * [hush 1.14.0: yes we do it right]
7089 */ 7209 */
7090 wait_more:
7091 while (1) { 7210 while (1) {
7092 int i; 7211 pid_t childpid;
7093 int dead;
7094
7095#if ENABLE_HUSH_FAST 7212#if ENABLE_HUSH_FAST
7213 int i;
7096 i = G.count_SIGCHLD; 7214 i = G.count_SIGCHLD;
7097#endif 7215#endif
7098 childpid = waitpid(-1, &status, attributes); 7216 childpid = waitpid(-1, &status, attributes);
@@ -7106,112 +7224,24 @@ static int checkjobs(struct pipe *fg_pipe)
7106//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); 7224//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
7107 } 7225 }
7108#endif 7226#endif
7227 /* ECHILD (no children), or 0 (no change in children status) */
7228 rcode = childpid;
7109 break; 7229 break;
7110 } 7230 }
7111 dead = WIFEXITED(status) || WIFSIGNALED(status); 7231 rcode = process_wait_result(fg_pipe, childpid, status);
7112 7232 if (rcode >= 0) {
7113#if DEBUG_JOBS 7233 /* fg_pipe exited or stopped */
7114 if (WIFSTOPPED(status)) 7234 break;
7115 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
7116 childpid, WSTOPSIG(status), WEXITSTATUS(status));
7117 if (WIFSIGNALED(status))
7118 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
7119 childpid, WTERMSIG(status), WEXITSTATUS(status));
7120 if (WIFEXITED(status))
7121 debug_printf_jobs("pid %d exited, exitcode %d\n",
7122 childpid, WEXITSTATUS(status));
7123#endif
7124 /* Were we asked to wait for fg pipe? */
7125 if (fg_pipe) {
7126 i = fg_pipe->num_cmds;
7127 while (--i >= 0) {
7128 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
7129 if (fg_pipe->cmds[i].pid != childpid)
7130 continue;
7131 if (dead) {
7132 int ex;
7133 fg_pipe->cmds[i].pid = 0;
7134 fg_pipe->alive_cmds--;
7135 ex = WEXITSTATUS(status);
7136 /* bash prints killer signal's name for *last*
7137 * process in pipe (prints just newline for SIGINT/SIGPIPE).
7138 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
7139 */
7140 if (WIFSIGNALED(status)) {
7141 int sig = WTERMSIG(status);
7142 if (i == fg_pipe->num_cmds-1)
7143 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
7144 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
7145 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
7146 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
7147 * Maybe we need to use sig | 128? */
7148 ex = sig + 128;
7149 }
7150 fg_pipe->cmds[i].cmd_exitcode = ex;
7151 } else {
7152 fg_pipe->stopped_cmds++;
7153 }
7154 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
7155 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
7156 if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
7157 /* All processes in fg pipe have exited or stopped */
7158 i = fg_pipe->num_cmds;
7159 while (--i >= 0) {
7160 rcode = fg_pipe->cmds[i].cmd_exitcode;
7161 /* usually last process gives overall exitstatus,
7162 * but with "set -o pipefail", last *failed* process does */
7163 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
7164 break;
7165 }
7166 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
7167/* Note: *non-interactive* bash does not continue if all processes in fg pipe
7168 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
7169 * and "killall -STOP cat" */
7170 if (G_interactive_fd) {
7171#if ENABLE_HUSH_JOB
7172 if (fg_pipe->alive_cmds != 0)
7173 insert_bg_job(fg_pipe);
7174#endif
7175 return rcode;
7176 }
7177 if (fg_pipe->alive_cmds == 0)
7178 return rcode;
7179 }
7180 /* There are still running processes in the fg pipe */
7181 goto wait_more; /* do waitpid again */
7182 }
7183 /* it wasnt fg_pipe, look for process in bg pipes */
7184 }
7185
7186#if ENABLE_HUSH_JOB
7187 /* We asked to wait for bg or orphaned children */
7188 /* No need to remember exitcode in this case */
7189 for (pi = G.job_list; pi; pi = pi->next) {
7190 for (i = 0; i < pi->num_cmds; i++) {
7191 if (pi->cmds[i].pid == childpid)
7192 goto found_pi_and_prognum;
7193 }
7194 } 7235 }
7195 /* Happens when shell is used as init process (init=/bin/sh) */ 7236 if (childpid == waitfor_pid) {
7196 debug_printf("checkjobs: pid %d was not in our list!\n", childpid); 7237 rcode = WEXITSTATUS(status);
7197 continue; /* do waitpid again */ 7238 if (WIFSIGNALED(status))
7198 7239 rcode = 128 + WTERMSIG(status);
7199 found_pi_and_prognum: 7240 rcode++;
7200 if (dead) { 7241 break; /* "wait PID" called us, give it exitcode+1 */
7201 /* child exited */
7202 pi->cmds[i].pid = 0;
7203 pi->alive_cmds--;
7204 if (!pi->alive_cmds) {
7205 if (G_interactive_fd)
7206 printf(JOB_STATUS_FORMAT, pi->jobid,
7207 "Done", pi->cmdtext);
7208 delete_finished_bg_job(pi);
7209 }
7210 } else {
7211 /* child stopped */
7212 pi->stopped_cmds++;
7213 } 7242 }
7214#endif 7243 /* This wasn't one of our processes, or */
7244 /* fg_pipe still has running processes, do waitpid again */
7215 } /* while (waitpid succeeds)... */ 7245 } /* while (waitpid succeeds)... */
7216 7246
7217 return rcode; 7247 return rcode;
@@ -7221,7 +7251,7 @@ static int checkjobs(struct pipe *fg_pipe)
7221static int checkjobs_and_fg_shell(struct pipe *fg_pipe) 7251static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
7222{ 7252{
7223 pid_t p; 7253 pid_t p;
7224 int rcode = checkjobs(fg_pipe); 7254 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
7225 if (G_saved_tty_pgrp) { 7255 if (G_saved_tty_pgrp) {
7226 /* Job finished, move the shell to the foreground */ 7256 /* Job finished, move the shell to the foreground */
7227 p = getpgrp(); /* our process group id */ 7257 p = getpgrp(); /* our process group id */
@@ -7893,7 +7923,7 @@ static int run_list(struct pipe *pi)
7893 /* else: e.g. "continue 2" should *break* once, *then* continue */ 7923 /* else: e.g. "continue 2" should *break* once, *then* continue */
7894 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ 7924 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
7895 if (G.depth_break_continue != 0 || fbc == BC_BREAK) { 7925 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
7896 checkjobs(NULL); 7926 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7897 break; 7927 break;
7898 } 7928 }
7899 /* "continue": simulate end of loop */ 7929 /* "continue": simulate end of loop */
@@ -7902,7 +7932,7 @@ static int run_list(struct pipe *pi)
7902 } 7932 }
7903#endif 7933#endif
7904 if (G_flag_return_in_progress == 1) { 7934 if (G_flag_return_in_progress == 1) {
7905 checkjobs(NULL); 7935 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7906 break; 7936 break;
7907 } 7937 }
7908 } else if (pi->followup == PIPE_BG) { 7938 } else if (pi->followup == PIPE_BG) {
@@ -7929,7 +7959,7 @@ static int run_list(struct pipe *pi)
7929 } else 7959 } else
7930#endif 7960#endif
7931 { /* This one just waits for completion */ 7961 { /* This one just waits for completion */
7932 rcode = checkjobs(pi); 7962 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
7933 debug_printf_exec(": checkjobs exitcode %d\n", rcode); 7963 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
7934 check_and_run_traps(); 7964 check_and_run_traps();
7935 } 7965 }
@@ -7943,7 +7973,7 @@ static int run_list(struct pipe *pi)
7943 cond_code = rcode; 7973 cond_code = rcode;
7944#endif 7974#endif
7945 check_jobs_and_continue: 7975 check_jobs_and_continue:
7946 checkjobs(NULL); 7976 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7947 dont_check_jobs_but_continue: ; 7977 dont_check_jobs_but_continue: ;
7948#if ENABLE_HUSH_LOOPS 7978#if ENABLE_HUSH_LOOPS
7949 /* Beware of "while false; true; do ..."! */ 7979 /* Beware of "while false; true; do ..."! */
@@ -9408,9 +9438,68 @@ static int FAST_FUNC builtin_umask(char **argv)
9408} 9438}
9409 9439
9410/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 9440/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
9441static int wait_for_child_or_signal(pid_t waitfor_pid)
9442{
9443 int ret = 0;
9444 for (;;) {
9445 int sig;
9446 sigset_t oldset, allsigs;
9447
9448 /* waitpid is not interruptible by SA_RESTARTed
9449 * signals which we use. Thus, this ugly dance:
9450 */
9451
9452 /* Make sure possible SIGCHLD is stored in kernel's
9453 * pending signal mask before we call waitpid.
9454 * Or else we may race with SIGCHLD, lose it,
9455 * and get stuck in sigwaitinfo...
9456 */
9457 sigfillset(&allsigs);
9458 sigprocmask(SIG_SETMASK, &allsigs, &oldset);
9459
9460 if (!sigisemptyset(&G.pending_set)) {
9461 /* Crap! we raced with some signal! */
9462 // sig = 0;
9463 goto restore;
9464 }
9465
9466 /*errno = 0; - checkjobs does this */
9467 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
9468 /* if ECHILD, there are no children (ret is -1 or 0) */
9469 /* if ret == 0, no children changed state */
9470 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
9471 if (errno == ECHILD || ret--) {
9472 if (ret < 0) /* if ECHILD, may need to fix */
9473 ret = 0;
9474 sigprocmask(SIG_SETMASK, &oldset, NULL);
9475 break;
9476 }
9477
9478 /* Wait for SIGCHLD or any other signal */
9479 //sig = sigwaitinfo(&allsigs, NULL);
9480 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
9481 /* Note: sigsuspend invokes signal handler */
9482 sigsuspend(&oldset);
9483 restore:
9484 sigprocmask(SIG_SETMASK, &oldset, NULL);
9485
9486 /* So, did we get a signal? */
9487 //if (sig > 0)
9488 // raise(sig); /* run handler */
9489 sig = check_and_run_traps();
9490 if (sig /*&& sig != SIGCHLD - always true */) {
9491 /* see note 2 */
9492 ret = 128 + sig;
9493 break;
9494 }
9495 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
9496 }
9497 return ret;
9498}
9499
9411static int FAST_FUNC builtin_wait(char **argv) 9500static int FAST_FUNC builtin_wait(char **argv)
9412{ 9501{
9413 int ret = EXIT_SUCCESS; 9502 int ret;
9414 int status; 9503 int status;
9415 9504
9416 argv = skip_dash_dash(argv); 9505 argv = skip_dash_dash(argv);
@@ -9431,74 +9520,51 @@ static int FAST_FUNC builtin_wait(char **argv)
9431 * ^C <-- after ~4 sec from keyboard 9520 * ^C <-- after ~4 sec from keyboard
9432 * $ 9521 * $
9433 */ 9522 */
9434 while (1) { 9523 return wait_for_child_or_signal(0 /*(no pid to wait for)*/);
9435 int sig;
9436 sigset_t oldset, allsigs;
9437
9438 /* waitpid is not interruptible by SA_RESTARTed
9439 * signals which we use. Thus, this ugly dance:
9440 */
9441
9442 /* Make sure possible SIGCHLD is stored in kernel's
9443 * pending signal mask before we call waitpid.
9444 * Or else we may race with SIGCHLD, lose it,
9445 * and get stuck in sigwaitinfo...
9446 */
9447 sigfillset(&allsigs);
9448 sigprocmask(SIG_SETMASK, &allsigs, &oldset);
9449
9450 if (!sigisemptyset(&G.pending_set)) {
9451 /* Crap! we raced with some signal! */
9452 // sig = 0;
9453 goto restore;
9454 }
9455
9456 checkjobs(NULL); /* waitpid(WNOHANG) inside */
9457 if (errno == ECHILD) {
9458 sigprocmask(SIG_SETMASK, &oldset, NULL);
9459 break;
9460 }
9461
9462 /* Wait for SIGCHLD or any other signal */
9463 //sig = sigwaitinfo(&allsigs, NULL);
9464 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
9465 /* Note: sigsuspend invokes signal handler */
9466 sigsuspend(&oldset);
9467 restore:
9468 sigprocmask(SIG_SETMASK, &oldset, NULL);
9469
9470 /* So, did we get a signal? */
9471 //if (sig > 0)
9472 // raise(sig); /* run handler */
9473 sig = check_and_run_traps();
9474 if (sig /*&& sig != SIGCHLD - always true */) {
9475 /* see note 2 */
9476 ret = 128 + sig;
9477 break;
9478 }
9479 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
9480 }
9481 return ret;
9482 } 9524 }
9483 9525
9484 /* This is probably buggy wrt interruptible-ness */ 9526 /* TODO: support "wait %jobspec" */
9485 while (*argv) { 9527 do {
9486 pid_t pid = bb_strtou(*argv, NULL, 10); 9528 pid_t pid = bb_strtou(*argv, NULL, 10);
9487 if (errno) { 9529 if (errno || pid <= 0) {
9488 /* mimic bash message */ 9530 /* mimic bash message */
9489 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); 9531 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
9490 return EXIT_FAILURE; 9532 ret = EXIT_FAILURE;
9533 continue; /* bash checks all argv[] */
9534 }
9535 /* Do we have such child? */
9536 ret = waitpid(pid, &status, WNOHANG);
9537 if (ret < 0) {
9538 /* No */
9539 if (errno == ECHILD) {
9540 if (G.last_bg_pid > 0 && pid == G.last_bg_pid) {
9541 /* "wait $!" but last bg task has already exited. Try:
9542 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
9543 * In bash it prints exitcode 0, then 3.
9544 */
9545 ret = 0; /* FIXME */
9546 continue;
9547 }
9548 /* Example: "wait 1". mimic bash message */
9549 bb_error_msg("wait: pid %d is not a child of this shell", (int)pid);
9550 } else {
9551 /* ??? */
9552 bb_perror_msg("wait %s", *argv);
9553 }
9554 ret = 127;
9555 continue; /* bash checks all argv[] */
9491 } 9556 }
9492 if (waitpid(pid, &status, 0) == pid) { 9557 if (ret == 0) {
9558 /* Yes, and it still runs */
9559 ret = wait_for_child_or_signal(pid);
9560 } else {
9561 /* Yes, and it just exited */
9562 process_wait_result(NULL, pid, status);
9493 ret = WEXITSTATUS(status); 9563 ret = WEXITSTATUS(status);
9494 if (WIFSIGNALED(status)) 9564 if (WIFSIGNALED(status))
9495 ret = 128 + WTERMSIG(status); 9565 ret = 128 + WTERMSIG(status);
9496 } else {
9497 bb_perror_msg("wait %s", *argv);
9498 ret = 127;
9499 } 9566 }
9500 argv++; 9567 } while (*++argv);
9501 }
9502 9568
9503 return ret; 9569 return ret;
9504} 9570}
diff --git a/shell/hush_test/hush-misc/wait1.right b/shell/hush_test/hush-misc/wait1.right
new file mode 100644
index 000000000..20f514da0
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait1.right
@@ -0,0 +1,2 @@
10
2[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
new file mode 100755
index 000000000..f9cf6d48c
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -0,0 +1,3 @@
1sleep 2 & sleep 1 & wait $!
2echo $?
3jobs
diff --git a/shell/hush_test/hush-misc/wait2.right b/shell/hush_test/hush-misc/wait2.right
new file mode 100644
index 000000000..f018c2c98
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait2.right
@@ -0,0 +1,2 @@
10
2[1] Running sleep 3
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
new file mode 100755
index 000000000..be20f95a5
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -0,0 +1,4 @@
1sleep 3 & sleep 2 & sleep 1
2wait $!
3echo $?
4jobs
diff --git a/shell/hush_test/hush-misc/wait3.right b/shell/hush_test/hush-misc/wait3.right
new file mode 100644
index 000000000..5437b1d73
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait3.right
@@ -0,0 +1,2 @@
13
2[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
new file mode 100755
index 000000000..ac541c3fc
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -0,0 +1,3 @@
1sleep 2 & (sleep 1;exit 3) & wait $!
2echo $?
3jobs