Merge commit '8e9140cfdef9dbd1eb61e4c75c91d452ab5e4a74' into nix

Conflicts:
	Makefile.config.in
	configure.ac
	dev-shell
	doc/manual/builtins.xml
	doc/manual/conf-file.xml
	doc/manual/local.mk
	doc/manual/nix-instantiate.xml
	doc/manual/nix-store.xml
	doc/manual/writing-nix-expressions.xml
	misc/emacs/nix-mode.el
	perl/lib/Nix/CopyClosure.pm
	release.nix
	scripts/nix-build.in
	scripts/nix-copy-closure.in
	src/download-via-ssh/download-via-ssh.cc
	src/libexpr/common-opts.cc
	src/libexpr/common-opts.hh
	src/libexpr/eval.cc
	src/libexpr/eval.hh
	src/libexpr/local.mk
	src/libexpr/nixexpr.cc
	src/libexpr/nixexpr.hh
	src/libexpr/parser.y
	src/libexpr/primops.cc
	src/libexpr/symbol-table.hh
	src/libmain/shared.cc
	src/libstore/local.mk
	src/nix-env/nix-env.cc
	src/nix-instantiate/nix-instantiate.cc
	src/nix-store/local.mk
	src/nix-store/nix-store.cc
	src/nix-store/serve-protocol.hh
	tests/lang.sh
	tests/lang/eval-okay-context.nix
	tests/lang/eval-okay-search-path.exp
	tests/lang/eval-okay-search-path.nix
	tests/local.mk
	tests/nix-copy-closure.nix
This commit is contained in:
Ludovic Courtès 2015-05-06 23:22:04 +02:00
commit c69944c511
12 changed files with 179 additions and 196 deletions

View File

@ -602,42 +602,29 @@ HookInstance::HookInstance()
builderOut.create(); builderOut.create();
/* Fork the hook. */ /* Fork the hook. */
pid = maybeVfork(); pid = startProcess([&]() {
switch (pid) {
case -1: commonChildInit(fromHook);
throw SysError("unable to fork");
case 0: if (chdir("/") == -1) throw SysError("changing into `/");
try { /* child */
commonChildInit(fromHook); /* Dup the communication pipes. */
if (dup2(toHook.readSide, STDIN_FILENO) == -1)
throw SysError("dupping to-hook read side");
if (chdir("/") == -1) throw SysError("changing into `/"); /* Use fd 4 for the builder's stdout/stderr. */
if (dup2(builderOut.writeSide, 4) == -1)
throw SysError("dupping builder's stdout/stderr");
/* Dup the communication pipes. */ execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
if (dup2(toHook.readSide, STDIN_FILENO) == -1) (format("%1%") % settings.maxSilentTime).str().c_str(),
throw SysError("dupping to-hook read side"); (format("%1%") % settings.printBuildTrace).str().c_str(),
(format("%1%") % settings.buildTimeout).str().c_str(),
NULL);
/* Use fd 4 for the builder's stdout/stderr. */ throw SysError(format("executing `%1%'") % buildHook);
if (dup2(builderOut.writeSide, 4) == -1) });
throw SysError("dupping builder's stdout/stderr");
execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
(format("%1%") % settings.maxSilentTime).str().c_str(),
(format("%1%") % settings.printBuildTrace).str().c_str(),
(format("%1%") % settings.buildTimeout).str().c_str(),
NULL);
throw SysError(format("executing `%1%'") % buildHook);
} catch (std::exception & e) {
writeToStderr("build hook error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* parent */
pid.setSeparatePG(true); pid.setSeparatePG(true);
pid.setKillSignal(SIGTERM); pid.setKillSignal(SIGTERM);
fromHook.writeSide.close(); fromHook.writeSide.close();
@ -2781,32 +2768,18 @@ void SubstitutionGoal::tryToRun()
const char * * argArr = strings2CharPtrs(args); const char * * argArr = strings2CharPtrs(args);
/* Fork the substitute program. */ /* Fork the substitute program. */
pid = maybeVfork(); pid = startProcess([&]() {
switch (pid) { commonChildInit(logPipe);
case -1: if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("unable to fork"); throw SysError("cannot dup output pipe into stdout");
case 0: execv(sub.c_str(), (char * *) argArr);
try { /* child */
commonChildInit(logPipe); throw SysError(format("executing `%1%'") % sub);
});
if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("cannot dup output pipe into stdout");
execv(sub.c_str(), (char * *) argArr);
throw SysError(format("executing `%1%'") % sub);
} catch (std::exception & e) {
writeToStderr("substitute error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* parent */
pid.setSeparatePG(true); pid.setSeparatePG(true);
pid.setKillSignal(SIGTERM); pid.setKillSignal(SIGTERM);
outPipe.writeSide.close(); outPipe.writeSide.close();

View File

@ -55,6 +55,7 @@ Settings::Settings()
envKeepDerivations = false; envKeepDerivations = false;
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1"; lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
showTrace = false; showTrace = false;
enableImportNative = false;
} }
@ -141,6 +142,8 @@ void Settings::update()
get(envKeepDerivations, "env-keep-derivations"); get(envKeepDerivations, "env-keep-derivations");
get(sshSubstituterHosts, "ssh-substituter-hosts"); get(sshSubstituterHosts, "ssh-substituter-hosts");
get(useSshSubstituter, "use-ssh-substituter"); get(useSshSubstituter, "use-ssh-substituter");
get(logServers, "log-servers");
get(enableImportNative, "allow-unsafe-native-code-during-evaluation");
string subs = getEnv("NIX_SUBSTITUTERS", "default"); string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") { if (subs == "default") {

View File

@ -197,6 +197,12 @@ struct Settings {
/* Whether to show a stack trace if Nix evaluation fails. */ /* Whether to show a stack trace if Nix evaluation fails. */
bool showTrace; bool showTrace;
/* A list of URL prefixes that can return Nix build logs. */
Strings logServers;
/* Whether the importNative primop should be enabled */
bool enableImportNative;
private: private:
SettingsMap settings, overrides; SettingsMap settings, overrides;

View File

@ -1083,31 +1083,16 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
setSubstituterEnv(); setSubstituterEnv();
run.pid = maybeVfork(); run.pid = startProcess([&]() {
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
switch (run.pid) { throw SysError("dupping stdin");
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
case -1: throw SysError("dupping stdout");
throw SysError("unable to fork"); if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1)
throw SysError("dupping stderr");
case 0: /* child */ execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
try { throw SysError(format("executing `%1%'") % substituter);
restoreAffinity(); });
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1)
throw SysError("dupping stderr");
execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
throw SysError(format("executing `%1%'") % substituter);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
}
_exit(1);
}
/* Parent. */
run.program = baseNameOf(substituter); run.program = baseNameOf(substituter);
run.to = toPipe.writeSide.borrow(); run.to = toPipe.writeSide.borrow();

View File

@ -1,16 +1,12 @@
#pragma once #pragma once
#include <string> #include <string>
#include <unordered_set>
#include "store-api.hh" #include "store-api.hh"
#include "util.hh" #include "util.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#if HAVE_TR1_UNORDERED_SET
#include <tr1/unordered_set>
#endif
class sqlite3; class sqlite3;
class sqlite3_stmt; class sqlite3_stmt;
@ -306,11 +302,7 @@ private:
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv); void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
#if HAVE_TR1_UNORDERED_SET typedef std::unordered_set<ino_t> InodeHash;
typedef std::tr1::unordered_set<ino_t> InodeHash;
#else
typedef std::set<ino_t> InodeHash;
#endif
InodeHash loadInodeHash(); InodeHash loadInodeHash();
Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash); Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash);

View File

@ -402,8 +402,23 @@ Path RemoteStore::addToStore(const Path & _srcPath,
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to); writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
writeInt(recursive ? 1 : 0, to); writeInt(recursive ? 1 : 0, to);
writeString(printHashType(hashAlgo), to); writeString(printHashType(hashAlgo), to);
dumpPath(srcPath, to, filter);
processStderr(); try {
to.written = 0;
to.warn = true;
dumpPath(srcPath, to, filter);
to.warn = false;
processStderr();
} catch (SysError & e) {
/* Daemon closed while we were sending the path. Probably OOM
or I/O error. */
if (e.errNo == EPIPE)
try {
processStderr();
} catch (EndOfFile & e) { }
throw;
}
return readStorePath(from); return readStorePath(from);
} }

View File

@ -54,8 +54,24 @@ FdSink::~FdSink()
} }
size_t threshold = 256 * 1024 * 1024;
static void warnLargeDump()
{
printMsg(lvlError, "warning: dumping very large path (> 256 MiB); this may run out of memory");
}
void FdSink::write(const unsigned char * data, size_t len) void FdSink::write(const unsigned char * data, size_t len)
{ {
static bool warned = false;
if (warn && !warned) {
written += len;
if (written > threshold) {
warnLargeDump();
warned = true;
}
}
writeFull(fd, data, len); writeFull(fd, data, len);
} }
@ -256,4 +272,15 @@ template Paths readStrings(Source & source);
template PathSet readStrings(Source & source); template PathSet readStrings(Source & source);
void StringSink::operator () (const unsigned char * data, size_t len)
{
static bool warned = false;
if (!warned && s.size() > threshold) {
warnLargeDump();
warned = true;
}
s.append((const char *) data, len);
}
} }

View File

@ -72,9 +72,11 @@ struct BufferedSource : Source
struct FdSink : BufferedSink struct FdSink : BufferedSink
{ {
int fd; int fd;
bool warn;
size_t written;
FdSink() : fd(-1) { } FdSink() : fd(-1), warn(false), written(0) { }
FdSink(int fd) : fd(fd) { } FdSink(int fd) : fd(fd), warn(false), written(0) { }
~FdSink(); ~FdSink();
void write(const unsigned char * data, size_t len); void write(const unsigned char * data, size_t len);
@ -95,10 +97,7 @@ struct FdSource : BufferedSource
struct StringSink : Sink struct StringSink : Sink
{ {
string s; string s;
void operator () (const unsigned char * data, size_t len) void operator () (const unsigned char * data, size_t len);
{
s.append((const char *) data, len);
}
}; };

View File

@ -41,8 +41,8 @@ public:
BaseError(const FormatOrString & fs, unsigned int status = 1); BaseError(const FormatOrString & fs, unsigned int status = 1);
~BaseError() throw () { }; ~BaseError() throw () { };
const char * what() const throw () { return err.c_str(); } const char * what() const throw () { return err.c_str(); }
const string & msg() const throw () { return err; } const string & msg() const { return err; }
const string & prefix() const throw () { return prefix_; } const string & prefix() const { return prefix_; }
BaseError & addPrefix(const FormatOrString & fs); BaseError & addPrefix(const FormatOrString & fs);
}; };

View File

@ -1,5 +1,8 @@
#include "config.h" #include "config.h"
#include "util.hh"
#include "affinity.hh"
#include <iostream> #include <iostream>
#include <cerrno> #include <cerrno>
#include <cstdio> #include <cstdio>
@ -16,8 +19,6 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#endif #endif
#include "util.hh"
extern char * * environ; extern char * * environ;
@ -714,6 +715,13 @@ Pid::Pid()
} }
Pid::Pid(pid_t pid)
{
Pid();
*this = pid;
}
Pid::~Pid() Pid::~Pid()
{ {
kill(); kill();
@ -801,43 +809,30 @@ void killUser(uid_t uid)
users to which the current process can send signals. So we users to which the current process can send signals. So we
fork a process, switch to uid, and send a mass kill. */ fork a process, switch to uid, and send a mass kill. */
Pid pid; Pid pid = startProcess([&]() {
pid = fork();
switch (pid) {
case -1: if (setuid(uid) == -1)
throw SysError("unable to fork"); throw SysError("setting uid");
case 0: while (true) {
try { /* child */
if (setuid(uid) == -1)
throw SysError("setting uid");
while (true) {
#ifdef __APPLE__ #ifdef __APPLE__
/* OSX's kill syscall takes a third parameter that, among other /* OSX's kill syscall takes a third parameter that, among
things, determines if kill(-1, signo) affects the calling other things, determines if kill(-1, signo) affects the
process. In the OSX libc, it's set to true, which means calling process. In the OSX libc, it's set to true,
"follow POSIX", which we don't want here which means "follow POSIX", which we don't want here
*/ */
if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break;
#else #else
if (kill(-1, SIGKILL) == 0) break; if (kill(-1, SIGKILL) == 0) break;
#endif #endif
if (errno == ESRCH) break; /* no more processes */ if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR) if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid); throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
} catch (std::exception & e) {
writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
_exit(1);
} }
_exit(0);
}
/* parent */ _exit(0);
});
int status = pid.wait(true); int status = pid.wait(true);
if (status != 0) if (status != 0)
throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status)); throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status));
@ -852,6 +847,25 @@ void killUser(uid_t uid)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
pid_t startProcess(std::function<void()> fun, const string & errorPrefix)
{
pid_t pid = fork();
if (pid == -1) throw SysError("unable to fork");
if (pid == 0) {
try {
restoreAffinity();
fun();
} catch (std::exception & e) {
writeToStderr(errorPrefix + string(e.what()) + "\n");
}
_exit(1);
}
return pid;
}
string runProgram(Path program, bool searchPath, const Strings & args) string runProgram(Path program, bool searchPath, const Strings & args)
{ {
checkInterrupt(); checkInterrupt();
@ -867,32 +881,17 @@ string runProgram(Path program, bool searchPath, const Strings & args)
pipe.create(); pipe.create();
/* Fork. */ /* Fork. */
Pid pid; Pid pid = startProcess([&]() {
pid = maybeVfork(); if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
switch (pid) { if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
execv(program.c_str(), (char * *) &cargs[0]);
case -1: throw SysError(format("executing `%1%'") % program);
throw SysError("unable to fork"); });
case 0: /* child */
try {
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
execv(program.c_str(), (char * *) &cargs[0]);
throw SysError(format("executing `%1%'") % program);
} catch (std::exception & e) {
writeToStderr("error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* Parent. */
pipe.writeSide.close(); pipe.writeSide.close();
@ -901,7 +900,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
/* Wait for the child to finish. */ /* Wait for the child to finish. */
int status = pid.wait(true); int status = pid.wait(true);
if (!statusOk(status)) if (!statusOk(status))
throw Error(format("program `%1%' %2%") throw ExecError(format("program `%1%' %2%")
% program % statusToString(status)); % program % statusToString(status));
return result; return result;
@ -928,13 +927,6 @@ void closeOnExec(int fd)
} }
#if HAVE_VFORK
pid_t (*maybeVfork)() = vfork;
#else
pid_t (*maybeVfork)() = fork;
#endif
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -7,6 +7,7 @@
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <functional>
#include <cstdio> #include <cstdio>
@ -237,6 +238,7 @@ class Pid
int killSignal; int killSignal;
public: public:
Pid(); Pid();
Pid(pid_t pid);
~Pid(); ~Pid();
void operator =(pid_t pid); void operator =(pid_t pid);
operator pid_t(); operator pid_t();
@ -252,11 +254,18 @@ public:
void killUser(uid_t uid); void killUser(uid_t uid);
/* Fork a process that runs the given function, and return the child
pid to the caller. */
pid_t startProcess(std::function<void()> fun, const string & errorPrefix = "error: ");
/* 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 Strings & args = Strings());
MakeError(ExecError, Error)
/* Close all file descriptors except stdin, stdout, stderr, and those /* Close all file descriptors except stdin, stdout, stderr, and those
listed in the given set. Good practice in child processes. */ listed in the given set. Good practice in child processes. */
void closeMostFDs(const set<int> & exceptions); void closeMostFDs(const set<int> & exceptions);
@ -264,9 +273,6 @@ void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */ /* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd); void closeOnExec(int fd);
/* Call vfork() if available, otherwise fork(). */
extern pid_t (*maybeVfork)();
/* User interruption. */ /* User interruption. */

View File

@ -735,12 +735,10 @@ static void processConnection(bool trusted)
during addTextToStore() / importPath(). If that during addTextToStore() / importPath(). If that
happens, just send the error message and exit. */ happens, just send the error message and exit. */
bool errorAllowed = canSendStderr; bool errorAllowed = canSendStderr;
if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0); stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
if (!errorAllowed) break; if (!errorAllowed) throw;
} catch (std::bad_alloc & e) { } catch (std::bad_alloc & e) {
if (canSendStderr) stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
throw; throw;
} }
@ -874,40 +872,27 @@ static void daemonLoop()
printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid); printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
/* Fork a child to handle the connection. */ /* Fork a child to handle the connection. */
pid_t child; startProcess([&]() {
child = fork(); /* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
switch (child) { /* Restore normal handling of SIGCHLD. */
setSigChldAction(false);
case -1: /* For debugging, stuff the pid into argv[1]. */
throw SysError("unable to fork"); if (clientPid != -1 && argvSaved[1]) {
string processName = int2String(clientPid);
case 0: strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
try { /* child */
/* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
/* Restore normal handling of SIGCHLD. */
setSigChldAction(false);
/* For debugging, stuff the pid into argv[1]. */
if (clientPid != -1 && argvSaved[1]) {
string processName = int2String(clientPid);
strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
}
/* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection(trusted);
} catch (std::exception & e) {
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
} }
exit(0);
} /* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection(trusted);
_exit(0);
}, "unexpected Nix daemon error: ");
} catch (Interrupted & e) { } catch (Interrupted & e) {
throw; throw;