aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/lib.h13
-rw-r--r--lib/xwrap.c53
2 files changed, 44 insertions, 22 deletions
diff --git a/lib/lib.h b/lib/lib.h
index d76dcc0c..90f44dcb 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -247,17 +247,8 @@ void mode_to_string(mode_t mode, char *buf);
char *basename_r(char *name);
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name));
-// Returning from a function can modify a potentially shared stack,
-// so this has to always inline.
-static inline pid_t xvfork(void)
-{
- pid_t p;
-
- toys.stacktop = 0;
- if ((p = vfork()) == -1) perror_exit("vfork");
-
- return p;
-}
+pid_t xvforkwrap(pid_t pid);
+#define XVFORK() xvforkwrap(vfork())
// Functions in need of further review/cleanup
#include "lib/pending.h"
diff --git a/lib/xwrap.c b/lib/xwrap.c
index e5dc4826..4880bbe0 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -129,6 +129,20 @@ void xflush(void)
if (fflush(stdout) || ferror(stdout)) perror_exit("write");;
}
+// This is called through the XVFORK macro because parent/child of vfork
+// share a stack, so child returning from a function would stomp the return
+// address parent would need. Solution: make vfork() an argument so processes
+// diverge before function gets called.
+pid_t xvforkwrap(pid_t pid)
+{
+ if (pid == -1) perror_exit("vfork");
+
+ // Signal to xexec() and friends that we vforked so can't recurse
+ toys.stacktop = 0;
+
+ return pid;
+}
+
// Die unless we can exec argv[] (or run builtin command). Note that anything
// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
void xexec(char **argv)
@@ -144,25 +158,27 @@ void xexec(char **argv)
}
// Spawn child process, capturing stdin/stdout.
-// argv[]: command to exec. If null, child returns to original program.
-// pipes[2]: stdin, stdout of new process. If -1 will not have pipe allocated.
+// argv[]: command to exec. If null, child re-runs original program with
+// toys.stacktop zeroed.
+// pipes[2]: stdin, stdout of new process, only allocated if zero on way in,
+// pass NULL to skip pipe allocation entirely.
// return: pid of child process
pid_t xpopen_both(char **argv, int *pipes)
{
int cestnepasun[4], pid;
- // Make the pipes? Not this won't set either pipe to 0 because if fds are
+ // Make the pipes? Note this won't set either pipe to 0 because if fds are
// allocated in order and if fd0 was free it would go to cestnepasun[0]
if (pipes) {
for (pid = 0; pid < 2; pid++) {
- if (pipes[pid] == -1) continue;
+ if (pipes[pid] != 0) continue;
if (pipe(cestnepasun+(2*pid))) perror_exit("pipe");
pipes[pid] = cestnepasun[pid+1];
}
}
- // Child process
- if (!(pid = xfork())) {
+ // Child process.
+ if (!(pid = CFG_TOYBOX_FORK ? xfork() : XVFORK())) {
// Dance of the stdin/stdout redirection.
if (pipes) {
// if we had no stdin/out, pipe handles could overlap, so test for it
@@ -181,16 +197,31 @@ pid_t xpopen_both(char **argv, int *pipes)
if (cestnepasun[3] > 2 || !cestnepasun[3]) close(cestnepasun[3]);
}
}
- if (argv) {
- if (CFG_TOYBOX) toy_exec(argv);
- execvp(argv[0], argv);
+ if (argv) xexec(argv);
+
+ // In fork() case, force recursion because we know it's us.
+ if (CFG_TOYBOX_FORK) {
+ toy_init(toys.which, toys.argv);
+ toys.stacktop = 0;
+ toys.which->toy_main();
+ xexit();
+ // In vfork() case, exec /proc/self/exe with high bit of first letter set
+ // to tell main() we reentered.
+ } else {
+ char *s = "/proc/self/exe";
+
+ // We did a nommu-friendly vfork but must exec to continue.
+ // setting high bit of argv[0][0] to let new process know
+ **toys.argv |= 0x80;
+ execv(s, toys.argv);
+ perror_msg(s);
+
_exit(127);
}
- return 0;
-
}
// Parent process
+ if (!CFG_TOYBOX_FORK) **toys.argv &= 0x7f;
if (pipes) {
if (pipes[0] != -1) close(cestnepasun[0]);
if (pipes[1] != -1) close(cestnepasun[3]);