mirror of
https://github.com/NixOS/nix
synced 2025-07-05 04:01:47 +02:00
Merge pull request #8901 from nix-windows/mingw
Build a minimized Nix with MinGW
This commit is contained in:
commit
ac253fb99e
120 changed files with 1435 additions and 341 deletions
12
Makefile
12
Makefile
|
@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config
|
||||||
|
|
||||||
# List makefiles
|
# List makefiles
|
||||||
|
|
||||||
|
include mk/platform.mk
|
||||||
|
|
||||||
ifeq ($(ENABLE_BUILD), yes)
|
ifeq ($(ENABLE_BUILD), yes)
|
||||||
makefiles = \
|
makefiles = \
|
||||||
mk/precompiled-headers.mk \
|
mk/precompiled-headers.mk \
|
||||||
|
@ -20,7 +22,10 @@ makefiles = \
|
||||||
src/nix/local.mk \
|
src/nix/local.mk \
|
||||||
src/libutil-c/local.mk \
|
src/libutil-c/local.mk \
|
||||||
src/libstore-c/local.mk \
|
src/libstore-c/local.mk \
|
||||||
src/libexpr-c/local.mk \
|
src/libexpr-c/local.mk
|
||||||
|
|
||||||
|
ifdef HOST_UNIX
|
||||||
|
makefiles += \
|
||||||
scripts/local.mk \
|
scripts/local.mk \
|
||||||
misc/bash/local.mk \
|
misc/bash/local.mk \
|
||||||
misc/fish/local.mk \
|
misc/fish/local.mk \
|
||||||
|
@ -29,6 +34,7 @@ makefiles = \
|
||||||
misc/launchd/local.mk \
|
misc/launchd/local.mk \
|
||||||
misc/upstart/local.mk
|
misc/upstart/local.mk
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_UNIT_TESTS), yes)
|
ifeq ($(ENABLE_UNIT_TESTS), yes)
|
||||||
makefiles += \
|
makefiles += \
|
||||||
|
@ -42,6 +48,7 @@ makefiles += \
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
||||||
|
ifdef HOST_UNIX
|
||||||
makefiles += \
|
makefiles += \
|
||||||
tests/functional/local.mk \
|
tests/functional/local.mk \
|
||||||
tests/functional/ca/local.mk \
|
tests/functional/ca/local.mk \
|
||||||
|
@ -51,6 +58,7 @@ makefiles += \
|
||||||
tests/functional/test-libstoreconsumer/local.mk \
|
tests/functional/test-libstoreconsumer/local.mk \
|
||||||
tests/functional/plugins/local.mk
|
tests/functional/plugins/local.mk
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
# Some makefiles require access to built programs and must be included late.
|
# Some makefiles require access to built programs and must be included late.
|
||||||
makefiles-late =
|
makefiles-late =
|
||||||
|
@ -79,8 +87,6 @@ else
|
||||||
unexport NIX_HARDENING_ENABLE
|
unexport NIX_HARDENING_ENABLE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include mk/platform.mk
|
|
||||||
|
|
||||||
ifdef HOST_WINDOWS
|
ifdef HOST_WINDOWS
|
||||||
# Windows DLLs are stricter about symbol visibility than Unix shared
|
# Windows DLLs are stricter about symbol visibility than Unix shared
|
||||||
# objects --- see https://gcc.gnu.org/wiki/Visibility for details.
|
# objects --- see https://gcc.gnu.org/wiki/Visibility for details.
|
||||||
|
|
12
flake.nix
12
flake.nix
|
@ -32,12 +32,6 @@
|
||||||
"armv6l-unknown-linux-gnueabihf"
|
"armv6l-unknown-linux-gnueabihf"
|
||||||
"armv7l-unknown-linux-gnueabihf"
|
"armv7l-unknown-linux-gnueabihf"
|
||||||
"x86_64-unknown-netbsd"
|
"x86_64-unknown-netbsd"
|
||||||
];
|
|
||||||
|
|
||||||
# Nix doesn't yet build on this platform, so we put it in a
|
|
||||||
# separate list. We just use this for `devShells` and
|
|
||||||
# `nixpkgsFor`, which this depends on.
|
|
||||||
shellCrossSystems = crossSystems ++ [
|
|
||||||
"x86_64-w64-mingw32"
|
"x86_64-w64-mingw32"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -83,7 +77,7 @@
|
||||||
in {
|
in {
|
||||||
inherit stdenvs native;
|
inherit stdenvs native;
|
||||||
static = native.pkgsStatic;
|
static = native.pkgsStatic;
|
||||||
cross = lib.genAttrs shellCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||||
});
|
});
|
||||||
|
|
||||||
installScriptFor = tarballs:
|
installScriptFor = tarballs:
|
||||||
|
@ -426,8 +420,8 @@
|
||||||
in
|
in
|
||||||
(makeShells "native" nixpkgsFor.${system}.native) //
|
(makeShells "native" nixpkgsFor.${system}.native) //
|
||||||
(lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin)
|
(lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin)
|
||||||
(makeShells "static" nixpkgsFor.${system}.static)) //
|
(makeShells "static" nixpkgsFor.${system}.static) //
|
||||||
(lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
|
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv))) //
|
||||||
{
|
{
|
||||||
default = self.devShells.${system}.native-stdenvPackages;
|
default = self.devShells.${system}.native-stdenvPackages;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
|
||||||
]])],
|
]])],
|
||||||
[status_80431=0],
|
[status_80431=0],
|
||||||
[status_80431=$?],
|
[status_80431=$?],
|
||||||
[
|
[status_80431=''])
|
||||||
# Assume we're bug-free when cross-compiling
|
|
||||||
])
|
|
||||||
AC_LANG_POP(C++)
|
AC_LANG_POP(C++)
|
||||||
AS_CASE([$status_80431],
|
AS_CASE([$status_80431],
|
||||||
|
[''],[
|
||||||
|
AC_MSG_RESULT(cannot check because cross compiling)
|
||||||
|
AC_MSG_NOTICE(assume we are bug free)
|
||||||
|
],
|
||||||
[0],[
|
[0],[
|
||||||
AC_MSG_RESULT(yes)
|
AC_MSG_RESULT(yes)
|
||||||
],
|
],
|
||||||
|
|
|
@ -42,19 +42,22 @@
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <grp.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <grp.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <pwd.h>
|
||||||
|
# include <sys/resource.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
# include <sys/utsname.h>
|
# include <sys/utsname.h>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
# include <termios.h>
|
# include <termios.h>
|
||||||
#include <unistd.h>
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "experimental-features.hh"
|
#include "experimental-features.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
using namespace nix::unix;
|
||||||
using std::cin;
|
using std::cin;
|
||||||
|
|
||||||
static void handleAlarm(int sig) {
|
static void handleAlarm(int sig) {
|
||||||
|
|
|
@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||||
v->mkString(arg.s);
|
v->mkString(arg.s);
|
||||||
},
|
},
|
||||||
[&](const AutoArgFile & arg) {
|
[&](const AutoArgFile & arg) {
|
||||||
v->mkString(readFile(arg.path));
|
v->mkString(readFile(arg.path.string()));
|
||||||
},
|
},
|
||||||
[&](const AutoArgStdin & arg) {
|
[&](const AutoArgStdin & arg) {
|
||||||
v->mkString(readFile(STDIN_FILENO));
|
v->mkString(readFile(STDIN_FILENO));
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "terminal.hh"
|
#include "terminal.hh"
|
||||||
|
|
||||||
#include <sys/queue.h>
|
|
||||||
#if HAVE_LOWDOWN
|
#if HAVE_LOWDOWN
|
||||||
|
# include <sys/queue.h>
|
||||||
# include <lowdown.h>
|
# include <lowdown.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType)
|
||||||
|
|
||||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||||
struct sigaction act, old;
|
struct sigaction act, old;
|
||||||
sigset_t savedSignalMask, set;
|
sigset_t savedSignalMask, set;
|
||||||
|
|
||||||
|
@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
||||||
};
|
};
|
||||||
|
|
||||||
setupSignals();
|
setupSignals();
|
||||||
|
#endif
|
||||||
char * s = readline(promptForType(promptType));
|
char * s = readline(promptForType(promptType));
|
||||||
Finally doFree([&]() { free(s); });
|
Finally doFree([&]() { free(s); });
|
||||||
|
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||||
restoreSignals();
|
restoreSignals();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (g_signal_received) {
|
if (g_signal_received) {
|
||||||
g_signal_received = 0;
|
g_signal_received = 0;
|
||||||
|
|
|
@ -33,15 +33,17 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO use portable implementation
|
||||||
|
# include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
|
||||||
#define GC_INCLUDE_NEW
|
#define GC_INCLUDE_NEW
|
||||||
|
@ -2627,9 +2629,11 @@ void EvalState::maybePrintStats()
|
||||||
|
|
||||||
void EvalState::printStatistics()
|
void EvalState::printStatistics()
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO use portable implementation
|
||||||
struct rusage buf;
|
struct rusage buf;
|
||||||
getrusage(RUSAGE_SELF, &buf);
|
getrusage(RUSAGE_SELF, &buf);
|
||||||
float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000);
|
float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000);
|
||||||
|
#endif
|
||||||
|
|
||||||
uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *);
|
uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *);
|
||||||
uint64_t bLists = nrListElems * sizeof(Value *);
|
uint64_t bLists = nrListElems * sizeof(Value *);
|
||||||
|
@ -2646,7 +2650,9 @@ void EvalState::printStatistics()
|
||||||
if (outPath != "-")
|
if (outPath != "-")
|
||||||
fs.open(outPath, std::fstream::out);
|
fs.open(outPath, std::fstream::out);
|
||||||
json topObj = json::object();
|
json topObj = json::object();
|
||||||
|
#ifndef _WIN32 // TODO implement
|
||||||
topObj["cpuTime"] = cpuTime;
|
topObj["cpuTime"] = cpuTime;
|
||||||
|
#endif
|
||||||
topObj["envs"] = {
|
topObj["envs"] = {
|
||||||
{"number", nrEnvs},
|
{"number", nrEnvs},
|
||||||
{"elements", nrValuesInEnvs},
|
{"elements", nrValuesInEnvs},
|
||||||
|
|
|
@ -161,6 +161,8 @@ struct DebugTrace {
|
||||||
bool isError;
|
bool isError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Don't want Windows function
|
||||||
|
#undef SearchPath
|
||||||
|
|
||||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,10 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include <dlfcn.h>
|
# include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
@ -331,6 +334,8 @@ static RegisterPrimOp primop_import({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||||
|
|
||||||
/* Want reasonable symbol names, so extern C */
|
/* Want reasonable symbol names, so extern C */
|
||||||
/* !!! Should we pass the Pos or the file name too? */
|
/* !!! Should we pass the Pos or the file name too? */
|
||||||
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
||||||
|
@ -403,6 +408,8 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Return a string representing the type of the expression. */
|
/* Return a string representing the type of the expression. */
|
||||||
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
@ -4593,6 +4600,7 @@ void EvalState::createBaseEnv()
|
||||||
)",
|
)",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO implement on Windows
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
if (evalSettings.enableNativeCode) {
|
if (evalSettings.enableNativeCode) {
|
||||||
addPrimOp({
|
addPrimOp({
|
||||||
|
@ -4606,6 +4614,7 @@ void EvalState::createBaseEnv()
|
||||||
.fun = prim_exec,
|
.fun = prim_exec,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
addPrimOp({
|
addPrimOp({
|
||||||
.name = "__traceVerbose",
|
.name = "__traceVerbose",
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
// Do not want the windows macro (alias to `SearchPathA`)
|
||||||
|
#undef SearchPath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A "search path" is a list of ways look for something, used with
|
* A "search path" is a list of ways look for something, used with
|
||||||
* `builtins.findFile` and `< >` lookup expressions.
|
* `builtins.findFile` and `< >` lookup expressions.
|
||||||
|
|
|
@ -26,7 +26,7 @@ ref<InputAccessor> makeStorePathAccessor(
|
||||||
// FIXME: should use `store->getFSAccessor()`
|
// FIXME: should use `store->getFSAccessor()`
|
||||||
auto root = std::filesystem::path { store->toRealPath(storePath) };
|
auto root = std::filesystem::path { store->toRealPath(storePath) };
|
||||||
auto accessor = makeFSInputAccessor(root);
|
auto accessor = makeFSInputAccessor(root);
|
||||||
accessor->setPathDisplay(root);
|
accessor->setPathDisplay(root.string());
|
||||||
return accessor;
|
return accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
{
|
{
|
||||||
initLibGit2();
|
initLibGit2();
|
||||||
|
|
||||||
if (pathExists(path.native())) {
|
if (pathExists(path.string())) {
|
||||||
if (git_repository_open(Setter(repo), path.c_str()))
|
if (git_repository_open(Setter(repo), path.string().c_str()))
|
||||||
throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
|
throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
|
||||||
} else {
|
} else {
|
||||||
if (git_repository_init(Setter(repo), path.c_str(), bare))
|
if (git_repository_init(Setter(repo), path.string().c_str(), bare))
|
||||||
throw Error("creating Git repository '%s': %s", path, git_error_last()->message);
|
throw Error("creating Git repository '%s': %s", path, git_error_last()->message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
|
std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
|
||||||
{
|
{
|
||||||
GitConfig config;
|
GitConfig config;
|
||||||
if (git_config_open_ondisk(Setter(config), configFile.c_str()))
|
if (git_config_open_ondisk(Setter(config), configFile.string().c_str()))
|
||||||
throw Error("parsing .gitmodules file: %s", git_error_last()->message);
|
throw Error("parsing .gitmodules file: %s", git_error_last()->message);
|
||||||
|
|
||||||
ConfigIterator it;
|
ConfigIterator it;
|
||||||
|
@ -288,7 +288,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
|
|
||||||
/* Get submodule info. */
|
/* Get submodule info. */
|
||||||
auto modulesFile = path / ".gitmodules";
|
auto modulesFile = path / ".gitmodules";
|
||||||
if (pathExists(modulesFile))
|
if (pathExists(modulesFile.string()))
|
||||||
info.submodules = parseSubmodules(modulesFile);
|
info.submodules = parseSubmodules(modulesFile);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
@ -377,10 +377,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
auto dir = this->path;
|
auto dir = this->path;
|
||||||
Strings gitArgs;
|
Strings gitArgs;
|
||||||
if (shallow) {
|
if (shallow) {
|
||||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec };
|
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec };
|
||||||
}
|
}
|
||||||
|
|
||||||
runProgram(RunOptions {
|
runProgram(RunOptions {
|
||||||
|
@ -426,7 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
.args = {
|
.args = {
|
||||||
"-c",
|
"-c",
|
||||||
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
|
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
|
||||||
"-C", path,
|
"-C", path.string(),
|
||||||
"verify-commit",
|
"verify-commit",
|
||||||
rev.gitRev()
|
rev.gitRev()
|
||||||
},
|
},
|
||||||
|
|
|
@ -108,7 +108,9 @@ std::string getArg(const std::string & opt,
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
static void sigHandler(int signo) { }
|
static void sigHandler(int signo) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void initNix()
|
void initNix()
|
||||||
|
@ -121,6 +123,7 @@ void initNix()
|
||||||
|
|
||||||
initLibStore();
|
initLibStore();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
unix::startSignalHandlerThread();
|
unix::startSignalHandlerThread();
|
||||||
|
|
||||||
/* Reset SIGCHLD to its default. */
|
/* Reset SIGCHLD to its default. */
|
||||||
|
@ -135,6 +138,7 @@ void initNix()
|
||||||
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
|
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
|
||||||
act.sa_handler = sigHandler;
|
act.sa_handler = sigHandler;
|
||||||
if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");
|
if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
/* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
|
/* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
|
||||||
|
@ -156,21 +160,26 @@ void initNix()
|
||||||
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/* Register a SIGSEGV handler to detect stack overflows.
|
/* Register a SIGSEGV handler to detect stack overflows.
|
||||||
Why not initLibExpr()? initGC() is essentially that, but
|
Why not initLibExpr()? initGC() is essentially that, but
|
||||||
detectStackOverflow is not an instance of the init function concept, as
|
detectStackOverflow is not an instance of the init function concept, as
|
||||||
it may have to be invoked more than once per process. */
|
it may have to be invoked more than once per process. */
|
||||||
detectStackOverflow();
|
detectStackOverflow();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* There is no privacy in the Nix system ;-) At least not for
|
/* There is no privacy in the Nix system ;-) At least not for
|
||||||
now. In particular, store objects should be readable by
|
now. In particular, store objects should be readable by
|
||||||
everybody. */
|
everybody. */
|
||||||
umask(0022);
|
umask(0022);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/* Initialise the PRNG. */
|
/* Initialise the PRNG. */
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, 0);
|
gettimeofday(&tv, 0);
|
||||||
srandom(tv.tv_usec);
|
srandom(tv.tv_usec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,6 +374,9 @@ RunPager::RunPager()
|
||||||
Pipe toPager;
|
Pipe toPager;
|
||||||
toPager.create();
|
toPager.create();
|
||||||
|
|
||||||
|
#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
|
throw Error("Commit signature verification not implemented on Windows yet");
|
||||||
|
#else
|
||||||
pid = startProcess([&]() {
|
pid = startProcess([&]() {
|
||||||
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
||||||
throw SysError("dupping stdin");
|
throw SysError("dupping stdin");
|
||||||
|
@ -383,17 +395,20 @@ RunPager::RunPager()
|
||||||
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||||
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
throw SysError("dupping standard output");
|
throw SysError("dupping standard output");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RunPager::~RunPager()
|
RunPager::~RunPager()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
dup2(std_out, STDOUT_FILENO);
|
dup2(std_out, STDOUT_FILENO);
|
||||||
pid.wait();
|
pid.wait();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "file-descriptor.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
#include "args.hh"
|
#include "args.hh"
|
||||||
#include "args/root.hh"
|
#include "args/root.hh"
|
||||||
|
@ -89,8 +90,10 @@ public:
|
||||||
~RunPager();
|
~RunPager();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
Pid pid;
|
Pid pid;
|
||||||
int std_out;
|
#endif
|
||||||
|
Descriptor std_out;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern volatile ::sig_atomic_t blockInt;
|
extern volatile ::sig_atomic_t blockInt;
|
||||||
|
@ -112,6 +115,7 @@ struct PrintFreed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/**
|
/**
|
||||||
* Install a SIGSEGV handler to detect stack overflows.
|
* Install a SIGSEGV handler to detect stack overflows.
|
||||||
*/
|
*/
|
||||||
|
@ -141,5 +145,6 @@ extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;
|
||||||
* logger. Exits the process immediately after.
|
* logger. Exits the process immediately after.
|
||||||
*/
|
*/
|
||||||
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
|
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||||
if (unlink(dstFile.c_str()) == -1)
|
if (unlink(dstFile.c_str()) == -1)
|
||||||
throw SysError("unlinking '%1%'", dstFile);
|
throw SysError("unlinking '%1%'", dstFile);
|
||||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
if (mkdir(dstFile.c_str()
|
||||||
|
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||||
|
, 0755
|
||||||
|
#endif
|
||||||
|
) == -1)
|
||||||
throw SysError("creating directory '%1%'", dstFile);
|
throw SysError("creating directory '%1%'", dstFile);
|
||||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||||
createLinks(state, srcFile, dstFile, priority);
|
createLinks(state, srcFile, dstFile, priority);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "daemon.hh"
|
#include "daemon.hh"
|
||||||
#include "monitor-fd.hh"
|
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "worker-protocol-impl.hh"
|
#include "worker-protocol-impl.hh"
|
||||||
|
@ -16,6 +15,10 @@
|
||||||
#include "args.hh"
|
#include "args.hh"
|
||||||
#include "git.hh"
|
#include "git.hh"
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
|
# include "monitor-fd.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix::daemon {
|
namespace nix::daemon {
|
||||||
|
|
||||||
Sink & operator << (Sink & sink, const Logger::Fields & fields)
|
Sink & operator << (Sink & sink, const Logger::Fields & fields)
|
||||||
|
@ -1018,7 +1021,9 @@ void processConnection(
|
||||||
TrustedFlag trusted,
|
TrustedFlag trusted,
|
||||||
RecursiveFlag recursive)
|
RecursiveFlag recursive)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Exchange the greeting. */
|
/* Exchange the greeting. */
|
||||||
unsigned int magic = readInt(from);
|
unsigned int magic = readInt(from);
|
||||||
|
|
|
@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer
|
||||||
|
|
||||||
Sync<State> state_;
|
Sync<State> state_;
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
/* We can't use a std::condition_variable to wake up the curl
|
/* We can't use a std::condition_variable to wake up the curl
|
||||||
thread, because it only monitors file descriptors. So use a
|
thread, because it only monitors file descriptors. So use a
|
||||||
pipe instead. */
|
pipe instead. */
|
||||||
Pipe wakeupPipe;
|
Pipe wakeupPipe;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::thread workerThread;
|
std::thread workerThread;
|
||||||
|
|
||||||
|
@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer
|
||||||
fileTransferSettings.httpConnections.get());
|
fileTransferSettings.httpConnections.get());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
wakeupPipe.create();
|
wakeupPipe.create();
|
||||||
fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
|
fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
|
||||||
|
#endif
|
||||||
|
|
||||||
workerThread = std::thread([&]() { workerThreadEntry(); });
|
workerThread = std::thread([&]() { workerThreadEntry(); });
|
||||||
}
|
}
|
||||||
|
@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->quit = true;
|
state->quit = true;
|
||||||
}
|
}
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
writeFull(wakeupPipe.writeSide.get(), " ", false);
|
writeFull(wakeupPipe.writeSide.get(), " ", false);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void workerThreadMain()
|
void workerThreadMain()
|
||||||
{
|
{
|
||||||
/* Cause this thread to be notified on SIGINT. */
|
/* Cause this thread to be notified on SIGINT. */
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
auto callback = createInterruptCallback([&]() {
|
auto callback = createInterruptCallback([&]() {
|
||||||
stopWorkerThread();
|
stopWorkerThread();
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
unshareFilesystem();
|
unshareFilesystem();
|
||||||
|
@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer
|
||||||
/* Wait for activity, including wakeup events. */
|
/* Wait for activity, including wakeup events. */
|
||||||
int numfds = 0;
|
int numfds = 0;
|
||||||
struct curl_waitfd extraFDs[1];
|
struct curl_waitfd extraFDs[1];
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
extraFDs[0].fd = wakeupPipe.readSide.get();
|
extraFDs[0].fd = wakeupPipe.readSide.get();
|
||||||
extraFDs[0].events = CURL_WAIT_POLLIN;
|
extraFDs[0].events = CURL_WAIT_POLLIN;
|
||||||
extraFDs[0].revents = 0;
|
extraFDs[0].revents = 0;
|
||||||
|
#endif
|
||||||
long maxSleepTimeMs = items.empty() ? 10000 : 100;
|
long maxSleepTimeMs = items.empty() ? 10000 : 100;
|
||||||
auto sleepTimeMs =
|
auto sleepTimeMs =
|
||||||
nextWakeup != std::chrono::steady_clock::time_point()
|
nextWakeup != std::chrono::steady_clock::time_point()
|
||||||
|
@ -693,7 +703,9 @@ struct curlFileTransfer : public FileTransfer
|
||||||
throw nix::Error("cannot enqueue download request because the download thread is shutting down");
|
throw nix::Error("cannot enqueue download request because the download thread is shutting down");
|
||||||
state->incoming.push(item);
|
state->incoming.push(item);
|
||||||
}
|
}
|
||||||
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||||
writeFull(wakeupPipe.writeSide.get(), " ");
|
writeFull(wakeupPipe.writeSide.get(), " ");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_S3
|
#if ENABLE_S3
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <dlfcn.h>
|
||||||
|
# include <sys/utsname.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __GLIBC__
|
#ifdef __GLIBC__
|
||||||
# include <gnu/lib-names.h>
|
# include <gnu/lib-names.h>
|
||||||
# include <nss.h>
|
# include <nss.h>
|
||||||
|
@ -46,7 +49,13 @@ static GlobalConfig::Register rSettings(&settings);
|
||||||
|
|
||||||
Settings::Settings()
|
Settings::Settings()
|
||||||
: nixPrefix(NIX_PREFIX)
|
: nixPrefix(NIX_PREFIX)
|
||||||
, nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
, nixStore(
|
||||||
|
#ifndef _WIN32
|
||||||
|
// On Windows `/nix/store` is not a canonical path, but we dont'
|
||||||
|
// want to deal with that yet.
|
||||||
|
canonPath
|
||||||
|
#endif
|
||||||
|
(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||||
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
||||||
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
||||||
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||||
|
@ -56,7 +65,9 @@ Settings::Settings()
|
||||||
, nixManDir(canonPath(NIX_MAN_DIR))
|
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||||
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
buildUsersGroup = isRootUser() ? "nixbld" : "";
|
buildUsersGroup = isRootUser() ? "nixbld" : "";
|
||||||
|
#endif
|
||||||
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||||
|
|
||||||
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||||
|
@ -239,11 +250,15 @@ StringSet Settings::getDefaultExtraPlatforms()
|
||||||
|
|
||||||
bool Settings::isWSL1()
|
bool Settings::isWSL1()
|
||||||
{
|
{
|
||||||
|
#if __linux__
|
||||||
struct utsname utsbuf;
|
struct utsname utsbuf;
|
||||||
uname(&utsbuf);
|
uname(&utsbuf);
|
||||||
// WSL1 uses -Microsoft suffix
|
// WSL1 uses -Microsoft suffix
|
||||||
// WSL2 uses -microsoft-standard suffix
|
// WSL2 uses -microsoft-standard suffix
|
||||||
return hasSuffix(utsbuf.release, "-Microsoft");
|
return hasSuffix(utsbuf.release, "-Microsoft");
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Path Settings::getDefaultSSLCertFile()
|
Path Settings::getDefaultSSLCertFile()
|
||||||
|
@ -341,6 +356,7 @@ void initPlugins()
|
||||||
for (const auto & file : pluginFiles) {
|
for (const auto & file : pluginFiles) {
|
||||||
/* handle is purposefully leaked as there may be state in the
|
/* handle is purposefully leaked as there may be state in the
|
||||||
DSO needed by the action of the plugin. */
|
DSO needed by the action of the plugin. */
|
||||||
|
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||||
void *handle =
|
void *handle =
|
||||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
|
@ -351,6 +367,9 @@ void initPlugins()
|
||||||
void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry");
|
void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry");
|
||||||
if (nix_plugin_entry)
|
if (nix_plugin_entry)
|
||||||
nix_plugin_entry();
|
nix_plugin_entry();
|
||||||
|
#else
|
||||||
|
throw Error("could not dynamically open plugin file '%s'", file);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -666,6 +666,7 @@ public:
|
||||||
Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
|
Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
|
||||||
"Whether to disable sandboxing when the kernel doesn't allow it."};
|
"Whether to disable sandboxing when the kernel doesn't allow it."};
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
Setting<bool> requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups",
|
Setting<bool> requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups",
|
||||||
R"(
|
R"(
|
||||||
Following the principle of least privilege,
|
Following the principle of least privilege,
|
||||||
|
@ -683,6 +684,7 @@ public:
|
||||||
(since `root` usually has permissions to call setgroups)
|
(since `root` usually has permissions to call setgroups)
|
||||||
and `false` otherwise.
|
and `false` otherwise.
|
||||||
)"};
|
)"};
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
Setting<std::string> sandboxShmSize{
|
Setting<std::string> sandboxShmSize{
|
||||||
|
|
|
@ -4,9 +4,12 @@ libstore_NAME = libnixstore
|
||||||
|
|
||||||
libstore_DIR := $(d)
|
libstore_DIR := $(d)
|
||||||
|
|
||||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
||||||
ifdef HOST_UNIX
|
ifdef HOST_UNIX
|
||||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc)
|
libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc)
|
||||||
|
endif
|
||||||
|
ifdef HOST_WINDOWS
|
||||||
|
libstore_SOURCES += $(wildcard $(d)/windows/*.cc)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libstore_LIBS = libutil
|
libstore_LIBS = libutil
|
||||||
|
@ -36,25 +39,39 @@ INCLUDE_libstore := -I $(d) -I $(d)/build
|
||||||
ifdef HOST_UNIX
|
ifdef HOST_UNIX
|
||||||
INCLUDE_libstore += -I $(d)/unix
|
INCLUDE_libstore += -I $(d)/unix
|
||||||
endif
|
endif
|
||||||
|
ifdef HOST_WINDOWS
|
||||||
|
INCLUDE_libstore += -I $(d)/windows
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef HOST_WINDOWS
|
||||||
|
NIX_ROOT = N:\\\\
|
||||||
|
else
|
||||||
|
NIX_ROOT =
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Prefix all but `NIX_STORE_DIR`, since we aren't doing a local store
|
||||||
|
# yet so a "logical" store dir that is the same as unix is prefered.
|
||||||
|
#
|
||||||
|
# Also, it keeps the unit tests working.
|
||||||
|
|
||||||
libstore_CXXFLAGS += \
|
libstore_CXXFLAGS += \
|
||||||
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
||||||
-DNIX_PREFIX=\"$(prefix)\" \
|
-DNIX_PREFIX=\"$(NIX_ROOT)$(prefix)\" \
|
||||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
-DNIX_DATA_DIR=\"$(NIX_ROOT)$(datadir)\" \
|
||||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
-DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \
|
||||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
-DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \
|
||||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
-DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \
|
||||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
-DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \
|
||||||
-DNIX_MAN_DIR=\"$(mandir)\" \
|
-DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \
|
||||||
-DLSOF=\"$(lsof)\"
|
-DLSOF=\"$(NIX_ROOT)$(lsof)\"
|
||||||
|
|
||||||
ifeq ($(embedded_sandbox_shell),yes)
|
ifeq ($(embedded_sandbox_shell),yes)
|
||||||
libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
|
libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
|
||||||
|
|
||||||
$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh
|
$(d)/unix/build/local-derivation-goal.cc: $(d)/unix/embedded-sandbox-shell.gen.hh
|
||||||
|
|
||||||
$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
$(d)/unix/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||||
$(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
|
$(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
else
|
else
|
||||||
|
@ -63,11 +80,11 @@ else
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||||
|
|
||||||
$(d)/build.cc:
|
$(d)/unix/build.cc:
|
||||||
|
|
||||||
clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||||
|
|
||||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644))
|
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "parsed-derivations.hh"
|
#include "parsed-derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "local-store.hh"
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
|
|
|
@ -63,7 +63,18 @@ StorePath StorePath::random(std::string_view name)
|
||||||
|
|
||||||
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
||||||
{
|
{
|
||||||
auto p = canonPath(std::string(path));
|
// On Windows, `/nix/store` is not a canonical path. More broadly it
|
||||||
|
// is unclear whether this function should be using the native
|
||||||
|
// notion of a canonical path at all. For example, it makes to
|
||||||
|
// support remote stores whose store dir is a non-native path (e.g.
|
||||||
|
// Windows <-> Unix ssh-ing).
|
||||||
|
auto p =
|
||||||
|
#ifdef _WIN32
|
||||||
|
path
|
||||||
|
#else
|
||||||
|
canonPath(std::string(path))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
if (dirOf(p) != storeDir)
|
if (dirOf(p) != storeDir)
|
||||||
throw BadStorePath("path '%s' is not in the Nix store", p);
|
throw BadStorePath("path '%s' is not in the Nix store", p);
|
||||||
return StorePath(baseNameOf(p));
|
return StorePath(baseNameOf(p));
|
||||||
|
|
|
@ -6,69 +6,9 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
|
||||||
{
|
|
||||||
AutoCloseFD fd;
|
|
||||||
|
|
||||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
|
||||||
if (!fd && (create || errno != ENOENT))
|
|
||||||
throw SysError("opening lock file '%1%'", path);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void deleteLockFile(const Path & path, int fd)
|
|
||||||
{
|
|
||||||
/* Get rid of the lock file. Have to be careful not to introduce
|
|
||||||
races. Write a (meaningless) token to the file to indicate to
|
|
||||||
other processes waiting on this lock that the lock is stale
|
|
||||||
(deleted). */
|
|
||||||
unlink(path.c_str());
|
|
||||||
writeFull(fd, "d");
|
|
||||||
/* Note that the result of unlink() is ignored; removing the lock
|
|
||||||
file is an optimisation, not a necessity. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool lockFile(int fd, LockType lockType, bool wait)
|
|
||||||
{
|
|
||||||
int type;
|
|
||||||
if (lockType == ltRead) type = LOCK_SH;
|
|
||||||
else if (lockType == ltWrite) type = LOCK_EX;
|
|
||||||
else if (lockType == ltNone) type = LOCK_UN;
|
|
||||||
else abort();
|
|
||||||
|
|
||||||
if (wait) {
|
|
||||||
while (flock(fd, type) != 0) {
|
|
||||||
checkInterrupt();
|
|
||||||
if (errno != EINTR)
|
|
||||||
throw SysError("acquiring/releasing lock");
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (flock(fd, type | LOCK_NB) != 0) {
|
|
||||||
checkInterrupt();
|
|
||||||
if (errno == EWOULDBLOCK) return false;
|
|
||||||
if (errno != EINTR)
|
|
||||||
throw SysError("acquiring/releasing lock");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PathLocks::PathLocks()
|
PathLocks::PathLocks()
|
||||||
: deletePaths(false)
|
: deletePaths(false)
|
||||||
{
|
{
|
||||||
|
@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PathLocks::lockPaths(const PathSet & paths,
|
|
||||||
const std::string & waitMsg, bool wait)
|
|
||||||
{
|
|
||||||
assert(fds.empty());
|
|
||||||
|
|
||||||
/* Note that `fds' is built incrementally so that the destructor
|
|
||||||
will only release those locks that we have already acquired. */
|
|
||||||
|
|
||||||
/* Acquire the lock for each path in sorted order. This ensures
|
|
||||||
that locks are always acquired in the same order, thus
|
|
||||||
preventing deadlocks. */
|
|
||||||
for (auto & path : paths) {
|
|
||||||
checkInterrupt();
|
|
||||||
Path lockPath = path + ".lock";
|
|
||||||
|
|
||||||
debug("locking path '%1%'", path);
|
|
||||||
|
|
||||||
AutoCloseFD fd;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
/* Open/create the lock file. */
|
|
||||||
fd = openLockFile(lockPath, true);
|
|
||||||
|
|
||||||
/* Acquire an exclusive lock. */
|
|
||||||
if (!lockFile(fd.get(), ltWrite, false)) {
|
|
||||||
if (wait) {
|
|
||||||
if (waitMsg != "") printError(waitMsg);
|
|
||||||
lockFile(fd.get(), ltWrite, true);
|
|
||||||
} else {
|
|
||||||
/* Failed to lock this path; release all other
|
|
||||||
locks. */
|
|
||||||
unlock();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("lock acquired on '%1%'", lockPath);
|
|
||||||
|
|
||||||
/* Check that the lock file hasn't become stale (i.e.,
|
|
||||||
hasn't been unlinked). */
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fd.get(), &st) == -1)
|
|
||||||
throw SysError("statting lock file '%1%'", lockPath);
|
|
||||||
if (st.st_size != 0)
|
|
||||||
/* This lock file has been unlinked, so we're holding
|
|
||||||
a lock on a deleted file. This means that other
|
|
||||||
processes may create and acquire a lock on
|
|
||||||
`lockPath', and proceed. So we must retry. */
|
|
||||||
debug("open lock file '%1%' has become stale", lockPath);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use borrow so that the descriptor isn't closed. */
|
|
||||||
fds.push_back(FDPair(fd.release(), lockPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PathLocks::~PathLocks()
|
PathLocks::~PathLocks()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -154,40 +32,10 @@ PathLocks::~PathLocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PathLocks::unlock()
|
|
||||||
{
|
|
||||||
for (auto & i : fds) {
|
|
||||||
if (deletePaths) deleteLockFile(i.second, i.first);
|
|
||||||
|
|
||||||
if (close(i.first) == -1)
|
|
||||||
printError(
|
|
||||||
"error (ignored): cannot close lock file on '%1%'",
|
|
||||||
i.second);
|
|
||||||
|
|
||||||
debug("lock released on '%1%'", i.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
fds.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PathLocks::setDeletion(bool deletePaths)
|
void PathLocks::setDeletion(bool deletePaths)
|
||||||
{
|
{
|
||||||
this->deletePaths = deletePaths;
|
this->deletePaths = deletePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
|
|
||||||
: fd(fd)
|
|
||||||
{
|
|
||||||
if (wait) {
|
|
||||||
if (!lockFile(fd, lockType, false)) {
|
|
||||||
printInfo("%s", waitMsg);
|
|
||||||
acquired = lockFile(fd, lockType, true);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
acquired = lockFile(fd, lockType, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/**
|
|
||||||
* Open (possibly create) a lock file and return the file descriptor.
|
|
||||||
* -1 is returned if create is false and the lock could not be opened
|
|
||||||
* because it doesn't exist. Any other error throws an exception.
|
|
||||||
*/
|
|
||||||
AutoCloseFD openLockFile(const Path & path, bool create);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an open lock file.
|
|
||||||
*/
|
|
||||||
void deleteLockFile(const Path & path, int fd);
|
|
||||||
|
|
||||||
enum LockType { ltRead, ltWrite, ltNone };
|
|
||||||
|
|
||||||
bool lockFile(int fd, LockType lockType, bool wait);
|
|
||||||
|
|
||||||
class PathLocks
|
class PathLocks
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -40,18 +24,6 @@ public:
|
||||||
void setDeletion(bool deletePaths);
|
void setDeletion(bool deletePaths);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FdLock
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
bool acquired = false;
|
|
||||||
|
|
||||||
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
|
||||||
|
|
||||||
~FdLock()
|
|
||||||
{
|
|
||||||
if (acquired)
|
|
||||||
lockFile(fd, ltNone, false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
#include "pathlocks-impl.hh"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,15 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
|
||||||
auto narAccessor = makeLazyNarAccessor(listing,
|
auto narAccessor = makeLazyNarAccessor(listing,
|
||||||
[cacheFile](uint64_t offset, uint64_t length) {
|
[cacheFile](uint64_t offset, uint64_t length) {
|
||||||
|
|
||||||
AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC);
|
AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening NAR cache file '%s'", cacheFile);
|
throw SysError("opening NAR cache file '%s'", cacheFile);
|
||||||
|
|
||||||
if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset)
|
if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset)
|
||||||
throw SysError("seeking in '%s'", cacheFile);
|
throw SysError("seeking in '%s'", cacheFile);
|
||||||
|
|
||||||
std::string buf(length, 0);
|
std::string buf(length, 0);
|
||||||
|
|
|
@ -55,6 +55,9 @@ bool SSHMaster::isMasterRunning() {
|
||||||
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
||||||
Strings && command, Strings && extraSshArgs)
|
Strings && command, Strings && extraSshArgs)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
|
throw UnimplementedError("cannot yet SSH on windows because spawning processes is not yet implemented");
|
||||||
|
#else
|
||||||
Path socketPath = startMaster();
|
Path socketPath = startMaster();
|
||||||
|
|
||||||
Pipe in, out;
|
Pipe in, out;
|
||||||
|
@ -105,8 +108,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
|
|
||||||
in.readSide = -1;
|
in.readSide = INVALID_DESCRIPTOR;
|
||||||
out.writeSide = -1;
|
out.writeSide = INVALID_DESCRIPTOR;
|
||||||
|
|
||||||
// Wait for the SSH connection to be established,
|
// Wait for the SSH connection to be established,
|
||||||
// So that we don't overwrite the password prompt with our progress bar.
|
// So that we don't overwrite the password prompt with our progress bar.
|
||||||
|
@ -126,15 +129,18 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
||||||
conn->in = std::move(in.writeSide);
|
conn->in = std::move(in.writeSide);
|
||||||
|
|
||||||
return conn;
|
return conn;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
|
|
||||||
Path SSHMaster::startMaster()
|
Path SSHMaster::startMaster()
|
||||||
{
|
{
|
||||||
if (!useMaster) return "";
|
if (!useMaster) return "";
|
||||||
|
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (state->sshMaster != -1) return state->socketPath;
|
if (state->sshMaster != INVALID_DESCRIPTOR) return state->socketPath;
|
||||||
|
|
||||||
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
||||||
|
|
||||||
|
@ -167,7 +173,7 @@ Path SSHMaster::startMaster()
|
||||||
throw SysError("unable to execute '%s'", args.front());
|
throw SysError("unable to execute '%s'", args.front());
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
out.writeSide = -1;
|
out.writeSide = INVALID_DESCRIPTOR;
|
||||||
|
|
||||||
std::string reply;
|
std::string reply;
|
||||||
try {
|
try {
|
||||||
|
@ -182,4 +188,6 @@ Path SSHMaster::startMaster()
|
||||||
return state->socketPath;
|
return state->socketPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ private:
|
||||||
|
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
Pid sshMaster;
|
Pid sshMaster;
|
||||||
|
#endif
|
||||||
std::unique_ptr<AutoDelete> tmpDir;
|
std::unique_ptr<AutoDelete> tmpDir;
|
||||||
Path socketPath;
|
Path socketPath;
|
||||||
};
|
};
|
||||||
|
@ -31,13 +33,19 @@ private:
|
||||||
void addCommonSSHOpts(Strings & args);
|
void addCommonSSHOpts(Strings & args);
|
||||||
bool isMasterRunning();
|
bool isMasterRunning();
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
|
Path startMaster();
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
|
SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
|
||||||
|
|
||||||
struct Connection
|
struct Connection
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||||
Pid sshPid;
|
Pid sshPid;
|
||||||
|
#endif
|
||||||
AutoCloseFD out, in;
|
AutoCloseFD out, in;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,8 +59,6 @@ public:
|
||||||
std::unique_ptr<Connection> startCommand(
|
std::unique_ptr<Connection> startCommand(
|
||||||
Strings && command,
|
Strings && command,
|
||||||
Strings && extraSshArgs = {});
|
Strings && extraSshArgs = {});
|
||||||
|
|
||||||
Path startMaster();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "git.hh"
|
#include "git.hh"
|
||||||
#include "remote-store.hh"
|
|
||||||
#include "posix-source-accessor.hh"
|
#include "posix-source-accessor.hh"
|
||||||
// FIXME this should not be here, see TODO below on
|
// FIXME this should not be here, see TODO below on
|
||||||
// `addMultipleToStore`.
|
// `addMultipleToStore`.
|
||||||
|
@ -21,6 +20,10 @@
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "users.hh"
|
#include "users.hh"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include "remote-store.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
@ -1266,9 +1269,10 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include "local-store.hh"
|
# include "local-store.hh"
|
||||||
# include "uds-remote-store.hh"
|
# include "uds-remote-store.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -1286,6 +1290,9 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
|
||||||
return {uri, params};
|
return {uri, params};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32 // Unused on Windows because the next `#ifndef`
|
||||||
|
[[maybe_unused]]
|
||||||
|
#endif
|
||||||
static bool isNonUriPath(const std::string & spec)
|
static bool isNonUriPath(const std::string & spec)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
@ -1298,6 +1305,9 @@ static bool isNonUriPath(const std::string & spec)
|
||||||
|
|
||||||
std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
|
std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
|
||||||
{
|
{
|
||||||
|
// TODO reenable on Windows once we have `LocalStore` and
|
||||||
|
// `UDSRemoteStore`.
|
||||||
|
#ifndef _WIN32
|
||||||
if (uri == "" || uri == "auto") {
|
if (uri == "" || uri == "auto") {
|
||||||
auto stateDir = getOr(params, "state", settings.nixStateDir);
|
auto stateDir = getOr(params, "state", settings.nixStateDir);
|
||||||
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
|
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
|
||||||
|
@ -1342,6 +1352,9 @@ std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Para
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `parseURL` function supports both IPv6 URIs as defined in
|
// The `parseURL` function supports both IPv6 URIs as defined in
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
static std::string gcSocketPath = "/gc-socket/socket";
|
static std::string gcSocketPath = "/gc-socket/socket";
|
||||||
static std::string gcRootsDir = "gcroots";
|
static std::string gcRootsDir = "gcroots";
|
|
@ -52,6 +52,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
std::string LocalStoreConfig::doc()
|
std::string LocalStoreConfig::doc()
|
||||||
{
|
{
|
||||||
return
|
return
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
||||||
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
38
src/libstore/unix/pathlocks-impl.hh
Normal file
38
src/libstore/unix/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "file-descriptor.hh"
|
||||||
|
|
||||||
|
namespace nix::unix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open (possibly create) a lock file and return the file descriptor.
|
||||||
|
* -1 is returned if create is false and the lock could not be opened
|
||||||
|
* because it doesn't exist. Any other error throws an exception.
|
||||||
|
*/
|
||||||
|
AutoCloseFD openLockFile(const Path & path, bool create);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an open lock file.
|
||||||
|
*/
|
||||||
|
void deleteLockFile(const Path & path, int fd);
|
||||||
|
|
||||||
|
enum LockType { ltRead, ltWrite, ltNone };
|
||||||
|
|
||||||
|
bool lockFile(int fd, LockType lockType, bool wait);
|
||||||
|
|
||||||
|
struct FdLock
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool acquired = false;
|
||||||
|
|
||||||
|
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
||||||
|
|
||||||
|
~FdLock()
|
||||||
|
{
|
||||||
|
if (acquired)
|
||||||
|
lockFile(fd, ltNone, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
165
src/libstore/unix/pathlocks.cc
Normal file
165
src/libstore/unix/pathlocks.cc
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::unix;
|
||||||
|
|
||||||
|
AutoCloseFD unix::openLockFile(const Path & path, bool create)
|
||||||
|
{
|
||||||
|
AutoCloseFD fd;
|
||||||
|
|
||||||
|
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||||
|
if (!fd && (create || errno != ENOENT))
|
||||||
|
throw SysError("opening lock file '%1%'", path);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void unix::deleteLockFile(const Path & path, int fd)
|
||||||
|
{
|
||||||
|
/* Get rid of the lock file. Have to be careful not to introduce
|
||||||
|
races. Write a (meaningless) token to the file to indicate to
|
||||||
|
other processes waiting on this lock that the lock is stale
|
||||||
|
(deleted). */
|
||||||
|
unlink(path.c_str());
|
||||||
|
writeFull(fd, "d");
|
||||||
|
/* Note that the result of unlink() is ignored; removing the lock
|
||||||
|
file is an optimisation, not a necessity. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool unix::lockFile(int fd, LockType lockType, bool wait)
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
if (lockType == ltRead) type = LOCK_SH;
|
||||||
|
else if (lockType == ltWrite) type = LOCK_EX;
|
||||||
|
else if (lockType == ltNone) type = LOCK_UN;
|
||||||
|
else abort();
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
while (flock(fd, type) != 0) {
|
||||||
|
checkInterrupt();
|
||||||
|
if (errno != EINTR)
|
||||||
|
throw SysError("acquiring/releasing lock");
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (flock(fd, type | LOCK_NB) != 0) {
|
||||||
|
checkInterrupt();
|
||||||
|
if (errno == EWOULDBLOCK) return false;
|
||||||
|
if (errno != EINTR)
|
||||||
|
throw SysError("acquiring/releasing lock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PathLocks::lockPaths(const PathSet & paths,
|
||||||
|
const std::string & waitMsg, bool wait)
|
||||||
|
{
|
||||||
|
assert(fds.empty());
|
||||||
|
|
||||||
|
/* Note that `fds' is built incrementally so that the destructor
|
||||||
|
will only release those locks that we have already acquired. */
|
||||||
|
|
||||||
|
/* Acquire the lock for each path in sorted order. This ensures
|
||||||
|
that locks are always acquired in the same order, thus
|
||||||
|
preventing deadlocks. */
|
||||||
|
for (auto & path : paths) {
|
||||||
|
checkInterrupt();
|
||||||
|
Path lockPath = path + ".lock";
|
||||||
|
|
||||||
|
debug("locking path '%1%'", path);
|
||||||
|
|
||||||
|
AutoCloseFD fd;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* Open/create the lock file. */
|
||||||
|
fd = openLockFile(lockPath, true);
|
||||||
|
|
||||||
|
/* Acquire an exclusive lock. */
|
||||||
|
if (!lockFile(fd.get(), ltWrite, false)) {
|
||||||
|
if (wait) {
|
||||||
|
if (waitMsg != "") printError(waitMsg);
|
||||||
|
lockFile(fd.get(), ltWrite, true);
|
||||||
|
} else {
|
||||||
|
/* Failed to lock this path; release all other
|
||||||
|
locks. */
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("lock acquired on '%1%'", lockPath);
|
||||||
|
|
||||||
|
/* Check that the lock file hasn't become stale (i.e.,
|
||||||
|
hasn't been unlinked). */
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd.get(), &st) == -1)
|
||||||
|
throw SysError("statting lock file '%1%'", lockPath);
|
||||||
|
if (st.st_size != 0)
|
||||||
|
/* This lock file has been unlinked, so we're holding
|
||||||
|
a lock on a deleted file. This means that other
|
||||||
|
processes may create and acquire a lock on
|
||||||
|
`lockPath', and proceed. So we must retry. */
|
||||||
|
debug("open lock file '%1%' has become stale", lockPath);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use borrow so that the descriptor isn't closed. */
|
||||||
|
fds.push_back(FDPair(fd.release(), lockPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PathLocks::unlock()
|
||||||
|
{
|
||||||
|
for (auto & i : fds) {
|
||||||
|
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||||
|
|
||||||
|
if (close(i.first) == -1)
|
||||||
|
printError(
|
||||||
|
"error (ignored): cannot close lock file on '%1%'",
|
||||||
|
i.second);
|
||||||
|
|
||||||
|
debug("lock released on '%1%'", i.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
fds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
|
||||||
|
: fd(fd)
|
||||||
|
{
|
||||||
|
if (wait) {
|
||||||
|
if (!lockFile(fd, lockType, false)) {
|
||||||
|
printInfo("%s", waitMsg);
|
||||||
|
acquired = lockFile(fd, lockType, true);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
acquired = lockFile(fd, lockType, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
37
src/libstore/windows/build.cc
Normal file
37
src/libstore/windows/build.cc
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
unsupported("buildPaths");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<KeyedBuildResult> Store::buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & reqs,
|
||||||
|
BuildMode buildMode,
|
||||||
|
std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
unsupported("buildPathsWithResults");
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
BuildMode buildMode)
|
||||||
|
{
|
||||||
|
unsupported("buildDerivation");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Store::ensurePath(const StorePath & path)
|
||||||
|
{
|
||||||
|
unsupported("ensurePath");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Store::repairPath(const StorePath & path)
|
||||||
|
{
|
||||||
|
unsupported("repairPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
src/libstore/windows/pathlocks-impl.hh
Normal file
2
src/libstore/windows/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#pragma once
|
||||||
|
///@file Needed because Unix-specific counterpart
|
16
src/libstore/windows/pathlocks.cc
Normal file
16
src/libstore/windows/pathlocks.cc
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "logging.hh"
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathLocks::unlock()
|
||||||
|
{
|
||||||
|
warn("PathLocks::unlock: not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,7 +9,9 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#ifndef _WIN32
|
||||||
# include <glob.h>
|
# include <glob.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -547,6 +549,7 @@ nlohmann::json Args::toJSON()
|
||||||
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
|
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
|
||||||
{
|
{
|
||||||
completions.setType(Completions::Type::Filenames);
|
completions.setType(Completions::Type::Filenames);
|
||||||
|
#ifndef _WIN32 // TODO implement globbing completions on Windows
|
||||||
glob_t globbuf;
|
glob_t globbuf;
|
||||||
int flags = GLOB_NOESCAPE;
|
int flags = GLOB_NOESCAPE;
|
||||||
#ifdef GLOB_ONLYDIR
|
#ifdef GLOB_ONLYDIR
|
||||||
|
@ -564,6 +567,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
globfree(&globbuf);
|
globfree(&globbuf);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)
|
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
# include "namespaces.hh"
|
# include "namespaces.hh"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include <sys/mount.h>
|
# include <sys/mount.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ unsigned int getMaxCPU()
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
rlim_t savedStackSize = 0;
|
rlim_t savedStackSize = 0;
|
||||||
|
|
||||||
void setStackSize(rlim_t stackSize)
|
void setStackSize(rlim_t stackSize)
|
||||||
|
@ -79,16 +82,20 @@ void setStackSize(rlim_t stackSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void restoreProcessContext(bool restoreMounts)
|
void restoreProcessContext(bool restoreMounts)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
unix::restoreSignals();
|
unix::restoreSignals();
|
||||||
|
#endif
|
||||||
if (restoreMounts) {
|
if (restoreMounts) {
|
||||||
#if __linux__
|
#if __linux__
|
||||||
restoreMountNamespace();
|
restoreMountNamespace();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
if (savedStackSize) {
|
if (savedStackSize) {
|
||||||
struct rlimit limit;
|
struct rlimit limit;
|
||||||
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
|
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
|
||||||
|
@ -96,6 +103,7 @@ void restoreProcessContext(bool restoreMounts)
|
||||||
setrlimit(RLIMIT_STACK, &limit);
|
setrlimit(RLIMIT_STACK, &limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include <sys/resource.h>
|
# include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
|
@ -14,16 +17,18 @@ namespace nix {
|
||||||
*/
|
*/
|
||||||
unsigned int getMaxCPU();
|
unsigned int getMaxCPU();
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO implement on Windows, if needed.
|
||||||
/**
|
/**
|
||||||
* Change the stack size.
|
* Change the stack size.
|
||||||
*/
|
*/
|
||||||
void setStackSize(rlim_t stackSize);
|
void setStackSize(rlim_t stackSize);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore the original inherited Unix process context (such as signal
|
* Restore the original inherited Unix process context (such as signal
|
||||||
* masks, stack size).
|
* masks, stack size).
|
||||||
|
|
||||||
* See startSignalHandlerThread(), saveSignalMask().
|
* See unix::startSignalHandlerThread(), unix::saveSignalMask().
|
||||||
*/
|
*/
|
||||||
void restoreProcessContext(bool restoreMounts = true);
|
void restoreProcessContext(bool restoreMounts = true);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,13 @@ std::optional<std::string> getEnvNonEmpty(const std::string & key);
|
||||||
*/
|
*/
|
||||||
std::map<std::string, std::string> getEnv();
|
std::map<std::string, std::string> getEnv();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/**
|
||||||
|
* Implementation of missing POSIX function.
|
||||||
|
*/
|
||||||
|
int unsetenv(const char * name);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like POSIX `setenv`, but always overrides.
|
* Like POSIX `setenv`, but always overrides.
|
||||||
*
|
*
|
||||||
|
|
|
@ -247,6 +247,23 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
class WinError;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience alias for when we use a `errno`-based error handling
|
||||||
|
* function on Unix, and `GetLastError()`-based error handling on on
|
||||||
|
* Windows.
|
||||||
|
*/
|
||||||
|
using NativeSysError =
|
||||||
|
#ifdef _WIN32
|
||||||
|
WinError
|
||||||
|
#else
|
||||||
|
SysError
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw an exception for the purpose of checking that exception
|
* Throw an exception for the purpose of checking that exception
|
||||||
* handling works; see 'initLibUtil()'.
|
* handling works; see 'initLibUtil()'.
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <winnt.h>
|
||||||
|
# include <fileapi.h>
|
||||||
|
# include "windows-error.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -20,7 +25,13 @@ std::string drainFD(Descriptor fd, bool block, const size_t reserveSize)
|
||||||
// the parser needs two extra bytes to append terminating characters, other users will
|
// the parser needs two extra bytes to append terminating characters, other users will
|
||||||
// not care very much about the extra memory.
|
// not care very much about the extra memory.
|
||||||
StringSink sink(reserveSize + 2);
|
StringSink sink(reserveSize + 2);
|
||||||
|
#ifdef _WIN32
|
||||||
|
// non-blocking is not supported this way on Windows
|
||||||
|
assert(block);
|
||||||
|
drainFD(fd, sink);
|
||||||
|
#else
|
||||||
drainFD(fd, sink, block);
|
drainFD(fd, sink, block);
|
||||||
|
#endif
|
||||||
return std::move(sink.s);
|
return std::move(sink.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +79,15 @@ Descriptor AutoCloseFD::get() const
|
||||||
void AutoCloseFD::close()
|
void AutoCloseFD::close()
|
||||||
{
|
{
|
||||||
if (fd != INVALID_DESCRIPTOR) {
|
if (fd != INVALID_DESCRIPTOR) {
|
||||||
if(::close(fd) == -1)
|
if(
|
||||||
|
#ifdef _WIN32
|
||||||
|
::CloseHandle(fd)
|
||||||
|
#else
|
||||||
|
::close(fd)
|
||||||
|
#endif
|
||||||
|
== -1)
|
||||||
/* This should never happen. */
|
/* This should never happen. */
|
||||||
throw SysError("closing file descriptor %1%", fd);
|
throw NativeSysError("closing file descriptor %1%", fd);
|
||||||
fd = INVALID_DESCRIPTOR;
|
fd = INVALID_DESCRIPTOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,14 +97,16 @@ void AutoCloseFD::fsync()
|
||||||
if (fd != INVALID_DESCRIPTOR) {
|
if (fd != INVALID_DESCRIPTOR) {
|
||||||
int result;
|
int result;
|
||||||
result =
|
result =
|
||||||
#if __APPLE__
|
#ifdef _WIN32
|
||||||
|
::FlushFileBuffers(fd)
|
||||||
|
#elif __APPLE__
|
||||||
::fcntl(fd, F_FULLFSYNC)
|
::fcntl(fd, F_FULLFSYNC)
|
||||||
#else
|
#else
|
||||||
::fsync(fd)
|
::fsync(fd)
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
throw SysError("fsync file descriptor %1%", fd);
|
throw NativeSysError("fsync file descriptor %1%", fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct Sink;
|
struct Sink;
|
||||||
|
@ -12,9 +17,21 @@ struct Source;
|
||||||
/**
|
/**
|
||||||
* Operating System capability
|
* Operating System capability
|
||||||
*/
|
*/
|
||||||
typedef int Descriptor;
|
typedef
|
||||||
|
#if _WIN32
|
||||||
|
HANDLE
|
||||||
|
#else
|
||||||
|
int
|
||||||
|
#endif
|
||||||
|
Descriptor;
|
||||||
|
|
||||||
const Descriptor INVALID_DESCRIPTOR = -1;
|
const Descriptor INVALID_DESCRIPTOR =
|
||||||
|
#if _WIN32
|
||||||
|
INVALID_HANDLE_VALUE
|
||||||
|
#else
|
||||||
|
-1
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a native `Descriptor` to a POSIX file descriptor
|
* Convert a native `Descriptor` to a POSIX file descriptor
|
||||||
|
@ -23,17 +40,26 @@ const Descriptor INVALID_DESCRIPTOR = -1;
|
||||||
*/
|
*/
|
||||||
static inline Descriptor toDescriptor(int fd)
|
static inline Descriptor toDescriptor(int fd)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (HANDLE) _get_osfhandle(fd);
|
||||||
|
#else
|
||||||
return fd;
|
return fd;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a POSIX file descriptor to a native `Descriptor`
|
* Convert a POSIX file descriptor to a native `Descriptor` in read-only
|
||||||
|
* mode.
|
||||||
*
|
*
|
||||||
* This is a no-op except on Windows.
|
* This is a no-op except on Windows.
|
||||||
*/
|
*/
|
||||||
static inline int fromDescriptor(Descriptor fd, int flags)
|
static inline int fromDescriptorReadOnly(Descriptor fd)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _open_osfhandle((intptr_t) fd, _O_RDONLY);
|
||||||
|
#else
|
||||||
return fd;
|
return fd;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,11 +90,24 @@ void writeLine(Descriptor fd, std::string s);
|
||||||
*/
|
*/
|
||||||
std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0);
|
std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0);
|
||||||
|
|
||||||
void drainFD(Descriptor fd, Sink & sink, bool block = true);
|
/**
|
||||||
|
* The Windows version is always blocking.
|
||||||
|
*/
|
||||||
|
void drainFD(
|
||||||
|
Descriptor fd
|
||||||
|
, Sink & sink
|
||||||
|
#ifndef _WIN32
|
||||||
|
, bool block = true
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline Descriptor getStandardOut() {
|
inline Descriptor getStandardOut() {
|
||||||
|
#ifndef _WIN32
|
||||||
return STDOUT_FILENO;
|
return STDOUT_FILENO;
|
||||||
|
#else
|
||||||
|
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,6 +139,8 @@ public:
|
||||||
void close();
|
void close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef _WIN32 // Not needed on Windows, where we don't fork
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close all file descriptors except those listed in the given set.
|
* Close all file descriptors except those listed in the given set.
|
||||||
* Good practice in child processes.
|
* Good practice in child processes.
|
||||||
|
@ -111,6 +152,15 @@ void closeMostFDs(const std::set<Descriptor> & exceptions);
|
||||||
*/
|
*/
|
||||||
void closeOnExec(Descriptor fd);
|
void closeOnExec(Descriptor fd);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# if _WIN32_WINNT >= 0x0600
|
||||||
|
Path handleToPath(Descriptor handle);
|
||||||
|
std::wstring handleToFileName(Descriptor handle);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
MakeError(EndOfFile, Error);
|
MakeError(EndOfFile, Error);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
52
src/libutil/file-path.hh
Normal file
52
src/libutil/file-path.hh
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paths are just `std::filesystem::path`s.
|
||||||
|
*
|
||||||
|
* @todo drop `NG` suffix and replace the ones in `types.hh`.
|
||||||
|
*/
|
||||||
|
typedef std::filesystem::path PathNG;
|
||||||
|
typedef std::list<Path> PathsNG;
|
||||||
|
typedef std::set<Path> PathSetNG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop gap until `std::filesystem::path_view` from P1030R6 exists in a
|
||||||
|
* future C++ standard.
|
||||||
|
*
|
||||||
|
* @todo drop `NG` suffix and replace the one in `types.hh`.
|
||||||
|
*/
|
||||||
|
struct PathViewNG : std::basic_string_view<PathNG::value_type>
|
||||||
|
{
|
||||||
|
using string_view = std::basic_string_view<PathNG::value_type>;
|
||||||
|
|
||||||
|
using string_view::string_view;
|
||||||
|
|
||||||
|
PathViewNG(const PathNG & path)
|
||||||
|
: std::basic_string_view<PathNG::value_type>(path.native())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
PathViewNG(const PathNG::string_type & path)
|
||||||
|
: std::basic_string_view<PathNG::value_type>(path)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
const string_view & native() const { return *this; }
|
||||||
|
string_view & native() { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string os_string_to_string(PathViewNG::string_view path);
|
||||||
|
|
||||||
|
PathNG::string_type string_to_os_string(std::string_view s);
|
||||||
|
|
||||||
|
std::optional<PathNG> maybePathNG(PathView path);
|
||||||
|
|
||||||
|
PathNG pathNG(PathView path);
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include "environment-variables.hh"
|
#include "environment-variables.hh"
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
|
#include "file-path.hh"
|
||||||
#include "file-path-impl.hh"
|
#include "file-path-impl.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
@ -18,6 +19,10 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -128,10 +133,10 @@ std::string_view baseNameOf(std::string_view path)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
auto last = path.size() - 1;
|
auto last = path.size() - 1;
|
||||||
while (last > 0 && path[last] == '/')
|
while (last > 0 && NativePathTrait::isPathSep(path[last]))
|
||||||
last -= 1;
|
last -= 1;
|
||||||
|
|
||||||
auto pos = path.rfind('/', last);
|
auto pos = NativePathTrait::rfindPathSep(path, last);
|
||||||
if (pos == path.npos)
|
if (pos == path.npos)
|
||||||
pos = 0;
|
pos = 0;
|
||||||
else
|
else
|
||||||
|
@ -164,11 +169,16 @@ struct stat stat(const Path & path)
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define STAT stat
|
||||||
|
#else
|
||||||
|
# define STAT lstat
|
||||||
|
#endif
|
||||||
|
|
||||||
struct stat lstat(const Path & path)
|
struct stat lstat(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (STAT(path.c_str(), &st))
|
||||||
throw SysError("getting status of '%1%'", path);
|
throw SysError("getting status of '%1%'", path);
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
@ -177,7 +187,7 @@ struct stat lstat(const Path & path)
|
||||||
std::optional<struct stat> maybeLstat(const Path & path)
|
std::optional<struct stat> maybeLstat(const Path & path)
|
||||||
{
|
{
|
||||||
std::optional<struct stat> st{std::in_place};
|
std::optional<struct stat> st{std::in_place};
|
||||||
if (lstat(path.c_str(), &*st))
|
if (STAT(path.c_str(), &*st))
|
||||||
{
|
{
|
||||||
if (errno == ENOENT || errno == ENOTDIR)
|
if (errno == ENOENT || errno == ENOTDIR)
|
||||||
st.reset();
|
st.reset();
|
||||||
|
@ -207,6 +217,7 @@ bool pathAccessible(const Path & path)
|
||||||
|
|
||||||
Path readLink(const Path & path)
|
Path readLink(const Path & path)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
std::vector<char> buf;
|
std::vector<char> buf;
|
||||||
for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) {
|
for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) {
|
||||||
|
@ -220,13 +231,16 @@ Path readLink(const Path & path)
|
||||||
else if (rlSize < bufSize)
|
else if (rlSize < bufSize)
|
||||||
return std::string(buf.data(), rlSize);
|
return std::string(buf.data(), rlSize);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
// TODO modern Windows does in fact support symlinks
|
||||||
|
throw UnimplementedError("reading symbolic link '%1%'", path);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isLink(const Path & path)
|
bool isLink(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st = lstat(path);
|
return getFileType(path) == DT_LNK;
|
||||||
return S_ISLNK(st.st_mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -274,7 +288,12 @@ unsigned char getFileType(const Path & path)
|
||||||
|
|
||||||
std::string readFile(const Path & path)
|
std::string readFile(const Path & path)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||||
|
// TODO
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", path);
|
throw SysError("opening file '%1%'", path);
|
||||||
return readFile(fd.get());
|
return readFile(fd.get());
|
||||||
|
@ -283,7 +302,12 @@ std::string readFile(const Path & path)
|
||||||
|
|
||||||
void readFile(const Path & path, Sink & sink)
|
void readFile(const Path & path, Sink & sink)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||||
|
// TODO
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%s'", path);
|
throw SysError("opening file '%s'", path);
|
||||||
drainFD(fd.get(), sink);
|
drainFD(fd.get(), sink);
|
||||||
|
@ -292,7 +316,12 @@ void readFile(const Path & path, Sink & sink)
|
||||||
|
|
||||||
void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
|
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||||
|
// TODO
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
, mode));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", path);
|
throw SysError("opening file '%1%'", path);
|
||||||
try {
|
try {
|
||||||
|
@ -312,7 +341,12 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
||||||
|
|
||||||
void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
|
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||||
|
// TODO
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
, mode));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", path);
|
throw SysError("opening file '%1%'", path);
|
||||||
|
|
||||||
|
@ -339,21 +373,23 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
||||||
|
|
||||||
void syncParent(const Path & path)
|
void syncParent(const Path & path)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0);
|
AutoCloseFD fd = toDescriptor(open(dirOf(path).c_str(), O_RDONLY, 0));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", path);
|
throw SysError("opening file '%1%'", path);
|
||||||
fd.fsync();
|
fd.fsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
|
static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
std::string name(baseNameOf(path));
|
std::string name(baseNameOf(path));
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
if (fstatat(parentfd, name.c_str(), &st,
|
||||||
|
AT_SYMLINK_NOFOLLOW) == -1) {
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError("getting status of '%1%'", path);
|
throw SysError("getting status of '%1%'", path);
|
||||||
}
|
}
|
||||||
|
@ -405,6 +441,10 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError("cannot unlink '%1%'", path);
|
throw SysError("cannot unlink '%1%'", path);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
// TODO implement
|
||||||
|
throw UnimplementedError("_deletePath");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _deletePath(const Path & path, uint64_t & bytesFreed)
|
static void _deletePath(const Path & path, uint64_t & bytesFreed)
|
||||||
|
@ -413,7 +453,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed)
|
||||||
if (dir == "")
|
if (dir == "")
|
||||||
dir = "/";
|
dir = "/";
|
||||||
|
|
||||||
AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)};
|
AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY));
|
||||||
if (!dirfd) {
|
if (!dirfd) {
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError("opening directory '%1%'", path);
|
throw SysError("opening directory '%1%'", path);
|
||||||
|
@ -436,11 +476,15 @@ Paths createDirs(const Path & path)
|
||||||
if (path == "/") return created;
|
if (path == "/") return created;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st) == -1) {
|
if (STAT(path.c_str(), &st) == -1) {
|
||||||
created = createDirs(dirOf(path));
|
created = createDirs(dirOf(path));
|
||||||
if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
|
if (mkdir(path.c_str()
|
||||||
|
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||||
|
, 0777
|
||||||
|
#endif
|
||||||
|
) == -1 && errno != EEXIST)
|
||||||
throw SysError("creating directory '%1%'", path);
|
throw SysError("creating directory '%1%'", path);
|
||||||
st = lstat(path);
|
st = STAT(path);
|
||||||
created.push_back(path);
|
created.push_back(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +570,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
|
||||||
while (1) {
|
while (1) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
|
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
|
||||||
if (mkdir(tmpDir.c_str(), mode) == 0) {
|
if (mkdir(tmpDir.c_str()
|
||||||
|
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||||
|
, mode
|
||||||
|
#endif
|
||||||
|
) == 0) {
|
||||||
#if __FreeBSD__
|
#if __FreeBSD__
|
||||||
/* Explicitly set the group of the directory. This is to
|
/* Explicitly set the group of the directory. This is to
|
||||||
work around around problems caused by BSD's group
|
work around around problems caused by BSD's group
|
||||||
|
@ -552,17 +600,24 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
||||||
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
|
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
|
||||||
// Strictly speaking, this is UB, but who cares...
|
// Strictly speaking, this is UB, but who cares...
|
||||||
// FIXME: use O_TMPFILE.
|
// FIXME: use O_TMPFILE.
|
||||||
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
|
AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str()));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("creating temporary file '%s'", tmpl);
|
throw SysError("creating temporary file '%s'", tmpl);
|
||||||
|
#ifndef _WIN32
|
||||||
closeOnExec(fd.get());
|
closeOnExec(fd.get());
|
||||||
|
#endif
|
||||||
return {std::move(fd), tmpl};
|
return {std::move(fd), tmpl};
|
||||||
}
|
}
|
||||||
|
|
||||||
void createSymlink(const Path & target, const Path & link)
|
void createSymlink(const Path & target, const Path & link)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
if (symlink(target.c_str(), link.c_str()))
|
if (symlink(target.c_str(), link.c_str()))
|
||||||
throw SysError("creating symlink from '%1%' to '%2%'", link, target);
|
throw SysError("creating symlink from '%1%' to '%2%'", link, target);
|
||||||
|
#else
|
||||||
|
// TODO modern Windows does in fact support symlinks
|
||||||
|
throw UnimplementedError("createSymlink");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void replaceSymlink(const Path & target, const Path & link)
|
void replaceSymlink(const Path & target, const Path & link)
|
||||||
|
@ -583,7 +638,8 @@ void replaceSymlink(const Path & target, const Path & link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWriteTime(const fs::path & p, const struct stat & st)
|
#ifndef _WIN32
|
||||||
|
static void setWriteTime(const fs::path & p, const struct stat & st)
|
||||||
{
|
{
|
||||||
struct timeval times[2];
|
struct timeval times[2];
|
||||||
times[0] = {
|
times[0] = {
|
||||||
|
@ -597,11 +653,14 @@ void setWriteTime(const fs::path & p, const struct stat & st)
|
||||||
if (lutimes(p.c_str(), times) != 0)
|
if (lutimes(p.c_str(), times) != 0)
|
||||||
throw SysError("changing modification time of '%s'", p);
|
throw SysError("changing modification time of '%s'", p);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
|
void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
// TODO: Rewrite the `is_*` to use `symlink_status()`
|
// TODO: Rewrite the `is_*` to use `symlink_status()`
|
||||||
auto statOfFrom = lstat(from.path().c_str());
|
auto statOfFrom = lstat(from.path().c_str());
|
||||||
|
#endif
|
||||||
auto fromStatus = from.symlink_status();
|
auto fromStatus = from.symlink_status();
|
||||||
|
|
||||||
// Mark the directory as writable so that we can delete its children
|
// Mark the directory as writable so that we can delete its children
|
||||||
|
@ -621,7 +680,9 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
|
||||||
throw Error("file '%s' has an unsupported type", from.path());
|
throw Error("file '%s' has an unsupported type", from.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
setWriteTime(to, statOfFrom);
|
setWriteTime(to, statOfFrom);
|
||||||
|
#endif
|
||||||
if (andDelete) {
|
if (andDelete) {
|
||||||
if (!fs::is_symlink(fromStatus))
|
if (!fs::is_symlink(fromStatus))
|
||||||
fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
|
fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
|
||||||
|
@ -648,14 +709,18 @@ void moveFile(const Path & oldName, const Path & newName)
|
||||||
auto newPath = fs::path(newName);
|
auto newPath = fs::path(newName);
|
||||||
// For the move to be as atomic as possible, copy to a temporary
|
// For the move to be as atomic as possible, copy to a temporary
|
||||||
// directory
|
// directory
|
||||||
fs::path temp = createTempDir(newPath.parent_path(), "rename-tmp");
|
fs::path temp = createTempDir(
|
||||||
|
os_string_to_string(PathViewNG { newPath.parent_path() }),
|
||||||
|
"rename-tmp");
|
||||||
Finally removeTemp = [&]() { fs::remove(temp); };
|
Finally removeTemp = [&]() { fs::remove(temp); };
|
||||||
auto tempCopyTarget = temp / "copy-target";
|
auto tempCopyTarget = temp / "copy-target";
|
||||||
if (e.code().value() == EXDEV) {
|
if (e.code().value() == EXDEV) {
|
||||||
fs::remove(newPath);
|
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);
|
||||||
copy(fs::directory_entry(oldPath), tempCopyTarget, true);
|
copy(fs::directory_entry(oldPath), tempCopyTarget, true);
|
||||||
renameFile(tempCopyTarget, newPath);
|
renameFile(
|
||||||
|
os_string_to_string(PathViewNG { tempCopyTarget }),
|
||||||
|
os_string_to_string(PathViewNG { newPath }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windef.h>
|
||||||
|
#endif
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
@ -31,6 +34,17 @@
|
||||||
#define DT_DIR 3
|
#define DT_DIR 3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyfill for MinGW
|
||||||
|
*
|
||||||
|
* Windows does in fact support symlinks, but the C runtime interfaces predate this.
|
||||||
|
*
|
||||||
|
* @todo get rid of this, and stop using `stat` when we want `lstat` too.
|
||||||
|
*/
|
||||||
|
#ifndef S_ISLNK
|
||||||
|
# define S_ISLNK(m) false
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct Sink;
|
struct Sink;
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "error.hh"
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
#include "fs-sink.hh"
|
#include "fs-sink.hh"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
# include <fileapi.h>
|
||||||
|
# include "file-path.hh"
|
||||||
|
# include "windows-error.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void copyRecursive(
|
void copyRecursive(
|
||||||
|
@ -65,8 +72,14 @@ static GlobalConfig::Register r1(&restoreSinkSettings);
|
||||||
void RestoreSink::createDirectory(const Path & path)
|
void RestoreSink::createDirectory(const Path & path)
|
||||||
{
|
{
|
||||||
Path p = dstPath + path;
|
Path p = dstPath + path;
|
||||||
if (mkdir(p.c_str(), 0777) == -1)
|
if (
|
||||||
throw SysError("creating directory '%1%'", p);
|
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||||
|
mkdir(p.c_str(), 0777) == -1
|
||||||
|
#else
|
||||||
|
!CreateDirectoryW(pathNG(p).c_str(), NULL)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
throw NativeSysError("creating directory '%1%'", p);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RestoreRegularFile : CreateRegularFileSink {
|
struct RestoreRegularFile : CreateRegularFileSink {
|
||||||
|
@ -81,18 +94,28 @@ void RestoreSink::createRegularFile(const Path & path, std::function<void(Create
|
||||||
{
|
{
|
||||||
Path p = dstPath + path;
|
Path p = dstPath + path;
|
||||||
RestoreRegularFile crf;
|
RestoreRegularFile crf;
|
||||||
crf.fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
|
crf.fd =
|
||||||
if (!crf.fd) throw SysError("creating file '%1%'", p);
|
#ifdef _WIN32
|
||||||
|
CreateFileW(pathNG(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||||
|
#else
|
||||||
|
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if (!crf.fd) throw NativeSysError("creating file '%1%'", p);
|
||||||
func(crf);
|
func(crf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreRegularFile::isExecutable()
|
void RestoreRegularFile::isExecutable()
|
||||||
{
|
{
|
||||||
|
// Windows doesn't have a notion of executable file permissions we
|
||||||
|
// care about here, right?
|
||||||
|
#ifndef _WIN32
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd.get(), &st) == -1)
|
if (fstat(fd.get(), &st) == -1)
|
||||||
throw SysError("fstat");
|
throw SysError("fstat");
|
||||||
if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
||||||
throw SysError("fchmod");
|
throw SysError("fchmod");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreRegularFile::preallocateContents(uint64_t len)
|
void RestoreRegularFile::preallocateContents(uint64_t len)
|
||||||
|
|
|
@ -11,6 +11,9 @@ endif
|
||||||
ifdef HOST_LINUX
|
ifdef HOST_LINUX
|
||||||
libutil_SOURCES += $(wildcard $(d)/linux/*.cc)
|
libutil_SOURCES += $(wildcard $(d)/linux/*.cc)
|
||||||
endif
|
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
|
# Not just for this library itself, but also for downstream libraries using this library
|
||||||
|
|
||||||
|
@ -21,6 +24,9 @@ endif
|
||||||
ifdef HOST_LINUX
|
ifdef HOST_LINUX
|
||||||
INCLUDE_libutil += -I $(d)/linux
|
INCLUDE_libutil += -I $(d)/linux
|
||||||
endif
|
endif
|
||||||
|
ifdef HOST_WINDOWS
|
||||||
|
INCLUDE_libutil += -I $(d)/windows
|
||||||
|
endif
|
||||||
libutil_CXXFLAGS += $(INCLUDE_libutil)
|
libutil_CXXFLAGS += $(INCLUDE_libutil)
|
||||||
|
|
||||||
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
||||||
|
|
|
@ -116,7 +116,13 @@ Verbosity verbosity = lvlInfo;
|
||||||
void writeToStderr(std::string_view s)
|
void writeToStderr(std::string_view s)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
writeFull(STDERR_FILENO, s, false);
|
writeFull(
|
||||||
|
#ifdef _WIN32
|
||||||
|
GetStdHandle(STD_ERROR_HANDLE),
|
||||||
|
#else
|
||||||
|
STDERR_FILENO,
|
||||||
|
#endif
|
||||||
|
s, false);
|
||||||
} catch (SystemError & e) {
|
} catch (SystemError & e) {
|
||||||
/* Ignore failing writes to stderr. We need to ignore write
|
/* Ignore failing writes to stderr. We need to ignore write
|
||||||
errors to ensure that cleanup code that logs to stderr runs
|
errors to ensure that cleanup code that logs to stderr runs
|
||||||
|
@ -132,9 +138,18 @@ Logger * makeSimpleLogger(bool printBuildLogs)
|
||||||
|
|
||||||
std::atomic<uint64_t> nextId{0};
|
std::atomic<uint64_t> nextId{0};
|
||||||
|
|
||||||
|
static uint64_t getPid()
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
return getpid();
|
||||||
|
#else
|
||||||
|
return GetCurrentProcessId();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
|
Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
|
||||||
const std::string & s, const Logger::Fields & fields, ActivityId parent)
|
const std::string & s, const Logger::Fields & fields, ActivityId parent)
|
||||||
: logger(logger), id(nextId++ + (((uint64_t) getpid()) << 32))
|
: logger(logger), id(nextId++ + (((uint64_t) getPid()) << 32))
|
||||||
{
|
{
|
||||||
logger.startActivity(id, lvl, type, s, fields, parent);
|
logger.startActivity(id, lvl, type, s, fields, parent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root)
|
||||||
: root(std::move(root))
|
: root(std::move(root))
|
||||||
{
|
{
|
||||||
assert(root.empty() || root.is_absolute());
|
assert(root.empty() || root.is_absolute());
|
||||||
displayPrefix = root;
|
displayPrefix = root.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
PosixSourceAccessor::PosixSourceAccessor()
|
PosixSourceAccessor::PosixSourceAccessor()
|
||||||
|
@ -19,10 +19,10 @@ PosixSourceAccessor::PosixSourceAccessor()
|
||||||
|
|
||||||
std::pair<PosixSourceAccessor, CanonPath> PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
|
std::pair<PosixSourceAccessor, CanonPath> PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
|
||||||
{
|
{
|
||||||
std::filesystem::path path2 = absPath(path.native());
|
std::filesystem::path path2 = absPath(path.string());
|
||||||
return {
|
return {
|
||||||
PosixSourceAccessor { path2.root_path() },
|
PosixSourceAccessor { path2.root_path() },
|
||||||
CanonPath { static_cast<std::string>(path2.relative_path()) },
|
CanonPath { path2.relative_path().string() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,16 @@ void PosixSourceAccessor::readFile(
|
||||||
|
|
||||||
auto ap = makeAbsPath(path);
|
auto ap = makeAbsPath(path);
|
||||||
|
|
||||||
AutoCloseFD fd = open(ap.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
AutoCloseFD fd = toDescriptor(open(ap.string().c_str(), O_RDONLY
|
||||||
|
#ifndef _WIN32
|
||||||
|
| O_NOFOLLOW | O_CLOEXEC
|
||||||
|
#endif
|
||||||
|
));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", ap.native());
|
throw SysError("opening file '%1%'", ap.string());
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd.get(), &st) == -1)
|
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)
|
||||||
throw SysError("statting file");
|
throw SysError("statting file");
|
||||||
|
|
||||||
sizeCallback(st.st_size);
|
sizeCallback(st.st_size);
|
||||||
|
@ -62,7 +66,7 @@ void PosixSourceAccessor::readFile(
|
||||||
std::array<unsigned char, 64 * 1024> buf;
|
std::array<unsigned char, 64 * 1024> buf;
|
||||||
while (left) {
|
while (left) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
ssize_t rd = read(fd.get(), buf.data(), (size_t) std::min(left, (off_t) buf.size()));
|
ssize_t rd = read(fromDescriptorReadOnly(fd.get()), buf.data(), (size_t) std::min(left, (off_t) buf.size()));
|
||||||
if (rd == -1) {
|
if (rd == -1) {
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError("reading from file '%s'", showPath(path));
|
throw SysError("reading from file '%s'", showPath(path));
|
||||||
|
@ -80,7 +84,7 @@ void PosixSourceAccessor::readFile(
|
||||||
bool PosixSourceAccessor::pathExists(const CanonPath & path)
|
bool PosixSourceAccessor::pathExists(const CanonPath & path)
|
||||||
{
|
{
|
||||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||||
return nix::pathExists(makeAbsPath(path));
|
return nix::pathExists(makeAbsPath(path).string());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
|
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
|
||||||
|
@ -89,7 +93,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
|
||||||
|
|
||||||
// Note: we convert std::filesystem::path to Path because the
|
// Note: we convert std::filesystem::path to Path because the
|
||||||
// former is not hashable on libc++.
|
// former is not hashable on libc++.
|
||||||
Path absPath = makeAbsPath(path);
|
Path absPath = makeAbsPath(path).string();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto cache(_cache.lock());
|
auto cache(_cache.lock());
|
||||||
|
@ -127,11 +131,13 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
||||||
{
|
{
|
||||||
assertNoSymlinks(path);
|
assertNoSymlinks(path);
|
||||||
DirEntries res;
|
DirEntries res;
|
||||||
for (auto & entry : nix::readDirectory(makeAbsPath(path))) {
|
for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) {
|
||||||
std::optional<Type> type;
|
std::optional<Type> type;
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case DT_REG: type = Type::tRegular; break;
|
case DT_REG: type = Type::tRegular; break;
|
||||||
|
#ifndef _WIN32
|
||||||
case DT_LNK: type = Type::tSymlink; break;
|
case DT_LNK: type = Type::tSymlink; break;
|
||||||
|
#endif
|
||||||
case DT_DIR: type = Type::tDirectory; break;
|
case DT_DIR: type = Type::tDirectory; break;
|
||||||
}
|
}
|
||||||
res.emplace(entry.name, type);
|
res.emplace(entry.name, type);
|
||||||
|
@ -142,7 +148,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
||||||
std::string PosixSourceAccessor::readLink(const CanonPath & path)
|
std::string PosixSourceAccessor::readLink(const CanonPath & path)
|
||||||
{
|
{
|
||||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||||
return nix::readLink(makeAbsPath(path));
|
return nix::readLink(makeAbsPath(path).string());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::filesystem::path> PosixSourceAccessor::getPhysicalPath(const CanonPath & path)
|
std::optional<std::filesystem::path> PosixSourceAccessor::getPhysicalPath(const CanonPath & path)
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace nix {
|
||||||
struct Sink;
|
struct Sink;
|
||||||
struct Source;
|
struct Source;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
class Pid
|
class Pid
|
||||||
{
|
{
|
||||||
pid_t pid = -1;
|
pid_t pid = -1;
|
||||||
|
@ -43,13 +44,16 @@ public:
|
||||||
void setKillSignal(int signal);
|
void setKillSignal(int signal);
|
||||||
pid_t release();
|
pid_t release();
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/**
|
/**
|
||||||
* Kill all processes running under the specified uid by sending them
|
* Kill all processes running under the specified uid by sending them
|
||||||
* a SIGKILL.
|
* a SIGKILL.
|
||||||
*/
|
*/
|
||||||
void killUser(uid_t uid);
|
void killUser(uid_t uid);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,8 +72,9 @@ struct ProcessOptions
|
||||||
int cloneFlags = 0;
|
int cloneFlags = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a program and return its stdout in a string (i.e., like the
|
* Run a program and return its stdout in a string (i.e., like the
|
||||||
|
@ -84,8 +89,10 @@ struct RunOptions
|
||||||
Path program;
|
Path program;
|
||||||
bool searchPath = true;
|
bool searchPath = true;
|
||||||
Strings args;
|
Strings args;
|
||||||
|
#ifndef _WIN32
|
||||||
std::optional<uid_t> uid;
|
std::optional<uid_t> uid;
|
||||||
std::optional<uid_t> gid;
|
std::optional<uid_t> gid;
|
||||||
|
#endif
|
||||||
std::optional<Path> chdir;
|
std::optional<Path> chdir;
|
||||||
std::optional<std::map<std::string, std::string>> environment;
|
std::optional<std::map<std::string, std::string>> environment;
|
||||||
std::optional<std::string> input;
|
std::optional<std::string> input;
|
||||||
|
@ -111,6 +118,7 @@ public:
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the exit status of a child as returned by wait() into an
|
* Convert the exit status of a child as returned by wait() into an
|
||||||
|
@ -120,4 +128,6 @@ std::string statusToString(int status);
|
||||||
|
|
||||||
bool statusOk(int status);
|
bool statusOk(int status);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
#include <boost/coroutine2/coroutine.hpp>
|
#include <boost/coroutine2/coroutine.hpp>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <fileapi.h>
|
||||||
|
# include "windows-error.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -126,6 +131,14 @@ bool BufferedSource::hasData()
|
||||||
|
|
||||||
size_t FdSource::readUnbuffered(char * data, size_t len)
|
size_t FdSource::readUnbuffered(char * data, size_t len)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD n;
|
||||||
|
checkInterrupt();
|
||||||
|
if (!::ReadFile(fd, data, len, &n, NULL)) {
|
||||||
|
_good = false;
|
||||||
|
throw WinError("ReadFile when FdSource::readUnbuffered");
|
||||||
|
}
|
||||||
|
#else
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
do {
|
do {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
@ -133,6 +146,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
|
||||||
} while (n == -1 && errno == EINTR);
|
} while (n == -1 && errno == EINTR);
|
||||||
if (n == -1) { _good = false; throw SysError("reading from file"); }
|
if (n == -1) { _good = false; throw SysError("reading from file"); }
|
||||||
if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); }
|
if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); }
|
||||||
|
#endif
|
||||||
read += n;
|
read += n;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,12 @@
|
||||||
#include "environment-variables.hh"
|
#include "environment-variables.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
# include <io.h>
|
||||||
|
# define isatty _isatty
|
||||||
|
#else
|
||||||
# include <sys/ioctl.h>
|
# include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -92,6 +97,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
||||||
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
void updateWindowSize()
|
void updateWindowSize()
|
||||||
{
|
{
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
|
@ -101,6 +107,7 @@ void updateWindowSize()
|
||||||
windowSize_->second = ws.ws_col;
|
windowSize_->second = ws.ws_col;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
std::pair<unsigned short, unsigned short> getWindowSize()
|
std::pair<unsigned short, unsigned short> getWindowSize()
|
||||||
|
|
|
@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s,
|
||||||
bool filterAll = false,
|
bool filterAll = false,
|
||||||
unsigned int width = std::numeric_limits<unsigned int>::max());
|
unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculate the window size, updating a global variable. Used in the
|
* Recalculate the window size, updating a global variable. Used in the
|
||||||
* `SIGWINCH` signal handler.
|
* `SIGWINCH` signal handler.
|
||||||
*/
|
*/
|
||||||
void updateWindowSize();
|
void updateWindowSize();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of rows and columns of the terminal.
|
* @return the number of rows and columns of the terminal.
|
||||||
*
|
*
|
||||||
|
|
|
@ -81,8 +81,10 @@ void ThreadPool::doWork(bool mainThread)
|
||||||
{
|
{
|
||||||
ReceiveInterrupts receiveInterrupts;
|
ReceiveInterrupts receiveInterrupts;
|
||||||
|
|
||||||
|
#ifndef _WIN32 // Does Windows need anything similar for async exit handling?
|
||||||
if (!mainThread)
|
if (!mainThread)
|
||||||
unix::interruptCheck = [&]() { return (bool) quit; };
|
unix::interruptCheck = [&]() { return (bool) quit; };
|
||||||
|
#endif
|
||||||
|
|
||||||
bool didWork = false;
|
bool didWork = false;
|
||||||
std::exception_ptr exc;
|
std::exception_ptr exc;
|
||||||
|
|
31
src/libutil/unix/file-path.cc
Normal file
31
src/libutil/unix/file-path.cc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
PathNG::string_type string_to_os_string(std::string_view s)
|
||||||
|
{
|
||||||
|
return std::string { s };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PathNG> maybePathNG(PathView path)
|
||||||
|
{
|
||||||
|
return { path };
|
||||||
|
}
|
||||||
|
|
||||||
|
PathNG pathNG(PathView path)
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,16 +3,20 @@
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::string getUserName();
|
std::string getUserName();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/**
|
/**
|
||||||
* @return the given user's home directory from /etc/passwd.
|
* @return the given user's home directory from /etc/passwd.
|
||||||
*/
|
*/
|
||||||
Path getHomeOf(uid_t userId);
|
Path getHomeOf(uid_t userId);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return $HOME or the user's home directory from /etc/passwd.
|
* @return $HOME or the user's home directory from /etc/passwd.
|
||||||
|
@ -58,6 +62,8 @@ std::string expandTilde(std::string_view path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the current user UID 0 on Unix?
|
* Is the current user UID 0 on Unix?
|
||||||
|
*
|
||||||
|
* Currently always false on Windows, but that may change.
|
||||||
*/
|
*/
|
||||||
bool isRootUser();
|
bool isRootUser();
|
||||||
|
|
||||||
|
|
17
src/libutil/windows/environment-variables.cc
Normal file
17
src/libutil/windows/environment-variables.cc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "environment-variables.hh"
|
||||||
|
|
||||||
|
#include "processenv.h"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
int unsetenv(const char *name)
|
||||||
|
{
|
||||||
|
return -SetEnvironmentVariableA(name, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int setEnv(const char * name, const char * value)
|
||||||
|
{
|
||||||
|
return -SetEnvironmentVariableA(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue