klibc-bot for Herbert Xu
2020-Mar-28 21:49 UTC
[klibc] [klibc:update-dash] dash: eval: Add vfork support
Commit-ID: cef709c42bb5ac1c45e7c42f440bc1c010f39b9b Gitweb: http://git.kernel.org/?p=libs/klibc/klibc.git;a=commit;h=cef709c42bb5ac1c45e7c42f440bc1c010f39b9b Author: Herbert Xu <herbert at gondor.apana.org.au> AuthorDate: Sat, 19 May 2018 02:39:56 +0800 Committer: Ben Hutchings <ben at decadent.org.uk> CommitDate: Sat, 28 Mar 2020 21:42:55 +0000 [klibc] dash: eval: Add vfork support [ dash commit e94a964e7dd03d0dd6923d7fc62bc46bd4431189 ] This patch adds basic vfork support for the case of a simple command. Signed-off-by: Herbert Xu <herbert at gondor.apana.org.au> [bwh: Adjust context for klibc] Signed-off-by: Ben Hutchings <ben at decadent.org.uk> --- usr/dash/error.c | 5 ++++ usr/dash/eval.c | 6 ++-- usr/dash/exec.h | 2 ++ usr/dash/jobs.c | 86 +++++++++++++++++++++++++++++++++++++++++++------------- usr/dash/jobs.h | 4 +++ usr/dash/trap.c | 22 +++++++++++++-- usr/dash/trap.h | 1 + 7 files changed, 99 insertions(+), 27 deletions(-) diff --git a/usr/dash/error.c b/usr/dash/error.c index f9ea9198..728ff885 100644 --- a/usr/dash/error.c +++ b/usr/dash/error.c @@ -43,6 +43,7 @@ #include <stdio.h> #include <string.h> +#include "jobs.h" #include "shell.h" #include "main.h" #include "options.h" @@ -81,6 +82,10 @@ exraise(int e) if (handler == NULL) abort(); #endif + + if (vforked) + _exit(exitstatus); + INTOFF; exception = e; diff --git a/usr/dash/eval.c b/usr/dash/eval.c index 77a8bded..7bb636e1 100644 --- a/usr/dash/eval.c +++ b/usr/dash/eval.c @@ -892,10 +892,8 @@ bail: /* Fork off a child process if necessary. */ if (!(flags & EV_EXIT) || have_traps()) { INTOFF; - jp = makejob(cmd, 1); - if (forkshell(jp, cmd, FORK_FG) != 0) - break; - FORCEINTON; + jp = vforkexec(cmd, argv, path, cmdentry.u.index); + break; } shellexec(argv, path, cmdentry.u.index); /* NOTREACHED */ diff --git a/usr/dash/exec.h b/usr/dash/exec.h index 2b318257..423b07e6 100644 --- a/usr/dash/exec.h +++ b/usr/dash/exec.h @@ -58,6 +58,8 @@ struct cmdentry { #define DO_ALTPATH 0x08 /* using alternate path */ #define DO_REGBLTIN 0x10 /* regular built-ins and functions only */ +union node; + extern const char *pathopt; /* set by padvance */ void shellexec(char **, const char *, int) diff --git a/usr/dash/jobs.c b/usr/dash/jobs.c index 9e7244e1..989907ed 100644 --- a/usr/dash/jobs.c +++ b/usr/dash/jobs.c @@ -53,6 +53,7 @@ #include <termios.h> #undef CEOF /* syntax.h redefines this */ #endif +#include "exec.h" #include "eval.h" #include "redir.h" #include "show.h" @@ -97,6 +98,9 @@ static int ttyfd = -1; /* current job */ static struct job *curjob; +/* Set if we are in the vforked child */ +int vforked; + STATIC void set_curjob(struct job *, unsigned); STATIC int jobno(const struct job *); STATIC int sprint_status(char *, int, int); @@ -839,20 +843,29 @@ growjobtab(void) * Called with interrupts off. */ -STATIC inline void -forkchild(struct job *jp, union node *n, int mode) +static void forkchild(struct job *jp, union node *n, int mode) { + int lvforked; int oldlvl; TRACE(("Child shell %d\n", getpid())); + oldlvl = shlvl; - shlvl++; + lvforked = vforked; + + if (!lvforked) { + shlvl++; + + closescript(); + clear_traps(); + +#if JOBS + /* do job control only in root shell */ + jobctl = 0; +#endif + } - closescript(); - clear_traps(); #if JOBS - /* do job control only in root shell */ - jobctl = 0; if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { pid_t pgrp; @@ -878,17 +891,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); } -STATIC inline void -forkparent(struct job *jp, union node *n, int mode, pid_t pid) +static void forkparent(struct job *jp, union node *n, int mode, pid_t pid) { + if (pid < 0) { + TRACE(("Fork failed, errno=%d", errno)); + if (jp) + freejob(jp); + sh_error("Cannot fork"); + /* NOTREACHED */ + } + TRACE(("In parent shell: child = %d\n", pid)); if (!jp) return; @@ -925,19 +951,40 @@ forkshell(struct job *jp, union node *n, int mode) TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); pid = fork(); - if (pid < 0) { - TRACE(("Fork failed, errno=%d", errno)); - if (jp) - freejob(jp); - sh_error("Cannot fork"); - } if (pid == 0) forkchild(jp, n, mode); else forkparent(jp, n, mode, pid); + return pid; } +struct job *vforkexec(union node *n, char **argv, const char *path, int idx) +{ + struct job *jp; + int pid; + + jp = makejob(n, 1); + + sigblockall(NULL); + vforked++; + + pid = vfork(); + + if (!pid) { + forkchild(jp, n, FORK_FG); + sigclearmask(); + shellexec(argv, path, idx); + /* NOTREACHED */ + } + + vforked = 0; + sigclearmask(); + forkparent(jp, n, FORK_FG, pid); + + return jp; +} + /* * Wait for job to finish. * @@ -1105,7 +1152,7 @@ static int dowait(int block, struct job *jp) STATIC int waitproc(int block, int *status) { - sigset_t mask, oldmask; + sigset_t oldmask; int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; int err; @@ -1119,8 +1166,7 @@ waitproc(int block, int *status) if (err || (err = -!block)) break; - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &oldmask); + sigblockall(&oldmask); while (!gotsigchld && !pending_sig) sigsuspend(&oldmask); diff --git a/usr/dash/jobs.h b/usr/dash/jobs.h index 953ee871..6ac6c56d 100644 --- a/usr/dash/jobs.h +++ b/usr/dash/jobs.h @@ -83,6 +83,8 @@ struct job { struct job *prev_job; /* previous job */ }; +union node; + extern pid_t backgndpid; /* pid of last background process */ extern int job_warning; /* user was warned about stopped jobs */ #if JOBS @@ -90,6 +92,7 @@ extern int jobctl; /* true if doing job control */ #else #define jobctl 0 #endif +extern int vforked; /* Set if we are in the vforked child */ void setjobctl(int); int killcmd(int, char **); @@ -101,6 +104,7 @@ void showjobs(struct output *, int); int waitcmd(int, char **); struct job *makejob(union node *, int); int forkshell(struct job *, union node *, int); +struct job *vforkexec(union node *n, char **argv, const char *path, int idx); int waitforjob(struct job *); int stoppedjobs(void); diff --git a/usr/dash/trap.c b/usr/dash/trap.c index a3aeb33e..1ad27e99 100644 --- a/usr/dash/trap.c +++ b/usr/dash/trap.c @@ -181,16 +181,19 @@ void setsignal(int signo) { int action; + int lvforked; char *t, tsig; struct sigaction act; + lvforked = vforked; + if ((t = trap[signo]) == NULL) action = S_DFL; else if (*t != '\0') action = S_CATCH; else action = S_IGN; - if (rootshell && action == S_DFL) { + if (rootshell && action == S_DFL && !lvforked) { switch (signo) { case SIGINT: if (iflag || minusc || sflag == 0) @@ -255,7 +258,8 @@ setsignal(int signo) default: act.sa_handler = SIG_DFL; } - *t = action; + if (!lvforked) + *t = action; act.sa_flags = 0; sigfillset(&act.sa_mask); sigaction(signo, &act, 0); @@ -271,7 +275,8 @@ ignoresig(int signo) if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { signal(signo, SIG_IGN); } - sigmode[signo - 1] = S_HARD_IGN; + if (!vforked) + sigmode[signo - 1] = S_HARD_IGN; } @@ -283,6 +288,9 @@ ignoresig(int signo) void onsig(int signo) { + if (vforked) + return; + if (signo == SIGCHLD) { gotsigchld = 1; if (!trap[SIGCHLD]) @@ -455,6 +463,14 @@ int decode_signal(const char *string, int minsig) return -1; } +void sigblockall(sigset_t *oldmask) +{ + sigset_t mask; + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, oldmask); +} + /* * Human-readable signal name */ diff --git a/usr/dash/trap.h b/usr/dash/trap.h index a095b0e1..bbff1842 100644 --- a/usr/dash/trap.h +++ b/usr/dash/trap.h @@ -50,6 +50,7 @@ void dotrap(void); void setinteractive(int); void exitshell(void) __attribute__((__noreturn__)); int decode_signal(const char *, int); +void sigblockall(sigset_t *oldmask); const char *signal_name(int); static inline int have_traps(void)
Seemingly Similar Threads
- [klibc:update-dash] dash: jobs: Only clear gotsigchld when waiting for everything
- Error building ash: trap.c:398: error: conflicting types for 'onsig'
- Patch for auto-creating home directories
- [klibc:update-dash] trap: Globally rename pendingsigs to pending_sig
- [klibc:update-dash] dash: trap: Globally rename pendingsigs to pending_sig