summaryrefslogtreecommitdiffstats
path: root/package/busybox/busybox-1.9.0-hush_nommu_tick.patch
blob: 3174503aa05fbcf398bcb39f2e59b6e4fb913539 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
--- busybox-1.9.0/include/libbb.h	Fri Dec 21 23:00:31 2007
+++ busybox-1.9.0-hush_nommu_tick/include/libbb.h	Sun Feb 10 13:16:38 2008
@@ -641,6 +641,9 @@
   void re_exec(char **argv) ATTRIBUTE_NORETURN;
   void forkexit_or_rexec(char **argv);
   extern bool re_execed;
+  int  BUG_fork_is_unavailable_on_nommu(void);
+  int  BUG_daemon_is_unavailable_on_nommu(void);
+  void BUG_bb_daemonize_is_unavailable_on_nommu(void);
 # define fork()          BUG_fork_is_unavailable_on_nommu()
 # define daemon(a,b)     BUG_daemon_is_unavailable_on_nommu()
 # define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu()
--- busybox-1.9.0/shell/hush.c	Mon Dec 24 15:26:23 2007
+++ busybox-1.9.0-hush_nommu_tick/shell/hush.c	Mon Feb 11 09:34:27 2008
@@ -80,18 +80,28 @@
 #include <glob.h>      /* glob, of course */
 #include <getopt.h>    /* should be pretty obvious */
 /* #include <dmalloc.h> */
+extern char **environ;
+#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
 
-extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
 
-#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
+#if !BB_MMU && ENABLE_HUSH_TICK
+//#undef ENABLE_HUSH_TICK
+//#define ENABLE_HUSH_TICK 0
+#warning On NOMMU, hush command substitution is dangerous.
+#warning Dont use it for commands which produce lots of output.
+#warning For more info see shell/hush.c, generate_stream_from_list().
+#endif
 
+#if !BB_MMU && ENABLE_HUSH_JOB
+#undef ENABLE_HUSH_JOB
+#define ENABLE_HUSH_JOB 0
+#endif
 
-#if !BB_MMU
-/* A bit drastic. Can allow some simpler commands
- * by analysing command in generate_stream_from_list()
- */
-#undef ENABLE_HUSH_TICK
-#define ENABLE_HUSH_TICK 0
+#if !ENABLE_HUSH_INTERACTIVE
+#undef ENABLE_FEATURE_EDITING
+#define ENABLE_FEATURE_EDITING 0
+#undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
+#define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
 #endif
 
 
@@ -178,13 +188,6 @@
 #endif
 
 
-#if !ENABLE_HUSH_INTERACTIVE
-#undef ENABLE_FEATURE_EDITING
-#define ENABLE_FEATURE_EDITING 0
-#undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
-#define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
-#endif
-
 #define SPECIAL_VAR_SYMBOL   3
 
 #define PARSEFLAG_EXIT_FROM_LOOP 1
@@ -510,10 +513,10 @@
 static int free_pipe(struct pipe *pi, int indent);
 /*  really run the final data structures: */
 static int setup_redirects(struct child_prog *prog, int squirrel[]);
-static int run_list_real(struct pipe *pi);
+static int run_list(struct pipe *pi);
 static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN;
 static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN;
-static int run_pipe_real(struct pipe *pi);
+static int run_pipe(struct pipe *pi);
 /*   extended glob support: */
 static char **globhack(const char *src, char **strings);
 static int glob_needed(const char *s);
@@ -1418,7 +1421,7 @@
 	}
 }
 
-/* Called after [v]fork() in run_pipe_real(), or from builtin_exec().
+/* Called after [v]fork() in run_pipe(), or from builtin_exec().
  * Never returns.
  * XXX no exit() here.  If you don't exec, use _exit instead.
  * The at_exit handlers apparently confuse the calling process,
@@ -1440,9 +1443,8 @@
 	/* If a variable is assigned in a forest, and nobody listens,
 	 * was it ever really set?
 	 */
-	if (argv[0] == NULL) {
+	if (!argv[0])
 		_exit(EXIT_SUCCESS);
-	}
 
 	argv = expand_strvec_to_strvec(argv);
 
@@ -1486,15 +1488,15 @@
 	_exit(1);
 }
 
