1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-02 09:21:47 +02:00

Merge pull request #8901 from nix-windows/mingw

Build a minimized Nix with MinGW
This commit is contained in:
John Ericson 2024-04-17 12:52:47 -04:00 committed by GitHub
commit ac253fb99e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
120 changed files with 1435 additions and 341 deletions

View file

@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config
# List makefiles
include mk/platform.mk
ifeq ($(ENABLE_BUILD), yes)
makefiles = \
mk/precompiled-headers.mk \
@ -20,7 +22,10 @@ makefiles = \
src/nix/local.mk \
src/libutil-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 \
misc/bash/local.mk \
misc/fish/local.mk \
@ -29,6 +34,7 @@ makefiles = \
misc/launchd/local.mk \
misc/upstart/local.mk
endif
endif
ifeq ($(ENABLE_UNIT_TESTS), yes)
makefiles += \
@ -42,6 +48,7 @@ makefiles += \
endif
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
ifdef HOST_UNIX
makefiles += \
tests/functional/local.mk \
tests/functional/ca/local.mk \
@ -51,6 +58,7 @@ makefiles += \
tests/functional/test-libstoreconsumer/local.mk \
tests/functional/plugins/local.mk
endif
endif
# Some makefiles require access to built programs and must be included late.
makefiles-late =
@ -79,8 +87,6 @@ else
unexport NIX_HARDENING_ENABLE
endif
include mk/platform.mk
ifdef HOST_WINDOWS
# Windows DLLs are stricter about symbol visibility than Unix shared
# objects --- see https://gcc.gnu.org/wiki/Visibility for details.

View file

@ -32,12 +32,6 @@
"armv6l-unknown-linux-gnueabihf"
"armv7l-unknown-linux-gnueabihf"
"x86_64-unknown-netbsd"
];
# Nix doesn't yet build on this platform, so we put it in a
# separate list. We just use this for `devShells` and
# `nixpkgsFor`, which this depends on.
shellCrossSystems = crossSystems ++ [
"x86_64-w64-mingw32"
];
@ -83,7 +77,7 @@
in {
inherit stdenvs native;
static = native.pkgsStatic;
cross = lib.genAttrs shellCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
});
installScriptFor = tarballs:
@ -426,8 +420,8 @@
in
(makeShells "native" nixpkgsFor.${system}.native) //
(lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin)
(makeShells "static" nixpkgsFor.${system}.static)) //
(lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
(makeShells "static" nixpkgsFor.${system}.static) //
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv))) //
{
default = self.devShells.${system}.native-stdenvPackages;
}

View file

