aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lib.h13
-rw-r--r--lib/xwrap.c53
-rw-r--r--scripts/config2help.c1
-rw-r--r--toys.h24
-rw-r--r--toys/other/login.c8
-rw-r--r--toys/other/nbd_client.c1
-rw-r--r--toys/other/netcat.c23
-rw-r--r--toys/other/nsenter.c13
-rw-r--r--toys/other/setsid.c2
-rw-r--r--toys/other/timeout.c8
-rw-r--r--toys/pending/arping.c12
-rw-r--r--toys/posix/cpio.c11
-rw-r--r--toys/posix/time.c2
-rw-r--r--toys/posix/xargs.c4
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 <sys/statfs.h>
#include <sys/sysinfo.h>
+#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 <luto@amacapital.net>
+ * Copyright 2014 Andy Lutomirski <luto@amacapital.net>
*
- * No standard
+ * See http://man7.org/linux/man-pages/man1/nsenter.1.html
*
* unshare.c - run command in new context
*
* Copyright 2011 Rob Landley <rob@landley.net>
*
- * 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);