-/* Called after [v]fork() in run_pipe_real()
+/* Called after [v]fork() in run_pipe()
  */
 static void pseudo_exec(struct child_prog *child)
 {
 // FIXME: buggy wrt NOMMU! Must not modify any global data
-// until it does exec/_exit, but currently it does.
-	if (child->argv) {
+// until it does exec/_exit, but currently it does
+// (puts malloc'ed stuff into environment)
+	if (child->argv)
 		pseudo_exec_argv(child->argv);
-	}
 
 	if (child->group) {
 #if !BB_MMU
@@ -1503,11 +1505,12 @@
 		int rcode;
 
 #if ENABLE_HUSH_INTERACTIVE
-		debug_printf_exec("pseudo_exec: setting interactive_fd=0\n");
-		interactive_fd = 0;    /* crucial!!!! */
+// run_list_level now takes care of it?
+//		debug_printf_exec("pseudo_exec: setting interactive_fd=0\n");
+//		interactive_fd = 0;    /* crucial!!!! */
 #endif
-		debug_printf_exec("pseudo_exec: run_list_real\n");
-		rcode = run_list_real(child->group);
+		debug_printf_exec("pseudo_exec: run_list\n");
+		rcode = run_list(child->group);
 		/* OK to leak memory by not calling free_pipe_list,
 		 * since this process is about to exit */
 		_exit(rcode);
@@ -1649,6 +1652,7 @@
 // + killall -STOP cat
 
  wait_more:
+// TODO: safe_waitpid?
 	while ((childpid = waitpid(-1, &status, attributes)) > 0) {
 		const int dead = WIFEXITED(status) || WIFSIGNALED(status);
 
@@ -1673,7 +1677,7 @@
 					if (dead) {
 						fg_pipe->progs[i].pid = 0;
 						fg_pipe->running_progs--;
-						if (i == fg_pipe->num_progs-1)
+						if (i == fg_pipe->num_progs - 1)
 							/* last process gives overall exitstatus */
 							rcode = WEXITSTATUS(status);
 					} else {
@@ -1754,13 +1758,13 @@
 }
 #endif
 
-/* run_pipe_real() starts all the jobs, but doesn't wait for anything
+/* run_pipe() starts all the jobs, but doesn't wait for anything
  * to finish.  See checkjobs().
  *
  * return code is normally -1, when the caller has to wait for children
  * to finish to determine the exit status of the pipe.  If the pipe
  * is a simple builtin command, however, the action is done by the
- * time run_pipe_real returns, and the exit code is provided as the
+ * time run_pipe returns, and the exit code is provided as the
  * return value.
  *
  * The input of the pipe is always stdin, the output is always
@@ -1773,11 +1777,11 @@
  * Returns -1 only if started some children. IOW: we have to
  * mask out retvals of builtins etc with 0xff!
  */
-static int run_pipe_real(struct pipe *pi)
+static int run_pipe(struct pipe *pi)
 {
 	int i;
-	int nextin, nextout;
-	int pipefds[2];				/* pipefds[0] is for reading */
+	int nextin;
+	int pipefds[2];		/* pipefds[0] is for reading */
 	struct child_prog *child;
 	const struct built_in_command *x;
 	char *p;
@@ -1786,9 +1790,8 @@
 	int rcode;
 	const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG);
 
-	debug_printf_exec("run_pipe_real start: single_fg=%d\n", single_fg);
+	debug_printf_exec("run_pipe start: single_fg=%d\n", single_fg);
 
-	nextin = 0;
 #if ENABLE_HUSH_JOB
 	pi->pgrp = -1;
 #endif
@@ -1803,11 +1806,11 @@
 	if (single_fg && child->group && child->subshell == 0) {
 		debug_printf("non-subshell grouping\n");
 		setup_redirects(child, squirrel);
-		debug_printf_exec(": run_list_real\n");
-		rcode = run_list_real(child->group);
+		debug_printf_exec(": run_list\n");
+		rcode = run_list(child->group) & 0xff;
 		restore_redirects(squirrel);
-		debug_printf_exec("run_pipe_real return %d\n", rcode);
-		return rcode; // do we need to add '... & 0xff' ?
+		debug_printf_exec("run_pipe return %d\n", rcode);
+		return rcode;
 	}
 
 	if (single_fg && child->argv != NULL) {
@@ -1849,7 +1852,7 @@
 				rcode = x->function(argv_expanded) & 0xff;
 				free(argv_expanded);
 				restore_redirects(squirrel);
-				debug_printf_exec("run_pipe_real return %d\n", rcode);
+				debug_printf_exec("run_pipe return %d\n", rcode);
 				return rcode;
 			}
 		}
@@ -1866,20 +1869,21 @@
 				rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff;
 				free(argv_expanded);
 				restore_redirects(squirrel);
-				debug_printf_exec("run_pipe_real return %d\n", rcode);
+				debug_printf_exec("run_pipe return %d\n", rcode);
 				return rcode;
 			}
 		}
 #endif
 	}
 