@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
]])],
[status_80431=0],
[status_80431=$?],
[
# Assume we're bug-free when cross-compiling
])
[status_80431=''])
AC_LANG_POP(C++)
AS_CASE([$status_80431],
[''],[
AC_MSG_RESULT(cannot check because cross compiling)
AC_MSG_NOTICE(assume we are bug free)
],
[0],[
AC_MSG_RESULT(yes)
],

View file

@ -42,19 +42,22 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <termios.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>

View file

@ -22,6 +22,7 @@
#include "experimental-features.hh"
using namespace nix;
using namespace nix::unix;
using std::cin;
static void handleAlarm(int sig) {

View file

@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
v->mkString(arg.s);
},
[&](const AutoArgFile & arg) {
v->mkString(readFile(arg.path));
v->mkString(readFile(arg.path.string()));
},
[&](const AutoArgStdin & arg) {
v->mkString(readFile(STDIN_FILENO));

View file

@ -3,9 +3,9 @@
#include "finally.hh"
#include "terminal.hh"
#include <sys/queue.h>
#if HAVE_LOWDOWN
#include <lowdown.h>
# include <sys/queue.h>
# include <lowdown.h>
#endif
namespace nix {

View file

@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType)
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
{
#ifndef _WIN32 // TODO use more signals.hh for this
struct sigaction act, old;
sigset_t savedSignalMask, set;
@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
};
setupSignals();
#endif
char * s = readline(promptForType(promptType));
Finally doFree([&]() { free(s); });
#ifndef _WIN32 // TODO use more signals.hh for this
restoreSignals();
#endif
if (g_signal_received) {
g_signal_received = 0;

View file

@ -33,15 +33,17 @@
#include <optional>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <sys/resource.h>
#include <nlohmann/json.hpp>
#include <boost/container/small_vector.hpp>
#ifndef _WIN32 // TODO use portable implementation
# include <sys/resource.h>
#endif
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
@ -2627,9 +2629,11 @@ void EvalState::maybePrintStats()
void EvalState::printStatistics()
{
#ifndef _WIN32 // TODO use portable implementation
struct rusage buf;
getrusage(RUSAGE_SELF, &buf);
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 bLists = nrListElems * sizeof(Value *);
@ -2646,7 +2650,9 @@ void EvalState::printStatistics()
if (outPath != "-")
fs.open(outPath, std::fstream::out);
json topObj = json::object();
#ifndef _WIN32 // TODO implement
topObj["cpuTime"] = cpuTime;
#endif
topObj["envs"] = {
{"number", nrEnvs},
{"elements", nrValuesInEnvs},

View file

@ -161,6 +161,8 @@ struct DebugTrace {
bool isError;
};
// Don't want Windows function
#undef SearchPath
class EvalState : public std::enable_shared_from_this<EvalState>
{

View file

@ -28,7 +28,10 @@
#include <algorithm>
#include <cstring>
#include <regex>
#include <dlfcn.h>
#ifndef _WIN32
# include <dlfcn.h>
#endif
#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 */
/* !!! Should we pass the Pos or the file name too? */
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. */
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
if (evalSettings.enableNativeCode) {
addPrimOp({
@ -4606,6 +4614,7 @@ void EvalState::createBaseEnv()
.fun = prim_exec,
});
}
#endif
addPrimOp({
.name = "__traceVerbose",

View file

@ -8,6 +8,9 @@
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
* `builtins.findFile` and `< >` lookup expressions.

View file

@ -26,7 +26,7 @@ ref<InputAccessor> makeStorePathAccessor(
// FIXME: should use `store->getFSAccessor()`
auto root = std::filesystem::path { store->toRealPath(storePath) };
auto accessor = makeFSInputAccessor(root);
accessor->setPathDisplay(root);
accessor->setPathDisplay(root.string());
return accessor;
}

View file

@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{
initLibGit2();
if (pathExists(path.native())) {
if (git_repository_open(Setter(repo), path.c_str()))
if (pathExists(path.string())) {
if (git_repository_open(Setter(repo), path.string().c_str()))
throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
} 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);
}
}
@ -216,7 +216,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
{
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);
ConfigIterator it;
@ -288,7 +288,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
/* Get submodule info. */
auto modulesFile = path / ".gitmodules";
if (pathExists(modulesFile))
if (pathExists(modulesFile.string()))
info.submodules = parseSubmodules(modulesFile);
return info;
@ -377,10 +377,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
auto dir = this->path;
Strings gitArgs;
if (shallow) {
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
}
else {
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec };
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec };
}
runProgram(RunOptions {
@ -426,7 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
.args = {
"-c",
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
"-C", path,
"-C", path.string(),
"verify-commit",
rev.gitRev()
},

View file

@ -108,7 +108,9 @@ std::string getArg(const std::string & opt,
return *i;
}
#ifndef _WIN32
static void sigHandler(int signo) { }
#endif
void initNix()
@ -121,6 +123,7 @@ void initNix()
initLibStore();
#ifndef _WIN32
unix::startSignalHandlerThread();
/* Reset SIGCHLD to its default. */
@ -135,6 +138,7 @@ void initNix()
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
act.sa_handler = sigHandler;
if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");
#endif
#if __APPLE__
/* HACK: on darwin, we need cant use sigprocmask with SIGWINCH.
@ -156,21 +160,26 @@ void initNix()
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
#endif
#ifndef _WIN32
/* Register a SIGSEGV handler to detect stack overflows.
Why not initLibExpr()? initGC() is essentially that, but
detectStackOverflow is not an instance of the init function concept, as
it may have to be invoked more than once per process. */
detectStackOverflow();
#endif
/* There is no privacy in the Nix system ;-) At least not for
now. In particular, store objects should be readable by
everybody. */
umask(0022);
#ifndef _WIN32
/* Initialise the PRNG. */
struct timeval tv;
gettimeofday(&tv, 0);
srandom(tv.tv_usec);
#endif
}
@ -365,6 +374,9 @@ RunPager::RunPager()
Pipe toPager;
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([&]() {
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
throw SysError("dupping stdin");
@ -383,17 +395,20 @@ RunPager::RunPager()
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
throw SysError("dupping standard output");
#endif
}
RunPager::~RunPager()
{
try {
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
if (pid != -1) {
std::cout.flush();
dup2(std_out, STDOUT_FILENO);
pid.wait();
}
#endif
} catch (...) {
ignoreException();
}

View file

@ -1,6 +1,7 @@
#pragma once
///@file
#include "file-descriptor.hh"
#include "processes.hh"
#include "args.hh"
#include "args/root.hh"
@ -89,8 +90,10 @@ public:
~RunPager();
private:
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
Pid pid;
int std_out;
#endif
Descriptor std_out;
};
extern volatile ::sig_atomic_t blockInt;
@ -112,6 +115,7 @@ struct PrintFreed
};
#ifndef _WIN32
/**
* 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.
*/
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
#endif
}

View file

@ -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);
if (unlink(dstFile.c_str()) == -1)
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);
createLinks(state, target, dstFile, state.priorities[dstFile]);
createLinks(state, srcFile, dstFile, priority);

View file

@ -1,5 +1,4 @@
#include "daemon.hh"
#include "monitor-fd.hh"
#include "signals.hh"
#include "worker-protocol.hh"
#include "worker-protocol-impl.hh"
@ -16,6 +15,10 @@
#include "args.hh"
#include "git.hh"
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
# include "monitor-fd.hh"
#endif
namespace nix::daemon {
Sink & operator << (Sink & sink, const Logger::Fields & fields)
@ -1018,7 +1021,9 @@ void processConnection(
TrustedFlag trusted,
RecursiveFlag recursive)
{
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
#endif
/* Exchange the greeting. */
unsigned int magic = readInt(from);

View file

@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer
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
thread, because it only monitors file descriptors. So use a
pipe instead. */
Pipe wakeupPipe;
#endif
std::thread workerThread;
@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer
fileTransferSettings.httpConnections.get());
#endif
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
wakeupPipe.create();
fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
#endif
workerThread = std::thread([&]() { workerThreadEntry(); });
}
@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer
auto state(state_.lock());
state->quit = true;
}
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
writeFull(wakeupPipe.writeSide.get(), " ", false);
#endif
}
void workerThreadMain()
{
/* Cause this thread to be notified on SIGINT. */
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
auto callback = createInterruptCallback([&]() {
stopWorkerThread();
});
#endif
#if __linux__
unshareFilesystem();
@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer
/* Wait for activity, including wakeup events. */
int numfds = 0;
struct curl_waitfd extraFDs[1];
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
extraFDs[0].fd = wakeupPipe.readSide.get();
extraFDs[0].events = CURL_WAIT_POLLIN;
extraFDs[0].revents = 0;
#endif
long maxSleepTimeMs = items.empty() ? 10000 : 100;
auto sleepTimeMs =
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");
state->incoming.push(item);
}
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
writeFull(wakeupPipe.writeSide.get(), " ");
#endif
}
#if ENABLE_S3

View file

@ -9,11 +9,14 @@
#include <map>
#include <mutex>
#include <thread>
#include <dlfcn.h>
#include <sys/utsname.h>
#include <nlohmann/json.hpp>
#ifndef _WIN32
# include <dlfcn.h>
# include <sys/utsname.h>
#endif
#ifdef __GLIBC__
# include <gnu/lib-names.h>
# include <nss.h>
@ -46,7 +49,13 @@ static GlobalConfig::Register rSettings(&settings);
Settings::Settings()
: nixPrefix(NIX_PREFIX)
, nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
, nixStore(
#ifndef _WIN32
// On Windows `/nix/store` is not a canonical path, but we dont'
// want to deal with that yet.
canonPath
#endif
(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
@ -56,7 +65,9 @@ Settings::Settings()
, nixManDir(canonPath(NIX_MAN_DIR))
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
{
#ifndef _WIN32
buildUsersGroup = isRootUser() ? "nixbld" : "";
#endif
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
@ -239,11 +250,15 @@ StringSet Settings::getDefaultExtraPlatforms()
bool Settings::isWSL1()
{
#if __linux__
struct utsname utsbuf;
uname(&utsbuf);
// WSL1 uses -Microsoft suffix
// WSL2 uses -microsoft-standard suffix
return hasSuffix(utsbuf.release, "-Microsoft");
#else
return false;
#endif
}
Path Settings::getDefaultSSLCertFile()
@ -341,6 +356,7 @@ void initPlugins()
for (const auto & file : pluginFiles) {
/* handle is purposefully leaked as there may be state in the
DSO needed by the action of the plugin. */
#ifndef _WIN32 // TODO implement via DLL loading on Windows
void *handle =
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@ -351,6 +367,9 @@ void initPlugins()
void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry");
if (nix_plugin_entry)
nix_plugin_entry();
#else
throw Error("could not dynamically open plugin file '%s'", file);
#endif
}
}

View file

@ -666,6 +666,7 @@ public:
Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
"Whether to disable sandboxing when the kernel doesn't allow it."};
#ifndef _WIN32
Setting<bool> requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups",
R"(
Following the principle of least privilege,
@ -683,6 +684,7 @@ public:
(since `root` usually has permissions to call setgroups)
and `false` otherwise.
)"};
#endif
#if __linux__
Setting<std::string> sandboxShmSize{

View file

@ -4,9 +4,12 @@ libstore_NAME = libnixstore
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
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
libstore_LIBS = libutil
@ -36,25 +39,39 @@ INCLUDE_libstore := -I $(d) -I $(d)/build
ifdef HOST_UNIX
INCLUDE_libstore += -I $(d)/unix
endif
ifdef HOST_WINDOWS
INCLUDE_libstore += -I $(d)/windows
endif
ifdef HOST_WINDOWS
NIX_ROOT = N:\\\\
else
NIX_ROOT =
endif
# Prefix all but `NIX_STORE_DIR`, since we aren't doing a local store
# yet so a "logical" store dir that is the same as unix is prefered.
#
# Also, it keeps the unit tests working.
libstore_CXXFLAGS += \
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
-DNIX_PREFIX=\"$(prefix)\" \
-DNIX_PREFIX=\"$(NIX_ROOT)$(prefix)\" \
-DNIX_STORE_DIR=\"$(storedir)\" \
-DNIX_DATA_DIR=\"$(datadir)\" \
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DNIX_MAN_DIR=\"$(mandir)\" \
-DLSOF=\"$(lsof)\"
-DNIX_DATA_DIR=\"$(NIX_ROOT)$(datadir)\" \
-DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \
-DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \
-DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \
-DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \
-DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \
-DLSOF=\"$(NIX_ROOT)$(lsof)\"
ifeq ($(embedded_sandbox_shell),yes)
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
@mv $@.tmp $@
else
@ -63,11 +80,11 @@ else
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))

View file

@ -1,7 +1,6 @@
#include "derivations.hh"
#include "parsed-derivations.hh"
#include "globals.hh"
#include "local-store.hh"
#include "store-api.hh"
#include "thread-pool.hh"
#include "realisation.hh"

View file

