runProgram(): Distinguish between empty input and no input

For example, if we call brotli with an empty input, it shouldn't read
from the caller's stdin.
This commit is contained in:
Eelco Dolstra 2017-03-15 14:40:47 +01:00
parent 042975ea8e
commit 25dff2b7db
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
5 changed files with 30 additions and 15 deletions

View file

@ -671,7 +671,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
Path tmpDir = createTempDir(); Path tmpDir = createTempDir();
AutoDelete autoDelete(tmpDir, true); AutoDelete autoDelete(tmpDir, true);
// FIXME: this requires GNU tar for decompression. // FIXME: this requires GNU tar for decompression.
runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"}, ""); runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"});
unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false); unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false);
} }
replaceSymlink(unpackedStorePath, unpackedLink); replaceSymlink(unpackedStorePath, unpackedLink);

View file

@ -92,7 +92,7 @@ static ref<std::string> decompressBzip2(const std::string & in)
static ref<std::string> decompressBrotli(const std::string & in) static ref<std::string> decompressBrotli(const std::string & in)
{ {
// FIXME: use libbrotli // FIXME: use libbrotli
return make_ref<std::string>(runProgram(BRO, true, {"-d"}, in)); return make_ref<std::string>(runProgram(BRO, true, {"-d"}, {in}));
} }
ref<std::string> compress(const std::string & method, const std::string & in) ref<std::string> compress(const std::string & method, const std::string & in)

View file

@ -1,6 +1,7 @@
#include "util.hh" #include "util.hh"
#include "affinity.hh" #include "affinity.hh"
#include "sync.hh" #include "sync.hh"
#include "finally.hh"
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
@ -10,6 +11,7 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <thread> #include <thread>
#include <future>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
@ -837,23 +839,21 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss)
string runProgram(Path program, bool searchPath, const Strings & args, string runProgram(Path program, bool searchPath, const Strings & args,
const string & input) const std::experimental::optional<std::string> & input)
{ {
checkInterrupt(); checkInterrupt();
/* Create a pipe. */ /* Create a pipe. */
Pipe out, in; Pipe out, in;
out.create(); out.create();
if (!input.empty()) in.create(); if (input) in.create();
/* Fork. */ /* Fork. */
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");
if (!input.empty()) { if (input && dup2(in.readSide.get(), STDIN_FILENO) == -1)
if (dup2(in.readSide.get(), STDIN_FILENO) == -1) throw SysError("dupping stdin");
throw SysError("dupping stdin");
}
Strings args_(args); Strings args_(args);
args_.push_front(program); args_.push_front(program);
@ -872,10 +872,23 @@ string runProgram(Path program, bool searchPath, const Strings & args,
std::thread writerThread; std::thread writerThread;
if (!input.empty()) { std::promise<void> promise;
Finally doJoin([&]() {
if (writerThread.joinable())
writerThread.join();
});
if (input) {
in.readSide = -1; in.readSide = -1;
writerThread = std::thread([&]() { writerThread = std::thread([&]() {
writeFull(in.writeSide.get(), input); try {
writeFull(in.writeSide.get(), *input);
promise.set_value();
} catch (...) {
promise.set_exception(std::current_exception());
}
in.writeSide = -1; in.writeSide = -1;
}); });
} }
@ -888,8 +901,8 @@ string runProgram(Path program, bool searchPath, const Strings & args,
throw ExecError(status, format("program %1% %2%") throw ExecError(status, format("program %1% %2%")
% program % statusToString(status)); % program % statusToString(status));
if (!input.empty()) /* Wait for the writer thread to finish. */
writerThread.join(); if (input) promise.get_future().get();
return result; return result;
} }

View file

@ -14,6 +14,7 @@
#include <cstdio> #include <cstdio>
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <experimental/optional>
#ifndef HAVE_STRUCT_DIRENT_D_TYPE #ifndef HAVE_STRUCT_DIRENT_D_TYPE
#define DT_UNKNOWN 0 #define DT_UNKNOWN 0
@ -232,7 +233,8 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
/* Run a program and return its stdout in a string (i.e., like the /* Run a program and return its stdout in a string (i.e., like the
shell backtick operator). */ shell backtick operator). */
string runProgram(Path program, bool searchPath = false, string runProgram(Path program, bool searchPath = false,
const Strings & args = Strings(), const string & input = ""); const Strings & args = Strings(),
const std::experimental::optional<std::string> & input = {});
class ExecError : public Error class ExecError : public Error
{ {

View file

@ -170,10 +170,10 @@ int main(int argc, char * * argv)
Path unpacked = (Path) tmpDir + "/unpacked"; Path unpacked = (Path) tmpDir + "/unpacked";
createDirs(unpacked); createDirs(unpacked);
if (hasSuffix(baseNameOf(uri), ".zip")) if (hasSuffix(baseNameOf(uri), ".zip"))
runProgram("unzip", true, {"-qq", tmpFile, "-d", unpacked}, ""); runProgram("unzip", true, {"-qq", tmpFile, "-d", unpacked});
else else
// FIXME: this requires GNU tar for decompression. // FIXME: this requires GNU tar for decompression.
runProgram("tar", true, {"xf", tmpFile, "-C", unpacked}, ""); runProgram("tar", true, {"xf", tmpFile, "-C", unpacked});
/* If the archive unpacks to a single file/directory, then use /* If the archive unpacks to a single file/directory, then use
that as the top-level. */ that as the top-level. */