-	/* Going to fork a child per each pipe member */
-	pi->running_progs = 0;
-
 	/* Disable job control signals for shell (parent) and
 	 * for initial child code after fork */
 	set_jobctrl_sighandler(SIG_IGN);
 
+	/* Going to fork a child per each pipe member */
+	pi->running_progs = 0;
+	nextin = 0;
+
 	for (i = 0; i < pi->num_progs; i++) {
 		child = &(pi->progs[i]);
 		if (child->argv)
@@ -1888,42 +1892,34 @@
 			debug_printf_exec(": pipe member with no argv\n");
 
 		/* pipes are inserted between pairs of commands */
-		if ((i + 1) < pi->num_progs) {
-			pipe(pipefds);
-			nextout = pipefds[1];
-		} else {
-			nextout = 1;
-			pipefds[0] = -1;
-		}
+		pipefds[0] = 0;
+		pipefds[1] = 1;
+		if ((i + 1) < pi->num_progs)
+			xpipe(pipefds);
 
-		/* XXX test for failed fork()? */
-#if BB_MMU
-		child->pid = fork();
-#else
-		child->pid = vfork();
-#endif
+		child->pid = BB_MMU ? fork() : vfork();
 		if (!child->pid) { /* child */
-			/* Every child adds itself to new process group
-			 * with pgid == pid of first child in pipe */
 #if ENABLE_HUSH_JOB
+			/* Every child adds itself to new process group
+			 * with pgid == pid_of_first_child_in_pipe */
 			if (run_list_level == 1 && interactive_fd) {
+				pid_t pgrp;
 				/* Don't do pgrp restore anymore on fatal signals */
 				set_fatal_sighandler(SIG_DFL);
-				if (pi->pgrp < 0) /* true for 1st process only */
-					pi->pgrp = getpid();
-				if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) {
+				pgrp = pi->pgrp;
+				if (pgrp < 0) /* true for 1st process only */
+					pgrp = getpid();
+				if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) {
 					/* We do it in *every* child, not just first,
 					 * to avoid races */
-					tcsetpgrp(interactive_fd, pi->pgrp);
+					tcsetpgrp(interactive_fd, pgrp);
 				}
 			}
 #endif
-			/* in non-interactive case fatal sigs are already SIG_DFL */
 			xmove_fd(nextin, 0);
-			xmove_fd(nextout, 1);
-			if (pipefds[0] != -1) {
-				close(pipefds[0]);  /* opposite end of our output pipe */
-			}
+			xmove_fd(pipefds[1], 1); /* write end */
+			if (pipefds[0] > 1)
+				close(pipefds[0]); /* read end */
 			/* Like bash, explicit redirects override pipes,
 			 * and the pipe fd is available for dup'ing. */
 			setup_redirects(child, NULL);
@@ -1932,26 +1928,35 @@
 			set_jobctrl_sighandler(SIG_DFL);
 			set_misc_sighandler(SIG_DFL);
 			signal(SIGCHLD, SIG_DFL);
-			pseudo_exec(child);
+			pseudo_exec(child); /* does not return */
 		}
 
-		pi->running_progs++;
-
+		if (child->pid < 0) { /* [v]fork failed */
+			/* Clearly indicate, was it fork or vfork */
+			bb_perror_msg(BB_MMU ? "fork" : "vfork");
+		} else {
+			pi->running_progs++;
 #if ENABLE_HUSH_JOB
-		/* Second and next children need to know pid of first one */
-		if (pi->pgrp < 0)
-			pi->pgrp = child->pid;
+			/* Second and next children need to know pid of first one */
+			if (pi->pgrp < 0)
+				pi->pgrp = child->pid;
 #endif
-		if (nextin != 0)
-			close(nextin);
-		if (nextout != 1)
-			close(nextout);
+		}
 
-		/* If there isn't another process, nextin is garbage
-		   but it doesn't matter */
+		if (i)
+			close(nextin);
+		if ((i + 1) < pi->num_progs)
+			close(pipefds[1]); /* write end */
+		/* Pass read (output) pipe end to next iteration */
 		nextin = pipefds[0];
 	}
-	debug_printf_exec("run_pipe_real return -1\n");
+
+	if (!pi->running_progs) {
+		debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
+		return 1;
+	}
+
+	debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->running_progs);
 	return -1;
 }
 
@@ -2020,7 +2025,7 @@
 
 /* NB: called by pseudo_exec, and therefore must not modify any
  * global data until exec/_exit (we can be a child after vfork!) */
-static int run_list_real(struct pipe *pi)
+static int run_list(struct pipe *pi)
 {
 	struct pipe *rpipe;
 #if ENABLE_HUSH_LOOPS
@@ -2029,7 +2034,6 @@
 	char **for_list = NULL;
 	int flag_rep = 0;
 #endif
-	int save_num_progs;
 	int flag_skip = 1;
 	int rcode = 0; /* probably for gcc only */
 	int flag_restore = 0;
@@ -2041,7 +2045,7 @@
 	reserved_style rword;
 	reserved_style skip_more_for_this_rword = RES_XXXX;
 
-	debug_printf_exec("run_list_real start lvl %d\n", run_list_level + 1);
+	debug_printf_exec("run_list start lvl %d\n", run_list_level + 1);
 
 #if ENABLE_HUSH_LOOPS
 	/* check syntax for "for" */
@@ -2050,7 +2054,7 @@
 		 && (rpipe->next == NULL)
 		) {
 			syntax("malformed for"); /* no IN or no commands after IN */
-			debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level);
+			debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
 			return 1;
 		}
 		if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL)
@@ -2058,7 +2062,7 @@
 		) {
 			/* TODO: what is tested in the first condition? */
 			syntax("malformed for"); /* 2nd condition: not followed by IN */
-			debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level);
+			debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
 			return 1;
 		}
 	}
@@ -2106,9 +2110,10 @@
 		signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
 		signal(SIGINT, handler_ctrl_c);
 	}
-#endif
+#endif /* JOB */
 
 	for (; pi; pi = flag_restore ? rpipe : pi->next) {
+//why?		int save_num_progs;
 		rword = pi->res_word;
 #if ENABLE_HUSH_LOOPS
 		if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
@@ -2181,12 +2186,12 @@
 #endif
 		if (pi->num_progs == 0)
 			continue;
-		save_num_progs = pi->num_progs; /* save number of programs */
-		debug_printf_exec(": run_pipe_real with %d members\n", pi->num_progs);
-		rcode = run_pipe_real(pi);
+//why?		save_num_progs = pi->num_progs;
+		debug_printf_exec(": run_pipe with %d members\n", pi->num_progs);
+		rcode = run_pipe(pi);
 		if (rcode != -1) {
 			/* We only ran a builtin: rcode was set by the return value
-			 * of run_pipe_real(), and we don't need to wait for anything. */
+			 * of run_pipe(), and we don't need to wait for anything. */
 		} else if (pi->followup == PIPE_BG) {
 			/* What does bash do with attempts to background builtins? */
 			/* Even bash 3.2 doesn't do that well with nested bg:
@@ -2199,7 +2204,6 @@
 			rcode = EXIT_SUCCESS;
 		} else {
 #if ENABLE_HUSH_JOB
-			/* Paranoia, just "interactive_fd" should be enough? */
 			if (run_list_level == 1 && interactive_fd) {
 				/* waits for completion, then fg's main shell */
 				rcode = checkjobs_and_fg_shell(pi);
@@ -2213,7 +2217,7 @@
 		}
 		debug_printf_exec(": setting last_return_code=%d\n", rcode);
 		last_return_code = rcode;
-		pi->num_progs = save_num_progs; /* restore number of programs */
+//why?		pi->num_progs = save_num_progs;
 #if ENABLE_HUSH_IF
 		if (rword == RES_IF || rword == RES_ELIF)
 			next_if_code = rcode;  /* can be overwritten a number of times */
@@ -2244,7 +2248,7 @@
 		signal(SIGINT, SIG_IGN);
 	}
 #endif
-	debug_printf_exec("run_list_real lvl %d return %d\n", run_list_level + 1, rcode);
+	debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode);
 	return rcode;
 }
 
@@ -2318,19 +2322,19 @@
 }
 
 /* Select which version we will use */