@ -63,7 +63,18 @@ StorePath StorePath::random(std::string_view name)
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
{
auto p = canonPath(std::string(path));
// On Windows, `/nix/store` is not a canonical path. More broadly it
// is unclear whether this function should be using the native
// notion of a canonical path at all. For example, it makes to
// support remote stores whose store dir is a non-native path (e.g.
// Windows <-> Unix ssh-ing).
auto p =
#ifdef _WIN32
path
#else
canonPath(std::string(path))
#endif
;
if (dirOf(p) != storeDir)
throw BadStorePath("path '%s' is not in the Nix store", p);
return StorePath(baseNameOf(p));

View file

@ -6,69 +6,9 @@
#include <cerrno>
#include <cstdlib>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
namespace nix {
AutoCloseFD openLockFile(const Path & path, bool create)
{
AutoCloseFD fd;
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
if (!fd && (create || errno != ENOENT))
throw SysError("opening lock file '%1%'", path);
return fd;
}
void deleteLockFile(const Path & path, int fd)
{
/* Get rid of the lock file. Have to be careful not to introduce
races. Write a (meaningless) token to the file to indicate to
other processes waiting on this lock that the lock is stale
(deleted). */
unlink(path.c_str());
writeFull(fd, "d");
/* Note that the result of unlink() is ignored; removing the lock
file is an optimisation, not a necessity. */
}
bool lockFile(int fd, LockType lockType, bool wait)
{
int type;
if (lockType == ltRead) type = LOCK_SH;
else if (lockType == ltWrite) type = LOCK_EX;
else if (lockType == ltNone) type = LOCK_UN;
else abort();
if (wait) {
while (flock(fd, type) != 0) {
checkInterrupt();
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
else
return false;
}
} else {
while (flock(fd, type | LOCK_NB) != 0) {
checkInterrupt();
if (errno == EWOULDBLOCK) return false;
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
}
}
return true;
}
PathLocks::PathLocks()
: deletePaths(false)
{
@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
}
bool PathLocks::lockPaths(const PathSet & paths,
const std::string & waitMsg, bool wait)
{
assert(fds.empty());
/* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */
/* Acquire the lock for each path in sorted order. This ensures
that locks are always acquired in the same order, thus
preventing deadlocks. */
for (auto & path : paths) {
checkInterrupt();
Path lockPath = path + ".lock";
debug("locking path '%1%'", path);
AutoCloseFD fd;
while (1) {
/* Open/create the lock file. */
fd = openLockFile(lockPath, true);
/* Acquire an exclusive lock. */
if (!lockFile(fd.get(), ltWrite, false)) {
if (wait) {
if (waitMsg != "") printError(waitMsg);
lockFile(fd.get(), ltWrite, true);
} else {
/* Failed to lock this path; release all other
locks. */
unlock();
return false;
}
}
debug("lock acquired on '%1%'", lockPath);
/* Check that the lock file hasn't become stale (i.e.,
hasn't been unlinked). */
struct stat st;
if (fstat(fd.get(), &st) == -1)
throw SysError("statting lock file '%1%'", lockPath);
if (st.st_size != 0)
/* This lock file has been unlinked, so we're holding
a lock on a deleted file. This means that other
processes may create and acquire a lock on
`lockPath', and proceed. So we must retry. */
debug("open lock file '%1%' has become stale", lockPath);
else
break;
}
/* Use borrow so that the descriptor isn't closed. */
fds.push_back(FDPair(fd.release(), lockPath));
}
return true;
}
PathLocks::~PathLocks()
{
try {
@ -154,40 +32,10 @@ PathLocks::~PathLocks()
}
void PathLocks::unlock()
{
for (auto & i : fds) {
if (deletePaths) deleteLockFile(i.second, i.first);
if (close(i.first) == -1)
printError(
"error (ignored): cannot close lock file on '%1%'",
i.second);
debug("lock released on '%1%'", i.second);
}
fds.clear();
}
void PathLocks::setDeletion(bool deletePaths)
{
this->deletePaths = deletePaths;
}
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
: fd(fd)
{
if (wait) {
if (!lockFile(fd, lockType, false)) {
printInfo("%s", waitMsg);
acquired = lockFile(fd, lockType, true);
}
} else
acquired = lockFile(fd, lockType, false);
}
}

View file

@ -5,22 +5,6 @@
namespace nix {
/**
* Open (possibly create) a lock file and return the file descriptor.
* -1 is returned if create is false and the lock could not be opened
* because it doesn't exist. Any other error throws an exception.
*/
AutoCloseFD openLockFile(const Path & path, bool create);
/**
* Delete an open lock file.
*/
void deleteLockFile(const Path & path, int fd);
enum LockType { ltRead, ltWrite, ltNone };
bool lockFile(int fd, LockType lockType, bool wait);
class PathLocks
{
private:
@ -40,18 +24,6 @@ public:
void setDeletion(bool deletePaths);
};
struct FdLock
{
int fd;
bool acquired = false;
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
~FdLock()
{
if (acquired)
lockFile(fd, ltNone, false);
}
};
}
#include "pathlocks-impl.hh"

View file

@ -8,6 +8,7 @@
#include "types.hh"
#include "pathlocks.hh"
#include <optional>
#include <time.h>

View file

@ -71,11 +71,15 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
auto narAccessor = makeLazyNarAccessor(listing,
[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)
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);
std::string buf(length, 0);

View file

@ -55,6 +55,9 @@ bool SSHMaster::isMasterRunning() {
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
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();
Pipe in, out;
@ -105,8 +108,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
}, options);
in.readSide = -1;
out.writeSide = -1;
in.readSide = INVALID_DESCRIPTOR;
out.writeSide = INVALID_DESCRIPTOR;
// Wait for the SSH connection to be established,
// 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);
return conn;
#endif
}
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
Path SSHMaster::startMaster()
{
if (!useMaster) return "";
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";
@ -167,7 +173,7 @@ Path SSHMaster::startMaster()
throw SysError("unable to execute '%s'", args.front());
}, options);
out.writeSide = -1;
out.writeSide = INVALID_DESCRIPTOR;
std::string reply;
try {
@ -182,4 +188,6 @@ Path SSHMaster::startMaster()
return state->socketPath;
}
#endif
}

View file

@ -21,7 +21,9 @@ private:
struct State
{
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
Pid sshMaster;
#endif
std::unique_ptr<AutoDelete> tmpDir;
Path socketPath;
};
@ -31,13 +33,19 @@ private:
void addCommonSSHOpts(Strings & args);
bool isMasterRunning();
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
Path startMaster();
#endif
public:
SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
struct Connection
{
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
Pid sshPid;
#endif
AutoCloseFD out, in;
};
@ -51,8 +59,6 @@ public:
std::unique_ptr<Connection> startCommand(
Strings && command,
Strings && extraSshArgs = {});
Path startMaster();
};
}

View file

@ -13,7 +13,6 @@
#include "archive.hh"
#include "callback.hh"
#include "git.hh"
#include "remote-store.hh"
#include "posix-source-accessor.hh"
// FIXME this should not be here, see TODO below on
// `addMultipleToStore`.
@ -21,6 +20,10 @@
#include "signals.hh"
#include "users.hh"
#ifndef _WIN32
# include "remote-store.hh"
#endif
#include <nlohmann/json.hpp>
#include <regex>
@ -1266,9 +1269,10 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath)
}
#include "local-store.hh"
#include "uds-remote-store.hh"
#ifndef _WIN32
# include "local-store.hh"
# include "uds-remote-store.hh"
#endif
namespace nix {
@ -1286,6 +1290,9 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
return {uri, params};
}
#ifdef _WIN32 // Unused on Windows because the next `#ifndef`
[[maybe_unused]]
#endif
static bool isNonUriPath(const std::string & spec)
{
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)
{
// TODO reenable on Windows once we have `LocalStore` and
// `UDSRemoteStore`.
#ifndef _WIN32
if (uri == "" || uri == "auto") {
auto stateDir = getOr(params, "state", settings.nixStateDir);
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 {
return nullptr;
}
#else
return nullptr;
#endif
}
// The `parseURL` function supports both IPv6 URIs as defined in

View file

@ -29,6 +29,7 @@
namespace nix {
using namespace nix::unix;
static std::string gcSocketPath = "/gc-socket/socket";
static std::string gcRootsDir = "gcroots";

View file

@ -52,6 +52,8 @@
namespace nix {
using namespace nix::unix;
std::string LocalStoreConfig::doc()
{
return

View file

@ -9,6 +9,8 @@
namespace nix {
using namespace nix::unix;
#if __linux__
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)

View file

@ -0,0 +1,38 @@
#pragma once
///@file
#include "file-descriptor.hh"
namespace nix::unix {
/**
* Open (possibly create) a lock file and return the file descriptor.
* -1 is returned if create is false and the lock could not be opened
* because it doesn't exist. Any other error throws an exception.
*/
AutoCloseFD openLockFile(const Path & path, bool create);
/**
* Delete an open lock file.
*/
void deleteLockFile(const Path & path, int fd);
enum LockType { ltRead, ltWrite, ltNone };
bool lockFile(int fd, LockType lockType, bool wait);
struct FdLock
{
int fd;
bool acquired = false;
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
~FdLock()
{
if (acquired)
lockFile(fd, ltNone, false);
}
};
}

View file

@ -0,0 +1,165 @@
#include "pathlocks.hh"
#include "util.hh"
#include "sync.hh"
#include "signals.hh"
#include <cerrno>
#include <cstdlib>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
namespace nix {
using namespace nix::unix;
AutoCloseFD unix::openLockFile(const Path & path, bool create)
{
AutoCloseFD fd;
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
if (!fd && (create || errno != ENOENT))
throw SysError("opening lock file '%1%'", path);
return fd;
}
void unix::deleteLockFile(const Path & path, int fd)
{
/* Get rid of the lock file. Have to be careful not to introduce
races. Write a (meaningless) token to the file to indicate to
other processes waiting on this lock that the lock is stale
(deleted). */
unlink(path.c_str());
writeFull(fd, "d");
/* Note that the result of unlink() is ignored; removing the lock
file is an optimisation, not a necessity. */
}
bool unix::lockFile(int fd, LockType lockType, bool wait)
{
int type;
if (lockType == ltRead) type = LOCK_SH;
else if (lockType == ltWrite) type = LOCK_EX;
else if (lockType == ltNone) type = LOCK_UN;
else abort();
if (wait) {
while (flock(fd, type) != 0) {
checkInterrupt();
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
else
return false;
}
} else {
while (flock(fd, type | LOCK_NB) != 0) {
checkInterrupt();
if (errno == EWOULDBLOCK) return false;
if (errno != EINTR)
throw SysError("acquiring/releasing lock");
}
}
return true;
}
bool PathLocks::lockPaths(const PathSet & paths,
const std::string & waitMsg, bool wait)
{
assert(fds.empty());
/* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */
/* Acquire the lock for each path in sorted order. This ensures
that locks are always acquired in the same order, thus
preventing deadlocks. */
for (auto & path : paths) {
checkInterrupt();
Path lockPath = path + ".lock";
debug("locking path '%1%'", path);
AutoCloseFD fd;
while (1) {
/* Open/create the lock file. */
fd = openLockFile(lockPath, true);
/* Acquire an exclusive lock. */
if (!lockFile(fd.get(), ltWrite, false)) {
if (wait) {
if (waitMsg != "") printError(waitMsg);
lockFile(fd.get(), ltWrite, true);
} else {
/* Failed to lock this path; release all other
locks. */
unlock();
return false;
}
}
debug("lock acquired on '%1%'", lockPath);
/* Check that the lock file hasn't become stale (i.e.,
hasn't been unlinked). */
struct stat st;
if (fstat(fd.get(), &st) == -1)
throw SysError("statting lock file '%1%'", lockPath);
if (st.st_size != 0)
/* This lock file has been unlinked, so we're holding
a lock on a deleted file. This means that other
processes may create and acquire a lock on
`lockPath', and proceed. So we must retry. */
debug("open lock file '%1%' has become stale", lockPath);
else
break;
}
/* Use borrow so that the descriptor isn't closed. */
fds.push_back(FDPair(fd.release(), lockPath));
}
return true;
}
void PathLocks::unlock()
{
for (auto & i : fds) {
if (deletePaths) deleteLockFile(i.second, i.first);
if (close(i.first) == -1)
printError(
"error (ignored): cannot close lock file on '%1%'",
i.second);
debug("lock released on '%1%'", i.second);
}
fds.clear();
}
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
: fd(fd)
{
if (wait) {
if (!lockFile(fd, lockType, false)) {
printInfo("%s", waitMsg);
acquired = lockFile(fd, lockType, true);
}
} else
acquired = lockFile(fd, lockType, false);
}
}

View 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");
}
}

View file

@ -0,0 +1,2 @@
#pragma once
///@file Needed because Unix-specific counterpart

View file

@ -0,0 +1,16 @@
#include "logging.hh"
#include "pathlocks.hh"
namespace nix {
bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait)
{
return true;
}
void PathLocks::unlock()
{
warn("PathLocks::unlock: not yet implemented");
}
}

View file

@ -9,7 +9,9 @@
#include <fstream>
#include <string>
#include <regex>
#include <glob.h>
#ifndef _WIN32
# include <glob.h>
#endif
namespace nix {
@ -547,6 +549,7 @@ nlohmann::json Args::toJSON()
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
{
completions.setType(Completions::Type::Filenames);
#ifndef _WIN32 // TODO implement globbing completions on Windows
glob_t globbuf;
int flags = GLOB_NOESCAPE;
#ifdef GLOB_ONLYDIR
@ -564,6 +567,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix,
}
}
globfree(&globbuf);
#endif
}
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)

