aboutsummaryrefslogtreecommitdiff
path: root/nix/libutil
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2015-06-10 17:50:27 -0400
committerMark H Weaver <mhw@netris.org>2015-06-10 17:50:27 -0400
commit14928016556300a6763334d4279c3d117902caaf (patch)
treed0dc262b14164b82f97dd6e896ca9e93a1fabeea /nix/libutil
parent1511e0235525358abb52cf62abeb9457605b5093 (diff)
parent57cd353d87d6e9e6e882327be70b4d7b5ce863ba (diff)
downloadguix-14928016556300a6763334d4279c3d117902caaf.tar
guix-14928016556300a6763334d4279c3d117902caaf.tar.gz
Merge branch 'master' into core-updates
Diffstat (limited to 'nix/libutil')
-rw-r--r--nix/libutil/archive.cc171
-rw-r--r--nix/libutil/archive.hh12
-rw-r--r--nix/libutil/hash.cc101
-rw-r--r--nix/libutil/serialise.cc27
-rw-r--r--nix/libutil/serialise.hh11
-rw-r--r--nix/libutil/types.hh17
-rw-r--r--nix/libutil/util.cc216
-rw-r--r--nix/libutil/util.hh36
8 files changed, 336 insertions, 255 deletions
diff --git a/nix/libutil/archive.cc b/nix/libutil/archive.cc
index 70a1c580dd..6856ea0f28 100644
--- a/nix/libutil/archive.cc
+++ b/nix/libutil/archive.cc
@@ -1,10 +1,14 @@
+#define _XOPEN_SOURCE 600
+
#include "config.h"
#include <cerrno>
#include <algorithm>
#include <vector>
+#include <map>
+
+#include <strings.h> // for strcasecmp
-#define _XOPEN_SOURCE 600
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -18,39 +22,21 @@
namespace nix {
+bool useCaseHack =
+#if __APPLE__
+ true;
+#else
+ false;
+#endif
+
static string archiveVersion1 = "nix-archive-1";
+static string caseHackSuffix = "~nix~case~hack~";
PathFilter defaultPathFilter;
-static void dump(const string & path, Sink & sink, PathFilter & filter);
-
-
-static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
-{
- Strings names = readDirectory(path);
- vector<string> names2(names.begin(), names.end());
- sort(names2.begin(), names2.end());
-
- for (vector<string>::iterator i = names2.begin();
- i != names2.end(); ++i)
- {
- Path entry = path + "/" + *i;
- if (filter(entry)) {
- writeString("entry", sink);
- writeString("(", sink);
- writeString("name", sink);
- writeString(*i, sink);
- writeString("node", sink);
- dump(entry, sink, filter);
- writeString(")", sink);
- }
- }
-}
-
-
-static void dumpContents(const Path & path, size_t size,
+static void dumpContents(const Path & path, size_t size,
Sink & sink)
{
writeString("contents", sink);
@@ -58,7 +44,7 @@ static void dumpContents(const Path & path, size_t size,
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
-
+
unsigned char buf[65536];
size_t left = size;
@@ -89,12 +75,40 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
writeString("", sink);
}
dumpContents(path, (size_t) st.st_size, sink);
- }
+ }
else if (S_ISDIR(st.st_mode)) {
writeString("type", sink);
writeString("directory", sink);
- dumpEntries(path, sink, filter);
+
+ /* If we're on a case-insensitive system like Mac OS X, undo
+ the case hack applied by restorePath(). */
+ std::map<string, string> unhacked;
+ for (auto & i : readDirectory(path))
+ if (useCaseHack) {
+ string name(i.name);
+ size_t pos = i.name.find(caseHackSuffix);
+ if (pos != string::npos) {
+ printMsg(lvlDebug, format("removing case hack suffix from `%1%'") % (path + "/" + i.name));
+ name.erase(pos);
+ }
+ if (unhacked.find(name) != unhacked.end())
+ throw Error(format("file name collision in between `%1%' and `%2%'")
+ % (path + "/" + unhacked[name]) % (path + "/" + i.name));
+ unhacked[name] = i.name;
+ } else
+ unhacked[i.name] = i.name;
+
+ for (auto & i : unhacked)
+ if (filter(path + "/" + i.first)) {
+ writeString("entry", sink);
+ writeString("(", sink);
+ writeString("name", sink);
+ writeString(i.first, sink);
+ writeString("node", sink);
+ dump(path + "/" + i.second, sink, filter);
+ writeString(")", sink);
+ }
}
else if (S_ISLNK(st.st_mode)) {
@@ -123,6 +137,7 @@ static SerialisationError badArchive(string s)
}
+#if 0
static void skipGeneric(Source & source)
{
if (readString(source) == "(") {
@@ -130,43 +145,13 @@ static void skipGeneric(Source & source)
skipGeneric(source);
}
}
-
-
-static void parse(ParseSink & sink, Source & source, const Path & path);
-
-
-
-static void parseEntry(ParseSink & sink, Source & source, const Path & path)
-{
- string s, name;
-
- s = readString(source);
- if (s != "(") throw badArchive("expected open tag");
-
- while (1) {
- checkInterrupt();
-
- s = readString(source);
-
- if (s == ")") {
- break;
- } else if (s == "name") {
- name = readString(source);
- } else if (s == "node") {
- if (s == "") throw badArchive("entry name missing");
- parse(sink, source, path + "/" + name);
- } else {
- throw badArchive("unknown field " + s);
- skipGeneric(source);
- }
- }
-}
+#endif
static void parseContents(ParseSink & sink, Source & source, const Path & path)
{
unsigned long long size = readLongLong(source);
-
+
sink.preallocateContents(size);
unsigned long long left = size;
@@ -185,6 +170,15 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path)
}
+struct CaseInsensitiveCompare
+{
+ bool operator() (const string & a, const string & b) const
+ {
+ return strcasecmp(a.c_str(), b.c_str()) < 0;
+ }
+};
+
+
static void parse(ParseSink & sink, Source & source, const Path & path)
{
string s;
@@ -194,6 +188,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
+ std::map<Path, int, CaseInsensitiveCompare> names;
+
while (1) {
checkInterrupt();
@@ -221,9 +217,9 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
else if (t == "symlink") {
type = tpSymlink;
}
-
+
else throw badArchive("unknown file type " + t);
-
+
}
else if (s == "contents" && type == tpRegular) {
@@ -236,7 +232,40 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
}
else if (s == "entry" && type == tpDirectory) {
- parseEntry(sink, source, path);
+ string name, prevName;
+
+ s = readString(source);
+ if (s != "(") throw badArchive("expected open tag");
+
+ while (1) {
+ checkInterrupt();
+
+ s = readString(source);
+
+ if (s == ")") {
+ break;
+ } else if (s == "name") {
+ name = readString(source);
+ if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
+ throw Error(format("NAR contains invalid file name `%1%'") % name);
+ if (name <= prevName)
+ throw Error("NAR directory is not sorted");
+ prevName = name;
+ if (useCaseHack) {
+ auto i = names.find(name);
+ if (i != names.end()) {
+ printMsg(lvlDebug, format("case collision between `%1%' and `%2%'") % i->first % name);
+ name += caseHackSuffix;
+ name += int2String(++i->second);
+ } else
+ names[name] = 0;
+ }
+ } else if (s == "node") {
+ if (s.empty()) throw badArchive("entry name missing");
+ parse(sink, source, path + "/" + name);
+ } else
+ throw badArchive("unknown field " + s);
+ }
}
else if (s == "target" && type == tpSymlink) {
@@ -244,17 +273,15 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
sink.createSymlink(path, target);
}
- else {
+ else
throw badArchive("unknown field " + s);
- skipGeneric(source);
- }
}
}
void parseDump(ParseSink & sink, Source & source)
{
- string version;
+ string version;
try {
version = readString(source);
} catch (SerialisationError & e) {
@@ -323,7 +350,7 @@ struct RestoreSink : ParseSink
}
};
-
+
void restorePath(const Path & path, Source & source)
{
RestoreSink sink;
@@ -331,5 +358,5 @@ void restorePath(const Path & path, Source & source)
parseDump(sink, source);
}
-
+
}
diff --git a/nix/libutil/archive.hh b/nix/libutil/archive.hh
index ccac92074d..c216e9768f 100644
--- a/nix/libutil/archive.hh
+++ b/nix/libutil/archive.hh
@@ -28,7 +28,7 @@ namespace nix {
where:
- attrs(as) = concat(map(attr, as)) + encN(0)
+ attrs(as) = concat(map(attr, as)) + encN(0)
attrs((a, b)) = encS(a) + encS(b)
encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
@@ -58,7 +58,7 @@ void dumpPath(const Path & path, Sink & sink,
struct ParseSink
{
virtual void createDirectory(const Path & path) { };
-
+
virtual void createRegularFile(const Path & path) { };
virtual void isExecutable() { };
virtual void preallocateContents(unsigned long long size) { };
@@ -66,10 +66,14 @@ struct ParseSink
virtual void createSymlink(const Path & path, const string & target) { };
};
-
+
void parseDump(ParseSink & sink, Source & source);
void restorePath(const Path & path, Source & source);
-
+
+// FIXME: global variables are bad m'kay.
+extern bool useCaseHack;
+
+
}
diff --git a/nix/libutil/hash.cc b/nix/libutil/hash.cc
index 050446610f..2da00a53de 100644
--- a/nix/libutil/hash.cc
+++ b/nix/libutil/hash.cc
@@ -84,7 +84,7 @@ string printHash(const Hash & hash)
return string(buf, hash.hashSize * 2);
}
-
+
Hash parseHash(HashType ht, const string & s)
{
Hash hash(ht);
@@ -92,7 +92,7 @@ Hash parseHash(HashType ht, const string & s)
throw Error(format("invalid hash `%1%'") % s);
for (unsigned int i = 0; i < hash.hashSize; i++) {
string s2(s, i * 2, 2);
- if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
+ if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw Error(format("invalid hash `%1%'") % s);
std::istringstream str(s2);
int n;
@@ -103,24 +103,6 @@ Hash parseHash(HashType ht, const string & s)
}
-static unsigned char divMod(unsigned char * bytes, unsigned char y)
-{
- unsigned int borrow = 0;
-
- int pos = Hash::maxHashSize - 1;
- while (pos >= 0 && !bytes[pos]) --pos;
-
- for ( ; pos >= 0; --pos) {
- unsigned int s = bytes[pos] + (borrow << 8);
- unsigned int d = s / y;
- borrow = s % y;
- bytes[pos] = d;
- }
-
- return borrow;
-}
-
-
unsigned int hashLength32(const Hash & hash)
{
return (hash.hashSize * 8 - 1) / 5 + 1;
@@ -136,19 +118,19 @@ string printHash32(const Hash & hash)
Hash hash2(hash);
unsigned int len = hashLength32(hash);
- const char * chars = base32Chars.data();
-
- string s(len, '0');
-
- int pos = len - 1;
- while (pos >= 0) {
- unsigned char digit = divMod(hash2.hash, 32);
- s[pos--] = chars[digit];
+ string s;
+ s.reserve(len);
+
+ for (int n = len - 1; n >= 0; n--) {
+ unsigned int b = n * 5;
+ unsigned int i = b / 8;
+ unsigned int j = b % 8;
+ unsigned char c =
+ (hash.hash[i] >> j)
+ | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
+ s.push_back(base32Chars[c & 0x1f]);
}
- for (unsigned int i = 0; i < hash2.maxHashSize; ++i)
- assert(hash2.hash[i] == 0);
-
return s;
}
@@ -159,51 +141,24 @@ string printHash16or32(const Hash & hash)
}
-static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
-{
- unsigned char carry = 0;
-
- for (int pos = 0; pos < maxSize; ++pos) {
- unsigned int m = bytes[pos] * y + carry;
- bytes[pos] = m & 0xff;
- carry = m >> 8;
- }
-
- return carry;
-}
-
-
-static bool add(unsigned char * bytes, unsigned char y, int maxSize)
-{
- unsigned char carry = y;
-
- for (int pos = 0; pos < maxSize; ++pos) {
- unsigned int m = bytes[pos] + carry;
- bytes[pos] = m & 0xff;
- carry = m >> 8;
- if (carry == 0) break;
- }
-
- return carry;
-}
-
-
Hash parseHash32(HashType ht, const string & s)
{
Hash hash(ht);
+ unsigned int len = hashLength32(ht);
+ assert(s.size() == len);
- const char * chars = base32Chars.data();
-
- for (unsigned int i = 0; i < s.length(); ++i) {
- char c = s[i];
+ for (unsigned int n = 0; n < len; ++n) {
+ char c = s[len - n - 1];
unsigned char digit;
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
- if (chars[digit] == c) break;
+ if (base32Chars[digit] == c) break;
if (digit >= 32)
- throw Error(format("invalid base-32 hash `%1%'") % s);
- if (mul(hash.hash, 32, hash.hashSize) ||
- add(hash.hash, digit, hash.hashSize))
- throw Error(format("base-32 hash `%1%' is too large") % s);
+ throw Error(format("invalid base-32 hash '%1%'") % s);
+ unsigned int b = n * 5;
+ unsigned int i = b / 8;
+ unsigned int j = b % 8;
+ hash.hash[i] |= digit << j;
+ if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
}
return hash;
@@ -299,7 +254,7 @@ Hash hashFile(HashType ht, const Path & path)
if (n == -1) throw SysError(format("reading file `%1%'") % path);
update(ht, ctx, buf, n);
}
-
+
finish(ht, ctx, hash.hash);
return hash;
}
@@ -311,7 +266,7 @@ HashSink::HashSink(HashType ht) : ht(ht)
bytes = 0;
start(ht, *ctx);
}
-
+
HashSink::~HashSink()
{
bufPos = 0;
@@ -369,7 +324,7 @@ HashType parseHashType(const string & s)
else return htUnknown;
}
-
+
string printHashType(HashType ht)
{
if (ht == htMD5) return "md5";
@@ -378,5 +333,5 @@ string printHashType(HashType ht)
else throw Error("cannot print unknown hash type");
}
-
+
}
diff --git a/nix/libutil/serialise.cc b/nix/libutil/serialise.cc
index 6b71f52c15..9241750750 100644
--- a/nix/libutil/serialise.cc
+++ b/nix/libutil/serialise.cc
@@ -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)
{
+ static bool warned = false;
+ if (warn && !warned) {
+ written += len;
+ if (written > threshold) {
+ warnLargeDump();
+ warned = true;
+ }
+ }
writeFull(fd, data, len);
}
@@ -256,4 +272,15 @@ template Paths 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);
+}
+
+
}
diff --git a/nix/libutil/serialise.hh b/nix/libutil/serialise.hh
index e5a9df1d05..6a6f028aa6 100644
--- a/nix/libutil/serialise.hh
+++ b/nix/libutil/serialise.hh
@@ -72,9 +72,11 @@ struct BufferedSource : Source
struct FdSink : BufferedSink
{
int fd;
+ bool warn;
+ size_t written;
- FdSink() : fd(-1) { }
- FdSink(int fd) : fd(fd) { }
+ FdSink() : fd(-1), warn(false), written(0) { }
+ FdSink(int fd) : fd(fd), warn(false), written(0) { }
~FdSink();
void write(const unsigned char * data, size_t len);
@@ -95,10 +97,7 @@ struct FdSource : BufferedSource
struct StringSink : Sink
{
string s;
- void operator () (const unsigned char * data, size_t len)
- {
- s.append((const char *) data, len);
- }
+ void operator () (const unsigned char * data, size_t len);
};
diff --git a/nix/libutil/types.hh b/nix/libutil/types.hh
index 4b5ce9a78c..160884ee1a 100644
--- a/nix/libutil/types.hh
+++ b/nix/libutil/types.hh
@@ -8,6 +8,15 @@
#include <boost/format.hpp>
+/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
+ * its (virtual) destructor and what() in c++11 mode, in violation of spec
+ */
+#ifdef __GNUC__
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#define EXCEPTION_NEEDS_THROW_SPEC
+#endif
+#endif
+
namespace nix {
@@ -39,10 +48,14 @@ protected:
public:
unsigned int status; // exit status
BaseError(const FormatOrString & fs, unsigned int status = 1);
+#ifdef EXCEPTION_NEEDS_THROW_SPEC
~BaseError() throw () { };
const char * what() const throw () { return err.c_str(); }
- const string & msg() const throw () { return err; }
- const string & prefix() const throw () { return prefix_; }
+#else
+ const char * what() const noexcept { return err.c_str(); }
+#endif
+ const string & msg() const { return err; }
+ const string & prefix() const { return prefix_; }
BaseError & addPrefix(const FormatOrString & fs);
};
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 846674a29d..dab4235b04 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -1,5 +1,8 @@
#include "config.h"
+#include "util.hh"
+#include "affinity.hh"
+
#include <iostream>
#include <cerrno>
#include <cstdio>
@@ -16,7 +19,9 @@
#include <sys/syscall.h>
#endif
-#include "util.hh"
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
extern char * * environ;
@@ -125,7 +130,6 @@ Path canonPath(const Path & path, bool resolveSymlinks)
i = temp.begin(); /* restart */
end = temp.end();
s = "";
- /* !!! potential for infinite loop */
}
}
}
@@ -189,8 +193,12 @@ Path readLink(const Path & path)
if (!S_ISLNK(st.st_mode))
throw Error(format("`%1%' is not a symlink") % path);
char buf[st.st_size];
- if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
- throw SysError(format("reading symbolic link `%1%'") % path);
+ ssize_t rlsize = readlink(path.c_str(), buf, st.st_size);
+ if (rlsize == -1)
+ throw SysError(format("reading symbolic link '%1%'") % path);
+ else if (rlsize > st.st_size)
+ throw Error(format("symbolic link ‘%1%’ size overflow %2% > %3%")
+ % path % rlsize % st.st_size);
return string(buf, st.st_size);
}
@@ -202,9 +210,10 @@ bool isLink(const Path & path)
}
-Strings readDirectory(const Path & path)
+DirEntries readDirectory(const Path & path)
{
- Strings names;
+ DirEntries entries;
+ entries.reserve(64);
AutoCloseDir dir = opendir(path.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % path);
@@ -214,11 +223,21 @@ Strings readDirectory(const Path & path)
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
- names.push_back(name);
+ entries.emplace_back(name, dirent->d_ino, dirent->d_type);
}
if (errno) throw SysError(format("reading directory `%1%'") % path);
- return names;
+ return entries;
+}
+
+
+unsigned char getFileType(const Path & path)
+{
+ struct stat st = lstat(path);
+ if (S_ISDIR(st.st_mode)) return DT_DIR;
+ if (S_ISLNK(st.st_mode)) return DT_LNK;
+ if (S_ISREG(st.st_mode)) return DT_REG;
+ return DT_UNKNOWN;
}
@@ -249,8 +268,8 @@ void writeFile(const Path & path, const string & s)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (fd == -1)
- throw SysError(format("opening file `%1%'") % path);
- writeFull(fd, (unsigned char *) s.data(), s.size());
+ throw SysError(format("opening file '%1%'") % path);
+ writeFull(fd, s);
}
@@ -277,7 +296,7 @@ string readLine(int fd)
void writeLine(int fd, string s)
{
s += '\n';
- writeFull(fd, (const unsigned char *) s.data(), s.size());
+ writeFull(fd, s);
}
@@ -293,16 +312,14 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
bytesFreed += st.st_blocks * 512;
if (S_ISDIR(st.st_mode)) {
- Strings names = readDirectory(path);
-
/* Make the directory writable. */
if (!(st.st_mode & S_IWUSR)) {
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path);
}
- for (Strings::iterator i = names.begin(); i != names.end(); ++i)
- _deletePath(path + "/" + *i, bytesFreed);
+ for (auto & i : readDirectory(path))
+ _deletePath(path + "/" + i.name, bytesFreed);
}
if (remove(path.c_str()) == -1)
@@ -380,6 +397,9 @@ Paths createDirs(const Path & path)
created.push_back(path);
}
+ if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1)
+ throw SysError(format("statting symlink `%1%'") % path);
+
if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path);
return created;
@@ -469,7 +489,10 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs)
void writeToStderr(const string & s)
{
try {
- _writeToStderr((const unsigned char *) s.data(), s.size());
+ if (_writeToStderr)
+ _writeToStderr((const unsigned char *) s.data(), s.size());
+ else
+ writeFull(STDERR_FILENO, s);
} catch (SysError & e) {
/* Ignore failing writes to stderr if we're in an exception
handler, otherwise throw an exception. We need to ignore
@@ -481,13 +504,7 @@ void writeToStderr(const string & s)
}
-static void defaultWriteToStderr(const unsigned char * buf, size_t count)
-{
- writeFull(STDERR_FILENO, buf, count);
-}
-
-
-void (*_writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
+void (*_writeToStderr) (const unsigned char * buf, size_t count) = 0;
void readFull(int fd, unsigned char * buf, size_t count)
@@ -521,6 +538,12 @@ void writeFull(int fd, const unsigned char * buf, size_t count)
}
+void writeFull(int fd, const string & s)
+{
+ writeFull(fd, (const unsigned char *) s.data(), s.size());
+}
+
+
string drainFD(int fd)
{
string result;
@@ -707,10 +730,14 @@ void AutoCloseDir::close()
Pid::Pid()
+ : pid(-1), separatePG(false), killSignal(SIGKILL)
+{
+}
+
+
+Pid::Pid(pid_t pid)
+ : pid(pid), separatePG(false), killSignal(SIGKILL)
{
- pid = -1;
- separatePG = false;
- killSignal = SIGKILL;
}
@@ -734,11 +761,12 @@ Pid::operator pid_t()
}
-void Pid::kill()
+void Pid::kill(bool quiet)
{
if (pid == -1 || pid == 0) return;
- printMsg(lvlError, format("killing process %1%") % pid);
+ if (!quiet)
+ printMsg(lvlError, format("killing process %1%") % pid);
/* Send the requested signal to the child. If it has its own
process group, send the signal to every process in the child
@@ -801,43 +829,30 @@ void killUser(uid_t uid)
users to which the current process can send signals. So we
fork a process, switch to uid, and send a mass kill. */
- Pid pid;
- pid = fork();
- switch (pid) {
-
- case -1:
- throw SysError("unable to fork");
-
- case 0:
- try { /* child */
+ Pid pid = startProcess([&]() {
- if (setuid(uid) == -1)
- throw SysError("setting uid");
+ if (setuid(uid) == -1)
+ throw SysError("setting uid");
- while (true) {
+ while (true) {
#ifdef __APPLE__
- /* OSX's kill syscall takes a third parameter that, among other
- things, determines if kill(-1, signo) affects the calling
- process. In the OSX libc, it's set to true, which means
- "follow POSIX", which we don't want here
+ /* OSX's kill syscall takes a third parameter that, among
+ other things, determines if kill(-1, signo) affects the
+ calling process. In the OSX libc, it's set to true,
+ 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
- if (kill(-1, SIGKILL) == 0) break;
+ if (kill(-1, SIGKILL) == 0) break;
#endif
- if (errno == ESRCH) break; /* no more processes */
- if (errno != EINTR)
- 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);
+ if (errno == ESRCH) break; /* no more processes */
+ if (errno != EINTR)
+ throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
+
_exit(0);
- }
+ });
- /* parent */
int status = pid.wait(true);
if (status != 0)
throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status));
@@ -852,47 +867,69 @@ void killUser(uid_t uid)
//////////////////////////////////////////////////////////////////////
+pid_t startProcess(std::function<void()> fun,
+ bool dieWithParent, const string & errorPrefix, bool runExitHandlers)
+{
+ pid_t pid = fork();
+ if (pid == -1) throw SysError("unable to fork");
+
+ if (pid == 0) {
+ _writeToStderr = 0;
+ try {
+#if __linux__
+ if (dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
+ throw SysError("setting death signal");
+#endif
+ restoreAffinity();
+ fun();
+ } catch (std::exception & e) {
+ try {
+ std::cerr << errorPrefix << e.what() << "\n";
+ } catch (...) { }
+ } catch (...) { }
+ if (runExitHandlers)
+ exit(1);
+ else
+ _exit(1);
+ }
+
+ return pid;
+}
+
+
+std::vector<const char *> stringsToCharPtrs(const Strings & ss)
+{
+ std::vector<const char *> res;
+ for (auto & s : ss) res.push_back(s.c_str());
+ res.push_back(0);
+ return res;
+}
+
+
string runProgram(Path program, bool searchPath, const Strings & args)
{
checkInterrupt();
- std::vector<const char *> cargs; /* careful with c_str()! */
- cargs.push_back(program.c_str());
- for (Strings::const_iterator i = args.begin(); i != args.end(); ++i)
- cargs.push_back(i->c_str());
- cargs.push_back(0);
-
/* Create a pipe. */
Pipe pipe;
pipe.create();
/* Fork. */
- Pid pid;
- pid = maybeVfork();
-
- switch (pid) {
-
- case -1:
- throw SysError("unable to fork");
-
- case 0: /* child */
- try {
- if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
- throw SysError("dupping stdout");
+ Pid pid = startProcess([&]() {
+ 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);
+ Strings args_(args);
+ args_.push_front(program);
+ auto cargs = stringsToCharPtrs(args_);
- } catch (std::exception & e) {
- writeToStderr("error: " + string(e.what()) + "\n");
- }
- _exit(1);
- }
+ if (searchPath)
+ execvp(program.c_str(), (char * *) &cargs[0]);
+ else
+ execv(program.c_str(), (char * *) &cargs[0]);
- /* Parent. */
+ throw SysError(format("executing `%1%'") % program);
+ });
pipe.writeSide.close();
@@ -901,7 +938,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
/* Wait for the child to finish. */
int status = pid.wait(true);
if (!statusOk(status))
- throw Error(format("program `%1%' %2%")
+ throw ExecError(format("program `%1%' %2%")
% program % statusToString(status));
return result;
@@ -928,13 +965,6 @@ void closeOnExec(int fd)
}
-#if HAVE_VFORK
-pid_t (*maybeVfork)() = vfork;
-#else
-pid_t (*maybeVfork)() = fork;
-#endif
-
-
//////////////////////////////////////////////////////////////////////
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index ce2d77c19a..6a84ed8851 100644
--- a/nix/libutil/util.hh
+++ b/nix/libutil/util.hh
@@ -7,6 +7,7 @@
#include <dirent.h>
#include <unistd.h>
#include <signal.h>
+#include <functional>
#include <cstdio>
@@ -63,7 +64,20 @@ bool isLink(const Path & path);
/* Read the contents of a directory. The entries `.' and `..' are
removed. */
-Strings readDirectory(const Path & path);
+struct DirEntry
+{
+ string name;
+ ino_t ino;
+ unsigned char type; // one of DT_*
+ DirEntry(const string & name, ino_t ino, unsigned char type)
+ : name(name), ino(ino), type(type) { }
+};
+
+typedef vector<DirEntry> DirEntries;
+
+DirEntries readDirectory(const Path & path);
+
+unsigned char getFileType(const Path & path);
/* Read the contents of a file into a string. */
string readFile(int fd);
@@ -157,6 +171,7 @@ extern void (*_writeToStderr) (const unsigned char * buf, size_t count);
requested number of bytes. */
void readFull(int fd, unsigned char * buf, size_t count);
void writeFull(int fd, const unsigned char * buf, size_t count);
+void writeFull(int fd, const string & s);
MakeError(EndOfFile, Error)
@@ -237,10 +252,11 @@ class Pid
int killSignal;
public:
Pid();
+ Pid(pid_t pid);
~Pid();
void operator =(pid_t pid);
operator pid_t();
- void kill();
+ void kill(bool quiet = false);
int wait(bool block);
void setSeparatePG(bool separatePG);
void setKillSignal(int signal);
@@ -252,11 +268,24 @@ public:
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, bool dieWithParent = true,
+ const string & errorPrefix = "error: ", bool runExitHandlers = false);
+
+
/* Run a program and return its stdout in a string (i.e., like the
shell backtick operator). */
string runProgram(Path program, bool searchPath = false,
const Strings & args = Strings());
+MakeError(ExecError, Error)
+
+/* Convert a list of strings to a null-terminated vector of char
+ *'s. The result must not be accessed beyond the lifetime of the
+ list of strings. */
+std::vector<const char *> stringsToCharPtrs(const Strings & ss);
+
/* Close all file descriptors except stdin, stdout, stderr, and those
listed in the given set. Good practice in child processes. */
void closeMostFDs(const set<int> & exceptions);
@@ -264,9 +293,6 @@ void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd);
-/* Call vfork() if available, otherwise fork(). */
-extern pid_t (*maybeVfork)();
-
/* User interruption. */