From patchwork Wed May 9 09:01:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herbert Xu X-Patchwork-Id: 10388813 X-Patchwork-Delegate: herbert@gondor.apana.org.au Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6377760170 for ; Wed, 9 May 2018 09:01:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4FF6828E6E for ; Wed, 9 May 2018 09:01:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 447CC28E74; Wed, 9 May 2018 09:01:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6302C28E6E for ; Wed, 9 May 2018 09:01:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934094AbeEIJBW (ORCPT ); Wed, 9 May 2018 05:01:22 -0400 Received: from orcrist.hmeau.com ([104.223.48.154]:43842 "EHLO deadmen.hmeau.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934007AbeEIJBT (ORCPT ); Wed, 9 May 2018 05:01:19 -0400 Received: from gondobar.mordor.me.apana.org.au ([192.168.128.4] helo=gondobar) by deadmen.hmeau.com with esmtps (Exim 4.89 #2 (Debian)) id 1fGKyL-0006ME-U2; Wed, 09 May 2018 17:01:17 +0800 Received: from herbert by gondobar with local (Exim 4.89) (envelope-from ) id 1fGKyK-0002er-Lm; Wed, 09 May 2018 17:01:16 +0800 Date: Wed, 9 May 2018 17:01:16 +0800 From: Herbert Xu To: DASH Mailing List Subject: eval: Add vfork support Message-ID: <20180509090116.zvajquf2vo3xh36k@gondor.apana.org.au> MIME-Version: 1.0 Content-Disposition: inline User-Agent: NeoMutt/20170113 (1.7.2) Sender: dash-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: dash@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds basic vfork support for the case of a simple command. Signed-off-by: Herbert Xu diff --git a/src/error.c b/src/error.c index f9ea919..728ff88 100644 --- a/src/error.c +++ b/src/error.c @@ -43,6 +43,7 @@ #include #include +#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/src/eval.c b/src/eval.c index 23be7ba..ffa642b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -864,10 +864,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/src/exec.h b/src/exec.h index 9ccb305..5317451 100644 --- a/src/exec.h +++ b/src/exec.h @@ -58,6 +58,8 @@ struct cmdentry { #define DO_ALTPATH 0x08 /* using alternate path */ #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ +union node; + extern const char *pathopt; /* set by padvance */ void shellexec(char **, const char *, int) diff --git a/src/jobs.c b/src/jobs.c index 606d603..3dbc6f0 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -53,6 +53,7 @@ #include #undef CEOF /* syntax.h redefines this */ #endif +#include "exec.h" #include "eval.h" #include "redir.h" #include "show.h" @@ -99,6 +100,9 @@ static struct job *curjob; /* number of presumed living untracked jobs */ static int jobless; +/* 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); @@ -845,20 +849,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; @@ -884,18 +897,31 @@ 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); jobless = 0; } -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) { while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); @@ -935,19 +961,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. * @@ -1136,7 +1183,7 @@ STATIC int onsigchild() { STATIC int waitproc(int block, int *status) { - sigset_t mask, oldmask; + sigset_t oldmask; int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; int err; @@ -1153,8 +1200,7 @@ waitproc(int block, int *status) block = 0; - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &oldmask); + sigblockall(&oldmask); while (!gotsigchld && !pending_sig) sigsuspend(&oldmask); diff --git a/src/jobs.h b/src/jobs.h index 953ee87..6ac6c56 100644 --- a/src/jobs.h +++ b/src/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/src/trap.c b/src/trap.c index 69eb8ab..ab0ecd4 100644 --- a/src/trap.c +++ b/src/trap.c @@ -182,16 +182,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) @@ -256,7 +259,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); @@ -272,7 +276,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; } @@ -284,6 +289,9 @@ ignoresig(int signo) void onsig(int signo) { + if (vforked) + return; + if (signo == SIGCHLD) { gotsigchld = 1; if (!trap[SIGCHLD]) @@ -431,3 +439,11 @@ 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); +} diff --git a/src/trap.h b/src/trap.h index b9dfcf2..5fd65af 100644 --- a/src/trap.h +++ b/src/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); static inline int have_traps(void) {