View file

@ -19,7 +19,9 @@
# include "namespaces.hh"
#endif
#include <sys/mount.h>
#ifndef _WIN32
# include <sys/mount.h>
#endif
namespace nix {
@ -57,6 +59,7 @@ unsigned int getMaxCPU()
//////////////////////////////////////////////////////////////////////
#ifndef _WIN32
rlim_t savedStackSize = 0;
void setStackSize(rlim_t stackSize)
@ -79,16 +82,20 @@ void setStackSize(rlim_t stackSize)
}
}
}
#endif
void restoreProcessContext(bool restoreMounts)
{
#ifndef _WIN32
unix::restoreSignals();
#endif
if (restoreMounts) {
#if __linux__
restoreMountNamespace();
#endif
}
#ifndef _WIN32
if (savedStackSize) {
struct rlimit limit;
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
@ -96,6 +103,7 @@ void restoreProcessContext(bool restoreMounts)
setrlimit(RLIMIT_STACK, &limit);
}
}
#endif
}

View file

@ -2,7 +2,10 @@
///@file
#include <optional>
#include <sys/resource.h>
#ifndef _WIN32
# include <sys/resource.h>
#endif
#include "types.hh"
@ -14,16 +17,18 @@ namespace nix {
*/
unsigned int getMaxCPU();
#ifndef _WIN32 // TODO implement on Windows, if needed.
/**
* Change the stack size.
*/
void setStackSize(rlim_t stackSize);
#endif
/**
* Restore the original inherited Unix process context (such as signal
* masks, stack size).
* See startSignalHandlerThread(), saveSignalMask().
* See unix::startSignalHandlerThread(), unix::saveSignalMask().
*/
void restoreProcessContext(bool restoreMounts = true);

View file

@ -28,6 +28,13 @@ std::optional<std::string> getEnvNonEmpty(const std::string & key);
*/
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.
*

View file

@ -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
* handling works; see 'initLibUtil()'.

View file

@ -5,6 +5,11 @@
#include <fcntl.h>
#include <unistd.h>
#ifdef _WIN32
# include <winnt.h>
# include <fileapi.h>
# include "windows-error.hh"
#endif
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
// not care very much about the extra memory.
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);
#endif
return std::move(sink.s);
}
@ -68,9 +79,15 @@ Descriptor AutoCloseFD::get() const
void AutoCloseFD::close()
{
if (fd != INVALID_DESCRIPTOR) {
if(::close(fd) == -1)
if(
#ifdef _WIN32
::CloseHandle(fd)
#else
::close(fd)
#endif
== -1)
/* This should never happen. */
throw SysError("closing file descriptor %1%", fd);
throw NativeSysError("closing file descriptor %1%", fd);
fd = INVALID_DESCRIPTOR;
}
}
@ -80,14 +97,16 @@ void AutoCloseFD::fsync()
if (fd != INVALID_DESCRIPTOR) {
int result;
result =
#if __APPLE__
#ifdef _WIN32
::FlushFileBuffers(fd)
#elif __APPLE__
::fcntl(fd, F_FULLFSYNC)
#else
::fsync(fd)
#endif
;
if (result == -1)
throw SysError("fsync file descriptor %1%", fd);
throw NativeSysError("fsync file descriptor %1%", fd);
}
}

View file

@ -4,6 +4,11 @@
#include "types.hh"
#include "error.hh"
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
namespace nix {
struct Sink;
@ -12,9 +17,21 @@ struct Source;
/**
* 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
@ -23,17 +40,26 @@ const Descriptor INVALID_DESCRIPTOR = -1;
*/
static inline Descriptor toDescriptor(int fd)
{
#ifdef _WIN32
return (HANDLE) _get_osfhandle(fd);
#else
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.
*/
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;
#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);
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]]
inline Descriptor getStandardOut() {
#ifndef _WIN32
return STDOUT_FILENO;
#else
return GetStdHandle(STD_OUTPUT_HANDLE);
#endif
}
/**
@ -100,6 +139,8 @@ public:
void close();
};
#ifndef _WIN32 // Not needed on Windows, where we don't fork
/**
* Close all file descriptors except those listed in the given set.
* Good practice in child processes.
@ -111,6 +152,15 @@ void closeMostFDs(const std::set<Descriptor> & exceptions);
*/
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);
}

52
src/libutil/file-path.hh Normal file
View 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);
}

View file

