diff options
Diffstat (limited to 'lib/xwrap.c')
-rw-r--r-- | lib/xwrap.c | 53 |
1 files changed, 42 insertions, 11 deletions
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]); |