From 7d6af77804adc069a83e8566250f868a6cb9786e Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 29 Sep 2015 05:09:46 -0500 Subject: Make defconfig build for nommu. Adds XVFORK() macro, teaches xpopen_both() to call /proc/self/exe with NULL argv (and converts cpio -p to use that), adds TOYBOX_FORK guards to some unconverted commands. --- lib/lib.h | 13 ++---------- lib/xwrap.c | 53 +++++++++++++++++++++++++++++++++++++++---------- scripts/config2help.c | 1 + toys.h | 24 +++++++++++----------- toys/other/login.c | 8 +++----- toys/other/nbd_client.c | 1 + toys/other/netcat.c | 23 ++++++++++++++++----- toys/other/nsenter.c | 13 +++++------- toys/other/setsid.c | 2 +- toys/other/timeout.c | 8 ++------ toys/pending/arping.c | 12 +++-------- toys/posix/cpio.c | 11 ++++++---- toys/posix/time.c | 2 +- toys/posix/xargs.c | 4 ++-- 14 files changed, 100 insertions(+), 75 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]); diff --git a/scripts/config2help.c b/scripts/config2help.c index 2ed189aa..f3bb8c3f 100644 --- a/scripts/config2help.c +++ b/scripts/config2help.c @@ -5,6 +5,7 @@ struct toy_context toys; char libbuf[4096], toybuf[4096]; void show_help(FILE *out) {;} void toy_exec(char *argv[]) {;} +void toy_init(struct toy_list *which, char *argv[]) {;} // Parse config files into data structures. diff --git a/toys.h b/toys.h index d8efa667..9c33ff21 100644 --- a/toys.h +++ b/toys.h @@ -67,6 +67,18 @@ #include #include +#include "lib/lib.h" +#include "lib/lsm.h" +#include "toys/e2fs.h" + +// Get list of function prototypes for all enabled command_main() functions. + +#define NEWTOY(name, opts, flags) void name##_main(void); +#define OLDTOY(name, oldname, flags) void oldname##_main(void); +#include "generated/newtoys.h" +#include "generated/flags.h" +#include "generated/globals.h" + // These live in main.c struct toy_list *toy_find(char *name); @@ -132,15 +144,3 @@ extern char **environ; #define GLOBALS(...) #define ARRAY_LEN(array) (sizeof(array)/sizeof(*array)) - -#include "lib/lib.h" -#include "lib/lsm.h" -#include "toys/e2fs.h" - -// Get list of function prototypes for all enabled command_main() functions. - -#define NEWTOY(name, opts, flags) void name##_main(void); -#define OLDTOY(name, oldname, flags) void oldname##_main(void); -#include "generated/newtoys.h" -#include "generated/flags.h" -#include "generated/globals.h" diff --git a/toys/other/login.c b/toys/other/login.c index c727bf96..7f9559aa 100644 --- a/toys/other/login.c +++ b/toys/other/login.c @@ -159,9 +159,7 @@ void login_main(void) syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, ttyname(tty), hh ? "from" : "", hh ? TT.hostname : ""); - // can't xexec here because name doesn't match argv[0] - snprintf(toybuf, sizeof(toybuf)-1, "-%s", basename_r(pwd->pw_shell)); - toy_exec((char *[]){toybuf, 0}); - execl(pwd->pw_shell, toybuf, NULL); - error_exit("Failed to spawn shell"); + // not using xexec(), login calls absolute path from filesystem so must exec() + execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0); + perror_exit("exec shell '%s'", pwd->pw_shell); } diff --git a/toys/other/nbd_client.c b/toys/other/nbd_client.c index a82ff7c8..3ad366f3 100644 --- a/toys/other/nbd_client.c +++ b/toys/other/nbd_client.c @@ -12,6 +12,7 @@ USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN)) config NBD_CLIENT bool "nbd-client" + depends on TOYBOX_FORK default y help usage: nbd-client [-ns] HOST PORT DEVICE diff --git a/toys/other/netcat.c b/toys/other/netcat.c index 3cc3f0a0..1c75eb26 100644 --- a/toys/other/netcat.c +++ b/toys/other/netcat.c @@ -26,10 +26,10 @@ config NETCAT_LISTEN bool "netcat server options (-let)" default y depends on NETCAT + depends on TOYBOX_FORK help - usage: netcat [-t] [-lL COMMAND...] + usage: netcat [-lL COMMAND...] - -t allocate tty (must come before -l or -L) -l listen for one incoming connection. -L listen for multiple incoming connections (server mode). @@ -38,6 +38,16 @@ config NETCAT_LISTEN For a quick-and-dirty server, try something like: netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l + +config NETCAT_LISTEN_TTY + bool + default y + depends on NETCAT_LISTEN + depends on TOYBOX_FORK + help + usage: netcat [-t] + + -t allocate tty (must come before -l or -L) */ #define FOR_netcat @@ -139,7 +149,7 @@ void netcat_main(void) // Do we need to return immediately because -l has arguments? if ((toys.optflags & FLAG_l) && toys.optc) { - if (xfork()) goto cleanup; + if (CFG_TOYBOX_FORK && xfork()) goto cleanup; close(0); close(1); close(2); @@ -161,7 +171,10 @@ void netcat_main(void) // Do we need to fork and/or redirect for exec? else { - if (toys.optflags&FLAG_L) child = fork(); + if (toys.optflags&FLAG_L) { + toys.stacktop = 0; + child = vfork(); + } if (!child && toys.optc) { int fd = pollfds[0].fd; @@ -183,7 +196,7 @@ void netcat_main(void) // (Does not play well with -L, but what _should_ that do?) set_alarm(0); - if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) + if (CFG_NETCAT_LISTEN && ((toys.optflags&(FLAG_L|FLAG_l)) && toys.optc)) xexec(toys.optargs); // Poll loop copying stdin->socket and socket->stdout. diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c index 18a2cd21..bda77ac1 100644 --- a/toys/other/nsenter.c +++ b/toys/other/nsenter.c @@ -1,14 +1,14 @@ /* nsenter.c - Enter existing namespaces * - * Copyright 2014 andy Lutomirski + * Copyright 2014 Andy Lutomirski * - * No standard + * See http://man7.org/linux/man-pages/man1/nsenter.1.html * * unshare.c - run command in new context * * Copyright 2011 Rob Landley * - * No Standard + * See http://man7.org/linux/man-pages/man1/unshare.1.html * // Note: flags go in same order (right to left) for shared subset @@ -149,12 +149,9 @@ void unshare_main(void) } if ((toys.optflags & FLAG_p) && !(toys.optflags & FLAG_F)) { - pid_t pid = xfork(); + toys.exitval = xrun(toys.optargs); - if (pid) { - while (waitpid(pid, 0, 0) == -1 && errno == EINTR); - return; - } + return; } } diff --git a/toys/other/setsid.c b/toys/other/setsid.c index 83db044d..95698260 100644 --- a/toys/other/setsid.c +++ b/toys/other/setsid.c @@ -19,7 +19,7 @@ config SETSID void setsid_main(void) { - while (setsid()<0) if (xvfork()) _exit(0); + while (setsid()<0) if (XVFORK()) _exit(0); if (toys.optflags) { setpgid(0, 0); tcsetpgrp(0, getpid()); diff --git a/toys/other/timeout.c b/toys/other/timeout.c index bd716e63..0e912f7c 100644 --- a/toys/other/timeout.c +++ b/toys/other/timeout.c @@ -62,14 +62,10 @@ void timeout_main(void) if (TT.s_signal && -1 == (TT.nextsig = sig_to_num(TT.s_signal))) error_exit("bad -s: '%s'", TT.s_signal); - if (!(TT.pid = xvfork())) xexec(toys.optargs+1); + if (!(TT.pid = XVFORK())) xexec(toys.optargs+1); else { - int status; - xsignal(SIGALRM, handler); setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf); - while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR); - toys.exitval = WIFEXITED(status) - ? WEXITSTATUS(status) : WTERMSIG(status) + 127; + toys.exitval = xwaitpid(TT.pid); } } diff --git a/toys/pending/arping.c b/toys/pending/arping.c index 3e522bdf..be43cab1 100644 --- a/toys/pending/arping.c +++ b/toys/pending/arping.c @@ -39,15 +39,9 @@ GLOBALS( char *src_ip; int sockfd; - unsigned start; - unsigned end; - unsigned sent_at; - unsigned sent_nr; - unsigned rcvd_nr; - unsigned brd_sent; - unsigned rcvd_req; - unsigned brd_rcv; - unsigned unicast_flag; + unsigned long start, end; + unsigned sent_at, sent_nr, rcvd_nr, brd_sent, rcvd_req, brd_rcv, + unicast_flag; ) struct sockaddr_ll src_pk, dst_pk; diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c index 312bb939..a442f0d1 100644 --- a/toys/posix/cpio.c +++ b/toys/posix/cpio.c @@ -82,12 +82,15 @@ void cpio_main(void) // In passthrough mode, parent stays in original dir and generates archive // to pipe, child does chdir to new dir and reads archive from stdin (pipe). if (TT.pass) { - if (!(pid = xpopen(0, &pipe, 0))) { + if (toys.stacktop) { + // xpopen() doesn't return from child due to vfork(), instead restarts + // with !toys.stacktop + pid = xpopen(0, &pipe, 0); + afd = pipe; + } else { + // child toys.optflags |= FLAG_i; xchdir(TT.pass); - } else { - toys.optflags |= FLAG_o; - afd = pipe; } } diff --git a/toys/posix/time.c b/toys/posix/time.c index b3cfd26b..2151826d 100644 --- a/toys/posix/time.c +++ b/toys/posix/time.c @@ -28,7 +28,7 @@ void time_main(void) struct timeval tv, tv2; gettimeofday(&tv, NULL); - if (!(pid = xvfork())) xexec(toys.optargs); + if (!(pid = XVFORK())) xexec(toys.optargs); else { int stat; struct rusage ru; diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c index 50c42620..b4cb80a0 100644 --- a/toys/posix/xargs.c +++ b/toys/posix/xargs.c @@ -111,6 +111,7 @@ void xargs_main(void) struct double_list *dlist = NULL, *dtemp; int entries, bytes, done = 0, status; char *data = NULL, **out; + pid_t pid; if (!(toys.optflags & FLAG_0)) TT.delim = '\n'; @@ -168,8 +169,7 @@ void xargs_main(void) for (dtemp = dlist; dtemp; dtemp = dtemp->next) handle_entries(dtemp->data, out+entries); - pid_t pid=xvfork(); - if (!pid) { + if (!(pid = XVFORK())) { xclose(0); open("/dev/null", O_RDONLY); xexec(out); -- cgit v1.2.3