@ -1,5 +1,6 @@
#include "environment-variables.hh"
#include "file-system.hh"
#include "file-path.hh"
#include "file-path-impl.hh"
#include "signals.hh"
#include "finally.hh"
@ -18,6 +19,10 @@
#include <sys/time.h>
#include <unistd.h>
#ifdef _WIN32
# include <io.h>
#endif
namespace fs = std::filesystem;
namespace nix {
@ -128,10 +133,10 @@ std::string_view baseNameOf(std::string_view path)
return "";
auto last = path.size() - 1;
while (last > 0 && path[last] == '/')
while (last > 0 && NativePathTrait::isPathSep(path[last]))
last -= 1;
auto pos = path.rfind('/', last);
auto pos = NativePathTrait::rfindPathSep(path, last);
if (pos == path.npos)
pos = 0;
else
@ -164,11 +169,16 @@ struct stat stat(const Path & path)
return st;
}
#ifdef _WIN32
# define STAT stat
#else
# define STAT lstat
#endif
struct stat lstat(const Path & path)
{
struct stat st;
if (lstat(path.c_str(), &st))
if (STAT(path.c_str(), &st))
throw SysError("getting status of '%1%'", path);
return st;
}
@ -177,7 +187,7 @@ struct stat lstat(const Path & path)
std::optional<struct stat> maybeLstat(const Path & path)
{
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)
st.reset();
@ -207,6 +217,7 @@ bool pathAccessible(const Path & path)
Path readLink(const Path & path)
{
#ifndef _WIN32
checkInterrupt();
std::vector<char> buf;
for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) {
@ -220,13 +231,16 @@ Path readLink(const Path & path)
else if (rlSize < bufSize)
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)
{
struct stat st = lstat(path);
return S_ISLNK(st.st_mode);
return getFileType(path) == DT_LNK;
}
@ -274,7 +288,12 @@ unsigned char getFileType(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)
throw SysError("opening file '%1%'", path);
return readFile(fd.get());
@ -283,7 +302,12 @@ std::string readFile(const Path & path)
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)
throw SysError("opening file '%s'", path);
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)
{
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)
throw SysError("opening file '%1%'", path);
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)
{
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)
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)
{
AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0);
AutoCloseFD fd = toDescriptor(open(dirOf(path).c_str(), O_RDONLY, 0));
if (!fd)
throw SysError("opening file '%1%'", path);
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();
std::string name(baseNameOf(path));
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;
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;
throw SysError("cannot unlink '%1%'", path);
}
#else
// TODO implement
throw UnimplementedError("_deletePath");
#endif
}
static void _deletePath(const Path & path, uint64_t & bytesFreed)
@ -413,7 +453,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed)
if (dir == "")
dir = "/";
AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)};
AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY));
if (!dirfd) {
if (errno == ENOENT) return;
throw SysError("opening directory '%1%'", path);
@ -436,11 +476,15 @@ Paths createDirs(const Path & path)
if (path == "/") return created;
struct stat st;
if (lstat(path.c_str(), &st) == -1) {
if (STAT(path.c_str(), &st) == -1) {
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);
st = lstat(path);
st = STAT(path);
created.push_back(path);
}
@ -526,7 +570,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
while (1) {
checkInterrupt();
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__
/* Explicitly set the group of the directory. This is to
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");
// Strictly speaking, this is UB, but who cares...
// FIXME: use O_TMPFILE.
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str()));
if (!fd)
throw SysError("creating temporary file '%s'", tmpl);
#ifndef _WIN32
closeOnExec(fd.get());
#endif
return {std::move(fd), tmpl};
}
void createSymlink(const Path & target, const Path & link)
{
#ifndef _WIN32
if (symlink(target.c_str(), link.c_str()))
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)
@ -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];
times[0] = {
@ -597,11 +653,14 @@ void setWriteTime(const fs::path & p, const struct stat & st)
if (lutimes(p.c_str(), times) != 0)
throw SysError("changing modification time of '%s'", p);
}
#endif
void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
{
#ifndef _WIN32
// TODO: Rewrite the `is_*` to use `symlink_status()`
auto statOfFrom = lstat(from.path().c_str());
#endif
auto fromStatus = from.symlink_status();
// 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());
}
#ifndef _WIN32
setWriteTime(to, statOfFrom);
#endif
if (andDelete) {
if (!fs::is_symlink(fromStatus))
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);
// For the move to be as atomic as possible, copy to a temporary
// 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); };
auto tempCopyTarget = temp / "copy-target";
if (e.code().value() == EXDEV) {
fs::remove(newPath);
warn("Cant rename %s as %s, copying instead", oldName, newName);
copy(fs::directory_entry(oldPath), tempCopyTarget, true);
renameFile(tempCopyTarget, newPath);
renameFile(
os_string_to_string(PathViewNG { tempCopyTarget }),
os_string_to_string(PathViewNG { newPath }));
}
}
}

View file

@ -14,6 +14,9 @@
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#ifdef _WIN32
# include <windef.h>
#endif
#include <signal.h>
#include <boost/lexical_cast.hpp>
@ -31,6 +34,17 @@
#define DT_DIR 3
#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 {
struct Sink;

View file

@ -1,8 +1,15 @@
#include <fcntl.h>
#include "error.hh"
#include "config.hh"
#include "fs-sink.hh"
#if _WIN32
# include <fileapi.h>
# include "file-path.hh"
# include "windows-error.hh"
#endif
namespace nix {
void copyRecursive(
@ -65,8 +72,14 @@ static GlobalConfig::Register r1(&restoreSinkSettings);
void RestoreSink::createDirectory(const Path & path)
{
Path p = dstPath + path;
if (mkdir(p.c_str(), 0777) == -1)
throw SysError("creating directory '%1%'", p);
if (
#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 {
@ -81,18 +94,28 @@ void RestoreSink::createRegularFile(const Path & path, std::function<void(Create
{
Path p = dstPath + path;
RestoreRegularFile crf;
crf.fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
if (!crf.fd) throw SysError("creating file '%1%'", p);
crf.fd =
#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);
}
void RestoreRegularFile::isExecutable()
{
// Windows doesn't have a notion of executable file permissions we
// care about here, right?
#ifndef _WIN32
struct stat st;
if (fstat(fd.get(), &st) == -1)
throw SysError("fstat");
if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
throw SysError("fchmod");
#endif
}
void RestoreRegularFile::preallocateContents(uint64_t len)

View file

@ -11,6 +11,9 @@ endif
ifdef HOST_LINUX
libutil_SOURCES += $(wildcard $(d)/linux/*.cc)
endif
ifdef HOST_WINDOWS
libutil_SOURCES += $(wildcard $(d)/windows/*.cc)
endif
# Not just for this library itself, but also for downstream libraries using this library
@ -21,6 +24,9 @@ endif
ifdef HOST_LINUX
INCLUDE_libutil += -I $(d)/linux
endif
ifdef HOST_WINDOWS
INCLUDE_libutil += -I $(d)/windows
endif
libutil_CXXFLAGS += $(INCLUDE_libutil)
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context

View file

@ -116,7 +116,13 @@ Verbosity verbosity = lvlInfo;
void writeToStderr(std::string_view s)
{
try {
writeFull(STDERR_FILENO, s, false);
writeFull(
#ifdef _WIN32
GetStdHandle(STD_ERROR_HANDLE),
#else
STDERR_FILENO,
#endif
s, false);
} catch (SystemError & e) {
/* Ignore failing writes to stderr. We need to ignore write
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};
static uint64_t getPid()
{
#ifndef _WIN32
return getpid();
#else
return GetCurrentProcessId();
#endif
}
Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
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);
}

View file

@ -10,7 +10,7 @@ PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root)
: root(std::move(root))
{
assert(root.empty() || root.is_absolute());
displayPrefix = root;
displayPrefix = root.string();
}
PosixSourceAccessor::PosixSourceAccessor()
@ -19,10 +19,10 @@ PosixSourceAccessor::PosixSourceAccessor()
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 {
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);
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)
throw SysError("opening file '%1%'", ap.native());
throw SysError("opening file '%1%'", ap.string());
struct stat st;
if (fstat(fd.get(), &st) == -1)
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)
throw SysError("statting file");
sizeCallback(st.st_size);
@ -62,7 +66,7 @@ void PosixSourceAccessor::readFile(
std::array<unsigned char, 64 * 1024> buf;
while (left) {
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 (errno != EINTR)
throw SysError("reading from file '%s'", showPath(path));
@ -80,7 +84,7 @@ void PosixSourceAccessor::readFile(
bool PosixSourceAccessor::pathExists(const CanonPath & path)
{
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)
@ -89,7 +93,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
// Note: we convert std::filesystem::path to Path because the
// former is not hashable on libc++.
Path absPath = makeAbsPath(path);
Path absPath = makeAbsPath(path).string();
{
auto cache(_cache.lock());
@ -127,11 +131,13 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
{
assertNoSymlinks(path);
DirEntries res;
for (auto & entry : nix::readDirectory(makeAbsPath(path))) {
for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) {
std::optional<Type> type;
switch (entry.type) {
case DT_REG: type = Type::tRegular; break;
#ifndef _WIN32
case DT_LNK: type = Type::tSymlink; break;
#endif
case DT_DIR: type = Type::tDirectory; break;
}
res.emplace(entry.name, type);
@ -142,7 +148,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
std::string PosixSourceAccessor::readLink(const CanonPath & path)
{
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)

View file

@ -25,6 +25,7 @@ namespace nix {
struct Sink;
struct Source;
#ifndef _WIN32
class Pid
{
pid_t pid = -1;
@ -43,13 +44,16 @@ public:
void setKillSignal(int signal);
pid_t release();
};
#endif
#ifndef _WIN32
/**
* Kill all processes running under the specified uid by sending them
* a SIGKILL.
*/
void killUser(uid_t uid);
#endif
/**
@ -68,8 +72,9 @@ struct ProcessOptions
int cloneFlags = 0;
};
#ifndef _WIN32
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
@ -84,8 +89,10 @@ struct RunOptions
Path program;
bool searchPath = true;
Strings args;
#ifndef _WIN32
std::optional<uid_t> uid;
std::optional<uid_t> gid;
#endif
std::optional<Path> chdir;
std::optional<std::map<std::string, std::string>> environment;
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
@ -120,4 +128,6 @@ std::string statusToString(int status);
bool statusOk(int status);
#endif
}

View file

@ -7,6 +7,11 @@
#include <boost/coroutine2/coroutine.hpp>
#ifdef _WIN32
# include <fileapi.h>
# include "windows-error.hh"
#endif
namespace nix {
@ -126,6 +131,14 @@ bool BufferedSource::hasData()
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;
do {
checkInterrupt();
@ -133,6 +146,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
} while (n == -1 && errno == EINTR);
if (n == -1) { _good = false; throw SysError("reading from file"); }
if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); }
#endif
read += n;
return n;
}

View file

@ -2,7 +2,12 @@
#include "environment-variables.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>
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}};
#ifndef _WIN32
void updateWindowSize()
{
struct winsize ws;
@ -101,6 +107,7 @@ void updateWindowSize()
windowSize_->second = ws.ws_col;
}
}
#endif
std::pair<unsigned short, unsigned short> getWindowSize()

View file

@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s,
bool filterAll = false,
unsigned int width = std::numeric_limits<unsigned int>::max());
#ifndef _WIN32
/**
* Recalculate the window size, updating a global variable. Used in the
* `SIGWINCH` signal handler.
*/
void updateWindowSize();
#endif
/**
* @return the number of rows and columns of the terminal.
*

View file

@ -81,8 +81,10 @@ void ThreadPool::doWork(bool mainThread)
{
ReceiveInterrupts receiveInterrupts;
#ifndef _WIN32 // Does Windows need anything similar for async exit handling?
if (!mainThread)
unix::interruptCheck = [&]() { return (bool) quit; };
#endif
bool didWork = false;
std::exception_ptr exc;

View 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;
}
}

View file

@ -3,16 +3,20 @@
#include "types.hh"
#include <sys/types.h>
#ifndef _WIN32
# include <sys/types.h>
#endif
namespace nix {
std::string getUserName();
#ifndef _WIN32
/**
* @return the given user's home directory from /etc/passwd.
*/
Path getHomeOf(uid_t userId);
#endif
/**
* @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?
*
* Currently always false on Windows, but that may change.
*/
bool isRootUser();

View file

@ -0,0 +1,17 @@
#include "environment-variables.hh"
#include "processenv.h"
namespace nix {
int unsetenv(const char *name)
{
return -SetEnvironmentVariableA(name, nullptr);
}
int setEnv(const char * name, const char * value)
{
return -SetEnvironmentVariableA(name, value);
}
}

Some files were not shown because too many files have changed in this diff Show more