mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-21 21:46:15 +02:00
Build a minimized Nix with MinGW
At this point many features are stripped out, but this works: - Can run libnix{util,store,expr} unit tests - Can run some Nix commands Co-Authored-By volth <volth@volth.com> Co-Authored-By Brian McKenna <brian@brianmckenna.org>
This commit is contained in:
parent
2248a3f545
commit
8433027e35
111 changed files with 1162 additions and 140 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.
|
||||||
|
|
|
@ -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 <sys/utsname.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.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/wait.h>
|
||||||
|
# include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
|
@ -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,9 +3,9 @@
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "terminal.hh"
|
#include "terminal.hh"
|
||||||
|
|
||||||
#include <sys/queue.h>
|
|
||||||
#if HAVE_LOWDOWN
|
#if HAVE_LOWDOWN
|
||||||
#include <lowdown.h>
|
# include <sys/queue.h>
|
||||||
|
# include <lowdown.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -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>
|
||||||
#include <dlfcn.h>
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# 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>
|
||||||
|
@ -56,7 +59,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 +244,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 +350,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 +361,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
|
||||||
|
@ -55,9 +58,9 @@ libstore_CXXFLAGS += \
|
||||||
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
|
||||||
|
@ -66,11 +69,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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,7 +9,9 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <glob.h>
|
#ifndef _WIN32
|
||||||
|
# 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
|
||||||
|
|
||||||
#include <sys/mount.h>
|
#ifndef _WIN32
|
||||||
|
# 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>
|
||||||
#include <sys/resource.h>
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# 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"
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#if _WIN32
|
||||||
|
# include <io.h>
|
||||||
|
# define isatty _isatty
|
||||||
|
#else
|
||||||
|
# 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"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#ifndef _WIN32
|
||||||
|
# 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
148
src/libutil/windows/file-descriptor.cc
Normal file
148
src/libutil/windows/file-descriptor.cc
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
#include "file-system.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
#include "finally.hh"
|
||||||
|
#include "serialise.hh"
|
||||||
|
#include "windows-error.hh"
|
||||||
|
#include "file-path.hh"
|
||||||
|
|
||||||
|
#include <fileapi.h>
|
||||||
|
#include <error.h>
|
||||||
|
#include <namedpipeapi.h>
|
||||||
|
#include <namedpipeapi.h>
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string readFile(HANDLE handle)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER li;
|
||||||
|
if (!GetFileSizeEx(handle, &li))
|
||||||
|
throw WinError("%s:%d statting file", __FILE__, __LINE__);
|
||||||
|
|
||||||
|
return drainFD(handle, true, li.QuadPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void readFull(HANDLE handle, char * buf, size_t count)
|
||||||
|
{
|
||||||
|
while (count) {
|
||||||
|
checkInterrupt();
|
||||||
|
DWORD res;
|
||||||
|
if (!ReadFile(handle, (char *) buf, count, &res, NULL))
|
||||||
|
throw WinError("%s:%d reading from file", __FILE__, __LINE__);
|
||||||
|
if (res == 0) throw EndOfFile("unexpected end-of-file");
|
||||||
|
count -= res;
|
||||||
|
buf += res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts)
|
||||||
|
{
|
||||||
|
while (!s.empty()) {
|
||||||
|
if (allowInterrupts) checkInterrupt();
|
||||||
|
DWORD res;
|
||||||
|
#if _WIN32_WINNT >= 0x0600
|
||||||
|
auto path = handleToPath(handle); // debug; do it before becuase handleToPath changes lasterror
|
||||||
|
if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) {
|
||||||
|
throw WinError("writing to file %1%:%2%", handle, path);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) {
|
||||||
|
throw WinError("writing to file %1%", handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (res > 0)
|
||||||
|
s.remove_prefix(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string readLine(HANDLE handle)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
while (1) {
|
||||||
|
checkInterrupt();
|
||||||
|
char ch;
|
||||||
|
// FIXME: inefficient
|
||||||
|
DWORD rd;
|
||||||
|
if (!ReadFile(handle, &ch, 1, &rd, NULL)) {
|
||||||
|
throw WinError("reading a line");
|
||||||
|
} else if (rd == 0)
|
||||||
|
throw EndOfFile("unexpected EOF reading a line");
|
||||||
|
else {
|
||||||
|
if (ch == '\n') return s;
|
||||||
|
s += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void drainFD(HANDLE handle, Sink & sink/*, bool block*/)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> buf(64 * 1024);
|
||||||
|
while (1) {
|
||||||
|
checkInterrupt();
|
||||||
|
DWORD rd;
|
||||||
|
if (!ReadFile(handle, buf.data(), buf.size(), &rd, NULL)) {
|
||||||
|
WinError winError("%s:%d reading from handle %p", __FILE__, __LINE__, handle);
|
||||||
|
if (winError.lastError == ERROR_BROKEN_PIPE)
|
||||||
|
break;
|
||||||
|
throw winError;
|
||||||
|
}
|
||||||
|
else if (rd == 0) break;
|
||||||
|
sink({(char *) buf.data(), (size_t) rd});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
void Pipe::create()
|
||||||
|
{
|
||||||
|
SECURITY_ATTRIBUTES saAttr = {0};
|
||||||
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
saAttr.lpSecurityDescriptor = NULL;
|
||||||
|
saAttr.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
HANDLE hReadPipe, hWritePipe;
|
||||||
|
if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0))
|
||||||
|
throw WinError("CreatePipe");
|
||||||
|
|
||||||
|
readSide = hReadPipe;
|
||||||
|
writeSide = hWritePipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if _WIN32_WINNT >= 0x0600
|
||||||
|
|
||||||
|
std::wstring handleToFileName(HANDLE handle) {
|
||||||
|
std::vector<wchar_t> buf(0x100);
|
||||||
|
DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED);
|
||||||
|
if (dw == 0) {
|
||||||
|
if (handle == GetStdHandle(STD_INPUT_HANDLE )) return L"<stdin>";
|
||||||
|
if (handle == GetStdHandle(STD_OUTPUT_HANDLE)) return L"<stdout>";
|
||||||
|
if (handle == GetStdHandle(STD_ERROR_HANDLE )) return L"<stderr>";
|
||||||
|
return (boost::wformat(L"<unnnamed handle %X>") % handle).str();
|
||||||
|
}
|
||||||
|
if (dw > buf.size()) {
|
||||||
|
buf.resize(dw);
|
||||||
|
if (GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED) != dw-1)
|
||||||
|
throw WinError("GetFinalPathNameByHandleW");
|
||||||
|
dw -= 1;
|
||||||
|
}
|
||||||
|
return std::wstring(buf.data(), dw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Path handleToPath(HANDLE handle) {
|
||||||
|
return os_string_to_string(handleToFileName(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
52
src/libutil/windows/file-path.cc
Normal file
52
src/libutil/windows/file-path.cc
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <codecvt>
|
||||||
|
#include <iostream>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include "file-path.hh"
|
||||||
|
#include "file-path-impl.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string os_string_to_string(PathViewNG::string_view path)
|
||||||
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||||
|
return converter.to_bytes(PathNG::string_type { path });
|
||||||
|
}
|
||||||
|
|
||||||
|
PathNG::string_type string_to_os_string(std::string_view s)
|
||||||
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||||
|
return converter.from_bytes(std::string { s });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PathNG> maybePathNG(PathView path)
|
||||||
|
{
|
||||||
|
if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait<char>::isPathSep(path[2])) {
|
||||||
|
PathNG::string_type sw = string_to_os_string(
|
||||||
|
std::string { "\\\\?\\" } + path);
|
||||||
|
std::replace(sw.begin(), sw.end(), '/', '\\');
|
||||||
|
return sw;
|
||||||
|
}
|
||||||
|
if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' &&
|
||||||
|
('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait<char>::isPathSep(path[6])) {
|
||||||
|
PathNG::string_type sw = string_to_os_string(path);
|
||||||
|
std::replace(sw.begin(), sw.end(), '/', '\\');
|
||||||
|
return sw;
|
||||||
|
}
|
||||||
|
return std::optional<PathNG::string_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
PathNG pathNG(PathView path)
|
||||||
|
{
|
||||||
|
std::optional<PathNG::string_type> sw = maybePathNG(path);
|
||||||
|
if (!sw) {
|
||||||
|
// FIXME why are we not using the regular error handling?
|
||||||
|
std::cerr << "invalid path for WinAPI call ["<<path<<"]"<<std::endl;
|
||||||
|
_exit(111);
|
||||||
|
}
|
||||||
|
return *sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/libutil/windows/processes.cc
Normal file
48
src/libutil/windows/processes.cc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "current-process.hh"
|
||||||
|
#include "environment-variables.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
#include "processes.hh"
|
||||||
|
#include "finally.hh"
|
||||||
|
#include "serialise.hh"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <sys/prctl.h>
|
||||||
|
# include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
|
const std::optional<std::string> & input, bool isInteractive)
|
||||||
|
{
|
||||||
|
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output = error code + "standard out" output stream
|
||||||
|
std::pair<int, std::string> runProgram(RunOptions && options)
|
||||||
|
{
|
||||||
|
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void runProgram2(const RunOptions & options)
|
||||||
|
{
|
||||||
|
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
src/libutil/windows/signals-impl.hh
Normal file
41
src/libutil/windows/signals-impl.hh
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* User interruption. */
|
||||||
|
|
||||||
|
static inline void setInterrupted(bool isInterrupted)
|
||||||
|
{
|
||||||
|
/* Do nothing for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool getInterrupted()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setInterruptThrown()
|
||||||
|
{
|
||||||
|
/* Do nothing for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
void inline checkInterrupt()
|
||||||
|
{
|
||||||
|
/* Do nothing for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing, unlike Unix counterpart, but allows avoiding C++
|
||||||
|
*/
|
||||||
|
struct ReceiveInterrupts
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Explicit destructor avoids dead code warnings.
|
||||||
|
*/
|
||||||
|
~ReceiveInterrupts() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
50
src/libutil/windows/users.cc
Normal file
50
src/libutil/windows/users.cc
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include "util.hh"
|
||||||
|
#include "users.hh"
|
||||||
|
#include "environment-variables.hh"
|
||||||
|
#include "file-system.hh"
|
||||||
|
#include "windows-error.hh"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string getUserName()
|
||||||
|
{
|
||||||
|
// Get the required buffer size
|
||||||
|
DWORD size = 0;
|
||||||
|
if (!GetUserNameA(nullptr, &size)) {
|
||||||
|
auto lastError = GetLastError();
|
||||||
|
if (lastError != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
throw WinError(lastError, "cannot figure out size of user name");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
// Allocate a buffer of sufficient size
|
||||||
|
//
|
||||||
|
// - 1 because no need for null byte
|
||||||
|
name.resize(size - 1);
|
||||||
|
|
||||||
|
// Retrieve the username
|
||||||
|
if (!GetUserNameA(&name[0], &size))
|
||||||
|
throw WinError("cannot figure out user name");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path getHome()
|
||||||
|
{
|
||||||
|
static Path homeDir = []()
|
||||||
|
{
|
||||||
|
Path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default");
|
||||||
|
assert(!homeDir.empty());
|
||||||
|
return canonPath(homeDir);
|
||||||
|
}();
|
||||||
|
return homeDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRootUser() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
src/libutil/windows/windows-error.cc
Normal file
31
src/libutil/windows/windows-error.cc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "windows-error.hh"
|
||||||
|
|
||||||
|
#include <error.h>
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string WinError::renderError(DWORD lastError)
|
||||||
|
{
|
||||||
|
LPSTR errorText = NULL;
|
||||||
|
|
||||||
|
FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text
|
||||||
|
|FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text
|
||||||
|
|FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
|
||||||
|
NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM
|
||||||
|
lastError,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPTSTR)&errorText, // output
|
||||||
|
0, // minimum size for output buffer
|
||||||
|
NULL); // arguments - see note
|
||||||
|
|
||||||
|
if (NULL != errorText ) {
|
||||||
|
std::string s2 { errorText };
|
||||||
|
LocalFree(errorText);
|
||||||
|
return s2;
|
||||||
|
}
|
||||||
|
return fmt("CODE=%d", lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
src/libutil/windows/windows-error.hh
Normal file
51
src/libutil/windows/windows-error.hh
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include <errhandlingapi.h>
|
||||||
|
|
||||||
|
#include "error.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windows Error type.
|
||||||
|
*
|
||||||
|
* Unless you need to catch a specific error number, don't catch this in
|
||||||
|
* portable code. Catch `SystemError` instead.
|
||||||
|
*/
|
||||||
|
class WinError : public SystemError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DWORD lastError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct using the explicitly-provided error number.
|
||||||
|
* `FormatMessageA` will be used to try to add additional
|
||||||
|
* information to the message.
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
WinError(DWORD lastError, const Args & ... args)
|
||||||
|
: SystemError(""), lastError(lastError)
|
||||||
|
{
|
||||||
|
auto hf = HintFmt(args...);
|
||||||
|
err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct using `GetLastError()` and the ambient "last error".
|
||||||
|
*
|
||||||
|
* Be sure to not perform another last-error-modifying operation
|
||||||
|
* before calling this constructor!
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
WinError(const Args & ... args)
|
||||||
|
: WinError(GetLastError(), args ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string renderError(DWORD lastError);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1342,8 +1342,16 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs
|
||||||
RunPager pager;
|
RunPager pager;
|
||||||
|
|
||||||
for (auto & i : gens) {
|
for (auto & i : gens) {
|
||||||
|
#ifdef _WIN32 // TODO portable wrapper in libutil
|
||||||
|
tm * tp = localtime(&i.creationTime);
|
||||||
|
if (!tp)
|
||||||
|
throw Error("cannot convert time");
|
||||||
|
auto & t = *tp;
|
||||||
|
#else
|
||||||
tm t;
|
tm t;
|
||||||
if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time");
|
if (!localtime_r(&i.creationTime, &t))
|
||||||
|
throw Error("cannot convert time");
|
||||||
|
#endif
|
||||||
logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||",
|
logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||",
|
||||||
i.number,
|
i.number,
|
||||||
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
|
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
|
||||||
|
|
|
@ -168,7 +168,7 @@ static int main_nix_instantiate(int argc, char * * argv)
|
||||||
for (auto & i : files) {
|
for (auto & i : files) {
|
||||||
auto p = state->findFile(i);
|
auto p = state->findFile(i);
|
||||||
if (auto fn = p.getPhysicalPath())
|
if (auto fn = p.getPhysicalPath())
|
||||||
std::cout << fn->native() << std::endl;
|
std::cout << fn->string() << std::endl;
|
||||||
else
|
else
|
||||||
throw Error("'%s' has no physical path", p);
|
throw Error("'%s' has no physical path", p);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue