aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2019-03-10 14:30:18 -0500
committerRob Landley <rob@landley.net>2019-03-10 14:30:18 -0500
commit975960e20fd4371caccff9949d8594419624e061 (patch)
tree7f18b9a89f6ec1d209d9ed1c4006c8cbab511e45 /lib
parentd5c1fe2ae383de59bd796c6a593ffea519310228 (diff)
downloadtoybox-975960e20fd4371caccff9949d8594419624e061.tar.gz
Make multiple sendfile variants, and teach xpopen_both() to use existing
stdin/stdout filehandles.
Diffstat (limited to 'lib')
-rw-r--r--lib/lib.h3
-rw-r--r--lib/xwrap.c115
2 files changed, 85 insertions, 33 deletions
diff --git a/lib/lib.h b/lib/lib.h
index 9de18413..599ade06 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -232,6 +232,9 @@ void loopfiles_rw(char **argv, int flags, int permissions,
void (*function)(int fd, char *name));
void loopfiles(char **argv, void (*function)(int fd, char *name));
void loopfiles_lines(char **argv, void (*function)(char **pline, long len));
+long long sendfile_len(int in, int out, long long len);
+long long xsendfile_len(int in, int out, long long len);
+void xsendfile_pad(int in, int out, long long len);
long long xsendfile(int in, int out);
int wfchmodat(int rc, char *name, mode_t mode);
int copy_tempfile(int fdin, char *name, char **tempname);
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 12016a21..72e5ea4b 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -54,8 +54,7 @@ void xexit(void)
free(al);
}
- if (fflush(NULL) || ferror(stdout))
- if (!toys.exitval) perror_msg("write");
+ if (fflush(0) || ferror(stdout)) if (!toys.exitval) perror_msg("write");
_xexit();
}
@@ -220,41 +219,49 @@ void xexec(char **argv)
// Spawn child process, capturing stdin/stdout.
// 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.
+// pipes[2]: Filehandle to move to stdin/stdout of new process.
+// If -1, replace with pipe handle connected to stdin/stdout.
+// NULL treated as {0, 1}, I.E. leave stdin/stdout as is
// return: pid of child process
pid_t xpopen_both(char **argv, int *pipes)
{
int cestnepasun[4], pid;
- // 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] != 0) continue;
- if (pipe(cestnepasun+(2*pid))) perror_exit("pipe");
- pipes[pid] = cestnepasun[pid+1];
- }
+ // Make the pipes?
+ memset(cestnepasun, 0, sizeof(cestnepasun));
+ if (pipes) for (pid = 0; pid < 2; pid++) {
+ if (pipes[pid] != -1) continue;
+ if (pipe(cestnepasun+(2*pid))) perror_exit("pipe");
}
- // Child process.
if (!(pid = CFG_TOYBOX_FORK ? xfork() : XVFORK())) {
- // Dance of the stdin/stdout redirection.
+ // Child process: Dance of the stdin/stdout redirection.
if (pipes) {
// if we had no stdin/out, pipe handles could overlap, so test for it
// and free up potentially overlapping pipe handles before reuse
- if (pipes[1] != -1) close(cestnepasun[2]);
- if (pipes[0] != -1) {
+ if (cestnepasun[2]) {
+ close(cestnepasun[2]);
+ pipes[1] = cestnepasun[3];
+ }
+ if (cestnepasun[1]) {
close(cestnepasun[1]);
- if (cestnepasun[0]) {
- dup2(cestnepasun[0], 0);
- close(cestnepasun[0]);
- }
+ pipes[0] = cestnepasun[0];
}
- if (pipes[1] != -1) {
- dup2(cestnepasun[3], 1);
- dup2(cestnepasun[3], 2);
- if (cestnepasun[3] > 2 || !cestnepasun[3]) close(cestnepasun[3]);
+
+ // If swapping stdin/stdout
+ if (!pipes[1]) pipes[1] = dup(pipes[1]);
+
+ // Are we redirecting stdin?
+ if (pipes[0]) {
+ dup2(pipes[0], 0);
+ close(pipes[0]);
+ }
+
+ // Are we redirecting stdout? (If so, direct stderr to same place.)
+ if (pipes[1] != 1) {
+ dup2(pipes[1], 1);
+ dup2(pipes[1], 2);
+ if (cestnepasun[2]) close(cestnepasun[2]);
}
}
if (argv) xexec(argv);
@@ -280,11 +287,18 @@ pid_t xpopen_both(char **argv, int *pipes)
}
}
- // Parent process
+ // Parent process: vfork had a shared environment, clean up.
if (!CFG_TOYBOX_FORK) **toys.argv &= 0x7f;
+
if (pipes) {
- if (pipes[0] != -1) close(cestnepasun[0]);
- if (pipes[1] != -1) close(cestnepasun[3]);
+ if (cestnepasun[1]) {
+ pipes[0] = cestnepasun[1];
+ close(cestnepasun[0]);
+ }
+ if (cestnepasun[2]) {
+ pipes[1] = cestnepasun[2];
+ close(cestnepasun[3]);
+ }
}
return pid;
@@ -315,8 +329,8 @@ pid_t xpopen(char **argv, int *pipe, int isstdout)
{
int pipes[2], pid;
- pipes[!isstdout] = -1;
- pipes[!!isstdout] = 0;
+ pipes[0] = isstdout ? 0 : -1;
+ pipes[1] = isstdout ? -1 : 1;
pid = xpopen_both(argv, pipes);
*pipe = pid ? pipes[!!isstdout] : -1;
@@ -784,16 +798,19 @@ void xpidfile(char *name)
close(fd);
}
-// Copy the rest of in to out and close both files.
-
-long long xsendfile(int in, int out)
+// Return bytes copied from in to out. If bytes <0 copy all of in to out.
+long long sendfile_len(int in, int out, long long bytes)
{
long long total = 0;
long len;
if (in<0) return 0;
for (;;) {
- len = xread(in, libbuf, sizeof(libbuf));
+ if (bytes == total) break;
+ len = bytes-total;
+ if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
+
+ len = xread(in, libbuf, sizeof(libbuf)>(bytes-total));
if (len<1) break;
xwrite(out, libbuf, len);
total += len;
@@ -802,6 +819,38 @@ long long xsendfile(int in, int out)
return total;
}
+// error_exit if we couldn't copy all bytes
+long long xsendfile_len(int in, int out, long long bytes)
+{
+ long long len = sendfile_len(in, out, bytes);
+
+ if (bytes != len) error_exit("short file");
+
+ return len;
+}
+
+// warn and pad with zeroes if we couldn't copy all bytes
+void xsendfile_pad(int in, int out, long long len)
+{
+ len -= xsendfile_len(in, out, len);
+ if (len) {
+ perror_msg("short read");
+ memset(libbuf, 0, sizeof(libbuf));
+ while (len) {
+ int i = len>sizeof(libbuf) ? sizeof(libbuf) : len;
+
+ xwrite(out, libbuf, i);
+ len -= i;
+ }
+ }
+}
+
+// copy all of in to out
+long long xsendfile(int in, int out)
+{
+ return xsendfile_len(in, out, -1);
+}
+
double xstrtod(char *s)
{
char *end;