mirror of
https://github.com/NixOS/nix.git
synced 2025-11-22 02:09:36 +01:00
Tagging release 2.26.2
-----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEtUHVUwEnDgvPFcpdgXC0cm1xmN4FAmetA5oTHGVkb2xzdHJh QGdtYWlsLmNvbQAKCRCBcLRybXGY3g2pB/9JAFyjmaXuccbMTO/6x9qwsWuuXNLk OQWzfbdUekvsihZZSFZg1r7KqqXHCi64f0nxLPsJ/0oeDWZktJ5KnbV630nuUlDj ulLCpKdvhWFa8dVx9LiziGwQw4KLx8PjOfwThtQ4DqCWxWEmu6lKkijag9cE+ai4 3mw9YtUjBRxlXyhYLzWz3whLbv37c/m+R8iGS8xm8W260pmei6D0beOIPdfXYBQF PzPlPORyI08A06uqyA3z7bTxzmSMnzvu0QInCPCKSHzFUnTZPHUYuYStFl28NrZS fXKK59L0G7QEfdTRAmqQkdHdtPj2RlYFiMN0kQiNLflvKfGGWdi/kvdx =rRix -----END PGP SIGNATURE----- Merge tag '2.26.2' into sync-2.26.2 Tagging release 2.26.2
This commit is contained in:
commit
4055239936
1395 changed files with 24694 additions and 16040 deletions
|
|
@ -82,7 +82,7 @@ void SourceAccessor::dumpPath(
|
|||
name.erase(pos);
|
||||
}
|
||||
if (!unhacked.emplace(name, i.first).second)
|
||||
throw Error("file name collision in between '%s' and '%s'",
|
||||
throw Error("file name collision between '%s' and '%s'",
|
||||
(path / unhacked[name]),
|
||||
(path / i.first));
|
||||
} else
|
||||
|
|
@ -128,9 +128,10 @@ void dumpString(std::string_view s, Sink & sink)
|
|||
}
|
||||
|
||||
|
||||
static SerialisationError badArchive(const std::string & s)
|
||||
template<typename... Args>
|
||||
static SerialisationError badArchive(std::string_view s, const Args & ... args)
|
||||
{
|
||||
return SerialisationError("bad archive: " + s);
|
||||
return SerialisationError("bad archive: " + s, args...);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -167,120 +168,97 @@ struct CaseInsensitiveCompare
|
|||
|
||||
static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath & path)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
s = readString(source);
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
||||
std::map<Path, int, CaseInsensitiveCompare> names;
|
||||
|
||||
auto getString = [&]() {
|
||||
checkInterrupt();
|
||||
return readString(source);
|
||||
};
|
||||
|
||||
// For first iteration
|
||||
s = getString();
|
||||
auto expectTag = [&](std::string_view expected) {
|
||||
auto tag = getString();
|
||||
if (tag != expected)
|
||||
throw badArchive("expected tag '%s', got '%s'", expected, tag);
|
||||
};
|
||||
|
||||
while (1) {
|
||||
expectTag("(");
|
||||
|
||||
if (s == ")") {
|
||||
break;
|
||||
}
|
||||
expectTag("type");
|
||||
|
||||
else if (s == "type") {
|
||||
std::string t = getString();
|
||||
auto type = getString();
|
||||
|
||||
if (t == "regular") {
|
||||
sink.createRegularFile(path, [&](auto & crf) {
|
||||
while (1) {
|
||||
s = getString();
|
||||
if (type == "regular") {
|
||||
sink.createRegularFile(path, [&](auto & crf) {
|
||||
auto tag = getString();
|
||||
|
||||
if (s == "contents") {
|
||||
parseContents(crf, source);
|
||||
}
|
||||
|
||||
else if (s == "executable") {
|
||||
auto s2 = getString();
|
||||
if (s2 != "") throw badArchive("executable marker has non-empty value");
|
||||
crf.isExecutable();
|
||||
}
|
||||
|
||||
else break;
|
||||
}
|
||||
});
|
||||
if (tag == "executable") {
|
||||
auto s2 = getString();
|
||||
if (s2 != "") throw badArchive("executable marker has non-empty value");
|
||||
crf.isExecutable();
|
||||
tag = getString();
|
||||
}
|
||||
|
||||
else if (t == "directory") {
|
||||
sink.createDirectory(path);
|
||||
if (tag == "contents")
|
||||
parseContents(crf, source);
|
||||
|
||||
std::string prevName;
|
||||
|
||||
while (1) {
|
||||
s = getString();
|
||||
|
||||
if (s == "entry") {
|
||||
std::string name;
|
||||
|
||||
s = getString();
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
||||
while (1) {
|
||||
s = getString();
|
||||
|
||||
if (s == ")") {
|
||||
break;
|
||||
} else if (s == "name") {
|
||||
name = getString();
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos)
|
||||
throw Error("NAR contains invalid file name '%1%'", name);
|
||||
if (name <= prevName)
|
||||
throw Error("NAR directory is not sorted");
|
||||
prevName = name;
|
||||
if (archiveSettings.useCaseHack) {
|
||||
auto i = names.find(name);
|
||||
if (i != names.end()) {
|
||||
debug("case collision between '%1%' and '%2%'", i->first, name);
|
||||
name += caseHackSuffix;
|
||||
name += std::to_string(++i->second);
|
||||
auto j = names.find(name);
|
||||
if (j != names.end())
|
||||
throw Error("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first);
|
||||
} else
|
||||
names[name] = 0;
|
||||
}
|
||||
} else if (s == "node") {
|
||||
if (name.empty()) throw badArchive("entry name missing");
|
||||
parse(sink, source, path / name);
|
||||
} else
|
||||
throw badArchive("unknown field " + s);
|
||||
}
|
||||
}
|
||||
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (t == "symlink") {
|
||||
s = getString();
|
||||
|
||||
if (s != "target")
|
||||
throw badArchive("expected 'target' got " + s);
|
||||
|
||||
std::string target = getString();
|
||||
sink.createSymlink(path, target);
|
||||
|
||||
// for the next iteration
|
||||
s = getString();
|
||||
}
|
||||
|
||||
else throw badArchive("unknown file type " + t);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
throw badArchive("unknown field " + s);
|
||||
expectTag(")");
|
||||
});
|
||||
}
|
||||
|
||||
else if (type == "directory") {
|
||||
sink.createDirectory(path);
|
||||
|
||||
std::map<Path, int, CaseInsensitiveCompare> names;
|
||||
|
||||
std::string prevName;
|
||||
|
||||
while (1) {
|
||||
auto tag = getString();
|
||||
|
||||
if (tag == ")") break;
|
||||
|
||||
if (tag != "entry")
|
||||
throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
|
||||
|
||||
expectTag("(");
|
||||
|
||||
expectTag("name");
|
||||
|
||||
auto name = getString();
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos)
|
||||
throw badArchive("NAR contains invalid file name '%1%'", name);
|
||||
if (name <= prevName)
|
||||
throw badArchive("NAR directory is not sorted");
|
||||
prevName = name;
|
||||
if (archiveSettings.useCaseHack) {
|
||||
auto i = names.find(name);
|
||||
if (i != names.end()) {
|
||||
debug("case collision between '%1%' and '%2%'", i->first, name);
|
||||
name += caseHackSuffix;
|
||||
name += std::to_string(++i->second);
|
||||
auto j = names.find(name);
|
||||
if (j != names.end())
|
||||
throw badArchive("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first);
|
||||
} else
|
||||
names[name] = 0;
|
||||
}
|
||||
|
||||
expectTag("node");
|
||||
|
||||
parse(sink, source, path / name);
|
||||
|
||||
expectTag(")");
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == "symlink") {
|
||||
expectTag("target");
|
||||
|
||||
auto target = getString();
|
||||
sink.createSymlink(path, target);
|
||||
|
||||
expectTag(")");
|
||||
}
|
||||
|
||||
else throw badArchive("unknown file type '%s'", type);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -299,9 +277,9 @@ void parseDump(FileSystemObjectSink & sink, Source & source)
|
|||
}
|
||||
|
||||
|
||||
void restorePath(const std::filesystem::path & path, Source & source)
|
||||
void restorePath(const std::filesystem::path & path, Source & source, bool startFsync)
|
||||
{
|
||||
RestoreSink sink;
|
||||
RestoreSink sink{startFsync};
|
||||
sink.dstPath = path;
|
||||
parseDump(sink, source);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ void dumpString(std::string_view s, Sink & sink);
|
|||
|
||||
void parseDump(FileSystemObjectSink & sink, Source & source);
|
||||
|
||||
void restorePath(const std::filesystem::path & path, Source & source);
|
||||
void restorePath(const std::filesystem::path & path, Source & source, bool startFsync = false);
|
||||
|
||||
/**
|
||||
* Read a NAR from 'source' and write it to 'sink'.
|
||||
|
|
|
|||
|
|
@ -91,9 +91,6 @@ struct Parser {
|
|||
|
||||
/**
|
||||
* @brief Parse the next character(s)
|
||||
*
|
||||
* @param r
|
||||
* @return std::shared_ptr<Parser>
|
||||
*/
|
||||
virtual void operator()(std::shared_ptr<Parser> & state, Strings & r) = 0;
|
||||
|
||||
|
|
@ -293,7 +290,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
// We match one space after `nix` so that we preserve indentation.
|
||||
// No space is necessary for an empty line. An empty line has basically no effect.
|
||||
if (std::regex_match(line, match, std::regex("^#!\\s*nix(:? |$)(.*)$")))
|
||||
shebangContent += match[2].str() + "\n";
|
||||
shebangContent += std::string_view{match[2].first, match[2].second} + "\n";
|
||||
}
|
||||
for (const auto & word : parseShebangContent(shebangContent)) {
|
||||
cmdline.push_back(word);
|
||||
|
|
@ -351,7 +348,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
|
||||
/* Now that all the other args are processed, run the deferred completions.
|
||||
*/
|
||||
for (auto d : deferredCompletions)
|
||||
for (const auto & d : deferredCompletions)
|
||||
d.completer(*completions, d.n, d.prefix);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
|
@ -113,6 +114,16 @@ protected:
|
|||
, arity(1)
|
||||
{ }
|
||||
|
||||
Handler(std::filesystem::path * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
Handler(std::optional<std::filesystem::path> * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest, const T & val)
|
||||
: fun([dest, val](std::vector<std::string> ss) { *dest = val; })
|
||||
|
|
@ -283,6 +294,18 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect a path argument.
|
||||
*/
|
||||
void expectArg(const std::string & label, std::filesystem::path * dest, bool optional = false)
|
||||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.optional = optional,
|
||||
.handler = {dest}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect 0 or more arguments.
|
||||
*/
|
||||
|
|
@ -348,7 +371,7 @@ using Commands = std::map<std::string, std::function<ref<Command>()>>;
|
|||
|
||||
/**
|
||||
* An argument parser that supports multiple subcommands,
|
||||
* i.e. ‘<command> <subcommand>’.
|
||||
* i.e. `<command> <subcommand>`.
|
||||
*/
|
||||
class MultiCommand : virtual public Args
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
../../build-utils-meson
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <cassert>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
|
|
@ -21,7 +22,9 @@ public:
|
|||
|
||||
Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
|
||||
|
||||
Callback(Callback && callback) : fun(std::move(callback.fun))
|
||||
// NOTE: std::function is noexcept move-constructible since C++20.
|
||||
Callback(Callback && callback) noexcept(std::is_nothrow_move_constructible_v<decltype(fun)>)
|
||||
: fun(std::move(callback.fun))
|
||||
{
|
||||
auto prev = callback.done.test_and_set();
|
||||
if (prev) done.test_and_set();
|
||||
|
|
|
|||
184
src/libutil/checked-arithmetic.hh
Normal file
184
src/libutil/checked-arithmetic.hh
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Checked arithmetic with classes that make it hard to accidentally make something an unchecked operation.
|
||||
*/
|
||||
|
||||
#include <compare>
|
||||
#include <concepts> // IWYU pragma: keep
|
||||
#include <exception>
|
||||
#include <ostream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nix::checked {
|
||||
|
||||
class DivideByZero : std::exception
|
||||
{};
|
||||
|
||||
/**
|
||||
* Numeric value enforcing checked arithmetic. Performing mathematical operations on such values will return a Result
|
||||
* type which needs to be checked.
|
||||
*/
|
||||
template<std::integral T>
|
||||
struct Checked
|
||||
{
|
||||
using Inner = T;
|
||||
|
||||
// TODO: this must be a "trivial default constructor", which means it
|
||||
// cannot set the value to NOT DO UB on uninit.
|
||||
T value;
|
||||
|
||||
Checked() = default;
|
||||
explicit Checked(T const value)
|
||||
: value{value}
|
||||
{
|
||||
}
|
||||
Checked(Checked<T> const & other) = default;
|
||||
Checked(Checked<T> && other) = default;
|
||||
Checked<T> & operator=(Checked<T> const & other) = default;
|
||||
|
||||
std::strong_ordering operator<=>(Checked<T> const & other) const = default;
|
||||
std::strong_ordering operator<=>(T const & other) const
|
||||
{
|
||||
return value <=> other;
|
||||
}
|
||||
|
||||
explicit operator T() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
enum class OverflowKind {
|
||||
NoOverflow,
|
||||
Overflow,
|
||||
DivByZero,
|
||||
};
|
||||
|
||||
class Result
|
||||
{
|
||||
T value;
|
||||
OverflowKind overflowed_;
|
||||
|
||||
public:
|
||||
Result(T value, bool overflowed)
|
||||
: value{value}
|
||||
, overflowed_{overflowed ? OverflowKind::Overflow : OverflowKind::NoOverflow}
|
||||
{
|
||||
}
|
||||
Result(T value, OverflowKind overflowed)
|
||||
: value{value}
|
||||
, overflowed_{overflowed}
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(Result other) const
|
||||
{
|
||||
return value == other.value && overflowed_ == other.overflowed_;
|
||||
}
|
||||
|
||||
std::optional<T> valueChecked() const
|
||||
{
|
||||
if (overflowed_ != OverflowKind::NoOverflow) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result as if the arithmetic were performed as wrapping arithmetic.
|
||||
*
|
||||
* \throws DivideByZero if the operation was a divide by zero.
|
||||
*/
|
||||
T valueWrapping() const
|
||||
{
|
||||
if (overflowed_ == OverflowKind::DivByZero) {
|
||||
throw DivideByZero{};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool overflowed() const
|
||||
{
|
||||
return overflowed_ == OverflowKind::Overflow;
|
||||
}
|
||||
|
||||
bool divideByZero() const
|
||||
{
|
||||
return overflowed_ == OverflowKind::DivByZero;
|
||||
}
|
||||
};
|
||||
|
||||
Result operator+(Checked<T> const other) const
|
||||
{
|
||||
return (*this) + other.value;
|
||||
}
|
||||
Result operator+(T const other) const
|
||||
{
|
||||
T result;
|
||||
bool overflowed = __builtin_add_overflow(value, other, &result);
|
||||
return Result{result, overflowed};
|
||||
}
|
||||
|
||||
Result operator-(Checked<T> const other) const
|
||||
{
|
||||
return (*this) - other.value;
|
||||
}
|
||||
Result operator-(T const other) const
|
||||
{
|
||||
T result;
|
||||
bool overflowed = __builtin_sub_overflow(value, other, &result);
|
||||
return Result{result, overflowed};
|
||||
}
|
||||
|
||||
Result operator*(Checked<T> const other) const
|
||||
{
|
||||
return (*this) * other.value;
|
||||
}
|
||||
Result operator*(T const other) const
|
||||
{
|
||||
T result;
|
||||
bool overflowed = __builtin_mul_overflow(value, other, &result);
|
||||
return Result{result, overflowed};
|
||||
}
|
||||
|
||||
Result operator/(Checked<T> const other) const
|
||||
{
|
||||
return (*this) / other.value;
|
||||
}
|
||||
/**
|
||||
* Performs a checked division.
|
||||
*
|
||||
* If the right hand side is zero, the result is marked as a DivByZero and
|
||||
* valueWrapping will throw.
|
||||
*/
|
||||
Result operator/(T const other) const
|
||||
{
|
||||
constexpr T const minV = std::numeric_limits<T>::min();
|
||||
|
||||
// It's only possible to overflow with signed division since doing so
|
||||
// requires crossing the two's complement limits by MIN / -1 (since
|
||||
// two's complement has one more in range in the negative direction
|
||||
// than in the positive one).
|
||||
if (std::is_signed<T>() && (value == minV && other == -1)) {
|
||||
return Result{minV, true};
|
||||
} else if (other == 0) {
|
||||
return Result{0, OverflowKind::DivByZero};
|
||||
} else {
|
||||
T result = value / other;
|
||||
return Result{result, false};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<std::integral T>
|
||||
std::ostream & operator<<(std::ostream & ios, Checked<T> v)
|
||||
{
|
||||
ios << v.value;
|
||||
return ios;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#include "config-global.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
bool GlobalConfig::set(const std::string & name, const std::string & value)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include "config.hh"
|
||||
#include "args.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@ public:
|
|||
* Re-applies all previously attempted changes to unknown settings
|
||||
*/
|
||||
void reapplyUnknownSettings();
|
||||
|
||||
virtual ~AbstractConfig() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -260,6 +262,7 @@ public:
|
|||
operator const T &() const { return value; }
|
||||
operator T &() { return value; }
|
||||
const T & get() const { return value; }
|
||||
T & get() { return value; }
|
||||
template<typename U>
|
||||
bool operator ==(const U & v2) const { return value == v2; }
|
||||
template<typename U>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,7 @@ unsigned int getMaxCPU()
|
|||
auto cgroupFS = getCgroupFS();
|
||||
if (!cgroupFS) return 0;
|
||||
|
||||
auto cgroups = getCgroups("/proc/self/cgroup");
|
||||
auto cgroup = cgroups[""];
|
||||
if (cgroup == "") return 0;
|
||||
|
||||
auto cpuFile = *cgroupFS + "/" + cgroup + "/cpu.max";
|
||||
auto cpuFile = *cgroupFS + "/" + getCurrentCgroup() + "/cpu.max";
|
||||
|
||||
auto cpuMax = readFile(cpuFile);
|
||||
auto cpuMaxParts = tokenizeString<std::vector<std::string>>(cpuMax, " \n");
|
||||
|
|
@ -49,7 +45,7 @@ unsigned int getMaxCPU()
|
|||
auto period = cpuMaxParts[1];
|
||||
if (quota != "max")
|
||||
return std::ceil(std::stoi(quota) / std::stof(period));
|
||||
} catch (Error &) { ignoreException(lvlDebug); }
|
||||
} catch (Error &) { ignoreExceptionInDestructor(lvlDebug); }
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
#include "util.hh"
|
||||
#include "environment-variables.hh"
|
||||
|
||||
extern char * * environ __attribute__((weak));
|
||||
extern char ** environ __attribute__((weak));
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::optional<std::string> getEnv(const std::string & key)
|
||||
{
|
||||
char * value = getenv(key.c_str());
|
||||
if (!value) return {};
|
||||
if (!value)
|
||||
return {};
|
||||
return std::string(value);
|
||||
}
|
||||
|
||||
std::optional<std::string> getEnvNonEmpty(const std::string & key) {
|
||||
std::optional<std::string> getEnvNonEmpty(const std::string & key)
|
||||
{
|
||||
auto value = getEnv(key);
|
||||
if (value == "") return {};
|
||||
if (value == "")
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,22 @@
|
|||
#include <optional>
|
||||
|
||||
#include "types.hh"
|
||||
#include "file-path.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
static constexpr auto environmentVariablesCategory = "Options that change environment variables";
|
||||
|
||||
/**
|
||||
* @return an environment variable.
|
||||
*/
|
||||
std::optional<std::string> getEnv(const std::string & key);
|
||||
|
||||
/**
|
||||
* Like `getEnv`, but using `OsString` to avoid coercions.
|
||||
*/
|
||||
std::optional<OsString> getEnvOs(const OsString & key);
|
||||
|
||||
/**
|
||||
* @return a non empty environment variable. Returns nullopt if the env
|
||||
* variable is set to ""
|
||||
|
|
@ -43,6 +51,11 @@ int unsetenv(const char * name);
|
|||
*/
|
||||
int setEnv(const char * name, const char * value);
|
||||
|
||||
/**
|
||||
* Like `setEnv`, but using `OsString` to avoid coercions.
|
||||
*/
|
||||
int setEnvOs(const OsString & name, const OsString & value);
|
||||
|
||||
/**
|
||||
* Clear the environment.
|
||||
*/
|
||||
|
|
|
|||
15
src/libutil/exec.hh
Normal file
15
src/libutil/exec.hh
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "os-string.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* `execvpe` is a GNU extension, so we need to implement it for other POSIX
|
||||
* platforms.
|
||||
*
|
||||
* We use our own implementation unconditionally for consistency.
|
||||
*/
|
||||
int execvpe(const OsChar * file0, const OsChar * const argv[], const OsChar * const envp[]);
|
||||
|
||||
}
|
||||
98
src/libutil/executable-path.cc
Normal file
98
src/libutil/executable-path.cc
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "environment-variables.hh"
|
||||
#include "executable-path.hh"
|
||||
#include "strings-inline.hh"
|
||||
#include "util.hh"
|
||||
#include "file-path-impl.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
}
|
||||
|
||||
constexpr static const OsStringView path_var_separator{
|
||||
&ExecutablePath::separator,
|
||||
1,
|
||||
};
|
||||
|
||||
ExecutablePath ExecutablePath::load()
|
||||
{
|
||||
// "If PATH is unset or is set to null, the path search is
|
||||
// implementation-defined."
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
return ExecutablePath::parse(getEnvOs(OS_STR("PATH")).value_or(OS_STR("")));
|
||||
}
|
||||
|
||||
ExecutablePath ExecutablePath::parse(const OsString & path)
|
||||
{
|
||||
auto strings = path.empty() ? (std::list<OsString>{})
|
||||
: basicSplitString<std::list<OsString>, OsChar>(path, path_var_separator);
|
||||
|
||||
std::vector<fs::path> ret;
|
||||
ret.reserve(strings.size());
|
||||
|
||||
std::transform(
|
||||
std::make_move_iterator(strings.begin()),
|
||||
std::make_move_iterator(strings.end()),
|
||||
std::back_inserter(ret),
|
||||
[](OsString && str) {
|
||||
return fs::path{
|
||||
str.empty()
|
||||
// "A zero-length prefix is a legacy feature that
|
||||
// indicates the current working directory. It
|
||||
// appears as two adjacent <colon> characters
|
||||
// ("::"), as an initial <colon> preceding the rest
|
||||
// of the list, or as a trailing <colon> following
|
||||
// the rest of the list."
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
? OS_STR(".")
|
||||
: std::move(str),
|
||||
};
|
||||
});
|
||||
|
||||
return {ret};
|
||||
}
|
||||
|
||||
OsString ExecutablePath::render() const
|
||||
{
|
||||
std::vector<PathViewNG> path2;
|
||||
path2.reserve(directories.size());
|
||||
for (auto & p : directories)
|
||||
path2.push_back(p.native());
|
||||
return basicConcatStringsSep(path_var_separator, path2);
|
||||
}
|
||||
|
||||
std::optional<fs::path>
|
||||
ExecutablePath::findName(const OsString & exe, std::function<bool(const fs::path &)> isExecutable) const
|
||||
{
|
||||
// "If the pathname being sought contains a <slash>, the search
|
||||
// through the path prefixes shall not be performed."
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
assert(OsPathTrait<fs::path::value_type>::rfindPathSep(exe) == exe.npos);
|
||||
|
||||
for (auto & dir : directories) {
|
||||
auto candidate = dir / exe;
|
||||
if (isExecutable(candidate))
|
||||
return candidate.lexically_normal();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
fs::path ExecutablePath::findPath(const fs::path & exe, std::function<bool(const fs::path &)> isExecutable) const
|
||||
{
|
||||
// "If the pathname being sought contains a <slash>, the search
|
||||
// through the path prefixes shall not be performed."
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
if (exe.filename() == exe) {
|
||||
auto resOpt = findName(exe, isExecutable);
|
||||
if (resOpt)
|
||||
return *resOpt;
|
||||
else
|
||||
throw ExecutableLookupError("Could not find executable '%s'", exe.string());
|
||||
} else {
|
||||
return exe;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
81
src/libutil/executable-path.hh
Normal file
81
src/libutil/executable-path.hh
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "file-system.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
MakeError(ExecutableLookupError, Error);
|
||||
|
||||
/**
|
||||
* @todo rename, it is not just good for execuatable paths, but also
|
||||
* other lists of paths.
|
||||
*/
|
||||
struct ExecutablePath
|
||||
{
|
||||
std::vector<std::filesystem::path> directories;
|
||||
|
||||
constexpr static const OsChar separator =
|
||||
#ifdef WIN32
|
||||
L';'
|
||||
#else
|
||||
':'
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* Parse `path` into a list of paths.
|
||||
*
|
||||
* On Unix we split on `:`, on Windows we split on `;`.
|
||||
*
|
||||
* For Unix, this is according to the POSIX spec for `PATH`.
|
||||
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
*/
|
||||
static ExecutablePath parse(const OsString & path);
|
||||
|
||||
/**
|
||||
* Load the `PATH` environment variable and `parse` it.
|
||||
*/
|
||||
static ExecutablePath load();
|
||||
|
||||
/**
|
||||
* Opposite of `parse`
|
||||
*/
|
||||
OsString render() const;
|
||||
|
||||
/**
|
||||
* Search for an executable.
|
||||
*
|
||||
* For Unix, this is according to the POSIX spec for `PATH`.
|
||||
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
*
|
||||
* @param exe This must just be a name, and not contain any `/` (or
|
||||
* `\` on Windows). in case it does, per the spec no lookup should
|
||||
* be perfomed, and the path (it is not just a file name) as is.
|
||||
* This is the caller's respsonsibility.
|
||||
*
|
||||
* This is a pure function, except for the default `isExecutable`
|
||||
* argument, which uses the ambient file system to check if a file is
|
||||
* executable (and exists).
|
||||
*
|
||||
* @return path to a resolved executable
|
||||
*/
|
||||
std::optional<std::filesystem::path> findName(
|
||||
const OsString & exe,
|
||||
std::function<bool(const std::filesystem::path &)> isExecutableFile = isExecutableFileAmbient) const;
|
||||
|
||||
/**
|
||||
* Like the `findName` but also allows a file path as input.
|
||||
*
|
||||
* This implements the full POSIX spec: if the path is just a name,
|
||||
* it searches like the above. Otherwise, it returns the path as is.
|
||||
* If (in the name case) the search fails, an exception is thrown.
|
||||
*/
|
||||
std::filesystem::path findPath(
|
||||
const std::filesystem::path & exe,
|
||||
std::function<bool(const std::filesystem::path &)> isExecutable = isExecutableFileAmbient) const;
|
||||
|
||||
bool operator==(const ExecutablePath &) const = default;
|
||||
};
|
||||
|
||||
} // namespace nix
|
||||
|
|
@ -2,9 +2,10 @@
|
|||
///@file
|
||||
|
||||
#include "error.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "types.hh"
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
|
|
@ -98,10 +99,4 @@ public:
|
|||
void to_json(nlohmann::json &, const ExperimentalFeature &);
|
||||
void from_json(const nlohmann::json &, ExperimentalFeature &);
|
||||
|
||||
/**
|
||||
* It is always rendered as a string
|
||||
*/
|
||||
template<>
|
||||
struct json_avoids_null<ExperimentalFeature> : std::true_type {};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,14 +88,15 @@ void dumpPath(
|
|||
void restorePath(
|
||||
const Path & path,
|
||||
Source & source,
|
||||
FileSerialisationMethod method)
|
||||
FileSerialisationMethod method,
|
||||
bool startFsync)
|
||||
{
|
||||
switch (method) {
|
||||
case FileSerialisationMethod::Flat:
|
||||
writeFile(path, source);
|
||||
writeFile(path, source, 0666, startFsync);
|
||||
break;
|
||||
case FileSerialisationMethod::NixArchive:
|
||||
restorePath(path, source);
|
||||
restorePath(path, source, startFsync);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,12 +65,13 @@ void dumpPath(
|
|||
/**
|
||||
* Restore a serialisation of the given file system object.
|
||||
*
|
||||
* @TODO use an arbitrary `FileSystemObjectSink`.
|
||||
* \todo use an arbitrary `FileSystemObjectSink`.
|
||||
*/
|
||||
void restorePath(
|
||||
const Path & path,
|
||||
Source & source,
|
||||
FileSerialisationMethod method);
|
||||
FileSerialisationMethod method,
|
||||
bool startFsync = false);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "serialise.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -44,8 +45,9 @@ AutoCloseFD::AutoCloseFD() : fd{INVALID_DESCRIPTOR} {}
|
|||
|
||||
AutoCloseFD::AutoCloseFD(Descriptor fd) : fd{fd} {}
|
||||
|
||||
|
||||
AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd}
|
||||
// NOTE: This can be noexcept since we are just copying a value and resetting
|
||||
// the file descriptor in the rhs.
|
||||
AutoCloseFD::AutoCloseFD(AutoCloseFD && that) noexcept : fd{that.fd}
|
||||
{
|
||||
that.fd = INVALID_DESCRIPTOR;
|
||||
}
|
||||
|
|
@ -65,7 +67,7 @@ AutoCloseFD::~AutoCloseFD()
|
|||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +94,7 @@ void AutoCloseFD::close()
|
|||
}
|
||||
}
|
||||
|
||||
void AutoCloseFD::fsync()
|
||||
void AutoCloseFD::fsync() const
|
||||
{
|
||||
if (fd != INVALID_DESCRIPTOR) {
|
||||
int result;
|
||||
|
|
@ -111,6 +113,18 @@ void AutoCloseFD::fsync()
|
|||
}
|
||||
|
||||
|
||||
|
||||
void AutoCloseFD::startFsync() const
|
||||
{
|
||||
#if __linux__
|
||||
if (fd != -1) {
|
||||
/* Ignore failure, since fsync must be run later anyway. This is just a performance optimization. */
|
||||
::sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD::operator bool() const
|
||||
{
|
||||
return fd != INVALID_DESCRIPTOR;
|
||||
|
|
|
|||
|
|
@ -77,8 +77,13 @@ void writeFull(Descriptor fd, std::string_view s, bool allowInterrupts = true);
|
|||
|
||||
/**
|
||||
* Read a line from a file descriptor.
|
||||
*
|
||||
* @param fd The file descriptor to read from
|
||||
* @param eofOk If true, return an unterminated line if EOF is reached. (e.g. the empty string)
|
||||
*
|
||||
* @return A line of text ending in `\n`, or a string without `\n` if `eofOk` is true and EOF is reached.
|
||||
*/
|
||||
std::string readLine(Descriptor fd);
|
||||
std::string readLine(Descriptor fd, bool eofOk = false);
|
||||
|
||||
/**
|
||||
* Write a line to a file descriptor.
|
||||
|
|
@ -101,8 +106,25 @@ void drainFD(
|
|||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
* Get [Standard Input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin))
|
||||
*/
|
||||
[[gnu::always_inline]]
|
||||
inline Descriptor getStandardOut() {
|
||||
inline Descriptor getStandardInput()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return STDIN_FILENO;
|
||||
#else
|
||||
return GetStdHandle(STD_INPUT_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get [Standard Output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout))
|
||||
*/
|
||||
[[gnu::always_inline]]
|
||||
inline Descriptor getStandardOutput()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return STDOUT_FILENO;
|
||||
#else
|
||||
|
|
@ -110,6 +132,19 @@ inline Descriptor getStandardOut() {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get [Standard Error](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr))
|
||||
*/
|
||||
[[gnu::always_inline]]
|
||||
inline Descriptor getStandardError()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return STDERR_FILENO;
|
||||
#else
|
||||
return GetStdHandle(STD_ERROR_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic cleanup of resources.
|
||||
*/
|
||||
|
|
@ -120,7 +155,7 @@ public:
|
|||
AutoCloseFD();
|
||||
AutoCloseFD(Descriptor fd);
|
||||
AutoCloseFD(const AutoCloseFD & fd) = delete;
|
||||
AutoCloseFD(AutoCloseFD&& fd);
|
||||
AutoCloseFD(AutoCloseFD&& fd) noexcept;
|
||||
~AutoCloseFD();
|
||||
AutoCloseFD& operator =(const AutoCloseFD & fd) = delete;
|
||||
AutoCloseFD& operator =(AutoCloseFD&& fd);
|
||||
|
|
@ -128,7 +163,18 @@ public:
|
|||
explicit operator bool() const;
|
||||
Descriptor release();
|
||||
void close();
|
||||
void fsync();
|
||||
|
||||
/**
|
||||
* Perform a blocking fsync operation.
|
||||
*/
|
||||
void fsync() const;
|
||||
|
||||
/**
|
||||
* Asynchronously flush to disk without blocking, if available on
|
||||
* the platform. This is just a performance optimization, and
|
||||
* fsync must be run later even if this is called.
|
||||
*/
|
||||
void startFsync() const;
|
||||
};
|
||||
|
||||
class Pipe
|
||||
|
|
@ -143,10 +189,10 @@ public:
|
|||
namespace unix {
|
||||
|
||||
/**
|
||||
* Close all file descriptors except those listed in the given set.
|
||||
* Close all file descriptors except stdio fds (ie 0, 1, 2).
|
||||
* Good practice in child processes.
|
||||
*/
|
||||
void closeMostFDs(const std::set<Descriptor> & exceptions);
|
||||
void closeExtraFDs();
|
||||
|
||||
/**
|
||||
* Set the close-on-exec flag for the given file descriptor.
|
||||
|
|
|
|||
|
|
@ -91,13 +91,10 @@ struct WindowsPathTrait
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* @todo Revisit choice of `char` or `wchar_t` for `WindowsPathTrait`
|
||||
* argument.
|
||||
*/
|
||||
using NativePathTrait =
|
||||
template<typename CharT>
|
||||
using OsPathTrait =
|
||||
#ifdef _WIN32
|
||||
WindowsPathTrait<char>
|
||||
WindowsPathTrait<CharT>
|
||||
#else
|
||||
UnixPathTrait
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
#include "types.hh"
|
||||
#include "os-string.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -22,39 +22,26 @@ typedef std::set<std::filesystem::path> PathSetNG;
|
|||
*
|
||||
* @todo drop `NG` suffix and replace the one in `types.hh`.
|
||||
*/
|
||||
struct PathViewNG : std::basic_string_view<std::filesystem::path::value_type>
|
||||
struct PathViewNG : OsStringView
|
||||
{
|
||||
using string_view = std::basic_string_view<std::filesystem::path::value_type>;
|
||||
using string_view = OsStringView;
|
||||
|
||||
using string_view::string_view;
|
||||
|
||||
PathViewNG(const std::filesystem::path & path)
|
||||
: std::basic_string_view<std::filesystem::path::value_type>(path.native())
|
||||
: OsStringView{path.native()}
|
||||
{ }
|
||||
|
||||
PathViewNG(const std::filesystem::path::string_type & path)
|
||||
: std::basic_string_view<std::filesystem::path::value_type>(path)
|
||||
PathViewNG(const OsString & path)
|
||||
: OsStringView{path}
|
||||
{ }
|
||||
|
||||
const string_view & native() const { return *this; }
|
||||
string_view & native() { return *this; }
|
||||
};
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path);
|
||||
|
||||
std::filesystem::path::string_type string_to_os_string(std::string_view s);
|
||||
|
||||
std::optional<std::filesystem::path> maybePath(PathView path);
|
||||
|
||||
std::filesystem::path pathNG(PathView path);
|
||||
|
||||
/**
|
||||
* Create string literals with the native character width of paths
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
# define PATHNG_LITERAL(s) s
|
||||
#else
|
||||
# define PATHNG_LITERAL(s) L ## s
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@
|
|||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "serialise.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
|
||||
|
|
@ -25,16 +27,11 @@
|
|||
|
||||
#include "strings-inline.hh"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Treat the string as possibly an absolute path, by inspecting the
|
||||
* start of it. Return whether it was probably intended to be
|
||||
* absolute.
|
||||
*/
|
||||
static bool isAbsolute(PathView path)
|
||||
namespace fs { using namespace std::filesystem; }
|
||||
|
||||
bool isAbsolute(PathView path)
|
||||
{
|
||||
return fs::path { path }.is_absolute();
|
||||
}
|
||||
|
|
@ -72,6 +69,10 @@ Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
||||
std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks)
|
||||
{
|
||||
return absPath(path.string(), std::nullopt, resolveSymlinks);
|
||||
}
|
||||
|
||||
Path canonPath(PathView path, bool resolveSymlinks)
|
||||
{
|
||||
|
|
@ -92,7 +93,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
arbitrary (but high) limit to prevent infinite loops. */
|
||||
unsigned int followCount = 0, maxFollow = 1024;
|
||||
|
||||
auto ret = canonPathInner<NativePathTrait>(
|
||||
auto ret = canonPathInner<OsPathTrait<char>>(
|
||||
path,
|
||||
[&followCount, &temp, maxFollow, resolveSymlinks]
|
||||
(std::string & result, std::string_view & remaining) {
|
||||
|
|
@ -122,7 +123,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
|
||||
Path dirOf(const PathView path)
|
||||
{
|
||||
Path::size_type pos = NativePathTrait::rfindPathSep(path);
|
||||
Path::size_type pos = OsPathTrait<char>::rfindPathSep(path);
|
||||
if (pos == path.npos)
|
||||
return ".";
|
||||
return fs::path{path}.parent_path().string();
|
||||
|
|
@ -135,10 +136,10 @@ std::string_view baseNameOf(std::string_view path)
|
|||
return "";
|
||||
|
||||
auto last = path.size() - 1;
|
||||
while (last > 0 && NativePathTrait::isPathSep(path[last]))
|
||||
while (last > 0 && OsPathTrait<char>::isPathSep(path[last]))
|
||||
last -= 1;
|
||||
|
||||
auto pos = NativePathTrait::rfindPathSep(path, last);
|
||||
auto pos = OsPathTrait<char>::rfindPathSep(path, last);
|
||||
if (pos == path.npos)
|
||||
pos = 0;
|
||||
else
|
||||
|
|
@ -205,10 +206,10 @@ bool pathExists(const Path & path)
|
|||
return maybeLstat(path).has_value();
|
||||
}
|
||||
|
||||
bool pathAccessible(const Path & path)
|
||||
bool pathAccessible(const std::filesystem::path & path)
|
||||
{
|
||||
try {
|
||||
return pathExists(path);
|
||||
return pathExists(path.string());
|
||||
} catch (SysError & e) {
|
||||
// swallow EPERM
|
||||
if (e.errNo == EPERM) return false;
|
||||
|
|
@ -237,6 +238,11 @@ std::string readFile(const Path & path)
|
|||
return readFile(fd.get());
|
||||
}
|
||||
|
||||
std::string readFile(const std::filesystem::path & path)
|
||||
{
|
||||
return readFile(os_string_to_string(PathViewNG { path }));
|
||||
}
|
||||
|
||||
|
||||
void readFile(const Path & path, Sink & sink)
|
||||
{
|
||||
|
|
@ -318,6 +324,51 @@ void syncParent(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
void recursiveSync(const Path & path)
|
||||
{
|
||||
/* If it's a file or symlink, just fsync and return. */
|
||||
auto st = lstat(path);
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY, 0));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
fd.fsync();
|
||||
return;
|
||||
} else if (S_ISLNK(st.st_mode))
|
||||
return;
|
||||
|
||||
/* Otherwise, perform a depth-first traversal of the directory and
|
||||
fsync all the files. */
|
||||
std::deque<fs::path> dirsToEnumerate;
|
||||
dirsToEnumerate.push_back(path);
|
||||
std::vector<fs::path> dirsToFsync;
|
||||
while (!dirsToEnumerate.empty()) {
|
||||
auto currentDir = dirsToEnumerate.back();
|
||||
dirsToEnumerate.pop_back();
|
||||
for (auto & entry : std::filesystem::directory_iterator(currentDir)) {
|
||||
auto st = entry.symlink_status();
|
||||
if (fs::is_directory(st)) {
|
||||
dirsToEnumerate.emplace_back(entry.path());
|
||||
} else if (fs::is_regular_file(st)) {
|
||||
AutoCloseFD fd = toDescriptor(open(entry.path().string().c_str(), O_RDONLY, 0));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", entry.path());
|
||||
fd.fsync();
|
||||
}
|
||||
}
|
||||
dirsToFsync.emplace_back(std::move(currentDir));
|
||||
}
|
||||
|
||||
/* Fsync all the directories. */
|
||||
for (auto dir = dirsToFsync.rbegin(); dir != dirsToFsync.rend(); ++dir) {
|
||||
AutoCloseFD fd = toDescriptor(open(dir->string().c_str(), O_RDONLY, 0));
|
||||
if (!fd)
|
||||
throw SysError("opening directory '%1%'", *dir);
|
||||
fd.fsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
|
@ -329,7 +380,7 @@ static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & b
|
|||
if (fstatat(parentfd, name.c_str(), &st,
|
||||
AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
throw SysError("getting status of %1%", path);
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
|
|
@ -361,15 +412,15 @@ static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & b
|
|||
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
|
||||
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1)
|
||||
throw SysError("chmod '%1%'", path);
|
||||
throw SysError("chmod %1%", path);
|
||||
}
|
||||
|
||||
int fd = openat(parentfd, path.c_str(), O_RDONLY);
|
||||
if (fd == -1)
|
||||
throw SysError("opening directory '%1%'", path);
|
||||
throw SysError("opening directory %1%", path);
|
||||
AutoCloseDir dir(fdopendir(fd));
|
||||
if (!dir)
|
||||
throw SysError("opening directory '%1%'", path);
|
||||
throw SysError("opening directory %1%", path);
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||
|
|
@ -378,13 +429,13 @@ static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & b
|
|||
if (childName == "." || childName == "..") continue;
|
||||
_deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed);
|
||||
}
|
||||
if (errno) throw SysError("reading directory '%1%'", path);
|
||||
if (errno) throw SysError("reading directory %1%", path);
|
||||
}
|
||||
|
||||
int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
|
||||
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("cannot unlink '%1%'", path);
|
||||
throw SysError("cannot unlink %1%", path);
|
||||
}
|
||||
#else
|
||||
// TODO implement
|
||||
|
|
@ -446,7 +497,7 @@ void deletePath(const fs::path & path, uint64_t & bytesFreed)
|
|||
|
||||
AutoDelete::AutoDelete() : del{false} {}
|
||||
|
||||
AutoDelete::AutoDelete(const fs::path & p, bool recursive) : _path(p)
|
||||
AutoDelete::AutoDelete(const std::filesystem::path & p, bool recursive) : _path(p)
|
||||
{
|
||||
del = true;
|
||||
this->recursive = recursive;
|
||||
|
|
@ -463,7 +514,7 @@ AutoDelete::~AutoDelete()
|
|||
}
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -547,29 +598,40 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
|||
|
||||
void createSymlink(const Path & target, const Path & link)
|
||||
{
|
||||
fs::create_symlink(target, link);
|
||||
try {
|
||||
fs::create_symlink(target, link);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
throw SysError("creating symlink '%1%' -> '%2%'", link, target);
|
||||
}
|
||||
}
|
||||
|
||||
void replaceSymlink(const Path & target, const Path & link)
|
||||
void replaceSymlink(const fs::path & target, const fs::path & link)
|
||||
{
|
||||
for (unsigned int n = 0; true; n++) {
|
||||
Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link)));
|
||||
auto tmp = link.parent_path() / fs::path{fmt(".%d_%s", n, link.filename().string())};
|
||||
tmp = tmp.lexically_normal();
|
||||
|
||||
try {
|
||||
createSymlink(target, tmp);
|
||||
fs::create_symlink(target, tmp);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
throw;
|
||||
throw SysError("creating symlink '%1%' -> '%2%'", tmp, target);
|
||||
}
|
||||
|
||||
try {
|
||||
fs::rename(tmp, link);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
throw SysError("renaming '%1%' to '%2%'", tmp, link);
|
||||
}
|
||||
|
||||
std::filesystem::rename(tmp, link);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setWriteTime(
|
||||
const std::filesystem::path & path,
|
||||
const fs::path & path,
|
||||
time_t accessedTime,
|
||||
time_t modificationTime,
|
||||
std::optional<bool> optIsSymlink)
|
||||
|
|
@ -581,7 +643,7 @@ void setWriteTime(
|
|||
// doesn't support access time just modification time.
|
||||
//
|
||||
// System clock vs File clock issues also make that annoying.
|
||||
warn("Changing file times is not yet implemented on Windows, path is '%s'", path);
|
||||
warn("Changing file times is not yet implemented on Windows, path is %s", path);
|
||||
#elif HAVE_UTIMENSAT && HAVE_DECL_AT_SYMLINK_NOFOLLOW
|
||||
struct timespec times[2] = {
|
||||
{
|
||||
|
|
@ -594,7 +656,7 @@ void setWriteTime(
|
|||
},
|
||||
};
|
||||
if (utimensat(AT_FDCWD, path.c_str(), times, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
throw SysError("changing modification time of '%s' (using `utimensat`)", path);
|
||||
throw SysError("changing modification time of %s (using `utimensat`)", path);
|
||||
#else
|
||||
struct timeval times[2] = {
|
||||
{
|
||||
|
|
@ -608,7 +670,7 @@ void setWriteTime(
|
|||
};
|
||||
#if HAVE_LUTIMES
|
||||
if (lutimes(path.c_str(), times) == -1)
|
||||
throw SysError("changing modification time of '%s'", path);
|
||||
throw SysError("changing modification time of %s", path);
|
||||
#else
|
||||
bool isSymlink = optIsSymlink
|
||||
? *optIsSymlink
|
||||
|
|
@ -616,9 +678,9 @@ void setWriteTime(
|
|||
|
||||
if (!isSymlink) {
|
||||
if (utimes(path.c_str(), times) == -1)
|
||||
throw SysError("changing modification time of '%s' (not a symlink)", path);
|
||||
throw SysError("changing modification time of %s (not a symlink)", path);
|
||||
} else {
|
||||
throw Error("Cannot modification time of symlink '%s'", path);
|
||||
throw Error("Cannot modification time of symlink %s", path);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -647,7 +709,7 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete)
|
|||
copyFile(entry, to / entry.path().filename(), andDelete);
|
||||
}
|
||||
} else {
|
||||
throw Error("file '%s' has an unsupported type", from);
|
||||
throw Error("file %s has an unsupported type", from);
|
||||
}
|
||||
|
||||
setWriteTime(to, lstat(from.string().c_str()));
|
||||
|
|
@ -674,7 +736,7 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
auto tempCopyTarget = temp / "copy-target";
|
||||
if (e.code().value() == EXDEV) {
|
||||
fs::remove(newPath);
|
||||
warn("Can’t rename %s as %s, copying instead", oldName, newName);
|
||||
warn("can’t rename %s as %s, copying instead", oldName, newName);
|
||||
copyFile(oldPath, tempCopyTarget, true);
|
||||
std::filesystem::rename(
|
||||
os_string_to_string(PathViewNG { tempCopyTarget }),
|
||||
|
|
@ -685,4 +747,33 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isExecutableFileAmbient(const fs::path & exe) {
|
||||
// Check file type, because directory being executable means
|
||||
// something completely different.
|
||||
// `is_regular_file` follows symlinks before checking.
|
||||
return std::filesystem::is_regular_file(exe)
|
||||
&& access(exe.string().c_str(),
|
||||
#ifdef WIN32
|
||||
0 // TODO do better
|
||||
#else
|
||||
X_OK
|
||||
#endif
|
||||
) == 0;
|
||||
}
|
||||
|
||||
std::filesystem::path makeParentCanonical(const std::filesystem::path & rawPath)
|
||||
{
|
||||
std::filesystem::path path(absPath(rawPath));;
|
||||
try {
|
||||
auto parent = path.parent_path();
|
||||
if (parent == path) {
|
||||
// `path` is a root directory => trivially canonical
|
||||
return parent;
|
||||
}
|
||||
return std::filesystem::canonical(parent) / path.filename();
|
||||
} catch (fs::filesystem_error & e) {
|
||||
throw SysError("canonicalising parent path of '%1%'", path);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -42,20 +42,42 @@ namespace nix {
|
|||
struct Sink;
|
||||
struct Source;
|
||||
|
||||
/**
|
||||
* Return whether the path denotes an absolute path.
|
||||
*/
|
||||
bool isAbsolute(PathView path);
|
||||
|
||||
/**
|
||||
* @return An absolutized path, resolving paths relative to the
|
||||
* specified directory, or the current directory otherwise. The path
|
||||
* is also canonicalised.
|
||||
*
|
||||
* In the process of being deprecated for `std::filesystem::absolute`.
|
||||
*/
|
||||
Path absPath(PathView path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
inline Path absPath(const Path & path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false)
|
||||
{
|
||||
return absPath(PathView{path}, dir, resolveSymlinks);
|
||||
}
|
||||
|
||||
std::filesystem::path absPath(const std::filesystem::path & path,
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
/**
|
||||
* Canonicalise a path by removing all `.` or `..` components and
|
||||
* double or trailing slashes. Optionally resolves all symlink
|
||||
* components such that each component of the resulting path is *not*
|
||||
* a symbolic link.
|
||||
*
|
||||
* In the process of being deprecated for
|
||||
* `std::filesystem::path::lexically_normal` (for the `resolveSymlinks =
|
||||
* false` case), and `std::filesystem::weakly_canonical` (for the
|
||||
* `resolveSymlinks = true` case).
|
||||
*/
|
||||
Path canonPath(PathView path, bool resolveSymlinks = false);
|
||||
|
||||
|
|
@ -64,12 +86,18 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
|
|||
* everything before the final `/`. If the path is the root or an
|
||||
* immediate child thereof (e.g., `/foo`), this means `/`
|
||||
* is returned.
|
||||
*
|
||||
* In the process of being deprecated for
|
||||
* `std::filesystem::path::parent_path`.
|
||||
*/
|
||||
Path dirOf(const PathView path);
|
||||
|
||||
/**
|
||||
* @return the base name of the given canonical path, i.e., everything
|
||||
* following the final `/` (trailing slashes are removed).
|
||||
*
|
||||
* In the process of being deprecated for
|
||||
* `std::filesystem::path::filename`.
|
||||
*/
|
||||
std::string_view baseNameOf(std::string_view path);
|
||||
|
||||
|
|
@ -98,20 +126,59 @@ std::optional<struct stat> maybeLstat(const Path & path);
|
|||
|
||||
/**
|
||||
* @return true iff the given path exists.
|
||||
*
|
||||
* In the process of being deprecated for `fs::symlink_exists`.
|
||||
*/
|
||||
bool pathExists(const Path & path);
|
||||
|
||||
namespace fs {
|
||||
|
||||
/**
|
||||
* ```
|
||||
* symlink_exists(p) = std::filesystem::exists(std::filesystem::symlink_status(p))
|
||||
* ```
|
||||
* Missing convenience analogous to
|
||||
* ```
|
||||
* std::filesystem::exists(p) = std::filesystem::exists(std::filesystem::status(p))
|
||||
* ```
|
||||
*/
|
||||
inline bool symlink_exists(const std::filesystem::path & path) {
|
||||
return std::filesystem::exists(std::filesystem::symlink_status(path));
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
||||
/**
|
||||
* Canonicalize a path except for the last component.
|
||||
*
|
||||
* This is useful for getting the canonical location of a symlink.
|
||||
*
|
||||
* Consider the case where `foo/l` is a symlink. `canonical("foo/l")` will
|
||||
* resolve the symlink `l` to its target.
|
||||
* `makeParentCanonical("foo/l")` will not resolve the symlink `l` to its target,
|
||||
* but does ensure that the returned parent part of the path, `foo` is resolved
|
||||
* to `canonical("foo")`, and can therefore be retrieved without traversing any
|
||||
* symlinks.
|
||||
*
|
||||
* If a relative path is passed, it will be made absolute, so that the parent
|
||||
* can always be canonicalized.
|
||||
*/
|
||||
std::filesystem::path makeParentCanonical(const std::filesystem::path & path);
|
||||
|
||||
/**
|
||||
* A version of pathExists that returns false on a permission error.
|
||||
* Useful for inferring default paths across directories that might not
|
||||
* be readable.
|
||||
* @return true iff the given path can be accessed and exists
|
||||
*/
|
||||
bool pathAccessible(const Path & path);
|
||||
bool pathAccessible(const std::filesystem::path & path);
|
||||
|
||||
/**
|
||||
* Read the contents (target) of a symbolic link. The result is not
|
||||
* in any way canonicalised.
|
||||
*
|
||||
* In the process of being deprecated for
|
||||
* `std::filesystem::read_symlink`.
|
||||
*/
|
||||
Path readLink(const Path & path);
|
||||
|
||||
|
|
@ -124,20 +191,34 @@ Descriptor openDirectory(const std::filesystem::path & path);
|
|||
* Read the contents of a file into a string.
|
||||
*/
|
||||
std::string readFile(const Path & path);
|
||||
std::string readFile(const std::filesystem::path & path);
|
||||
void readFile(const Path & path, Sink & sink);
|
||||
|
||||
/**
|
||||
* Write a string to a file.
|
||||
*/
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false);
|
||||
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, bool sync = false)
|
||||
{
|
||||
return writeFile(path.string(), s, mode, sync);
|
||||
}
|
||||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false);
|
||||
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, bool sync = false)
|
||||
{
|
||||
return writeFile(path.string(), source, mode, sync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush a file's parent directory to disk
|
||||
* Flush a path's parent directory to disk.
|
||||
*/
|
||||
void syncParent(const Path & path);
|
||||
|
||||
/**
|
||||
* Flush a file or entire directory tree to disk.
|
||||
*/
|
||||
void recursiveSync(const Path & path);
|
||||
|
||||
/**
|
||||
* Delete a path; i.e., in the case of a directory, it is deleted
|
||||
* recursively. It's not an error if the path does not exist. The
|
||||
|
|
@ -149,6 +230,9 @@ void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed);
|
|||
|
||||
/**
|
||||
* Create a directory and all its parents, if necessary.
|
||||
*
|
||||
* In the process of being deprecated for
|
||||
* `std::filesystem::create_directories`.
|
||||
*/
|
||||
void createDirs(const Path & path);
|
||||
inline void createDirs(PathView path)
|
||||
|
|
@ -165,7 +249,7 @@ void createDir(const Path & path, mode_t mode = 0755);
|
|||
* Set the access and modification times of the given path, not
|
||||
* following symlinks.
|
||||
*
|
||||
* @param accessTime Specified in seconds.
|
||||
* @param accessedTime Specified in seconds.
|
||||
*
|
||||
* @param modificationTime Specified in seconds.
|
||||
*
|
||||
|
|
@ -187,13 +271,19 @@ void setWriteTime(const std::filesystem::path & path, const struct stat & st);
|
|||
|
||||
/**
|
||||
* Create a symlink.
|
||||
*
|
||||
*/
|
||||
void createSymlink(const Path & target, const Path & link);
|
||||
|
||||
/**
|
||||
* Atomically create or replace a symlink.
|
||||
*/
|
||||
void replaceSymlink(const Path & target, const Path & link);
|
||||
void replaceSymlink(const std::filesystem::path & target, const std::filesystem::path & link);
|
||||
|
||||
inline void replaceSymlink(const Path & target, const Path & link)
|
||||
{
|
||||
return replaceSymlink(std::filesystem::path{target}, std::filesystem::path{link});
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to 'renameFile', but fallback to a copy+remove if `src` and `dst`
|
||||
|
|
@ -263,6 +353,12 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");
|
|||
*/
|
||||
Path defaultTempDir();
|
||||
|
||||
/**
|
||||
* Interpret `exe` as a location in the ambient file system and return
|
||||
* whether it resolves to a file that is executable.
|
||||
*/
|
||||
bool isExecutableFileAmbient(const std::filesystem::path & exe);
|
||||
|
||||
/**
|
||||
* Used in various places.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ public:
|
|||
// Copying Finallys is definitely not a good idea and will cause them to be
|
||||
// called twice.
|
||||
Finally(Finally &other) = delete;
|
||||
Finally(Finally &&other) : fun(std::move(other.fun)) {
|
||||
// NOTE: Move constructor can be nothrow if the callable type is itself nothrow
|
||||
// move-constructible.
|
||||
Finally(Finally && other) noexcept(std::is_nothrow_move_constructible_v<Fn>)
|
||||
: fun(std::move(other.fun))
|
||||
{
|
||||
other.movedFrom = true;
|
||||
}
|
||||
~Finally() noexcept(false)
|
||||
|
|
|
|||
|
|
@ -49,11 +49,13 @@ void copyRecursive(
|
|||
break;
|
||||
}
|
||||
|
||||
case SourceAccessor::tMisc:
|
||||
throw Error("file '%1%' has an unsupported type", from);
|
||||
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo:
|
||||
case SourceAccessor::tUnknown:
|
||||
default:
|
||||
unreachable();
|
||||
throw Error("file '%1%' has an unsupported type of %2%", from, stat.typeString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +87,17 @@ void RestoreSink::createDirectory(const CanonPath & path)
|
|||
|
||||
struct RestoreRegularFile : CreateRegularFileSink {
|
||||
AutoCloseFD fd;
|
||||
bool startFsync = false;
|
||||
|
||||
~RestoreRegularFile()
|
||||
{
|
||||
/* Initiate an fsync operation without waiting for the
|
||||
result. The real fsync should be run before registering a
|
||||
store path, but this is a performance optimization to allow
|
||||
the disk write to start early. */
|
||||
if (fd && startFsync)
|
||||
fd.startFsync();
|
||||
}
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void isExecutable() override;
|
||||
|
|
@ -96,9 +109,10 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
|
|||
auto p = append(dstPath, path);
|
||||
|
||||
RestoreRegularFile crf;
|
||||
crf.startFsync = startFsync;
|
||||
crf.fd =
|
||||
#ifdef _WIN32
|
||||
CreateFileW(p.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
CreateFileW(p.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
#else
|
||||
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -78,6 +78,11 @@ struct NullFileSystemObjectSink : FileSystemObjectSink
|
|||
struct RestoreSink : FileSystemObjectSink
|
||||
{
|
||||
std::filesystem::path dstPath;
|
||||
bool startFsync = false;
|
||||
|
||||
explicit RestoreSink(bool startFsync)
|
||||
: startFsync{startFsync}
|
||||
{ }
|
||||
|
||||
void createDirectory(const CanonPath & path) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -200,7 +200,11 @@ std::optional<Mode> convertMode(SourceAccessor::Type type)
|
|||
case SourceAccessor::tSymlink: return Mode::Symlink;
|
||||
case SourceAccessor::tRegular: return Mode::Regular;
|
||||
case SourceAccessor::tDirectory: return Mode::Directory;
|
||||
case SourceAccessor::tMisc: return std::nullopt;
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo: return std::nullopt;
|
||||
case SourceAccessor::tUnknown:
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
|
|
@ -314,9 +318,13 @@ Mode dump(
|
|||
return Mode::Symlink;
|
||||
}
|
||||
|
||||
case SourceAccessor::tMisc:
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo:
|
||||
case SourceAccessor::tUnknown:
|
||||
default:
|
||||
throw Error("file '%1%' has an unsupported type", path);
|
||||
throw Error("file '%1%' has an unsupported type of %2%", path, st.typeString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ void parseTree(
|
|||
/**
|
||||
* Helper putting the previous three `parse*` functions together.
|
||||
*
|
||||
* @rootModeIfBlob How to interpret a root blob, for which there is no
|
||||
* @param rootModeIfBlob How to interpret a root blob, for which there is no
|
||||
* disambiguating dir entry to answer that questino. If the root it not
|
||||
* a blob, this is ignored.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -134,7 +134,8 @@ std::string Hash::to_string(HashFormat hashFormat, bool includeAlgo) const
|
|||
|
||||
Hash Hash::dummy(HashAlgorithm::SHA256);
|
||||
|
||||
Hash Hash::parseSRI(std::string_view original) {
|
||||
Hash Hash::parseSRI(std::string_view original)
|
||||
{
|
||||
auto rest = original;
|
||||
|
||||
// Parse the has type before the separater, if there was one.
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <list>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
enum struct ExperimentalFeature;
|
||||
|
||||
const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
|
||||
|
||||
nlohmann::json * get(nlohmann::json & map, const std::string & key);
|
||||
|
|
@ -71,6 +72,12 @@ struct json_avoids_null<std::list<T>> : std::true_type {};
|
|||
template<typename K, typename V>
|
||||
struct json_avoids_null<std::map<K, V>> : std::true_type {};
|
||||
|
||||
/**
|
||||
* `ExperimentalFeature` is always rendered as a string.
|
||||
*/
|
||||
template<>
|
||||
struct json_avoids_null<ExperimentalFeature> : std::true_type {};
|
||||
|
||||
}
|
||||
|
||||
namespace nlohmann {
|
||||
|
|
@ -84,12 +91,14 @@ namespace nlohmann {
|
|||
* round trip. We do that with a static assert.
|
||||
*/
|
||||
template<typename T>
|
||||
struct adl_serializer<std::optional<T>> {
|
||||
struct adl_serializer<std::optional<T>>
|
||||
{
|
||||
/**
|
||||
* @brief Convert a JSON type to an `optional<T>` treating
|
||||
* `null` as `std::nullopt`.
|
||||
*/
|
||||
static void from_json(const json & json, std::optional<T> & t) {
|
||||
static void from_json(const json & json, std::optional<T> & t)
|
||||
{
|
||||
static_assert(
|
||||
nix::json_avoids_null<T>::value,
|
||||
"null is already in use for underlying type's JSON");
|
||||
|
|
@ -102,7 +111,8 @@ struct adl_serializer<std::optional<T>> {
|
|||
* @brief Convert an optional type to a JSON type treating `std::nullopt`
|
||||
* as `null`.
|
||||
*/
|
||||
static void to_json(json & json, const std::optional<T> & t) {
|
||||
static void to_json(json & json, const std::optional<T> & t)
|
||||
{
|
||||
static_assert(
|
||||
nix::json_avoids_null<T>::value,
|
||||
"null is already in use for underlying type's JSON");
|
||||
|
|
|
|||
|
|
@ -144,4 +144,23 @@ CgroupStats destroyCgroup(const Path & cgroup)
|
|||
return destroyCgroup(cgroup, true);
|
||||
}
|
||||
|
||||
std::string getCurrentCgroup()
|
||||
{
|
||||
auto cgroupFS = getCgroupFS();
|
||||
if (!cgroupFS)
|
||||
throw Error("cannot determine the cgroups file system");
|
||||
|
||||
auto ourCgroups = getCgroups("/proc/self/cgroup");
|
||||
auto ourCgroup = ourCgroups[""];
|
||||
if (ourCgroup == "")
|
||||
throw Error("cannot determine cgroup name from /proc/self/cgroup");
|
||||
return ourCgroup;
|
||||
}
|
||||
|
||||
std::string getRootCgroup()
|
||||
{
|
||||
static std::string rootCgroup = getCurrentCgroup();
|
||||
return rootCgroup;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,13 @@ struct CgroupStats
|
|||
*/
|
||||
CgroupStats destroyCgroup(const Path & cgroup);
|
||||
|
||||
std::string getCurrentCgroup();
|
||||
|
||||
/**
|
||||
* Get the cgroup that should be used as the parent when creating new
|
||||
* sub-cgroups. The first time this is called, the current cgroup will be
|
||||
* returned, and then all subsequent calls will return the original cgroup.
|
||||
*/
|
||||
std::string getRootCgroup();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ void saveMountNamespace()
|
|||
void restoreMountNamespace()
|
||||
{
|
||||
try {
|
||||
auto savedCwd = absPath(".");
|
||||
auto savedCwd = std::filesystem::current_path();
|
||||
|
||||
if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1)
|
||||
throw SysError("restoring parent mount namespace");
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
libraries += libutil
|
||||
|
||||
libutil_NAME = libnixutil
|
||||
|
||||
libutil_DIR := $(d)
|
||||
|
||||
libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc)
|
||||
ifdef HOST_UNIX
|
||||
libutil_SOURCES += $(wildcard $(d)/unix/*.cc)
|
||||
endif
|
||||
ifdef HOST_LINUX
|
||||
libutil_SOURCES += $(wildcard $(d)/linux/*.cc)
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
libutil_SOURCES += $(wildcard $(d)/windows/*.cc)
|
||||
endif
|
||||
|
||||
# Not just for this library itself, but also for downstream libraries using this library
|
||||
|
||||
INCLUDE_libutil := -I $(d)
|
||||
ifdef HOST_UNIX
|
||||
INCLUDE_libutil += -I $(d)/unix
|
||||
endif
|
||||
ifdef HOST_LINUX
|
||||
INCLUDE_libutil += -I $(d)/linux
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
INCLUDE_libutil += -I $(d)/windows
|
||||
endif
|
||||
libutil_CXXFLAGS += $(INCLUDE_libutil)
|
||||
|
||||
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
||||
|
||||
$(foreach i, $(wildcard $(d)/args/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/args, 0644)))
|
||||
$(foreach i, $(wildcard $(d)/signature/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/signature, 0644)))
|
||||
|
||||
|
||||
ifeq ($(HAVE_LIBCPUID), 1)
|
||||
libutil_LDFLAGS += -lcpuid
|
||||
endif
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-util.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
|
@ -38,7 +38,7 @@ void Logger::warn(const std::string & msg)
|
|||
|
||||
void Logger::writeToStdout(std::string_view s)
|
||||
{
|
||||
Descriptor standard_out = getStandardOut();
|
||||
Descriptor standard_out = getStandardOutput();
|
||||
writeFull(standard_out, s);
|
||||
writeFull(standard_out, "\n");
|
||||
}
|
||||
|
|
@ -85,10 +85,10 @@ public:
|
|||
|
||||
void logEI(const ErrorInfo & ei) override
|
||||
{
|
||||
std::stringstream oss;
|
||||
std::ostringstream oss;
|
||||
showErrorInfo(oss, ei, loggerSettings.showTrace.get());
|
||||
|
||||
log(ei.level, oss.str());
|
||||
log(ei.level, toView(oss));
|
||||
}
|
||||
|
||||
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
||||
|
|
@ -118,11 +118,7 @@ void writeToStderr(std::string_view s)
|
|||
{
|
||||
try {
|
||||
writeFull(
|
||||
#ifdef _WIN32
|
||||
GetStdHandle(STD_ERROR_HANDLE),
|
||||
#else
|
||||
STDERR_FILENO,
|
||||
#endif
|
||||
getStandardError(),
|
||||
s, false);
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore failing writes to stderr. We need to ignore write
|
||||
|
|
@ -284,61 +280,72 @@ static Logger::Fields getFields(nlohmann::json & json)
|
|||
return fields;
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg)
|
||||
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg, std::string_view source)
|
||||
{
|
||||
if (!hasPrefix(msg, "@nix ")) return std::nullopt;
|
||||
try {
|
||||
return nlohmann::json::parse(std::string(msg, 5));
|
||||
} catch (std::exception & e) {
|
||||
printError("bad JSON log message from builder: %s", e.what());
|
||||
printError("bad JSON log message from %s: %s",
|
||||
Uncolored(source),
|
||||
e.what());
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool handleJSONLogMessage(nlohmann::json & json,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities,
|
||||
bool trusted)
|
||||
std::string_view source, bool trusted)
|
||||
{
|
||||
std::string action = json["action"];
|
||||
try {
|
||||
std::string action = json["action"];
|
||||
|
||||
if (action == "start") {
|
||||
auto type = (ActivityType) json["type"];
|
||||
if (trusted || type == actFileTransfer)
|
||||
activities.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(json["id"]),
|
||||
std::forward_as_tuple(*logger, (Verbosity) json["level"], type,
|
||||
json["text"], getFields(json["fields"]), act.id));
|
||||
if (action == "start") {
|
||||
auto type = (ActivityType) json["type"];
|
||||
if (trusted || type == actFileTransfer)
|
||||
activities.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(json["id"]),
|
||||
std::forward_as_tuple(*logger, (Verbosity) json["level"], type,
|
||||
json["text"], getFields(json["fields"]), act.id));
|
||||
}
|
||||
|
||||
else if (action == "stop")
|
||||
activities.erase((ActivityId) json["id"]);
|
||||
|
||||
else if (action == "result") {
|
||||
auto i = activities.find((ActivityId) json["id"]);
|
||||
if (i != activities.end())
|
||||
i->second.result((ResultType) json["type"], getFields(json["fields"]));
|
||||
}
|
||||
|
||||
else if (action == "setPhase") {
|
||||
std::string phase = json["phase"];
|
||||
act.result(resSetPhase, phase);
|
||||
}
|
||||
|
||||
else if (action == "msg") {
|
||||
std::string msg = json["msg"];
|
||||
logger->log((Verbosity) json["level"], msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
warn(
|
||||
"Unable to handle a JSON message from %s: %s",
|
||||
Uncolored(source),
|
||||
e.what()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (action == "stop")
|
||||
activities.erase((ActivityId) json["id"]);
|
||||
|
||||
else if (action == "result") {
|
||||
auto i = activities.find((ActivityId) json["id"]);
|
||||
if (i != activities.end())
|
||||
i->second.result((ResultType) json["type"], getFields(json["fields"]));
|
||||
}
|
||||
|
||||
else if (action == "setPhase") {
|
||||
std::string phase = json["phase"];
|
||||
act.result(resSetPhase, phase);
|
||||
}
|
||||
|
||||
else if (action == "msg") {
|
||||
std::string msg = json["msg"];
|
||||
logger->log((Verbosity) json["level"], msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handleJSONLogMessage(const std::string & msg,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities, bool trusted)
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities, std::string_view source, bool trusted)
|
||||
{
|
||||
auto json = parseJSONMessage(msg);
|
||||
auto json = parseJSONMessage(msg, source);
|
||||
if (!json) return false;
|
||||
|
||||
return handleJSONLogMessage(*json, act, activities, trusted);
|
||||
return handleJSONLogMessage(*json, act, activities, source, trusted);
|
||||
}
|
||||
|
||||
Activity::~Activity()
|
||||
|
|
@ -346,7 +353,7 @@ Activity::~Activity()
|
|||
try {
|
||||
logger.stopActivity(id);
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,14 +185,25 @@ Logger * makeSimpleLogger(bool printBuildLogs = true);
|
|||
|
||||
Logger * makeJSONLogger(Logger & prevLogger);
|
||||
|
||||
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg);
|
||||
/**
|
||||
* @param source A noun phrase describing the source of the message, e.g. "the builder".
|
||||
*/
|
||||
std::optional<nlohmann::json> parseJSONMessage(const std::string & msg, std::string_view source);
|
||||
|
||||
/**
|
||||
* @param source A noun phrase describing the source of the message, e.g. "the builder".
|
||||
*/
|
||||
bool handleJSONLogMessage(nlohmann::json & json,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities,
|
||||
std::string_view source,
|
||||
bool trusted);
|
||||
|
||||
/**
|
||||
* @param source A noun phrase describing the source of the message, e.g. "the builder".
|
||||
*/
|
||||
bool handleJSONLogMessage(const std::string & msg,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities,
|
||||
std::string_view source,
|
||||
bool trusted);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ project('nix-util', 'cpp',
|
|||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
|
@ -14,7 +12,7 @@ project('nix-util', 'cpp',
|
|||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
subdir('build-utils-meson/deps-lists')
|
||||
subdir('nix-meson-build-support/deps-lists')
|
||||
|
||||
configdata = configuration_data()
|
||||
|
||||
|
|
@ -22,12 +20,13 @@ deps_private_maybe_subproject = [
|
|||
]
|
||||
deps_public_maybe_subproject = [
|
||||
]
|
||||
subdir('build-utils-meson/subprojects')
|
||||
subdir('nix-meson-build-support/subprojects')
|
||||
|
||||
# Check for each of these functions, and create a define like `#define
|
||||
# HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1
|
||||
# for found. One therefore uses it with `#if` not `#ifdef`.
|
||||
check_funcs = [
|
||||
'close_range',
|
||||
# Optionally used for changing the mtime of symlinks.
|
||||
'lutimes',
|
||||
# Optionally used for creating pipes on Unix
|
||||
|
|
@ -52,7 +51,7 @@ endforeach
|
|||
|
||||
configdata.set('HAVE_DECL_AT_SYMLINK_NOFOLLOW', cxx.has_header_symbol('fcntl.h', 'AT_SYMLINK_NOFOLLOW').to_int())
|
||||
|
||||
subdir('build-utils-meson/threads')
|
||||
subdir('nix-meson-build-support/libatomic')
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
socket = cxx.find_library('ws2_32')
|
||||
|
|
@ -107,6 +106,8 @@ deps_private += cpuid
|
|||
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
|
||||
deps_public += nlohmann_json
|
||||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
config_h = configure_file(
|
||||
configuration : configdata,
|
||||
output : 'config-util.hh',
|
||||
|
|
@ -119,7 +120,7 @@ add_project_arguments(
|
|||
language : 'cpp',
|
||||
)
|
||||
|
||||
subdir('build-utils-meson/diagnostics')
|
||||
subdir('nix-meson-build-support/common')
|
||||
|
||||
sources = files(
|
||||
'archive.cc',
|
||||
|
|
@ -133,6 +134,7 @@ sources = files(
|
|||
'english.cc',
|
||||
'environment-variables.cc',
|
||||
'error.cc',
|
||||
'executable-path.cc',
|
||||
'exit.cc',
|
||||
'experimental-features.cc',
|
||||
'file-content-address.cc',
|
||||
|
|
@ -166,6 +168,10 @@ sources = files(
|
|||
)
|
||||
|
||||
include_dirs = [include_directories('.')]
|
||||
if not cxx.has_header('widechar_width.h', required : false)
|
||||
# use vendored widechar_width.h
|
||||
include_dirs += include_directories('./widecharwidth')
|
||||
endif
|
||||
|
||||
headers = [config_h] + files(
|
||||
'abstract-setting-to-json.hh',
|
||||
|
|
@ -175,6 +181,7 @@ headers = [config_h] + files(
|
|||
'args/root.hh',
|
||||
'callback.hh',
|
||||
'canon-path.hh',
|
||||
'checked-arithmetic.hh',
|
||||
'chunked-vector.hh',
|
||||
'closure.hh',
|
||||
'comparator.hh',
|
||||
|
|
@ -187,6 +194,8 @@ headers = [config_h] + files(
|
|||
'english.hh',
|
||||
'environment-variables.hh',
|
||||
'error.hh',
|
||||
'exec.hh',
|
||||
'executable-path.hh',
|
||||
'exit.hh',
|
||||
'experimental-features.hh',
|
||||
'file-content-address.hh',
|
||||
|
|
@ -206,6 +215,7 @@ headers = [config_h] + files(
|
|||
'lru-cache.hh',
|
||||
'memory-source-accessor.hh',
|
||||
'muxable-pipe.hh',
|
||||
'os-string.hh',
|
||||
'pool.hh',
|
||||
'position.hh',
|
||||
'posix-source-accessor.hh',
|
||||
|
|
@ -250,7 +260,8 @@ else
|
|||
subdir('unix')
|
||||
endif
|
||||
|
||||
subdir('build-utils-meson/export-all-symbols')
|
||||
subdir('nix-meson-build-support/export-all-symbols')
|
||||
subdir('nix-meson-build-support/windows-version')
|
||||
|
||||
this_library = library(
|
||||
'nixutil',
|
||||
|
|
@ -271,4 +282,4 @@ if host_machine.system() == 'windows'
|
|||
libraries_private += ['-lws2_32']
|
||||
endif
|
||||
|
||||
subdir('build-utils-meson/export')
|
||||
subdir('nix-meson-build-support/export')
|
||||
|
|
|
|||
1
src/libutil/nix-meson-build-support
Symbolic link
1
src/libutil/nix-meson-build-support
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../nix-meson-build-support
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
prefix=@prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Nix
|
||||
Description: Nix Package Manager
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lnixutil
|
||||
Cflags: -I${includedir}/nix -std=c++2a
|
||||
52
src/libutil/os-string.hh
Normal file
52
src/libutil/os-string.hh
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Named because it is similar to the Rust type, except it is in the
|
||||
* native encoding not WTF-8.
|
||||
*
|
||||
* Same as `std::filesystem::path::value_type`, but manually defined to
|
||||
* avoid including a much more complex header.
|
||||
*/
|
||||
using OsChar =
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
wchar_t
|
||||
#else
|
||||
char
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* Named because it is similar to the Rust type, except it is in the
|
||||
* native encoding not WTF-8.
|
||||
*
|
||||
* Same as `std::filesystem::path::string_type`, but manually defined
|
||||
* for the same reason as `OsChar`.
|
||||
*/
|
||||
using OsString = std::basic_string<OsChar>;
|
||||
|
||||
/**
|
||||
* `std::string_view` counterpart for `OsString`.
|
||||
*/
|
||||
using OsStringView = std::basic_string_view<OsChar>;
|
||||
|
||||
std::string os_string_to_string(OsStringView path);
|
||||
|
||||
OsString string_to_os_string(std::string_view s);
|
||||
|
||||
/**
|
||||
* Create string literals with the native character width of paths
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
# define OS_STR(s) s
|
||||
#else
|
||||
# define OS_STR(s) L##s
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -1,39 +1,36 @@
|
|||
{ lib
|
||||
, stdenv
|
||||
, mkMesonDerivation
|
||||
, releaseTools
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
mkMesonLibrary,
|
||||
|
||||
, meson
|
||||
, ninja
|
||||
, pkg-config
|
||||
boost,
|
||||
brotli,
|
||||
libarchive,
|
||||
libcpuid,
|
||||
libsodium,
|
||||
nlohmann_json,
|
||||
openssl,
|
||||
|
||||
, boost
|
||||
, brotli
|
||||
, libarchive
|
||||
, libcpuid
|
||||
, libsodium
|
||||
, nlohmann_json
|
||||
, openssl
|
||||
# Configuration Options
|
||||
|
||||
# Configuration Options
|
||||
|
||||
, version
|
||||
version,
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) fileset;
|
||||
in
|
||||
|
||||
mkMesonDerivation (finalAttrs: {
|
||||
mkMesonLibrary (finalAttrs: {
|
||||
pname = "nix-util";
|
||||
inherit version;
|
||||
|
||||
workDir = ./.;
|
||||
fileset = fileset.unions [
|
||||
../../build-utils-meson
|
||||
./build-utils-meson
|
||||
../../nix-meson-build-support
|
||||
./nix-meson-build-support
|
||||
../../.version
|
||||
./.version
|
||||
./widecharwidth
|
||||
./meson.build
|
||||
./meson.options
|
||||
./linux/meson.build
|
||||
|
|
@ -43,20 +40,11 @@ mkMesonDerivation (finalAttrs: {
|
|||
(fileset.fileFilter (file: file.hasExt "hh") ./.)
|
||||
];
|
||||
|
||||
outputs = [ "out" "dev" ];
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
brotli
|
||||
libsodium
|
||||
openssl
|
||||
] ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
|
||||
;
|
||||
] ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||
|
||||
propagatedBuildInputs = [
|
||||
boost
|
||||
|
|
@ -84,14 +72,8 @@ mkMesonDerivation (finalAttrs: {
|
|||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
} // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic;
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -109,7 +109,15 @@ public:
|
|||
Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { }
|
||||
|
||||
public:
|
||||
Handle(Handle && h) : pool(h.pool), r(h.r) { h.r.reset(); }
|
||||
// NOTE: Copying std::shared_ptr and calling a .reset() on it is always noexcept.
|
||||
Handle(Handle && h) noexcept
|
||||
: pool(h.pool)
|
||||
, r(h.r)
|
||||
{
|
||||
static_assert(noexcept(h.r.reset()));
|
||||
static_assert(noexcept(std::shared_ptr(h.r)));
|
||||
h.r.reset();
|
||||
}
|
||||
|
||||
Handle(const Handle & l) = delete;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Pos::Pos(const Pos * other)
|
|||
}
|
||||
line = other->line;
|
||||
column = other->column;
|
||||
origin = std::move(other->origin);
|
||||
origin = other->origin;
|
||||
}
|
||||
|
||||
Pos::operator std::shared_ptr<Pos>() const
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root)
|
||||
: root(std::move(root))
|
||||
PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && argRoot)
|
||||
: root(std::move(argRoot))
|
||||
{
|
||||
assert(root.empty() || root.is_absolute());
|
||||
displayPrefix = root.string();
|
||||
|
|
@ -20,7 +20,7 @@ PosixSourceAccessor::PosixSourceAccessor()
|
|||
|
||||
SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
|
||||
{
|
||||
std::filesystem::path path2 = absPath(path.string());
|
||||
std::filesystem::path path2 = absPath(path);
|
||||
return {
|
||||
make_ref<PosixSourceAccessor>(path2.root_path()),
|
||||
CanonPath { path2.relative_path().string() },
|
||||
|
|
@ -122,7 +122,13 @@ std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonP
|
|||
S_ISREG(st->st_mode) ? tRegular :
|
||||
S_ISDIR(st->st_mode) ? tDirectory :
|
||||
S_ISLNK(st->st_mode) ? tSymlink :
|
||||
tMisc,
|
||||
S_ISCHR(st->st_mode) ? tChar :
|
||||
S_ISBLK(st->st_mode) ? tBlock :
|
||||
#ifdef S_ISSOCK
|
||||
S_ISSOCK(st->st_mode) ? tSocket :
|
||||
#endif
|
||||
S_ISFIFO(st->st_mode) ? tFifo :
|
||||
tUnknown,
|
||||
.fileSize = S_ISREG(st->st_mode) ? std::optional<uint64_t>(st->st_size) : std::nullopt,
|
||||
.isExecutable = S_ISREG(st->st_mode) && st->st_mode & S_IXUSR,
|
||||
};
|
||||
|
|
@ -156,7 +162,11 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
case std::filesystem::file_type::regular: return Type::tRegular; break;
|
||||
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
|
||||
case std::filesystem::file_type::directory: return Type::tDirectory; break;
|
||||
default: return tMisc;
|
||||
case std::filesystem::file_type::character: return Type::tChar; break;
|
||||
case std::filesystem::file_type::block: return Type::tBlock; break;
|
||||
case std::filesystem::file_type::fifo: return Type::tFifo; break;
|
||||
case std::filesystem::file_type::socket: return Type::tSocket; break;
|
||||
default: return tUnknown;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}();
|
||||
|
|
|
|||
|
|
@ -43,13 +43,25 @@ struct PosixSourceAccessor : virtual SourceAccessor
|
|||
std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override;
|
||||
|
||||
/**
|
||||
* Create a `PosixSourceAccessor` and `CanonPath` corresponding to
|
||||
* Create a `PosixSourceAccessor` and `SourcePath` corresponding to
|
||||
* some native path.
|
||||
*
|
||||
* The `PosixSourceAccessor` is rooted as far up the tree as
|
||||
* possible, (e.g. on Windows it could scoped to a drive like
|
||||
* `C:\`). This allows more `..` parent accessing to work.
|
||||
*
|
||||
* @note When `path` is trusted user input, canonicalize it using
|
||||
* `std::filesystem::canonical`, `makeParentCanonical`, `std::filesystem::weakly_canonical`, etc,
|
||||
* as appropriate for the use case. At least weak canonicalization is
|
||||
* required for the `SourcePath` to do anything useful at the location it
|
||||
* points to.
|
||||
*
|
||||
* @note A canonicalizing behavior is not built in `createAtRoot` so that
|
||||
* callers do not accidentally introduce symlink-related security vulnerabilities.
|
||||
* Furthermore, `createAtRoot` does not know whether the file pointed to by
|
||||
* `path` should be resolved if it is itself a symlink. In other words,
|
||||
* `createAtRoot` can not decide between aforementioned `canonical`, `makeParentCanonical`, etc. for its callers.
|
||||
*
|
||||
* See
|
||||
* [`std::filesystem::path::root_path`](https://en.cppreference.com/w/cpp/filesystem/path/root_path)
|
||||
* and
|
||||
|
|
|
|||
|
|
@ -18,11 +18,6 @@ private:
|
|||
std::shared_ptr<T> p;
|
||||
|
||||
public:
|
||||
|
||||
ref(const ref<T> & r)
|
||||
: p(r.p)
|
||||
{ }
|
||||
|
||||
explicit ref(const std::shared_ptr<T> & p)
|
||||
: p(p)
|
||||
{
|
||||
|
|
@ -75,8 +70,6 @@ public:
|
|||
return ref<T2>((std::shared_ptr<T2>) p);
|
||||
}
|
||||
|
||||
ref<T> & operator=(ref<T> const & rhs) = default;
|
||||
|
||||
bool operator == (const ref<T> & other) const
|
||||
{
|
||||
return p == other.p;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
///@file
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace nix::regex {
|
||||
|
||||
|
|
@ -10,22 +12,23 @@ namespace nix::regex {
|
|||
|
||||
static inline std::string either(std::string_view a, std::string_view b)
|
||||
{
|
||||
return std::string { a } + "|" + b;
|
||||
std::stringstream ss;
|
||||
ss << a << "|" << b;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static inline std::string group(std::string_view a)
|
||||
{
|
||||
return std::string { "(" } + a + ")";
|
||||
}
|
||||
|
||||
static inline std::string many(std::string_view a)
|
||||
{
|
||||
return std::string { "(?:" } + a + ")*";
|
||||
std::stringstream ss;
|
||||
ss << "(" << a << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static inline std::string list(std::string_view a)
|
||||
{
|
||||
return std::string { a } + many(group("," + a));
|
||||
std::stringstream ss;
|
||||
ss << a << "(," << a << ")*";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "serialise.hh"
|
||||
#include "signals.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
|
@ -9,7 +10,10 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
# include <fileapi.h>
|
||||
# include <winsock2.h>
|
||||
# include "windows-error.hh"
|
||||
#else
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -49,7 +53,7 @@ void BufferedSink::flush()
|
|||
|
||||
FdSink::~FdSink()
|
||||
{
|
||||
try { flush(); } catch (...) { ignoreException(); }
|
||||
try { flush(); } catch (...) { ignoreExceptionInDestructor(); }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -86,7 +90,6 @@ void Source::operator () (std::string_view data)
|
|||
|
||||
void Source::drainInto(Sink & sink)
|
||||
{
|
||||
std::string s;
|
||||
std::array<char, 8192> buf;
|
||||
while (true) {
|
||||
size_t n;
|
||||
|
|
@ -158,6 +161,30 @@ bool FdSource::good()
|
|||
}
|
||||
|
||||
|
||||
bool FdSource::hasData()
|
||||
{
|
||||
if (BufferedSource::hasData()) return true;
|
||||
|
||||
while (true) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
int fd_ = fromDescriptorReadOnly(fd);
|
||||
FD_SET(fd_, &fds);
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
auto n = select(fd_ + 1, &fds, nullptr, nullptr, &timeout);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("polling file descriptor");
|
||||
}
|
||||
return FD_ISSET(fd, &fds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t StringSource::read(char * data, size_t len)
|
||||
{
|
||||
if (pos == s.size()) throw EndOfFile("end of string reached");
|
||||
|
|
@ -399,7 +426,7 @@ Error readError(Source & source)
|
|||
auto type = readString(source);
|
||||
assert(type == "Error");
|
||||
auto level = (Verbosity) readInt(source);
|
||||
auto name = readString(source); // removed
|
||||
[[maybe_unused]] auto name = readString(source); // removed
|
||||
auto msg = readString(source);
|
||||
ErrorInfo info {
|
||||
.level = level,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
|
|
@ -104,6 +105,9 @@ struct BufferedSource : Source
|
|||
|
||||
size_t read(char * data, size_t len) override;
|
||||
|
||||
/**
|
||||
* Return true if the buffer is not empty.
|
||||
*/
|
||||
bool hasData();
|
||||
|
||||
protected:
|
||||
|
|
@ -162,6 +166,13 @@ struct FdSource : BufferedSource
|
|||
FdSource & operator=(FdSource && s) = default;
|
||||
|
||||
bool good() override;
|
||||
|
||||
/**
|
||||
* Return true if the buffer is not empty after a non-blocking
|
||||
* read.
|
||||
*/
|
||||
bool hasData();
|
||||
|
||||
protected:
|
||||
size_t readUnbuffered(char * data, size_t len) override;
|
||||
private:
|
||||
|
|
@ -192,7 +203,14 @@ struct StringSource : Source
|
|||
{
|
||||
std::string_view s;
|
||||
size_t pos;
|
||||
|
||||
// NOTE: Prevent unintentional dangling views when an implicit conversion
|
||||
// from std::string -> std::string_view occurs when the string is passed
|
||||
// by rvalue.
|
||||
StringSource(std::string &&) = delete;
|
||||
StringSource(std::string_view s) : s(s), pos(0) { }
|
||||
StringSource(const std::string& str): StringSource(std::string_view(str)) {}
|
||||
|
||||
size_t read(char * data, size_t len) override;
|
||||
};
|
||||
|
||||
|
|
@ -493,7 +511,7 @@ struct FramedSource : Source
|
|||
}
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,15 +540,16 @@ struct FramedSource : Source
|
|||
/**
|
||||
* Write as chunks in the format expected by FramedSource.
|
||||
*
|
||||
* The exception_ptr reference can be used to terminate the stream when you
|
||||
* detect that an error has occurred on the remote end.
|
||||
* The `checkError` function can be used to terminate the stream when you
|
||||
* detect that an error has occurred. It does so by throwing an exception.
|
||||
*/
|
||||
struct FramedSink : nix::BufferedSink
|
||||
{
|
||||
BufferedSink & to;
|
||||
std::exception_ptr & ex;
|
||||
std::function<void()> checkError;
|
||||
|
||||
FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex)
|
||||
FramedSink(BufferedSink & to, std::function<void()> && checkError)
|
||||
: to(to), checkError(checkError)
|
||||
{ }
|
||||
|
||||
~FramedSink()
|
||||
|
|
@ -539,19 +558,15 @@ struct FramedSink : nix::BufferedSink
|
|||
to << 0;
|
||||
to.flush();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
|
||||
void writeUnbuffered(std::string_view data) override
|
||||
{
|
||||
/* Don't send more data if the remote has
|
||||
encountered an error. */
|
||||
if (ex) {
|
||||
auto ex2 = ex;
|
||||
ex = nullptr;
|
||||
std::rethrow_exception(ex2);
|
||||
}
|
||||
/* Don't send more data if an error has occured. */
|
||||
checkError();
|
||||
|
||||
to << data.size();
|
||||
to(data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,26 @@ namespace nix {
|
|||
|
||||
static std::atomic<size_t> nextNumber{0};
|
||||
|
||||
bool SourceAccessor::Stat::isNotNARSerialisable()
|
||||
{
|
||||
return this->type != tRegular && this->type != tSymlink && this->type != tDirectory;
|
||||
}
|
||||
|
||||
std::string SourceAccessor::Stat::typeString() {
|
||||
switch (this->type) {
|
||||
case tRegular: return "regular";
|
||||
case tSymlink: return "symlink";
|
||||
case tDirectory: return "directory";
|
||||
case tChar: return "character device";
|
||||
case tBlock: return "block device";
|
||||
case tSocket: return "socket";
|
||||
case tFifo: return "fifo";
|
||||
case tUnknown:
|
||||
default: return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
SourceAccessor::SourceAccessor()
|
||||
: number(++nextNumber)
|
||||
, displayPrefix{"«unknown»"}
|
||||
|
|
@ -84,9 +104,10 @@ CanonPath SourceAccessor::resolveSymlinks(
|
|||
todo.pop_front();
|
||||
if (c == "" || c == ".")
|
||||
;
|
||||
else if (c == "..")
|
||||
res.pop();
|
||||
else {
|
||||
else if (c == "..") {
|
||||
if (!res.isRoot())
|
||||
res.pop();
|
||||
} else {
|
||||
res.push(c);
|
||||
if (mode == SymlinkResolution::Full || !todo.empty()) {
|
||||
if (auto st = maybeLstat(res); st && st->type == SourceAccessor::tSymlink) {
|
||||
|
|
@ -94,7 +115,7 @@ CanonPath SourceAccessor::resolveSymlinks(
|
|||
throw Error("infinite symlink recursion in path '%s'", showPath(path));
|
||||
auto target = readLink(res);
|
||||
res.pop();
|
||||
if (hasPrefix(target, "/"))
|
||||
if (isAbsolute(target))
|
||||
res = CanonPath::root;
|
||||
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,12 +88,13 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
|
||||
Unlike `DT_UNKNOWN`, this must not be used for deferring the lookup of types.
|
||||
*/
|
||||
tMisc
|
||||
tChar, tBlock, tSocket, tFifo,
|
||||
tUnknown
|
||||
};
|
||||
|
||||
struct Stat
|
||||
{
|
||||
Type type = tMisc;
|
||||
Type type = tUnknown;
|
||||
|
||||
/**
|
||||
* For regular files only: the size of the file. Not all
|
||||
|
|
@ -112,6 +113,9 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* file in the NAR. Only returned by NAR accessors.
|
||||
*/
|
||||
std::optional<uint64_t> narOffset;
|
||||
|
||||
bool isNotNARSerialisable();
|
||||
std::string typeString();
|
||||
};
|
||||
|
||||
Stat lstat(const CanonPath & path);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
//!@file Hashing utilities for use with unordered_map, etc. (ie low level implementation logic, not domain logic like
|
||||
//! Nix hashing)
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Hashing utilities for use with `std::unordered_map`, etc. (i.e. low
|
||||
* level implementation logic, not domain logic like Nix hashing).
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* hash_combine() from Boost. Hash several hashable values together
|
||||
* `hash_combine()` from Boost. Hash several hashable values together
|
||||
* into a single hash.
|
||||
*/
|
||||
inline void hash_combine(std::size_t & seed) {}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,51 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
template<class C, class CharT>
|
||||
C basicTokenizeString(std::basic_string_view<CharT> s, std::basic_string_view<CharT> separators)
|
||||
{
|
||||
C result;
|
||||
auto pos = s.find_first_not_of(separators, 0);
|
||||
while (pos != s.npos) {
|
||||
auto end = s.find_first_of(separators, pos + 1);
|
||||
if (end == s.npos)
|
||||
end = s.size();
|
||||
result.insert(result.end(), std::basic_string<CharT>(s, pos, end - pos));
|
||||
pos = s.find_first_not_of(separators, end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
std::string concatStringsSep(const std::string_view sep, const C & ss)
|
||||
C tokenizeString(std::string_view s, std::string_view separators)
|
||||
{
|
||||
return basicTokenizeString<C, char>(s, separators);
|
||||
}
|
||||
|
||||
template<class C, class CharT>
|
||||
C basicSplitString(std::basic_string_view<CharT> s, std::basic_string_view<CharT> separators)
|
||||
{
|
||||
C result;
|
||||
size_t pos = 0;
|
||||
while (pos <= s.size()) {
|
||||
auto end = s.find_first_of(separators, pos);
|
||||
if (end == s.npos)
|
||||
end = s.size();
|
||||
result.insert(result.end(), std::basic_string<CharT>(s, pos, end - pos));
|
||||
pos = end + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
C splitString(std::string_view s, std::string_view separators)
|
||||
{
|
||||
return basicSplitString<C, char>(s, separators);
|
||||
}
|
||||
|
||||
template<class CharT, class C>
|
||||
std::basic_string<CharT> basicConcatStringsSep(const std::basic_string_view<CharT> sep, const C & ss)
|
||||
{
|
||||
size_t size = 0;
|
||||
bool tail = false;
|
||||
|
|
@ -13,10 +56,10 @@ std::string concatStringsSep(const std::string_view sep, const C & ss)
|
|||
for (const auto & s : ss) {
|
||||
if (tail)
|
||||
size += sep.size();
|
||||
size += std::string_view(s).size();
|
||||
size += std::basic_string_view<CharT>{s}.size();
|
||||
tail = true;
|
||||
}
|
||||
std::string s;
|
||||
std::basic_string<CharT> s;
|
||||
s.reserve(size);
|
||||
tail = false;
|
||||
for (auto & i : ss) {
|
||||
|
|
@ -28,4 +71,36 @@ std::string concatStringsSep(const std::string_view sep, const C & ss)
|
|||
return s;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
std::string concatStringsSep(const std::string_view sep, const C & ss)
|
||||
{
|
||||
return basicConcatStringsSep<char, C>(sep, ss);
|
||||
}
|
||||
|
||||
template<class C>
|
||||
std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
// TODO? remove to make sure we don't rely on the empty item ignoring behavior,
|
||||
// or just get rid of this function by understanding the remaining calls.
|
||||
// for (auto & i : ss) {
|
||||
// // Make sure we don't rely on the empty item ignoring behavior
|
||||
// assert(!i.empty());
|
||||
// break;
|
||||
// }
|
||||
|
||||
// need a cast to string_view since this is also called with Symbols
|
||||
for (const auto & s : ss)
|
||||
size += sep.size() + std::string_view(s).size();
|
||||
std::string s;
|
||||
s.reserve(size);
|
||||
for (auto & i : ss) {
|
||||
if (s.size() != 0)
|
||||
s += sep;
|
||||
s += i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,12 +1,41 @@
|
|||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "strings-inline.hh"
|
||||
#include "util.hh"
|
||||
#include "os-string.hh"
|
||||
#include "error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
template std::string concatStringsSep(std::string_view, const Strings &);
|
||||
template std::string concatStringsSep(std::string_view, const StringSet &);
|
||||
struct view_stringbuf : public std::stringbuf
|
||||
{
|
||||
inline std::string_view toView()
|
||||
{
|
||||
auto begin = pbase();
|
||||
return {begin, begin + pubseekoff(0, std::ios_base::cur, std::ios_base::out)};
|
||||
}
|
||||
};
|
||||
|
||||
std::string_view toView(const std::ostringstream & os)
|
||||
{
|
||||
auto buf = static_cast<view_stringbuf *>(os.rdbuf());
|
||||
return buf->toView();
|
||||
}
|
||||
|
||||
template std::list<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::set<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
template std::list<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
template std::set<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
template std::list<OsString>
|
||||
basicSplitString(std::basic_string_view<OsChar> s, std::basic_string_view<OsChar> separators);
|
||||
|
||||
template std::string concatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
template std::string concatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
template std::string concatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
typedef std::string_view strings_2[2];
|
||||
|
|
@ -16,4 +45,111 @@ template std::string concatStringsSep(std::string_view, const strings_3 &);
|
|||
typedef std::string_view strings_4[4];
|
||||
template std::string concatStringsSep(std::string_view, const strings_4 &);
|
||||
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
* Shell split string: split a string into shell arguments, respecting quotes and backslashes.
|
||||
*
|
||||
* Used for NIX_SSHOPTS handling, which previously used `tokenizeString` and was broken by
|
||||
* Arguments that need to be passed to ssh with spaces in them.
|
||||
*
|
||||
* Read https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html for the
|
||||
* POSIX shell specification, which is technically what we are implementing here.
|
||||
*/
|
||||
std::list<std::string> shellSplitString(std::string_view s)
|
||||
{
|
||||
std::list<std::string> result;
|
||||
std::string current;
|
||||
bool startedCurrent = false;
|
||||
bool escaping = false;
|
||||
|
||||
auto pushCurrent = [&]() {
|
||||
if (startedCurrent) {
|
||||
result.push_back(current);
|
||||
current.clear();
|
||||
startedCurrent = false;
|
||||
}
|
||||
};
|
||||
|
||||
auto pushChar = [&](char c) {
|
||||
current.push_back(c);
|
||||
startedCurrent = true;
|
||||
};
|
||||
|
||||
auto pop = [&]() {
|
||||
auto c = s[0];
|
||||
s.remove_prefix(1);
|
||||
return c;
|
||||
};
|
||||
|
||||
auto inDoubleQuotes = [&]() {
|
||||
startedCurrent = true;
|
||||
// in double quotes, escaping with backslash is only effective for $, `, ", and backslash
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (escaping) {
|
||||
switch (c) {
|
||||
case '$':
|
||||
case '`':
|
||||
case '"':
|
||||
case '\\':
|
||||
pushChar(c);
|
||||
break;
|
||||
default:
|
||||
pushChar('\\');
|
||||
pushChar(c);
|
||||
break;
|
||||
}
|
||||
escaping = false;
|
||||
} else if (c == '\\') {
|
||||
escaping = true;
|
||||
} else if (c == '"') {
|
||||
return;
|
||||
} else {
|
||||
pushChar(c);
|
||||
}
|
||||
}
|
||||
if (s.empty()) {
|
||||
throw Error("unterminated double quote");
|
||||
}
|
||||
};
|
||||
|
||||
auto inSingleQuotes = [&]() {
|
||||
startedCurrent = true;
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (c == '\'') {
|
||||
return;
|
||||
}
|
||||
pushChar(c);
|
||||
}
|
||||
if (s.empty()) {
|
||||
throw Error("unterminated single quote");
|
||||
}
|
||||
};
|
||||
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (escaping) {
|
||||
pushChar(c);
|
||||
escaping = false;
|
||||
} else if (c == '\\') {
|
||||
escaping = true;
|
||||
} else if (c == ' ' || c == '\t') {
|
||||
pushCurrent();
|
||||
} else if (c == '"') {
|
||||
inDoubleQuotes();
|
||||
} else if (c == '\'') {
|
||||
inSingleQuotes();
|
||||
} else {
|
||||
pushChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
pushCurrent();
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,6 +8,43 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
/*
|
||||
* workaround for unavailable view() method (C++20) of std::ostringstream under MacOS with clang-16
|
||||
*/
|
||||
std::string_view toView(const std::ostringstream & os);
|
||||
|
||||
/**
|
||||
* String tokenizer.
|
||||
*
|
||||
* See also `basicSplitString()`, which preserves empty strings between separators, as well as at the start and end.
|
||||
*/
|
||||
template<class C, class CharT = char>
|
||||
C basicTokenizeString(std::basic_string_view<CharT> s, std::basic_string_view<CharT> separators);
|
||||
|
||||
/**
|
||||
* Like `basicTokenizeString` but specialized to the default `char`
|
||||
*/
|
||||
template<class C>
|
||||
C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||
|
||||
extern template std::list<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
extern template std::set<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
extern template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
/**
|
||||
* Split a string, preserving empty strings between separators, as well as at the start and end.
|
||||
*
|
||||
* Returns a non-empty collection of strings.
|
||||
*/
|
||||
template<class C, class CharT = char>
|
||||
C basicSplitString(std::basic_string_view<CharT> s, std::basic_string_view<CharT> separators);
|
||||
template<typename C>
|
||||
C splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
extern template std::list<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
extern template std::set<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
extern template std::vector<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
/**
|
||||
* Concatenate the given strings with a separator between the elements.
|
||||
*/
|
||||
|
|
@ -18,4 +55,27 @@ extern template std::string concatStringsSep(std::string_view, const std::list<s
|
|||
extern template std::string concatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
extern template std::string concatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
* Ignore any empty strings at the start of the list, and then concatenate the
|
||||
* given strings with a separator between the elements.
|
||||
*
|
||||
* @deprecated This function exists for historical reasons. You probably just
|
||||
* want to use `concatStringsSep`.
|
||||
*/
|
||||
template<class C>
|
||||
[[deprecated(
|
||||
"Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]] std::string
|
||||
dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss);
|
||||
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
* Shell split string: split a string into shell arguments, respecting quotes and backslashes.
|
||||
*
|
||||
* Used for NIX_SSHOPTS handling, which previously used `tokenizeString` and was broken by
|
||||
* Arguments that need to be passed to ssh with spaces in them.
|
||||
*/
|
||||
std::list<std::string> shellSplitString(std::string_view s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional<std::string> com
|
|||
"Failed to open archive (%s)");
|
||||
}
|
||||
|
||||
TarArchive::TarArchive(const fs::path & path)
|
||||
TarArchive::TarArchive(const std::filesystem::path & path)
|
||||
: archive{archive_read_new()}
|
||||
, buffer(defaultBufferSize)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,53 @@
|
|||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <widechar_width.h>
|
||||
|
||||
namespace {
|
||||
|
||||
inline std::pair<int, size_t> charWidthUTF8Helper(std::string_view s)
|
||||
{
|
||||
size_t bytes = 1;
|
||||
uint32_t ch = s[0];
|
||||
uint32_t max = 1U << 7;
|
||||
if ((ch & 0x80U) == 0U) {
|
||||
} else if ((ch & 0xe0U) == 0xc0U) {
|
||||
ch &= 0x1fU;
|
||||
bytes = 2;
|
||||
max = 1U << 11;
|
||||
} else if ((ch & 0xf0U) == 0xe0U) {
|
||||
ch &= 0x0fU;
|
||||
bytes = 3;
|
||||
max = 1U << 16;
|
||||
} else if ((ch & 0xf8U) == 0xf0U) {
|
||||
ch &= 0x07U;
|
||||
bytes = 4;
|
||||
max = 0x110000U;
|
||||
} else {
|
||||
return {bytes, bytes}; // invalid UTF-8 start byte
|
||||
}
|
||||
for (size_t i = 1; i < bytes; i++) {
|
||||
if (i < s.size() && (s[i] & 0xc0) == 0x80) {
|
||||
ch = (ch << 6) | (s[i] & 0x3f);
|
||||
} else {
|
||||
return {i, i}; // invalid UTF-8 encoding; assume one character per byte
|
||||
}
|
||||
}
|
||||
int width = bytes; // in case of overlong encoding
|
||||
if (ch < max) {
|
||||
width = widechar_wcwidth(ch);
|
||||
if (width == widechar_ambiguous) {
|
||||
width = 1; // just a guess...
|
||||
} else if (width == widechar_widened_in_9) {
|
||||
width = 2;
|
||||
} else if (width < 0) {
|
||||
width = 0;
|
||||
}
|
||||
}
|
||||
return {width, bytes};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -26,11 +73,11 @@ bool isTTY()
|
|||
|
||||
std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width)
|
||||
{
|
||||
std::string t, e;
|
||||
std::string t;
|
||||
size_t w = 0;
|
||||
auto i = s.begin();
|
||||
|
||||
while (w < (size_t) width && i != s.end()) {
|
||||
while (i != s.end()) {
|
||||
|
||||
if (*i == '\e') {
|
||||
std::string e;
|
||||
|
|
@ -45,6 +92,13 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
|||
while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++;
|
||||
// eat final byte
|
||||
if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++;
|
||||
} else if (i != s.end() && *i == ']') {
|
||||
// OSC
|
||||
e += *i++;
|
||||
// eat ESC
|
||||
while (i != s.end() && *i != '\e') e += *i++;
|
||||
// eat backslash
|
||||
if (i != s.end() && *i == '\\') e += last = *i++;
|
||||
} else {
|
||||
if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++;
|
||||
}
|
||||
|
|
@ -54,10 +108,12 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
|||
}
|
||||
|
||||
else if (*i == '\t') {
|
||||
i++; t += ' '; w++;
|
||||
while (w < (size_t) width && w % 8) {
|
||||
t += ' '; w++;
|
||||
}
|
||||
do {
|
||||
if (++w > (size_t) width)
|
||||
return t;
|
||||
t += ' ';
|
||||
} while (w % 8);
|
||||
i++;
|
||||
}
|
||||
|
||||
else if (*i == '\r' || *i == '\a')
|
||||
|
|
@ -65,35 +121,18 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
|||
i++;
|
||||
|
||||
else {
|
||||
w++;
|
||||
// Copy one UTF-8 character.
|
||||
if ((*i & 0xe0) == 0xc0) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
|
||||
} else if ((*i & 0xf0) == 0xe0) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
|
||||
}
|
||||
} else if ((*i & 0xf8) == 0xf0) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
|
||||
t += *i++;
|
||||
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
|
||||
}
|
||||
}
|
||||
} else
|
||||
t += *i++;
|
||||
auto [chWidth, bytes] = charWidthUTF8Helper({i, s.end()});
|
||||
w += chWidth;
|
||||
if (w > (size_t) width) {
|
||||
break;
|
||||
}
|
||||
t += {i, i + bytes};
|
||||
i += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
||||
|
|
|
|||
|
|
@ -110,11 +110,15 @@ void ThreadPool::doWork(bool mainThread)
|
|||
propagate it. */
|
||||
try {
|
||||
std::rethrow_exception(exc);
|
||||
} catch (const Interrupted &) {
|
||||
// The interrupted state may be picked up by multiple
|
||||
// workers, which is expected, so we should ignore
|
||||
// it silently and let the first one bubble up,
|
||||
// rethrown via the original state->exception.
|
||||
} catch (const ThreadPoolShutDown &) {
|
||||
// Similarly expected.
|
||||
} catch (std::exception & e) {
|
||||
if (!dynamic_cast<Interrupted*>(&e) &&
|
||||
!dynamic_cast<ThreadPoolShutDown*>(&e))
|
||||
ignoreException();
|
||||
} catch (...) {
|
||||
ignoreExceptionExceptInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ private:
|
|||
*/
|
||||
template<typename T>
|
||||
void processGraph(
|
||||
ThreadPool & pool,
|
||||
const std::set<T> & nodes,
|
||||
std::function<std::set<T>(const T &)> getEdges,
|
||||
std::function<void(const T &)> processNode)
|
||||
|
|
@ -97,6 +96,10 @@ void processGraph(
|
|||
|
||||
std::function<void(const T &)> worker;
|
||||
|
||||
/* Create pool last to ensure threads are stopped before other destructors
|
||||
* run */
|
||||
ThreadPool pool;
|
||||
|
||||
worker = [&](const T & node) {
|
||||
|
||||
{
|
||||
|
|
@ -147,8 +150,16 @@ void processGraph(
|
|||
}
|
||||
};
|
||||
|
||||
for (auto & node : nodes)
|
||||
pool.enqueue(std::bind(worker, std::ref(node)));
|
||||
for (auto & node : nodes) {
|
||||
try {
|
||||
pool.enqueue(std::bind(worker, std::ref(node)));
|
||||
} catch (ThreadPoolShutDown &) {
|
||||
/* Stop if the thread pool is shutting down. It means a
|
||||
previous work item threw an exception, so process()
|
||||
below will rethrow it. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pool.process();
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,11 @@ template<typename T>
|
|||
struct Explicit {
|
||||
T t;
|
||||
|
||||
bool operator ==(const Explicit<T> & other) const
|
||||
bool operator ==(const Explicit<T> & other) const = default;
|
||||
|
||||
bool operator <(const Explicit<T> & other) const
|
||||
{
|
||||
return t == other.t;
|
||||
return t < other.t;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,14 @@ int setEnv(const char * name, const char * value)
|
|||
return ::setenv(name, value, 1);
|
||||
}
|
||||
|
||||
std::optional<std::string> getEnvOs(const std::string & key)
|
||||
{
|
||||
return getEnv(key);
|
||||
}
|
||||
|
||||
int setEnvOs(const OsString & name, const OsString & value)
|
||||
{
|
||||
return setEnv(name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
|
|||
}
|
||||
|
||||
|
||||
std::string readLine(int fd)
|
||||
std::string readLine(int fd, bool eofOk)
|
||||
{
|
||||
std::string s;
|
||||
while (1) {
|
||||
|
|
@ -58,8 +58,12 @@ std::string readLine(int fd)
|
|||
if (rd == -1) {
|
||||
if (errno != EINTR)
|
||||
throw SysError("reading a line");
|
||||
} else if (rd == 0)
|
||||
throw EndOfFile("unexpected EOF reading a line");
|
||||
} else if (rd == 0) {
|
||||
if (eofOk)
|
||||
return s;
|
||||
else
|
||||
throw EndOfFile("unexpected EOF reading a line");
|
||||
}
|
||||
else {
|
||||
if (ch == '\n') return s;
|
||||
s += ch;
|
||||
|
|
@ -120,14 +124,38 @@ void Pipe::create()
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void unix::closeMostFDs(const std::set<int> & exceptions)
|
||||
#if __linux__ || __FreeBSD__
|
||||
static int unix_close_range(unsigned int first, unsigned int last, int flags)
|
||||
{
|
||||
#if !HAVE_CLOSE_RANGE
|
||||
return syscall(SYS_close_range, first, last, (unsigned int)flags);
|
||||
#else
|
||||
return close_range(first, last, flags);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void unix::closeExtraFDs()
|
||||
{
|
||||
constexpr int MAX_KEPT_FD = 2;
|
||||
static_assert(std::max({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) == MAX_KEPT_FD);
|
||||
|
||||
#if __linux__ || __FreeBSD__
|
||||
// first try to close_range everything we don't care about. if this
|
||||
// returns an error with these parameters we're running on a kernel
|
||||
// that does not implement close_range (i.e. pre 5.9) and fall back
|
||||
// to the old method. we should remove that though, in some future.
|
||||
if (unix_close_range(MAX_KEPT_FD + 1, ~0U, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
try {
|
||||
for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) {
|
||||
checkInterrupt();
|
||||
auto fd = std::stoi(s.path().filename());
|
||||
if (!exceptions.count(fd)) {
|
||||
if (fd > MAX_KEPT_FD) {
|
||||
debug("closing leaked FD %d", fd);
|
||||
close(fd);
|
||||
}
|
||||
|
|
@ -142,9 +170,8 @@ void unix::closeMostFDs(const std::set<int> & exceptions)
|
|||
#if HAVE_SYSCONF
|
||||
maxFD = sysconf(_SC_OPEN_MAX);
|
||||
#endif
|
||||
for (int fd = 0; fd < maxFD; ++fd)
|
||||
if (!exceptions.count(fd))
|
||||
close(fd); /* ignore result */
|
||||
for (int fd = MAX_KEPT_FD + 1; fd < maxFD; ++fd)
|
||||
close(fd); /* ignore result */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path)
|
||||
{
|
||||
return std::string { path };
|
||||
}
|
||||
|
||||
std::filesystem::path::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
return std::string { s };
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> maybePath(PathView path)
|
||||
{
|
||||
return { path };
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ sources += files(
|
|||
'file-path.cc',
|
||||
'file-system.cc',
|
||||
'muxable-pipe.cc',
|
||||
'os-string.cc',
|
||||
'processes.cc',
|
||||
'signals.cc',
|
||||
'users.cc',
|
||||
|
|
|
|||
21
src/libutil/unix/os-string.cc
Normal file
21
src/libutil/unix/os-string.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
|
||||
#include "file-path.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path)
|
||||
{
|
||||
return std::string{path};
|
||||
}
|
||||
|
||||
std::filesystem::path::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
return std::string{s};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "current-process.hh"
|
||||
#include "environment-variables.hh"
|
||||
#include "executable-path.hh"
|
||||
#include "signals.hh"
|
||||
#include "processes.hh"
|
||||
#include "finally.hh"
|
||||
|
|
@ -419,4 +420,12 @@ bool statusOk(int status)
|
|||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
}
|
||||
|
||||
int execvpe(const char * file0, const char * const argv[], const char * const envp[])
|
||||
{
|
||||
auto file = ExecutablePath::load().findPath(file0);
|
||||
// `const_cast` is safe. See the note in
|
||||
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html
|
||||
return execve(file.c_str(), const_cast<char *const *>(argv), const_cast<char *const *>(envp));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "error.hh"
|
||||
#include "logging.hh"
|
||||
#include "ansicolor.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -84,6 +85,12 @@ static inline bool getInterrupted()
|
|||
return unix::_isInterrupted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw `Interrupted` exception if the process has been interrupted.
|
||||
*
|
||||
* Call this in long-running loops and between slow operations to terminate
|
||||
* them as needed.
|
||||
*/
|
||||
void inline checkInterrupt()
|
||||
{
|
||||
using namespace unix;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ void unix::triggerInterrupt()
|
|||
try {
|
||||
callback();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
ignoreExceptionInDestructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
namespace fs { using namespace std::filesystem; }
|
||||
|
||||
std::string getUserName()
|
||||
{
|
||||
auto pw = getpwuid(geteuid());
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ ParsedURL parseURL(const std::string & url)
|
|||
std::smatch match;
|
||||
|
||||
if (std::regex_match(url, match, uriRegex)) {
|
||||
auto & base = match[1];
|
||||
std::string scheme = match[2];
|
||||
auto authority = match[3].matched
|
||||
? std::optional<std::string>(match[3]) : std::nullopt;
|
||||
|
|
@ -40,8 +39,6 @@ ParsedURL parseURL(const std::string & url)
|
|||
path = "/";
|
||||
|
||||
return ParsedURL{
|
||||
.url = url,
|
||||
.base = base,
|
||||
.scheme = scheme,
|
||||
.authority = authority,
|
||||
.path = percentDecode(path),
|
||||
|
|
@ -77,12 +74,16 @@ std::map<std::string, std::string> decodeQuery(const std::string & query)
|
|||
{
|
||||
std::map<std::string, std::string> result;
|
||||
|
||||
for (auto s : tokenizeString<Strings>(query, "&")) {
|
||||
for (const auto & s : tokenizeString<Strings>(query, "&")) {
|
||||
auto e = s.find('=');
|
||||
if (e != std::string::npos)
|
||||
result.emplace(
|
||||
s.substr(0, e),
|
||||
percentDecode(std::string_view(s).substr(e + 1)));
|
||||
if (e == std::string::npos) {
|
||||
warn("dubious URI query '%s' is missing equal sign '%s', ignoring", s, "=");
|
||||
continue;
|
||||
}
|
||||
|
||||
result.emplace(
|
||||
s.substr(0, e),
|
||||
percentDecode(std::string_view(s).substr(e + 1)));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -132,6 +133,12 @@ std::string ParsedURL::to_string() const
|
|||
+ (fragment.empty() ? "" : "#" + percentEncode(fragment));
|
||||
}
|
||||
|
||||
std::ostream & operator << (std::ostream & os, const ParsedURL & url)
|
||||
{
|
||||
os << url.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
bool ParsedURL::operator ==(const ParsedURL & other) const noexcept
|
||||
{
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ namespace nix {
|
|||
|
||||
struct ParsedURL
|
||||
{
|
||||
std::string url;
|
||||
/// URL without query/fragment
|
||||
std::string base;
|
||||
std::string scheme;
|
||||
std::optional<std::string> authority;
|
||||
std::string path;
|
||||
|
|
@ -26,6 +23,8 @@ struct ParsedURL
|
|||
ParsedURL canonicalise();
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & os, const ParsedURL & url);
|
||||
|
||||
MakeError(BadURL, Error);
|
||||
|
||||
std::string percentDecode(std::string_view in);
|
||||
|
|
|
|||
|
|
@ -7,15 +7,33 @@ namespace nix {
|
|||
|
||||
Path getCacheDir()
|
||||
{
|
||||
auto cacheDir = getEnv("XDG_CACHE_HOME");
|
||||
return cacheDir ? *cacheDir : getHome() + "/.cache";
|
||||
auto dir = getEnv("NIX_CACHE_HOME");
|
||||
if (dir) {
|
||||
return *dir;
|
||||
} else {
|
||||
auto xdgDir = getEnv("XDG_CACHE_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
} else {
|
||||
return getHome() + "/.cache/nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Path getConfigDir()
|
||||
{
|
||||
auto configDir = getEnv("XDG_CONFIG_HOME");
|
||||
return configDir ? *configDir : getHome() + "/.config";
|
||||
auto dir = getEnv("NIX_CONFIG_HOME");
|
||||
if (dir) {
|
||||
return *dir;
|
||||
} else {
|
||||
auto xdgDir = getEnv("XDG_CONFIG_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
} else {
|
||||
return getHome() + "/.config/nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Path> getConfigDirs()
|
||||
|
|
@ -23,6 +41,9 @@ std::vector<Path> getConfigDirs()
|
|||
Path configHome = getConfigDir();
|
||||
auto configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg");
|
||||
std::vector<Path> result = tokenizeString<std::vector<std::string>>(configDirs, ":");
|
||||
for (auto& p : result) {
|
||||
p += "/nix";
|
||||
}
|
||||
result.insert(result.begin(), configHome);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -30,19 +51,37 @@ std::vector<Path> getConfigDirs()
|
|||
|
||||
Path getDataDir()
|
||||
{
|
||||
auto dataDir = getEnv("XDG_DATA_HOME");
|
||||
return dataDir ? *dataDir : getHome() + "/.local/share";
|
||||
auto dir = getEnv("NIX_DATA_HOME");
|
||||
if (dir) {
|
||||
return *dir;
|
||||
} else {
|
||||
auto xdgDir = getEnv("XDG_DATA_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
} else {
|
||||
return getHome() + "/.local/share/nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path getStateDir()
|
||||
{
|
||||
auto stateDir = getEnv("XDG_STATE_HOME");
|
||||
return stateDir ? *stateDir : getHome() + "/.local/state";
|
||||
auto dir = getEnv("NIX_STATE_HOME");
|
||||
if (dir) {
|
||||
return *dir;
|
||||
} else {
|
||||
auto xdgDir = getEnv("XDG_STATE_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
} else {
|
||||
return getHome() + "/.local/state/nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path createNixStateDir()
|
||||
{
|
||||
Path dir = getStateDir() + "/nix";
|
||||
Path dir = getStateDir();
|
||||
createDirs(dir);
|
||||
return dir;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ Path getHomeOf(uid_t userId);
|
|||
Path getHome();
|
||||
|
||||
/**
|
||||
* @return $XDG_CACHE_HOME or $HOME/.cache.
|
||||
* @return $NIX_CACHE_HOME or $XDG_CACHE_HOME/nix or $HOME/.cache/nix.
|
||||
*/
|
||||
Path getCacheDir();
|
||||
|
||||
/**
|
||||
* @return $XDG_CONFIG_HOME or $HOME/.config.
|
||||
* @return $NIX_CONFIG_HOME or $XDG_CONFIG_HOME/nix or $HOME/.config/nix.
|
||||
*/
|
||||
Path getConfigDir();
|
||||
|
||||
|
|
@ -39,12 +39,12 @@ Path getConfigDir();
|
|||
std::vector<Path> getConfigDirs();
|
||||
|
||||
/**
|
||||
* @return $XDG_DATA_HOME or $HOME/.local/share.
|
||||
* @return $NIX_DATA_HOME or $XDG_DATA_HOME/nix or $HOME/.local/share/nix.
|
||||
*/
|
||||
Path getDataDir();
|
||||
|
||||
/**
|
||||
* @return $XDG_STATE_HOME or $HOME/.local/state.
|
||||
* @return $NIX_STATE_HOME or $XDG_STATE_HOME/nix or $HOME/.local/state/nix.
|
||||
*/
|
||||
Path getStateDir();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "util.hh"
|
||||
#include "fmt.hh"
|
||||
#include "file-path.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
|
|
@ -53,24 +55,6 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
||||
{
|
||||
C result;
|
||||
auto pos = s.find_first_not_of(separators, 0);
|
||||
while (pos != s.npos) {
|
||||
auto end = s.find_first_of(separators, pos + 1);
|
||||
if (end == s.npos) end = s.size();
|
||||
result.insert(result.end(), std::string(s, pos, end - pos));
|
||||
pos = s.find_first_not_of(separators, end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template Strings tokenizeString(std::string_view s, std::string_view separators);
|
||||
template StringSet tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
|
||||
std::string chomp(std::string_view s)
|
||||
{
|
||||
size_t i = s.find_last_not_of(" \n\r\t");
|
||||
|
|
@ -199,7 +183,7 @@ std::string shellEscape(const std::string_view s)
|
|||
}
|
||||
|
||||
|
||||
void ignoreException(Verbosity lvl)
|
||||
void ignoreExceptionInDestructor(Verbosity lvl)
|
||||
{
|
||||
/* Make sure no exceptions leave this function.
|
||||
printError() also throws when remote is closed. */
|
||||
|
|
@ -212,6 +196,17 @@ void ignoreException(Verbosity lvl)
|
|||
} catch (...) { }
|
||||
}
|
||||
|
||||
void ignoreExceptionExceptInterrupt(Verbosity lvl)
|
||||
{
|
||||
try {
|
||||
throw;
|
||||
} catch (const Interrupted & e) {
|
||||
throw;
|
||||
} catch (std::exception & e) {
|
||||
printMsg(lvl, "error (ignored): %1%", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
|
|
|
|||
|
|
@ -28,49 +28,11 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss);
|
|||
MakeError(FormatError, Error);
|
||||
|
||||
|
||||
/**
|
||||
* String tokenizer.
|
||||
*/
|
||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||
|
||||
|
||||
/**
|
||||
* Ignore any empty strings at the start of the list, and then concatenate the
|
||||
* given strings with a separator between the elements.
|
||||
*
|
||||
* @deprecated This function exists for historical reasons. You probably just
|
||||
* want to use `concatStringsSep`.
|
||||
*/
|
||||
template<class C>
|
||||
[[deprecated("Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]]
|
||||
std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
// TODO? remove to make sure we don't rely on the empty item ignoring behavior,
|
||||
// or just get rid of this function by understanding the remaining calls.
|
||||
// for (auto & i : ss) {
|
||||
// // Make sure we don't rely on the empty item ignoring behavior
|
||||
// assert(!i.empty());
|
||||
// break;
|
||||
// }
|
||||
|
||||
// need a cast to string_view since this is also called with Symbols
|
||||
for (const auto & s : ss) size += sep.size() + std::string_view(s).size();
|
||||
std::string s;
|
||||
s.reserve(size);
|
||||
for (auto & i : ss) {
|
||||
if (s.size() != 0) s += sep;
|
||||
s += i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class ... Parts>
|
||||
auto concatStrings(Parts && ... parts)
|
||||
template<class... Parts>
|
||||
auto concatStrings(Parts &&... parts)
|
||||
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
|
||||
{
|
||||
std::string_view views[sizeof...(parts)] = { parts... };
|
||||
std::string_view views[sizeof...(parts)] = {parts...};
|
||||
return concatStringsSep({}, views);
|
||||
}
|
||||
|
||||
|
|
@ -194,9 +156,26 @@ std::string toLower(std::string s);
|
|||
std::string shellEscape(const std::string_view s);
|
||||
|
||||
|
||||
/* Exception handling in destructors: print an error message, then
|
||||
ignore the exception. */
|
||||
void ignoreException(Verbosity lvl = lvlError);
|
||||
/**
|
||||
* Exception handling in destructors: print an error message, then
|
||||
* ignore the exception.
|
||||
*
|
||||
* If you're not in a destructor, you usually want to use `ignoreExceptionExceptInterrupt()`.
|
||||
*
|
||||
* This function might also be used in callbacks whose caller may not handle exceptions,
|
||||
* but ideally we propagate the exception using an exception_ptr in such cases.
|
||||
* See e.g. `PackBuilderContext`
|
||||
*/
|
||||
void ignoreExceptionInDestructor(Verbosity lvl = lvlError);
|
||||
|
||||
/**
|
||||
* Not destructor-safe.
|
||||
* Print an error message, then ignore the exception.
|
||||
* If the exception is an `Interrupted` exception, rethrow it.
|
||||
*
|
||||
* This may be used in a few places where Interrupt can't happen, but that's ok.
|
||||
*/
|
||||
void ignoreExceptionExceptInterrupt(Verbosity lvl = lvlError);
|
||||
|
||||
|
||||
|
||||
|
|
@ -295,6 +274,17 @@ std::optional<typename T::value_type> pop(T & c)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append items to a container. TODO: remove this once we can use
|
||||
* C++23's `append_range()`.
|
||||
*/
|
||||
template<class C, typename T>
|
||||
void append(C & c, std::initializer_list<T> l)
|
||||
{
|
||||
c.insert(c.end(), l.begin(), l.end());
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Callback;
|
||||
|
||||
|
|
@ -359,7 +349,9 @@ std::string showBytes(uint64_t bytes);
|
|||
*/
|
||||
inline std::string operator + (const std::string & s1, std::string_view s2)
|
||||
{
|
||||
auto s = s1;
|
||||
std::string s;
|
||||
s.reserve(s1.size() + s2.size());
|
||||
s.append(s1);
|
||||
s.append(s2);
|
||||
return s;
|
||||
}
|
||||
|
|
@ -372,10 +364,11 @@ inline std::string operator + (std::string && s, std::string_view s2)
|
|||
|
||||
inline std::string operator + (std::string_view s1, const char * s2)
|
||||
{
|
||||
auto s2Size = strlen(s2);
|
||||
std::string s;
|
||||
s.reserve(s1.size() + strlen(s2));
|
||||
s.reserve(s1.size() + s2Size);
|
||||
s.append(s1);
|
||||
s.append(s2);
|
||||
s.append(s2, s2Size);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
4
src/libutil/widecharwidth/LICENSE
Normal file
4
src/libutil/widecharwidth/LICENSE
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
widecharwidth - wcwidth implementation
|
||||
Written in 2018 by ridiculous_fish
|
||||
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
|
||||
You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
1559
src/libutil/widecharwidth/widechar_width.h
Normal file
1559
src/libutil/widecharwidth/widechar_width.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,34 @@
|
|||
#include "environment-variables.hh"
|
||||
|
||||
#include "processenv.h"
|
||||
#ifdef _WIN32
|
||||
# include "processenv.h"
|
||||
|
||||
namespace nix {
|
||||
|
||||
int unsetenv(const char *name)
|
||||
std::optional<OsString> getEnvOs(const OsString & key)
|
||||
{
|
||||
// Determine the required buffer size for the environment variable value
|
||||
DWORD bufferSize = GetEnvironmentVariableW(key.c_str(), nullptr, 0);
|
||||
if (bufferSize == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate a buffer to hold the environment variable value
|
||||
std::wstring value{bufferSize, L'\0'};
|
||||
|
||||
// Retrieve the environment variable value
|
||||
DWORD resultSize = GetEnvironmentVariableW(key.c_str(), &value[0], bufferSize);
|
||||
if (resultSize == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Resize the string to remove the extra null characters
|
||||
value.resize(resultSize);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int unsetenv(const char * name)
|
||||
{
|
||||
return -SetEnvironmentVariableA(name, nullptr);
|
||||
}
|
||||
|
|
@ -14,4 +38,10 @@ int setEnv(const char * name, const char * value)
|
|||
return -SetEnvironmentVariableA(name, value);
|
||||
}
|
||||
|
||||
int setEnvOs(const OsString & name, const OsString & value)
|
||||
{
|
||||
return -SetEnvironmentVariableW(name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "windows-error.hh"
|
||||
#include "file-path.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fileapi.h>
|
||||
#include <error.h>
|
||||
#include <namedpipeapi.h>
|
||||
|
|
@ -61,7 +62,7 @@ void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts)
|
|||
}
|
||||
|
||||
|
||||
std::string readLine(HANDLE handle)
|
||||
std::string readLine(HANDLE handle, bool eofOk)
|
||||
{
|
||||
std::string s;
|
||||
while (1) {
|
||||
|
|
@ -71,8 +72,12 @@ std::string readLine(HANDLE handle)
|
|||
DWORD rd;
|
||||
if (!ReadFile(handle, &ch, 1, &rd, NULL)) {
|
||||
throw WinError("reading a line");
|
||||
} else if (rd == 0)
|
||||
throw EndOfFile("unexpected EOF reading a line");
|
||||
} else if (rd == 0) {
|
||||
if (eofOk)
|
||||
return s;
|
||||
else
|
||||
throw EndOfFile("unexpected EOF reading a line");
|
||||
}
|
||||
else {
|
||||
if (ch == '\n') return s;
|
||||
s += ch;
|
||||
|
|
@ -148,3 +153,4 @@ Path windows::handleToPath(HANDLE handle) {
|
|||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -9,18 +9,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(std::filesystem::path::string_type { path });
|
||||
}
|
||||
|
||||
std::filesystem::path::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(std::string { s });
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> maybePath(PathView path)
|
||||
{
|
||||
if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait<char>::isPathSep(path[2])) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "file-system.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace nix {
|
||||
|
||||
Descriptor openDirectory(const std::filesystem::path & path)
|
||||
|
|
@ -15,3 +16,4 @@ Descriptor openDirectory(const std::filesystem::path & path)
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ sources += files(
|
|||
'file-path.cc',
|
||||
'file-system.cc',
|
||||
'muxable-pipe.cc',
|
||||
'os-string.cc',
|
||||
'processes.cc',
|
||||
'users.cc',
|
||||
'windows-async-pipe.cc',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#include <ioapiset.h>
|
||||
#include "windows-error.hh"
|
||||
#ifdef _WIN32
|
||||
# include <ioapiset.h>
|
||||
# include "windows-error.hh"
|
||||
|
||||
#include "logging.hh"
|
||||
#include "util.hh"
|
||||
#include "muxable-pipe.hh"
|
||||
# include "logging.hh"
|
||||
# include "util.hh"
|
||||
# include "muxable-pipe.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -68,3 +69,4 @@ void MuxablePipePollState::iterate(
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
28
src/libutil/windows/os-string.cc
Normal file
28
src/libutil/windows/os-string.cc
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
|
||||
#include "file-path.hh"
|
||||
#include "file-path-impl.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(std::filesystem::path::string_type{path});
|
||||
}
|
||||
|
||||
std::filesystem::path::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(std::string{s});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#include "current-process.hh"
|
||||
#include "environment-variables.hh"
|
||||
#include "error.hh"
|
||||
#include "executable-path.hh"
|
||||
#include "file-descriptor.hh"
|
||||
#include "file-path.hh"
|
||||
#include "signals.hh"
|
||||
|
|
@ -22,6 +23,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
|
|
@ -377,4 +380,13 @@ bool statusOk(int status)
|
|||
{
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
int execvpe(const wchar_t * file0, const wchar_t * const argv[], const wchar_t * const envp[])
|
||||
{
|
||||
auto file = ExecutablePath::load().findPath(file0);
|
||||
return _wexecve(file.c_str(), argv, envp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "file-system.hh"
|
||||
#include "windows-error.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
|
|
@ -50,3 +51,4 @@ bool isRootUser() {
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "windows-async-pipe.hh"
|
||||
#include "windows-error.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace nix::windows {
|
||||
|
||||
void AsyncPipe::createAsyncPipe(HANDLE iocp)
|
||||
|
|
@ -47,3 +49,5 @@ void AsyncPipe::close()
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include "file-descriptor.hh"
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace nix::windows {
|
||||
|
||||
|
|
@ -25,3 +26,4 @@ public:
|
|||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "windows-error.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <error.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
|
@ -29,3 +30,4 @@ std::string WinError::renderError(DWORD lastError)
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <errhandlingapi.h>
|
||||
|
||||
#include "error.hh"
|
||||
|
|
@ -49,3 +50,4 @@ private:
|
|||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue