2006-11-30 19:19:59 +00:00
|
|
|
|
#include "shared.hh"
|
|
|
|
|
#include "local-store.hh"
|
|
|
|
|
#include "util.hh"
|
2006-11-30 19:54:43 +00:00
|
|
|
|
#include "serialise.hh"
|
2006-11-30 20:13:59 +00:00
|
|
|
|
#include "worker-protocol.hh"
|
2006-11-30 20:45:20 +00:00
|
|
|
|
#include "archive.hh"
|
2013-08-07 11:51:55 +00:00
|
|
|
|
#include "affinity.hh"
|
2006-12-03 16:25:19 +00:00
|
|
|
|
#include "globals.hh"
|
2014-07-23 17:21:00 +00:00
|
|
|
|
#include "monitor-fd.hh"
|
2015-09-03 10:56:59 +00:00
|
|
|
|
#include "derivations.hh"
|
2006-11-30 19:19:59 +00:00
|
|
|
|
|
2014-07-17 14:57:07 +00:00
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
2010-06-24 17:51:04 +00:00
|
|
|
|
#include <cstring>
|
2006-12-03 03:03:36 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <signal.h>
|
2006-12-04 17:17:13 +00:00
|
|
|
|
#include <sys/types.h>
|
2006-12-05 17:21:42 +00:00
|
|
|
|
#include <sys/wait.h>
|
2006-12-04 17:17:13 +00:00
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
2006-12-05 17:21:42 +00:00
|
|
|
|
#include <errno.h>
|
2014-07-17 13:49:33 +00:00
|
|
|
|
#include <pwd.h>
|
2014-07-17 14:57:07 +00:00
|
|
|
|
#include <grp.h>
|
2016-09-02 17:30:28 +00:00
|
|
|
|
#include <fcntl.h>
|
2006-12-02 14:27:24 +00:00
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
#if __APPLE__ || __FreeBSD__
|
|
|
|
|
#include <sys/ucred.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-11-30 19:19:59 +00:00
|
|
|
|
using namespace nix;
|
|
|
|
|
|
2016-09-02 17:30:28 +00:00
|
|
|
|
#ifndef __linux__
|
|
|
|
|
#define SPLICE_F_MOVE 0
|
|
|
|
|
static ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags)
|
|
|
|
|
{
|
|
|
|
|
/* We ignore most parameters, we just have them for conformance with the linux syscall */
|
|
|
|
|
char buf[8192];
|
|
|
|
|
auto read_count = read(fd_in, buf, sizeof(buf));
|
|
|
|
|
if (read_count == -1)
|
|
|
|
|
return read_count;
|
|
|
|
|
auto write_count = decltype<read_count>(0);
|
|
|
|
|
while (write_count < read_count) {
|
|
|
|
|
auto res = write(fd_out, buf + write_count, read_count - write_count);
|
|
|
|
|
if (res == -1)
|
|
|
|
|
return res;
|
|
|
|
|
write_count += res;
|
|
|
|
|
}
|
|
|
|
|
return read_count;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2006-11-30 19:19:59 +00:00
|
|
|
|
|
2006-12-03 16:25:19 +00:00
|
|
|
|
static FdSource from(STDIN_FILENO);
|
|
|
|
|
static FdSink to(STDOUT_FILENO);
|
|
|
|
|
|
2016-04-25 13:26:07 +00:00
|
|
|
|
static bool canSendStderr;
|
2006-12-05 18:21:16 +00:00
|
|
|
|
|
2016-04-25 13:26:07 +00:00
|
|
|
|
static Logger * defaultLogger;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
|
2016-04-25 13:26:07 +00:00
|
|
|
|
|
|
|
|
|
/* Logger that forwards log messages to the client, *if* we're in a
|
|
|
|
|
state where the protocol allows it (i.e., when canSendStderr is
|
|
|
|
|
true). */
|
|
|
|
|
class TunnelLogger : public Logger
|
2006-11-30 19:19:59 +00:00
|
|
|
|
{
|
2016-04-25 13:26:07 +00:00
|
|
|
|
void log(Verbosity lvl, const FormatOrString & fs) override
|
|
|
|
|
{
|
|
|
|
|
if (lvl > verbosity) return;
|
|
|
|
|
|
|
|
|
|
if (canSendStderr) {
|
|
|
|
|
try {
|
|
|
|
|
to << STDERR_NEXT << (fs.s + "\n");
|
|
|
|
|
to.flush();
|
|
|
|
|
} catch (...) {
|
|
|
|
|
/* Write failed; that means that the other side is
|
|
|
|
|
gone. */
|
|
|
|
|
canSendStderr = false;
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
defaultLogger->log(lvl, fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override
|
|
|
|
|
{
|
|
|
|
|
log(lvl, fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopActivity(Activity & activity) override
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
2006-11-30 19:19:59 +00:00
|
|
|
|
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
/* startWork() means that we're starting an operation for which we
|
|
|
|
|
want to send out stderr to the client. */
|
|
|
|
|
static void startWork()
|
|
|
|
|
{
|
|
|
|
|
canSendStderr = true;
|
|
|
|
|
}
|
2006-11-30 19:54:43 +00:00
|
|
|
|
|
2006-11-30 20:13:59 +00:00
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
/* stopWork() means that we're done; stop sending stderr to the
|
|
|
|
|
client. */
|
2010-12-17 11:28:26 +00:00
|
|
|
|
static void stopWork(bool success = true, const string & msg = "", unsigned int status = 0)
|
2006-12-03 02:08:13 +00:00
|
|
|
|
{
|
|
|
|
|
canSendStderr = false;
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
|
|
|
|
if (success)
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << STDERR_LAST;
|
2006-12-04 17:17:13 +00:00
|
|
|
|
else {
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << STDERR_ERROR << msg;
|
|
|
|
|
if (status != 0) to << status;
|
2006-12-04 17:17:13 +00:00
|
|
|
|
}
|
2006-12-03 02:08:13 +00:00
|
|
|
|
}
|
2006-12-01 18:00:01 +00:00
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
|
2007-02-21 16:34:00 +00:00
|
|
|
|
struct TunnelSink : Sink
|
|
|
|
|
{
|
|
|
|
|
Sink & to;
|
2011-12-14 23:30:06 +00:00
|
|
|
|
TunnelSink(Sink & to) : to(to) { }
|
2011-12-15 16:19:53 +00:00
|
|
|
|
virtual void operator () (const unsigned char * data, size_t len)
|
2007-02-21 16:34:00 +00:00
|
|
|
|
{
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << STDERR_WRITE;
|
2011-12-16 21:29:46 +00:00
|
|
|
|
writeString(data, len, to);
|
2007-02-21 16:34:00 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2011-12-16 19:44:13 +00:00
|
|
|
|
struct TunnelSource : BufferedSource
|
2007-02-21 17:34:02 +00:00
|
|
|
|
{
|
|
|
|
|
Source & from;
|
2011-12-14 23:30:06 +00:00
|
|
|
|
TunnelSource(Source & from) : from(from) { }
|
2011-12-16 19:44:13 +00:00
|
|
|
|
size_t readUnbuffered(unsigned char * data, size_t len)
|
2007-02-21 17:34:02 +00:00
|
|
|
|
{
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << STDERR_READ << len;
|
2011-12-14 23:30:06 +00:00
|
|
|
|
to.flush();
|
2011-12-16 21:29:46 +00:00
|
|
|
|
size_t n = readString(data, len, from);
|
|
|
|
|
if (n == 0) throw EndOfFile("unexpected end-of-file");
|
|
|
|
|
return n;
|
2007-02-21 17:34:02 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2008-12-03 18:05:14 +00:00
|
|
|
|
/* If the NAR archive contains a single file at top-level, then save
|
|
|
|
|
the contents of the file to `s'. Otherwise barf. */
|
|
|
|
|
struct RetrieveRegularNARSink : ParseSink
|
|
|
|
|
{
|
2011-12-01 13:48:48 +00:00
|
|
|
|
bool regular;
|
2008-12-03 18:05:14 +00:00
|
|
|
|
string s;
|
|
|
|
|
|
2011-12-01 13:48:48 +00:00
|
|
|
|
RetrieveRegularNARSink() : regular(true) { }
|
|
|
|
|
|
2008-12-03 18:05:14 +00:00
|
|
|
|
void createDirectory(const Path & path)
|
|
|
|
|
{
|
2011-12-01 13:48:48 +00:00
|
|
|
|
regular = false;
|
2008-12-03 18:05:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void receiveContents(unsigned char * data, unsigned int len)
|
|
|
|
|
{
|
|
|
|
|
s.append((const char *) data, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createSymlink(const Path & path, const string & target)
|
|
|
|
|
{
|
2011-12-01 13:48:48 +00:00
|
|
|
|
regular = false;
|
2008-12-03 18:05:14 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Adapter class of a Source that saves all data read to `s'. */
|
|
|
|
|
struct SavingSourceAdapter : Source
|
|
|
|
|
{
|
|
|
|
|
Source & orig;
|
|
|
|
|
string s;
|
|
|
|
|
SavingSourceAdapter(Source & orig) : orig(orig) { }
|
2011-12-16 19:44:13 +00:00
|
|
|
|
size_t read(unsigned char * data, size_t len)
|
2008-12-03 18:05:14 +00:00
|
|
|
|
{
|
2011-12-16 19:44:13 +00:00
|
|
|
|
size_t n = orig.read(data, len);
|
|
|
|
|
s.append((const char *) data, n);
|
|
|
|
|
return n;
|
2008-12-03 18:05:14 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVersion,
|
2007-11-16 16:15:26 +00:00
|
|
|
|
Source & from, Sink & to, unsigned int op)
|
2006-12-03 02:08:13 +00:00
|
|
|
|
{
|
|
|
|
|
switch (op) {
|
|
|
|
|
|
|
|
|
|
case wopIsValidPath: {
|
2014-03-18 22:23:55 +00:00
|
|
|
|
/* 'readStorePath' could raise an error leading to the connection
|
|
|
|
|
being closed. To be able to recover from an invalid path error,
|
|
|
|
|
call 'startWork' early, and do 'assertStorePath' afterwards so
|
|
|
|
|
that the 'Error' exception handler doesn't close the
|
|
|
|
|
connection. */
|
2014-03-18 22:17:14 +00:00
|
|
|
|
Path path = readString(from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2016-06-01 12:49:12 +00:00
|
|
|
|
store->assertStorePath(path);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
bool result = store->isValidPath(path);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << result;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-11 15:08:47 +00:00
|
|
|
|
case wopQueryValidPaths: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
PathSet paths = readStorePaths<PathSet>(*store, from);
|
2012-07-11 15:08:47 +00:00
|
|
|
|
startWork();
|
|
|
|
|
PathSet res = store->queryValidPaths(paths);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << res;
|
2012-07-11 15:08:47 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopHasSubstitutes: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2016-05-04 14:04:52 +00:00
|
|
|
|
PathSet res = store->querySubstitutablePaths({path});
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << (res.find(path) != res.end());
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-11 21:52:18 +00:00
|
|
|
|
case wopQuerySubstitutablePaths: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
PathSet paths = readStorePaths<PathSet>(*store, from);
|
2012-07-11 21:52:18 +00:00
|
|
|
|
startWork();
|
|
|
|
|
PathSet res = store->querySubstitutablePaths(paths);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << res;
|
2012-07-11 21:52:18 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopQueryPathHash: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2016-04-19 16:50:15 +00:00
|
|
|
|
auto hash = store->queryPathInfo(path)->narHash;
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << printHash(hash);
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case wopQueryReferences:
|
2010-02-25 15:52:22 +00:00
|
|
|
|
case wopQueryReferrers:
|
2012-12-20 17:41:44 +00:00
|
|
|
|
case wopQueryValidDerivers:
|
2010-02-25 15:52:22 +00:00
|
|
|
|
case wopQueryDerivationOutputs: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2006-12-03 02:08:13 +00:00
|
|
|
|
PathSet paths;
|
|
|
|
|
if (op == wopQueryReferences)
|
2016-04-19 16:50:15 +00:00
|
|
|
|
paths = store->queryPathInfo(path)->references;
|
2010-02-25 15:52:22 +00:00
|
|
|
|
else if (op == wopQueryReferrers)
|
2006-12-03 02:08:13 +00:00
|
|
|
|
store->queryReferrers(path, paths);
|
2012-12-20 17:41:44 +00:00
|
|
|
|
else if (op == wopQueryValidDerivers)
|
|
|
|
|
paths = store->queryValidDerivers(path);
|
2010-02-25 15:52:22 +00:00
|
|
|
|
else paths = store->queryDerivationOutputs(path);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << paths;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-06 06:28:20 +00:00
|
|
|
|
case wopQueryDerivationOutputNames: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2011-11-06 06:28:20 +00:00
|
|
|
|
startWork();
|
|
|
|
|
StringSet names;
|
|
|
|
|
names = store->queryDerivationOutputNames(path);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << names;
|
2011-11-06 06:28:20 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-12 16:53:44 +00:00
|
|
|
|
case wopQueryDeriver: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2007-06-12 16:53:44 +00:00
|
|
|
|
startWork();
|
2016-04-19 16:50:15 +00:00
|
|
|
|
auto deriver = store->queryPathInfo(path)->deriver;
|
2007-06-12 16:53:44 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << deriver;
|
2007-06-12 16:53:44 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-17 22:55:39 +00:00
|
|
|
|
case wopQueryPathFromHashPart: {
|
|
|
|
|
string hashPart = readString(from);
|
|
|
|
|
startWork();
|
|
|
|
|
Path path = store->queryPathFromHashPart(hashPart);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << path;
|
2012-07-17 22:55:39 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopAddToStore: {
|
|
|
|
|
string baseName = readString(from);
|
2008-12-03 17:02:29 +00:00
|
|
|
|
bool fixed = readInt(from) == 1; /* obsolete */
|
2006-12-03 02:08:13 +00:00
|
|
|
|
bool recursive = readInt(from) == 1;
|
2008-12-03 17:02:29 +00:00
|
|
|
|
string s = readString(from);
|
|
|
|
|
/* Compatibility hack. */
|
|
|
|
|
if (!fixed) {
|
|
|
|
|
s = "sha256";
|
|
|
|
|
recursive = true;
|
|
|
|
|
}
|
|
|
|
|
HashType hashAlgo = parseHashType(s);
|
|
|
|
|
|
2008-12-03 18:05:14 +00:00
|
|
|
|
SavingSourceAdapter savedNAR(from);
|
|
|
|
|
RetrieveRegularNARSink savedRegular;
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2008-12-03 18:05:14 +00:00
|
|
|
|
if (recursive) {
|
|
|
|
|
/* Get the entire NAR dump from the client and save it to
|
|
|
|
|
a string so that we can pass it to
|
|
|
|
|
addToStoreFromDump(). */
|
|
|
|
|
ParseSink sink; /* null sink; just parse the NAR */
|
|
|
|
|
parseDump(sink, savedNAR);
|
2011-12-01 13:51:05 +00:00
|
|
|
|
} else
|
2008-12-03 18:05:14 +00:00
|
|
|
|
parseDump(savedRegular, from);
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2011-12-01 13:51:05 +00:00
|
|
|
|
if (!savedRegular.regular) throw Error("regular file expected");
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
Path path = store->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << path;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-12-01 18:00:01 +00:00
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopAddTextToStore: {
|
|
|
|
|
string suffix = readString(from);
|
|
|
|
|
string s = readString(from);
|
2016-06-01 12:49:12 +00:00
|
|
|
|
PathSet refs = readStorePaths<PathSet>(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
|
|
|
|
Path path = store->addTextToStore(suffix, s, refs);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << path;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-11-30 20:13:59 +00:00
|
|
|
|
|
2007-02-21 16:34:00 +00:00
|
|
|
|
case wopExportPath: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2016-05-03 13:11:14 +00:00
|
|
|
|
readInt(from); // obsolete
|
2007-02-21 16:34:00 +00:00
|
|
|
|
startWork();
|
|
|
|
|
TunnelSink sink(to);
|
2016-05-03 13:11:14 +00:00
|
|
|
|
store->exportPath(path, sink);
|
2007-02-21 16:34:00 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2007-02-21 16:34:00 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-16 22:31:25 +00:00
|
|
|
|
case wopImportPaths: {
|
2007-02-21 17:34:02 +00:00
|
|
|
|
startWork();
|
|
|
|
|
TunnelSource source(from);
|
2016-05-03 13:11:14 +00:00
|
|
|
|
Paths paths = store->importPaths(source, 0);
|
2007-02-21 17:34:02 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << paths;
|
2007-02-21 17:34:02 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-27 20:58:15 +00:00
|
|
|
|
case wopBuildPaths: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
PathSet drvs = readStorePaths<PathSet>(*store, from);
|
2015-12-02 17:13:33 +00:00
|
|
|
|
BuildMode mode = bmNormal;
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
|
|
|
|
|
mode = (BuildMode)readInt(from);
|
|
|
|
|
|
2016-02-26 14:20:10 +00:00
|
|
|
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
|
|
|
|
clients. */
|
2015-12-02 17:13:33 +00:00
|
|
|
|
if (mode == bmRepair && !trusted)
|
|
|
|
|
throw Error("repairing is not supported when building through the Nix daemon");
|
|
|
|
|
}
|
2006-12-03 02:08:13 +00:00
|
|
|
|
startWork();
|
2015-12-02 17:13:33 +00:00
|
|
|
|
store->buildPaths(drvs, mode);
|
2006-12-03 02:08:13 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-11-30 20:13:59 +00:00
|
|
|
|
|
2015-09-03 10:56:59 +00:00
|
|
|
|
case wopBuildDerivation: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path drvPath = readStorePath(*store, from);
|
2015-09-03 10:56:59 +00:00
|
|
|
|
BasicDerivation drv;
|
2016-06-01 12:49:12 +00:00
|
|
|
|
readDerivation(from, *store, drv);
|
2015-09-03 10:56:59 +00:00
|
|
|
|
BuildMode buildMode = (BuildMode) readInt(from);
|
|
|
|
|
startWork();
|
|
|
|
|
if (!trusted)
|
|
|
|
|
throw Error("you are not privileged to build derivations");
|
|
|
|
|
auto res = store->buildDerivation(drvPath, drv, buildMode);
|
|
|
|
|
stopWork();
|
|
|
|
|
to << res.status << res.errorMsg;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopEnsurePath: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2006-12-03 02:08:13 +00:00
|
|
|
|
store->ensurePath(path);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-11-30 20:13:59 +00:00
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopAddTempRoot: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2006-12-03 02:08:13 +00:00
|
|
|
|
store->addTempRoot(path);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-11-30 22:43:55 +00:00
|
|
|
|
|
2006-12-04 23:29:16 +00:00
|
|
|
|
case wopAddIndirectRoot: {
|
|
|
|
|
Path path = absPath(readString(from));
|
|
|
|
|
startWork();
|
|
|
|
|
store->addIndirectRoot(path);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2006-12-04 23:29:16 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
case wopSyncWithGC: {
|
2006-12-04 17:55:14 +00:00
|
|
|
|
startWork();
|
2006-12-03 02:08:13 +00:00
|
|
|
|
store->syncWithGC();
|
2006-12-04 17:55:14 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2006-12-03 02:08:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-12-02 14:27:24 +00:00
|
|
|
|
|
2006-12-05 01:31:45 +00:00
|
|
|
|
case wopFindRoots: {
|
|
|
|
|
startWork();
|
|
|
|
|
Roots roots = store->findRoots();
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << roots.size();
|
|
|
|
|
for (auto & i : roots)
|
|
|
|
|
to << i.first << i.second;
|
2006-12-05 01:31:45 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-05 02:18:46 +00:00
|
|
|
|
case wopCollectGarbage: {
|
2008-06-18 09:34:17 +00:00
|
|
|
|
GCOptions options;
|
|
|
|
|
options.action = (GCOptions::GCAction) readInt(from);
|
2016-06-01 12:49:12 +00:00
|
|
|
|
options.pathsToDelete = readStorePaths<PathSet>(*store, from);
|
2008-06-18 09:34:17 +00:00
|
|
|
|
options.ignoreLiveness = readInt(from);
|
|
|
|
|
options.maxFreed = readLongLong(from);
|
2016-07-27 13:03:20 +00:00
|
|
|
|
// obsolete fields
|
|
|
|
|
readInt(from);
|
|
|
|
|
readInt(from);
|
|
|
|
|
readInt(from);
|
2008-06-18 09:34:17 +00:00
|
|
|
|
|
|
|
|
|
GCResults results;
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-05 02:18:46 +00:00
|
|
|
|
startWork();
|
2008-06-18 09:34:17 +00:00
|
|
|
|
if (options.ignoreLiveness)
|
2006-12-05 02:18:46 +00:00
|
|
|
|
throw Error("you are not allowed to ignore liveness");
|
2008-06-18 09:34:17 +00:00
|
|
|
|
store->collectGarbage(options, results);
|
2006-12-05 02:18:46 +00:00
|
|
|
|
stopWork();
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << results.paths << results.bytesFreed << 0 /* obsolete */;
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-05 02:18:46 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2007-09-18 09:11:20 +00:00
|
|
|
|
|
|
|
|
|
case wopSetOptions: {
|
2012-07-30 23:55:41 +00:00
|
|
|
|
settings.keepFailed = readInt(from) != 0;
|
|
|
|
|
settings.keepGoing = readInt(from) != 0;
|
2014-03-17 16:35:11 +00:00
|
|
|
|
settings.set("build-fallback", readInt(from) ? "true" : "false");
|
2007-09-18 09:11:20 +00:00
|
|
|
|
verbosity = (Verbosity) readInt(from);
|
2015-10-29 12:26:55 +00:00
|
|
|
|
settings.set("build-max-jobs", std::to_string(readInt(from)));
|
|
|
|
|
settings.set("build-max-silent-time", std::to_string(readInt(from)));
|
2016-07-27 13:03:20 +00:00
|
|
|
|
settings.useBuildHook = readInt(from) != 0;
|
|
|
|
|
settings.verboseBuild = lvlError == (Verbosity) readInt(from);
|
|
|
|
|
readInt(from); // obsolete logType
|
|
|
|
|
readInt(from); // obsolete printBuildTrace
|
|
|
|
|
settings.set("build-cores", std::to_string(readInt(from)));
|
|
|
|
|
settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
|
2012-07-31 22:19:44 +00:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
|
|
|
|
|
unsigned int n = readInt(from);
|
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
|
string name = readString(from);
|
|
|
|
|
string value = readString(from);
|
2014-02-26 12:58:46 +00:00
|
|
|
|
if (name == "build-timeout" || name == "use-ssh-substituter")
|
|
|
|
|
settings.set(name, value);
|
2013-04-23 14:59:06 +00:00
|
|
|
|
else
|
2013-06-12 10:10:26 +00:00
|
|
|
|
settings.set(trusted ? name : "untrusted-" + name, value);
|
2012-07-31 22:19:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-17 16:35:11 +00:00
|
|
|
|
settings.update();
|
2007-09-18 09:11:20 +00:00
|
|
|
|
startWork();
|
|
|
|
|
stopWork();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-08-04 11:44:50 +00:00
|
|
|
|
|
|
|
|
|
case wopQuerySubstitutablePathInfo: {
|
|
|
|
|
Path path = absPath(readString(from));
|
|
|
|
|
startWork();
|
2012-07-11 14:43:24 +00:00
|
|
|
|
SubstitutablePathInfos infos;
|
2016-05-04 14:04:52 +00:00
|
|
|
|
store->querySubstitutablePathInfos({path}, infos);
|
2008-08-04 11:44:50 +00:00
|
|
|
|
stopWork();
|
2012-07-11 14:43:24 +00:00
|
|
|
|
SubstitutablePathInfos::iterator i = infos.find(path);
|
|
|
|
|
if (i == infos.end())
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 0;
|
2012-07-11 14:43:24 +00:00
|
|
|
|
else {
|
2016-07-27 13:03:20 +00:00
|
|
|
|
to << 1 << i->second.deriver << i->second.references << i->second.downloadSize << i->second.narSize;
|
2012-07-11 14:43:24 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2012-07-11 14:43:24 +00:00
|
|
|
|
case wopQuerySubstitutablePathInfos: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
PathSet paths = readStorePaths<PathSet>(*store, from);
|
2012-07-11 14:43:24 +00:00
|
|
|
|
startWork();
|
|
|
|
|
SubstitutablePathInfos infos;
|
|
|
|
|
store->querySubstitutablePathInfos(paths, infos);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << infos.size();
|
2015-07-17 17:24:28 +00:00
|
|
|
|
for (auto & i : infos) {
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << i.first << i.second.deriver << i.second.references
|
|
|
|
|
<< i.second.downloadSize << i.second.narSize;
|
2008-08-04 11:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2012-07-11 14:49:04 +00:00
|
|
|
|
case wopQueryAllValidPaths: {
|
2010-02-26 12:05:01 +00:00
|
|
|
|
startWork();
|
2012-07-11 14:49:04 +00:00
|
|
|
|
PathSet paths = store->queryAllValidPaths();
|
2010-02-26 12:05:01 +00:00
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << paths;
|
2010-02-26 12:05:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-17 12:08:01 +00:00
|
|
|
|
case wopQueryPathInfo: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2016-04-20 13:28:07 +00:00
|
|
|
|
std::shared_ptr<const ValidPathInfo> info;
|
2010-11-17 12:08:01 +00:00
|
|
|
|
startWork();
|
2016-04-20 13:28:07 +00:00
|
|
|
|
try {
|
|
|
|
|
info = store->queryPathInfo(path);
|
|
|
|
|
} catch (InvalidPath &) {
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw;
|
2016-03-30 15:35:48 +00:00
|
|
|
|
}
|
2016-04-20 13:28:07 +00:00
|
|
|
|
stopWork();
|
|
|
|
|
if (info) {
|
2016-05-04 10:53:24 +00:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
|
|
|
|
to << 1;
|
|
|
|
|
to << info->deriver << printHash(info->narHash) << info->references
|
2016-04-20 13:28:07 +00:00
|
|
|
|
<< info->registrationTime << info->narSize;
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
|
|
|
|
to << info->ultimate
|
2016-08-03 11:17:11 +00:00
|
|
|
|
<< info->sigs
|
|
|
|
|
<< info->ca;
|
2016-04-20 13:28:07 +00:00
|
|
|
|
}
|
2016-05-04 10:53:24 +00:00
|
|
|
|
} else {
|
|
|
|
|
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
|
2016-04-20 13:28:07 +00:00
|
|
|
|
to << 0;
|
2016-05-04 10:53:24 +00:00
|
|
|
|
}
|
2010-11-17 12:08:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 20:21:42 +00:00
|
|
|
|
case wopOptimiseStore:
|
2014-12-12 11:39:50 +00:00
|
|
|
|
startWork();
|
|
|
|
|
store->optimiseStore();
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << 1;
|
2014-12-12 11:39:50 +00:00
|
|
|
|
break;
|
2014-09-01 20:21:42 +00:00
|
|
|
|
|
2015-06-01 21:20:11 +00:00
|
|
|
|
case wopVerifyStore: {
|
2015-06-02 00:21:54 +00:00
|
|
|
|
bool checkContents = readInt(from) != 0;
|
|
|
|
|
bool repair = readInt(from) != 0;
|
|
|
|
|
startWork();
|
|
|
|
|
if (repair && !trusted)
|
|
|
|
|
throw Error("you are not privileged to repair paths");
|
|
|
|
|
bool errors = store->verifyStore(checkContents, repair);
|
|
|
|
|
stopWork();
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << errors;
|
2015-06-02 00:21:54 +00:00
|
|
|
|
break;
|
2015-06-01 21:20:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 13:30:22 +00:00
|
|
|
|
case wopAddSignatures: {
|
2016-06-01 12:49:12 +00:00
|
|
|
|
Path path = readStorePath(*store, from);
|
2016-04-05 13:30:22 +00:00
|
|
|
|
StringSet sigs = readStrings<StringSet>(from);
|
|
|
|
|
startWork();
|
|
|
|
|
if (!trusted)
|
|
|
|
|
throw Error("you are not privileged to add signatures");
|
|
|
|
|
store->addSignatures(path, sigs);
|
|
|
|
|
stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-02 18:26:02 +00:00
|
|
|
|
case wopNarFromPath: {
|
|
|
|
|
auto path = readStorePath(*store, from);
|
|
|
|
|
startWork();
|
|
|
|
|
dumpPath(path, to);
|
|
|
|
|
stopWork();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-03 02:08:13 +00:00
|
|
|
|
default:
|
|
|
|
|
throw Error(format("invalid operation %1%") % op);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-11-30 22:43:55 +00:00
|
|
|
|
|
2006-12-01 18:00:01 +00:00
|
|
|
|
|
2013-06-12 10:10:26 +00:00
|
|
|
|
static void processConnection(bool trusted)
|
2006-12-03 02:08:13 +00:00
|
|
|
|
{
|
2014-07-23 17:21:00 +00:00
|
|
|
|
MonitorFdHup monitor(from.fd);
|
|
|
|
|
|
2006-12-03 16:25:19 +00:00
|
|
|
|
canSendStderr = false;
|
2016-04-25 13:26:07 +00:00
|
|
|
|
defaultLogger = logger;
|
|
|
|
|
logger = new TunnelLogger();
|
2006-12-01 18:00:01 +00:00
|
|
|
|
|
2006-12-04 17:17:13 +00:00
|
|
|
|
/* Exchange the greeting. */
|
|
|
|
|
unsigned int magic = readInt(from);
|
|
|
|
|
if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
|
2015-07-19 23:16:16 +00:00
|
|
|
|
to << WORKER_MAGIC_2 << PROTOCOL_VERSION;
|
2011-12-14 23:30:06 +00:00
|
|
|
|
to.flush();
|
2007-09-18 09:11:20 +00:00
|
|
|
|
unsigned int clientVersion = readInt(from);
|
|
|
|
|
|
2016-07-27 13:03:20 +00:00
|
|
|
|
if (clientVersion < 0x10a)
|
|
|
|
|
throw Error("the Nix client version is too old");
|
|
|
|
|
|
2013-08-07 11:51:55 +00:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
|
|
|
|
|
setAffinityTo(readInt(from));
|
|
|
|
|
|
2016-07-27 13:03:20 +00:00
|
|
|
|
readInt(from); // obsolete reserveSpace
|
2012-08-22 14:58:24 +00:00
|
|
|
|
|
2006-12-03 16:25:19 +00:00
|
|
|
|
/* Send startup error messages to the client. */
|
|
|
|
|
startWork();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
2007-09-18 09:11:20 +00:00
|
|
|
|
/* If we can't accept clientVersion, then throw an error
|
|
|
|
|
*here* (not above). */
|
|
|
|
|
|
2008-03-20 18:15:20 +00:00
|
|
|
|
#if 0
|
2006-12-03 16:25:19 +00:00
|
|
|
|
/* Prevent users from doing something very dangerous. */
|
2006-12-05 17:44:19 +00:00
|
|
|
|
if (geteuid() == 0 &&
|
2006-12-06 20:18:29 +00:00
|
|
|
|
querySetting("build-users-group", "") == "")
|
2014-08-20 15:00:17 +00:00
|
|
|
|
throw Error("if you run ‘nix-daemon’ as root, then you MUST set ‘build-users-group’!");
|
2008-03-20 18:15:20 +00:00
|
|
|
|
#endif
|
2006-12-03 16:25:19 +00:00
|
|
|
|
|
|
|
|
|
/* Open the store. */
|
2016-06-01 12:49:12 +00:00
|
|
|
|
auto store = make_ref<LocalStore>(Store::Params()); // FIXME: get params from somewhere
|
2006-12-03 16:25:19 +00:00
|
|
|
|
|
|
|
|
|
stopWork();
|
2011-12-14 23:30:06 +00:00
|
|
|
|
to.flush();
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
/* Process client requests. */
|
|
|
|
|
unsigned int opCount = 0;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
WorkerOp op;
|
|
|
|
|
try {
|
|
|
|
|
op = (WorkerOp) readInt(from);
|
|
|
|
|
} catch (Interrupted & e) {
|
|
|
|
|
break;
|
|
|
|
|
} catch (EndOfFile & e) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-12-03 16:25:19 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
opCount++;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
performOp(store, trusted, clientVersion, from, to, op);
|
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
/* If we're not in a state where we can send replies, then
|
|
|
|
|
something went wrong processing the input of the
|
|
|
|
|
client. This can happen especially if I/O errors occur
|
|
|
|
|
during addTextToStore() / importPath(). If that
|
|
|
|
|
happens, just send the error message and exit. */
|
|
|
|
|
bool errorAllowed = canSendStderr;
|
2016-07-27 13:03:20 +00:00
|
|
|
|
stopWork(false, e.msg(), e.status);
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
if (!errorAllowed) throw;
|
|
|
|
|
} catch (std::bad_alloc & e) {
|
2016-07-27 13:03:20 +00:00
|
|
|
|
stopWork(false, "Nix daemon out of memory", 1);
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
throw;
|
|
|
|
|
}
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
to.flush();
|
2006-12-02 16:41:36 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
assert(!canSendStderr);
|
|
|
|
|
};
|
2006-12-03 02:08:13 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
canSendStderr = false;
|
|
|
|
|
_isInterrupted = false;
|
|
|
|
|
printMsg(lvlDebug, format("%1% operations") % opCount);
|
2006-12-04 17:55:14 +00:00
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
} catch (Error & e) {
|
2016-07-27 13:03:20 +00:00
|
|
|
|
stopWork(false, e.msg(), 1);
|
2011-12-14 23:30:06 +00:00
|
|
|
|
to.flush();
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 13:28:26 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2006-11-30 19:19:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 17:21:42 +00:00
|
|
|
|
static void sigChldHandler(int sigNo)
|
|
|
|
|
{
|
|
|
|
|
/* Reap all dead children. */
|
2008-11-14 16:50:01 +00:00
|
|
|
|
while (waitpid(-1, 0, WNOHANG) > 0) ;
|
2006-12-05 17:21:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void setSigChldAction(bool autoReap)
|
2006-12-04 17:17:13 +00:00
|
|
|
|
{
|
|
|
|
|
struct sigaction act, oact;
|
2006-12-05 17:21:42 +00:00
|
|
|
|
act.sa_handler = autoReap ? sigChldHandler : SIG_DFL;
|
2006-12-04 17:17:13 +00:00
|
|
|
|
sigfillset(&act.sa_mask);
|
|
|
|
|
act.sa_flags = 0;
|
|
|
|
|
if (sigaction(SIGCHLD, &act, &oact))
|
|
|
|
|
throw SysError("setting SIGCHLD handler");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-07-17 14:57:07 +00:00
|
|
|
|
bool matchUser(const string & user, const string & group, const Strings & users)
|
|
|
|
|
{
|
|
|
|
|
if (find(users.begin(), users.end(), "*") != users.end())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (find(users.begin(), users.end(), user) != users.end())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
for (auto & i : users)
|
|
|
|
|
if (string(i, 0, 1) == "@") {
|
|
|
|
|
if (group == string(i, 1)) return true;
|
|
|
|
|
struct group * gr = getgrnam(i.c_str() + 1);
|
|
|
|
|
if (!gr) continue;
|
|
|
|
|
for (char * * mem = gr->gr_mem; *mem; mem++)
|
|
|
|
|
if (user == string(*mem)) return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
struct PeerInfo
|
|
|
|
|
{
|
|
|
|
|
bool pidKnown;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
bool uidKnown;
|
|
|
|
|
uid_t uid;
|
|
|
|
|
bool gidKnown;
|
|
|
|
|
gid_t gid;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the identity of the caller, if possible. */
|
|
|
|
|
static PeerInfo getPeerInfo(int remote)
|
|
|
|
|
{
|
|
|
|
|
PeerInfo peer = { false, 0, false, 0, false, 0 };
|
|
|
|
|
|
|
|
|
|
#if defined(SO_PEERCRED)
|
|
|
|
|
|
|
|
|
|
ucred cred;
|
|
|
|
|
socklen_t credLen = sizeof(cred);
|
|
|
|
|
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
|
|
|
|
|
throw SysError("getting peer credentials");
|
|
|
|
|
peer = { true, cred.pid, true, cred.uid, true, cred.gid };
|
|
|
|
|
|
|
|
|
|
#elif defined(LOCAL_PEERCRED)
|
|
|
|
|
|
2015-10-04 11:53:23 +00:00
|
|
|
|
#if !defined(SOL_LOCAL)
|
|
|
|
|
#define SOL_LOCAL 0
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
xucred cred;
|
|
|
|
|
socklen_t credLen = sizeof(cred);
|
|
|
|
|
if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1)
|
|
|
|
|
throw SysError("getting peer credentials");
|
|
|
|
|
peer = { false, 0, true, cred.cr_uid, false, 0 };
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return peer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
#define SD_LISTEN_FDS_START 3
|
|
|
|
|
|
|
|
|
|
|
2014-08-13 01:50:44 +00:00
|
|
|
|
static void daemonLoop(char * * argv)
|
2006-12-04 17:17:13 +00:00
|
|
|
|
{
|
2014-12-12 16:14:28 +00:00
|
|
|
|
if (chdir("/") == -1)
|
|
|
|
|
throw SysError("cannot change current directory");
|
2014-08-13 01:50:44 +00:00
|
|
|
|
|
2006-12-04 17:17:13 +00:00
|
|
|
|
/* Get rid of children automatically; don't let them become
|
|
|
|
|
zombies. */
|
|
|
|
|
setSigChldAction(true);
|
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
AutoCloseFD fdSocket;
|
|
|
|
|
|
|
|
|
|
/* Handle socket-based activation by systemd. */
|
|
|
|
|
if (getEnv("LISTEN_FDS") != "") {
|
2015-10-29 12:26:55 +00:00
|
|
|
|
if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1")
|
2012-06-19 03:01:46 +00:00
|
|
|
|
throw Error("unexpected systemd environment variables");
|
|
|
|
|
fdSocket = SD_LISTEN_FDS_START;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise, create and bind to a Unix domain socket. */
|
|
|
|
|
else {
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
/* Create and bind to a Unix domain socket. */
|
|
|
|
|
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
|
2016-07-11 19:44:44 +00:00
|
|
|
|
if (!fdSocket)
|
2012-06-19 03:01:46 +00:00
|
|
|
|
throw SysError("cannot create Unix domain socket");
|
|
|
|
|
|
2013-03-08 00:24:59 +00:00
|
|
|
|
string socketPath = settings.nixDaemonSocketFile;
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
createDirs(dirOf(socketPath));
|
2007-08-30 09:50:44 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
/* Urgh, sockaddr_un allows path names of only 108 characters.
|
|
|
|
|
So chdir to the socket directory so that we can pass a
|
|
|
|
|
relative path name. */
|
2014-12-12 16:14:28 +00:00
|
|
|
|
if (chdir(dirOf(socketPath).c_str()) == -1)
|
|
|
|
|
throw SysError("cannot change current directory");
|
2012-06-19 03:01:46 +00:00
|
|
|
|
Path socketPathRel = "./" + baseNameOf(socketPath);
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
if (socketPathRel.size() >= sizeof(addr.sun_path))
|
2014-08-20 15:00:17 +00:00
|
|
|
|
throw Error(format("socket path ‘%1%’ is too long") % socketPathRel);
|
2012-06-19 03:01:46 +00:00
|
|
|
|
strcpy(addr.sun_path, socketPathRel.c_str());
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
unlink(socketPath.c_str());
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2012-06-19 03:01:46 +00:00
|
|
|
|
/* Make sure that the socket is created with 0666 permission
|
|
|
|
|
(everybody can connect --- provided they have access to the
|
|
|
|
|
directory containing the socket). */
|
|
|
|
|
mode_t oldMode = umask(0111);
|
2016-07-11 19:44:44 +00:00
|
|
|
|
int res = bind(fdSocket.get(), (struct sockaddr *) &addr, sizeof(addr));
|
2012-06-19 03:01:46 +00:00
|
|
|
|
umask(oldMode);
|
|
|
|
|
if (res == -1)
|
2014-08-20 15:00:17 +00:00
|
|
|
|
throw SysError(format("cannot bind to socket ‘%1%’") % socketPath);
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2014-12-12 16:14:28 +00:00
|
|
|
|
if (chdir("/") == -1) /* back to the root */
|
|
|
|
|
throw SysError("cannot change current directory");
|
2008-04-09 05:57:01 +00:00
|
|
|
|
|
2016-07-11 19:44:44 +00:00
|
|
|
|
if (listen(fdSocket.get(), 5) == -1)
|
2014-08-20 15:00:17 +00:00
|
|
|
|
throw SysError(format("cannot listen on socket ‘%1%’") % socketPath);
|
2012-06-19 03:01:46 +00:00
|
|
|
|
}
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2016-07-11 19:44:44 +00:00
|
|
|
|
closeOnExec(fdSocket.get());
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-04 17:17:13 +00:00
|
|
|
|
/* Loop accepting connections. */
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
/* Accept a connection. */
|
|
|
|
|
struct sockaddr_un remoteAddr;
|
|
|
|
|
socklen_t remoteAddrLen = sizeof(remoteAddr);
|
|
|
|
|
|
2016-07-11 19:44:44 +00:00
|
|
|
|
AutoCloseFD remote = accept(fdSocket.get(),
|
2006-12-04 17:17:13 +00:00
|
|
|
|
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
|
|
|
|
|
checkInterrupt();
|
2016-07-11 19:44:44 +00:00
|
|
|
|
if (!remote) {
|
2014-07-23 17:21:00 +00:00
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
|
throw SysError("accepting connection");
|
2009-09-30 11:32:04 +00:00
|
|
|
|
}
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2016-07-11 19:44:44 +00:00
|
|
|
|
closeOnExec(remote.get());
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2013-06-12 10:10:26 +00:00
|
|
|
|
bool trusted = false;
|
2016-07-11 19:44:44 +00:00
|
|
|
|
PeerInfo peer = getPeerInfo(remote.get());
|
2014-07-17 13:41:11 +00:00
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
struct passwd * pw = peer.uidKnown ? getpwuid(peer.uid) : 0;
|
2015-10-29 12:26:55 +00:00
|
|
|
|
string user = pw ? pw->pw_name : std::to_string(peer.uid);
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
|
2015-10-29 12:26:55 +00:00
|
|
|
|
string group = gr ? gr->gr_name : std::to_string(peer.gid);
|
2014-07-17 14:57:07 +00:00
|
|
|
|
|
2014-08-04 16:13:14 +00:00
|
|
|
|
Strings trustedUsers = settings.get("trusted-users", Strings({"root"}));
|
|
|
|
|
Strings allowedUsers = settings.get("allowed-users", Strings({"*"}));
|
|
|
|
|
|
2014-08-05 08:19:57 +00:00
|
|
|
|
if (matchUser(user, group, trustedUsers))
|
2014-07-17 14:57:07 +00:00
|
|
|
|
trusted = true;
|
|
|
|
|
|
2014-08-05 08:19:57 +00:00
|
|
|
|
if (!trusted && !matchUser(user, group, allowedUsers))
|
2014-08-20 15:00:17 +00:00
|
|
|
|
throw Error(format("user ‘%1%’ is not allowed to connect to the Nix daemon") % user);
|
2014-07-17 13:49:33 +00:00
|
|
|
|
|
2014-10-31 09:08:59 +00:00
|
|
|
|
printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
|
2015-10-29 12:26:55 +00:00
|
|
|
|
% (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
|
2014-10-31 09:08:59 +00:00
|
|
|
|
% (peer.uidKnown ? user : "<unknown>"));
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2006-12-04 17:17:13 +00:00
|
|
|
|
/* Fork a child to handle the connection. */
|
2014-12-10 15:35:42 +00:00
|
|
|
|
ProcessOptions options;
|
|
|
|
|
options.errorPrefix = "unexpected Nix daemon error: ";
|
|
|
|
|
options.dieWithParent = false;
|
|
|
|
|
options.runExitHandlers = true;
|
|
|
|
|
options.allowVfork = false;
|
2014-07-10 14:50:51 +00:00
|
|
|
|
startProcess([&]() {
|
2016-07-11 19:44:44 +00:00
|
|
|
|
fdSocket = -1;
|
2014-09-25 16:45:43 +00:00
|
|
|
|
|
2014-07-10 14:50:51 +00:00
|
|
|
|
/* 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]. */
|
2014-10-31 09:08:59 +00:00
|
|
|
|
if (peer.pidKnown && argv[1]) {
|
2015-10-29 12:26:55 +00:00
|
|
|
|
string processName = std::to_string(peer.pid);
|
2014-08-13 01:50:44 +00:00
|
|
|
|
strncpy(argv[1], processName.c_str(), strlen(argv[1]));
|
2014-07-10 14:50:51 +00:00
|
|
|
|
}
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2014-07-10 14:50:51 +00:00
|
|
|
|
/* Handle the connection. */
|
2016-07-11 19:44:44 +00:00
|
|
|
|
from.fd = remote.get();
|
|
|
|
|
to.fd = remote.get();
|
2014-07-10 14:50:51 +00:00
|
|
|
|
processConnection(trusted);
|
2012-07-30 21:13:25 +00:00
|
|
|
|
|
2014-11-19 16:09:27 +00:00
|
|
|
|
exit(0);
|
2014-12-10 15:35:42 +00:00
|
|
|
|
}, options);
|
2006-12-04 17:17:13 +00:00
|
|
|
|
|
|
|
|
|
} catch (Interrupted & e) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
printMsg(lvlError, format("error processing connection: %1%") % e.msg());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-08-13 01:50:44 +00:00
|
|
|
|
int main(int argc, char * * argv)
|
2006-11-30 19:19:59 +00:00
|
|
|
|
{
|
2014-08-13 01:50:44 +00:00
|
|
|
|
return handleExceptions(argv[0], [&]() {
|
|
|
|
|
initNix();
|
|
|
|
|
|
2016-09-02 17:30:28 +00:00
|
|
|
|
auto stdio = false;
|
|
|
|
|
|
2014-08-13 01:50:44 +00:00
|
|
|
|
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
|
|
|
|
if (*arg == "--daemon")
|
|
|
|
|
; /* ignored for backwards compatibility */
|
|
|
|
|
else if (*arg == "--help")
|
|
|
|
|
showManPage("nix-daemon");
|
|
|
|
|
else if (*arg == "--version")
|
|
|
|
|
printVersion("nix-daemon");
|
2016-09-02 17:30:28 +00:00
|
|
|
|
else if (*arg == "--stdio")
|
|
|
|
|
stdio = true;
|
2014-08-13 01:50:44 +00:00
|
|
|
|
else return false;
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
2016-09-02 17:30:28 +00:00
|
|
|
|
if (stdio) {
|
|
|
|
|
if (getStoreType() == tDaemon) {
|
|
|
|
|
/* Forward on this connection to the real daemon */
|
|
|
|
|
auto socketPath = settings.nixDaemonSocketFile;
|
|
|
|
|
auto s = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
if (s == -1)
|
|
|
|
|
throw SysError("creating Unix domain socket");
|
|
|
|
|
|
|
|
|
|
auto socketDir = dirOf(socketPath);
|
|
|
|
|
if (chdir(socketDir.c_str()) == -1)
|
|
|
|
|
throw SysError(format("changing to socket directory %1%") % socketDir);
|
|
|
|
|
|
|
|
|
|
auto socketName = baseNameOf(socketPath);
|
|
|
|
|
auto addr = sockaddr_un{};
|
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
if (socketName.size() + 1 >= sizeof(addr.sun_path))
|
|
|
|
|
throw Error(format("socket name %1% is too long") % socketName);
|
|
|
|
|
strcpy(addr.sun_path, socketName.c_str());
|
|
|
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
|
|
|
|
throw SysError(format("cannot connect to daemon at %1%") % socketPath);
|
|
|
|
|
|
|
|
|
|
auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
|
|
|
|
|
while (true) {
|
|
|
|
|
fd_set fds;
|
|
|
|
|
FD_ZERO(&fds);
|
|
|
|
|
FD_SET(s, &fds);
|
|
|
|
|
FD_SET(STDIN_FILENO, &fds);
|
|
|
|
|
if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
|
|
|
|
|
throw SysError("waiting for data from client or server");
|
|
|
|
|
if (FD_ISSET(s, &fds)) {
|
|
|
|
|
auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SIZE_MAX, SPLICE_F_MOVE);
|
|
|
|
|
if (res == -1)
|
|
|
|
|
throw SysError("splicing data from daemon socket to stdout");
|
|
|
|
|
else if (res == 0)
|
|
|
|
|
throw EndOfFile("unexpected EOF from daemon socket");
|
|
|
|
|
}
|
|
|
|
|
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
|
|
|
|
auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SIZE_MAX, SPLICE_F_MOVE);
|
|
|
|
|
if (res == -1)
|
|
|
|
|
throw SysError("splicing data from stdin to daemon socket");
|
|
|
|
|
else if (res == 0)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
processConnection(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
daemonLoop(argv);
|
|
|
|
|
}
|
2014-08-13 01:50:44 +00:00
|
|
|
|
});
|
2006-11-30 19:19:59 +00:00
|
|
|
|
}
|