aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2025-08-09 17:36:18 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2025-08-09 17:36:18 +0200
commitdf154028dc1257a03cbbcf322a6c31eb9552a661 (patch)
tree58cb0eff7d98f8cfa29625a3ef81d6d898e7a9b1 /shell
parent9f490785e00bd5c4deee09d9fee3badd8d70ada4 (diff)
downloadbusybox-w32-df154028dc1257a03cbbcf322a6c31eb9552a661.tar.gz
busybox-w32-df154028dc1257a03cbbcf322a6c31eb9552a661.tar.bz2
busybox-w32-df154028dc1257a03cbbcf322a6c31eb9552a661.zip
ash: eval: Add vfork support
Upstream commit: Date: Sat, 19 May 2018 02:39:56 +0800 eval: Add vfork support This patch adds basic vfork support for the case of a simple command. Upstream commit: Date: Tue, 12 Jan 2021 17:11:19 +1100 jobs: Always reset SIGINT/SIGQUIT handlers On Fri, Jan 08, 2021 at 08:55:41PM +0000, Harald van Dijk wrote: > On 18/05/2018 19:39, Herbert Xu wrote: > > This patch adds basic vfork support for the case of a simple command. > > ... @@ -879,17 +892,30 @@ forkchild(struct job *jp, union node *n, int > > mode) > > } > > } > > if (!oldlvl && iflag) { > > - setsignal(SIGINT); > > - setsignal(SIGQUIT); > > + if (mode != FORK_BG) { > > + setsignal(SIGINT); > > + setsignal(SIGQUIT); > > + } > > setsignal(SIGTERM); > > } > > + > > + if (lvforked) > > + return; > > + > > for (jp = curjob; jp; jp = jp->prev_job) > > freejob(jp); > > } > > This leaves SIGQUIT ignored in background jobs in interactive shells. > > ENV= dash -ic 'dash -c "kill -QUIT \$\$; echo huh" & wait' > > As of dash 0.5.11, this prints "huh". Before, the subprocess process killed > itself before it could print anything. Other shells do not leave SIGQUIT > ignored. > > (In a few other shells, this also prints "huh", but in those other shells, > that is because the inner shell chooses to ignore SIGQUIT, not because the > outer shell leaves it ignored.) Thanks for catching this. I have no idea how that got in there and it makes no sense whatsoever. This patch removes the if conditional. Fixes: e94a964e7dd0 ("eval: Add vfork support") Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c137
-rw-r--r--shell/hush.c3
2 files changed, 103 insertions, 37 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 81b4a8951..40f207d99 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -408,6 +408,7 @@ struct globals_misc {
408 uint8_t back_exitstatus;/* exit status of backquoted command */ 408 uint8_t back_exitstatus;/* exit status of backquoted command */
409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
410 smallint inps4; /* Prevent PS4 nesting. */ 410 smallint inps4; /* Prevent PS4 nesting. */
411 smallint vforked;
411 int savestatus; /* exit status of last command outside traps */ 412 int savestatus; /* exit status of last command outside traps */
412 int rootpid; /* pid of main shell */ 413 int rootpid; /* pid of main shell */
413 /* shell level: 0 for the main shell, 1 for its children, and so on */ 414 /* shell level: 0 for the main shell, 1 for its children, and so on */
@@ -431,7 +432,7 @@ struct globals_misc {
431 * but we do read it async. 432 * but we do read it async.
432 */ 433 */
433 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 434 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
434 volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */ 435 volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */
435 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 436 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
436 smallint exception_type; /* kind of exception: */ 437 smallint exception_type; /* kind of exception: */
437#define EXINT 0 /* SIGINT received */ 438#define EXINT 0 /* SIGINT received */
@@ -506,6 +507,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
506#define back_exitstatus (G_misc.back_exitstatus ) 507#define back_exitstatus (G_misc.back_exitstatus )
507#define job_warning (G_misc.job_warning) 508#define job_warning (G_misc.job_warning)
508#define inps4 (G_misc.inps4 ) 509#define inps4 (G_misc.inps4 )
510#define vforked (G_misc.vforked )
509#define savestatus (G_misc.savestatus ) 511#define savestatus (G_misc.savestatus )
510#define rootpid (G_misc.rootpid ) 512#define rootpid (G_misc.rootpid )
511#define shlvl (G_misc.shlvl ) 513#define shlvl (G_misc.shlvl )
@@ -580,6 +582,17 @@ var_end(const char *var)
580 return var; 582 return var;
581} 583}
582 584
585/* Our signal logic never blocks individual signals
586 * using signal mask - only by setting SIG_IGN handler.
587 * Therefore just unmasking all of them instead of "restore old mask"
588 * approach is safe, modulo a case where the shell itself inherited
589 * a _masked_ signal.
590 */
591static void
592sigclearmask(void)
593{
594 sigprocmask_allsigs(SIG_UNBLOCK);
595}
583 596
584/* ============ Parser data */ 597/* ============ Parser data */
585 598
@@ -678,6 +691,10 @@ raise_exception(int e)
678 if (exception_handler == NULL) 691 if (exception_handler == NULL)
679 abort(); 692 abort();
680#endif 693#endif
694
695 if (vforked)
696 _exit(exitstatus);
697
681 INTOFF; 698 INTOFF;
682 exception_type = e; 699 exception_type = e;
683 longjmp(exception_handler->loc, 1); 700 longjmp(exception_handler->loc, 1);
@@ -1481,6 +1498,7 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
1481 /* NOTREACHED */ 1498 /* NOTREACHED */
1482} 1499}
1483 1500
1501/* This function is called sh_error() in dash */
1484static void ash_msg_and_raise_error(const char *, ...) NORETURN; 1502static void ash_msg_and_raise_error(const char *, ...) NORETURN;
1485static void 1503static void
1486ash_msg_and_raise_error(const char *msg, ...) 1504ash_msg_and_raise_error(const char *msg, ...)
@@ -3665,7 +3683,6 @@ struct job {
3665 struct job *prev_job; /* previous job */ 3683 struct job *prev_job; /* previous job */
3666}; 3684};
3667 3685
3668static struct job *makejob(int);
3669static int forkshell(struct job *, union node *, int); 3686static int forkshell(struct job *, union node *, int);
3670static int waitforjob(struct job *); 3687static int waitforjob(struct job *);
3671 3688
@@ -3688,15 +3705,23 @@ ignoresig(int signo)
3688 /* No, need to do it */ 3705 /* No, need to do it */
3689 signal(signo, SIG_IGN); 3706 signal(signo, SIG_IGN);
3690 } 3707 }
3691 sigmode[signo - 1] = S_HARD_IGN; 3708 if (!vforked)
3709 sigmode[signo - 1] = S_HARD_IGN;
3692} 3710}
3693 3711
3694/* 3712/*
3695 * Only one usage site - in setsignal() 3713 * Only one usage site - in setsignal()
3714 * This function is called onsig() in dash
3696 */ 3715 */
3697static void 3716static void
3698signal_handler(int signo) 3717signal_handler(int signo)
3699{ 3718{
3719 // parent momentarily has vforked == 1 too, but it masks
3720 // all signals until after it resets vforked to 0.
3721 if (vforked)
3722 /* We are vfork child, DO NOT MODIFY ANY VARIABLES! */
3723 return;
3724
3700 if (signo == SIGCHLD) { 3725 if (signo == SIGCHLD) {
3701 gotsigchld = 1; 3726 gotsigchld = 1;
3702 if (!trap[SIGCHLD]) 3727 if (!trap[SIGCHLD])
@@ -3724,10 +3749,13 @@ signal_handler(int signo)
3724static void 3749static void
3725setsignal(int signo) 3750setsignal(int signo)
3726{ 3751{
3752 int lvforked;
3727 char *t; 3753 char *t;
3728 char cur_act, new_act; 3754 char cur_act, new_act;
3729 struct sigaction act; 3755 struct sigaction act;
3730 3756
3757 lvforked = vforked;
3758
3731 t = trap[signo]; 3759 t = trap[signo];
3732 new_act = S_DFL; 3760 new_act = S_DFL;
3733 if (t != NULL) { /* trap for this sig is set */ 3761 if (t != NULL) { /* trap for this sig is set */
@@ -3736,7 +3764,7 @@ setsignal(int signo)
3736 new_act = S_IGN; 3764 new_act = S_IGN;
3737 } 3765 }
3738 3766
3739 if (rootshell && new_act == S_DFL) { 3767 if (rootshell && new_act == S_DFL && !lvforked) {
3740 switch (signo) { 3768 switch (signo) {
3741 case SIGINT: 3769 case SIGINT:
3742 if (iflag || minusc || sflag == 0) 3770 if (iflag || minusc || sflag == 0)
@@ -3806,8 +3834,8 @@ setsignal(int signo)
3806 if (cur_act == S_HARD_IGN || cur_act == new_act) 3834 if (cur_act == S_HARD_IGN || cur_act == new_act)
3807 return; 3835 return;
3808 3836
3809 *t = new_act; 3837 if (!lvforked)
3810 3838 *t = new_act;
3811 act.sa_handler = SIG_DFL; 3839 act.sa_handler = SIG_DFL;
3812 switch (new_act) { 3840 switch (new_act) {
3813 case S_CATCH: 3841 case S_CATCH:
@@ -4342,15 +4370,16 @@ waitproc(int block, int *status)
4342 if (err || (err = -!block)) 4370 if (err || (err = -!block))
4343 break; 4371 break;
4344 4372
4345 sigfillset(&oldmask);
4346 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4347 while (!gotsigchld && !pending_sig)
4348 sigsuspend(&oldmask);
4349 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4350 //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): 4373 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4351 //while (!gotsigchld && !pending_sig) 4374 //while (!gotsigchld && !pending_sig)
4352 // pause(); 4375 // pause();
4353 4376
4377 sigblockall(&oldmask);
4378
4379 while (!gotsigchld && !pending_sig)
4380 sigsuspend(&oldmask);
4381
4382 sigclearmask();
4354 } while (gotsigchld); 4383 } while (gotsigchld);
4355 4384
4356 return err; 4385 return err;
@@ -5187,18 +5216,28 @@ static void closescript(void);
5187static NOINLINE void 5216static NOINLINE void
5188forkchild(struct job *jp, union node *n, int mode) 5217forkchild(struct job *jp, union node *n, int mode)
5189{ 5218{
5219 int lvforked;
5190 int oldlvl; 5220 int oldlvl;
5191 5221
5192 TRACE(("Child shell %d\n", getpid())); 5222 TRACE(("Child shell %d\n", getpid()));
5193 oldlvl = shlvl; 5223 oldlvl = shlvl;
5194 shlvl++; 5224 lvforked = vforked;
5225
5226 if (!lvforked) {
5227 shlvl++;
5228
5229 closescript();
5230
5231#if JOBS
5232 /* do job control only in root shell */
5233 jobctl = 0;
5234#endif
5235 }
5195 5236
5196 /* man bash: "Non-builtin commands run by bash have signal handlers 5237 /* man bash: "Non-builtin commands run by bash have signal handlers
5197 * set to the values inherited by the shell from its parent". 5238 * set to the values inherited by the shell from its parent".
5198 * Do we do it correctly? */ 5239 * Do we do it correctly? */
5199 5240
5200 closescript();
5201
5202 if (n && n->type == NCMD /* is it single cmd? */ 5241 if (n && n->type == NCMD /* is it single cmd? */
5203 /* && n->ncmd.args->type == NARG - always true? */ 5242 /* && n->ncmd.args->type == NARG - always true? */
5204 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 5243 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
@@ -5244,10 +5283,9 @@ forkchild(struct job *jp, union node *n, int mode)
5244 trap_ptr = xmemdup(trap, sizeof(trap)); 5283 trap_ptr = xmemdup(trap, sizeof(trap));
5245 /* Fall through into clearing traps */ 5284 /* Fall through into clearing traps */
5246 } 5285 }
5247 clear_traps(); 5286 if (!lvforked)
5287 clear_traps();
5248#if JOBS 5288#if JOBS
5249 /* do job control only in root shell */
5250 jobctl = 0;
5251 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { 5289 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
5252 pid_t pgrp; 5290 pid_t pgrp;
5253 5291
@@ -5302,6 +5340,10 @@ forkchild(struct job *jp, union node *n, int mode)
5302 return; 5340 return;
5303 } 5341 }
5304#endif 5342#endif
5343
5344 if (lvforked)
5345 return;
5346
5305 for (jp = curjob; jp; jp = jp->prev_job) 5347 for (jp = curjob; jp; jp = jp->prev_job)
5306 freejob(jp); 5348 freejob(jp);
5307} 5349}
@@ -5313,7 +5355,15 @@ forkchild(struct job *jp, union node *n, int mode)
5313static void 5355static void
5314forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5356forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5315{ 5357{
5316 TRACE(("In parent shell: child = %d\n", pid)); 5358 if (pid < 0) {
5359 TRACE(("Fork failed, errno=%d", errno));
5360 if (jp)
5361 freejob(jp);
5362 ash_msg_and_raise_perror("can't fork");
5363 /* NOTREACHED */
5364 }
5365
5366 TRACE(("In parent shell: child = %d\n", pid));
5317 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 5367 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5318 return; 5368 return;
5319#if JOBS 5369#if JOBS
@@ -5352,21 +5402,44 @@ forkshell(struct job *jp, union node *n, int mode)
5352 5402
5353 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); 5403 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5354 pid = fork(); 5404 pid = fork();
5355 if (pid < 0) {
5356 TRACE(("Fork failed, errno=%d", errno));
5357 if (jp)
5358 freejob(jp);
5359 ash_msg_and_raise_perror("can't fork");
5360 }
5361 if (pid == 0) { 5405 if (pid == 0) {
5362 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ 5406 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
5363 forkchild(jp, n, mode); 5407 forkchild(jp, n, mode);
5364 } else { 5408 } else
5365 forkparent(jp, n, mode, pid); 5409 forkparent(jp, n, mode, pid);
5366 } 5410
5367 return pid; 5411 return pid;
5368} 5412}
5369 5413
5414static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
5415
5416static struct job*
5417vforkexec(union node *n, char **argv, const char *path, int idx)
5418{
5419 struct job *jp;
5420 int pid;
5421
5422 jp = makejob(1);
5423
5424 sigblockall(NULL);
5425 vforked = 1;
5426
5427 pid = vfork();
5428
5429 if (!pid) {
5430 forkchild(jp, n, FORK_FG);
5431 sigclearmask();
5432 shellexec(argv[0], argv, path, idx);
5433 /* NOTREACHED */
5434 }
5435
5436 vforked = 0;
5437 sigclearmask();
5438 forkparent(jp, n, FORK_FG, pid);
5439
5440 return jp;
5441}
5442
5370/* 5443/*
5371 * Wait for job to finish. 5444 * Wait for job to finish.
5372 * 5445 *
@@ -8341,7 +8414,6 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8341 * have to change the find_command routine as well. 8414 * have to change the find_command routine as well.
8342 * argv[-1] must exist and be writable! See tryexec() for why. 8415 * argv[-1] must exist and be writable! See tryexec() for why.
8343 */ 8416 */
8344static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8345static void shellexec(char *prog, char **argv, const char *path, int idx) 8417static void shellexec(char *prog, char **argv, const char *path, int idx)
8346{ 8418{
8347 char *cmdname; 8419 char *cmdname;
@@ -10612,6 +10684,7 @@ evalcommand(union node *cmd, int flags)
10612 break; 10684 break;
10613 } 10685 }
10614#endif 10686#endif
10687 /* Fork off a child process if necessary. */
10615 /* Can we avoid forking? For example, very last command 10688 /* Can we avoid forking? For example, very last command
10616 * in a script or a subshell does not need forking, 10689 * in a script or a subshell does not need forking,
10617 * we can just exec it. 10690 * we can just exec it.
@@ -10620,14 +10693,8 @@ evalcommand(union node *cmd, int flags)
10620 /* No, forking off a child is necessary */ 10693 /* No, forking off a child is necessary */
10621 INTOFF; 10694 INTOFF;
10622 get_tty_state(); 10695 get_tty_state();
10623 jp = makejob(1); 10696 jp = vforkexec(cmd, argv, path, cmdentry.u.index);
10624 if (forkshell(jp, cmd, FORK_FG) != 0) { 10697 break;
10625 /* parent */
10626 break;
10627 }
10628 /* child */
10629 FORCEINTON;
10630 /* fall through to exec'ing external program */
10631 } 10698 }
10632 shellexec(argv[0], argv, path, cmdentry.u.index); 10699 shellexec(argv[0], argv, path, cmdentry.u.index);
10633 /* NOTREACHED */ 10700 /* NOTREACHED */
diff --git a/shell/hush.c b/shell/hush.c
index 2cceef25a..ad078ee54 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -12160,8 +12160,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
12160 * Or else we may race with SIGCHLD, lose it, 12160 * Or else we may race with SIGCHLD, lose it,
12161 * and get stuck in sigsuspend... 12161 * and get stuck in sigsuspend...
12162 */ 12162 */
12163 sigfillset(&oldset); /* block all signals, remember old set */ 12163 sigblockall(&oldset); /* block all signals, remember old set */
12164 sigprocmask2(SIG_SETMASK, &oldset);
12165 12164
12166 if (!sigisemptyset(&G.pending_set)) { 12165 if (!sigisemptyset(&G.pending_set)) {
12167 /* Crap! we raced with some signal! */ 12166 /* Crap! we raced with some signal! */