-static int run_list(struct pipe *pi)
+static int run_and_free_list(struct pipe *pi)
 {
 	int rcode = 0;
-	debug_printf_exec("run_list entered\n");
-	if (fake_mode == 0) {
-		debug_printf_exec(": run_list_real with %d members\n", pi->num_progs);
-		rcode = run_list_real(pi);
+	debug_printf_exec("run_and_free_list entered\n");
+	if (!fake_mode) {
+		debug_printf_exec(": run_list with %d members\n", pi->num_progs);
+		rcode = run_list(pi);
 	}
 	/* free_pipe_list has the side effect of clearing memory.
-	 * In the long run that function can be merged with run_list_real,
+	 * In the long run that function can be merged with run_list,
 	 * but doing that now would hobble the debugging effort. */
-	free_pipe_list(pi, 0);
-	debug_printf_exec("run_list return %d\n", rcode);
+	free_pipe_list(pi, /* indent: */ 0);
+	debug_printf_exec("run_nad_free_list return %d\n", rcode);
 	return rcode;
 }
 
@@ -3224,15 +3228,17 @@
 	int pid, channel[2];
 
 	xpipe(channel);
-	pid = fork();
-	if (pid < 0) {
-		bb_perror_msg_and_die("fork");
-	} else if (pid == 0) {
+/* *** NOMMU WARNING *** */
+/* By using vfork here, we suspend parent till child exits or execs.
+ * If child will not do it before it fills the pipe, it can block forever
+ * in write(STDOUT_FILENO), and parent (shell) will be also stuck.
+ */
+	pid = BB_MMU ? fork() : vfork();
+	if (pid < 0)
+		bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
+	if (pid == 0) { /* child */
 		close(channel[0]);
-		if (channel[1] != 1) {
-			dup2(channel[1], 1);
-			close(channel[1]);
-		}
+		xmove_fd(channel[1], 1);
 		/* Prevent it from trying to handle ctrl-z etc */
 #if ENABLE_HUSH_JOB
 		run_list_level = 1;
@@ -3244,11 +3250,12 @@
 		 * everywhere outside actual command execution. */
 		/*set_jobctrl_sighandler(SIG_IGN);*/
 		set_misc_sighandler(SIG_DFL);
-		_exit(run_list_real(head));   /* leaks memory */
+		_exit(run_list(head));   /* leaks memory */
 	}
 	close(channel[1]);
 	pf = fdopen(channel[0], "r");
 	return pf;
+	/* head is freed by the caller */
 }
 
 /* Return code is exit status of the process that is run. */
@@ -3272,7 +3279,8 @@
 	b_free(&result);
 
 	p = generate_stream_from_list(inner.list_head);
-	if (p == NULL) return 1;
+	if (p == NULL)
+		return 1;
 	close_on_exec_on(fileno(p));
 	setup_file_in_str(&pipe_str, p);
 
@@ -3297,7 +3305,7 @@
 	 * at the same time.  That would be a lot of work, and contrary
 	 * to the KISS philosophy of this program. */
 	retcode = fclose(p);
-	free_pipe_list(inner.list_head, 0);
+	free_pipe_list(inner.list_head, /* indent: */ 0);
 	debug_printf("closed FILE from child, retcode=%d\n", retcode);
 	return retcode;
 }
@@ -3677,8 +3685,8 @@
 			done_word(&temp, &ctx);
 			done_pipe(&ctx, PIPE_SEQ);
 			debug_print_tree(ctx.list_head, 0);
-			debug_printf_exec("parse_stream_outer: run_list\n");
-			run_list(ctx.list_head);
+			debug_printf_exec("parse_stream_outer: run_and_free_list\n");
+			run_and_free_list(ctx.list_head);
 		} else {
 			if (ctx.old_flag != 0) {
 				free(ctx.stack);
@@ -3687,7 +3695,7 @@
 			temp.nonnull = 0;
 			temp.o_quote = 0;
 			inp->p = NULL;
-			free_pipe_list(ctx.list_head, 0);
+			free_pipe_list(ctx.list_head, /* indent: */ 0);
 		}
 		b_free(&temp);
 	} while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP));   /* loop on syntax errors, return on EOF */
@@ -3901,14 +3909,14 @@
 
 	if (argv[optind] == NULL) {
 		opt = parse_and_run_file(stdin);
-		goto final_return;
+	} else {
+		debug_printf("\nrunning script '%s'\n", argv[optind]);
+		global_argv = argv + optind;
+		global_argc = argc - optind;
+		input = xfopen(argv[optind], "r");
+		fcntl(fileno(input), F_SETFD, FD_CLOEXEC);
+		opt = parse_and_run_file(input);
 	}
-
-	debug_printf("\nrunning script '%s'\n", argv[optind]);
-	global_argv = argv + optind;
-	global_argc = argc - optind;
-	input = xfopen(argv[optind], "r");
-	opt = parse_and_run_file(input);
 
  final_return: