[klibc] [klibc:update-dash] dash: eval: Add vfork support

klibc-bot for Herbert Xu herbert at gondor.apana.org.au
Sat Mar 28 14:49:25 PDT 2020


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)


More information about the klibc mailing list