From 852391765dd132c74ce3dc63b8ee07c6112b097c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 27 Mar 2024 12:01:07 -0400 Subject: [PATCH 1/2] Add unix (and linux) dirs In the Nix commit, platform-specific sources will go here. --- local.mk | 5 +++++ src/libcmd/local.mk | 2 +- src/libexpr/local.mk | 2 +- src/libfetchers/local.mk | 6 ++++++ src/libmain/local.mk | 7 ++++++- src/libstore/local.mk | 6 ++++++ src/libutil/local.mk | 12 ++++++++++++ src/nix/local.mk | 14 ++++++++++++-- src/resolve-system-dependencies/local.mk | 2 +- 9 files changed, 50 insertions(+), 6 deletions(-) diff --git a/local.mk b/local.mk index 7e3c77a65..408a82327 100644 --- a/local.mk +++ b/local.mk @@ -5,6 +5,11 @@ ERROR_SWITCH_ENUM = -Werror=switch-enum $(foreach i, config.h $(wildcard src/lib*/*.hh), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) +ifdef HOST_UNIX + $(foreach i, $(wildcard src/lib*/unix/*.hh), \ + $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) +endif + $(GCH): src/libutil/util.hh config.h GCH_CXXFLAGS = $(INCLUDE_libutil) diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk index 7a7c46ee2..9aa33a9d3 100644 --- a/src/libcmd/local.mk +++ b/src/libcmd/local.mk @@ -6,7 +6,7 @@ libcmd_DIR := $(d) libcmd_SOURCES := $(wildcard $(d)/*.cc) -libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain +libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS) diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 17f793ec3..ecadc5e5d 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,7 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) -libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) -I src/libmain $(INCLUDE_libexpr) +libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) libexpr_LIBS = libutil libstore libfetchers diff --git a/src/libfetchers/local.mk b/src/libfetchers/local.mk index e229a0993..0fef1466b 100644 --- a/src/libfetchers/local.mk +++ b/src/libfetchers/local.mk @@ -5,10 +5,16 @@ libfetchers_NAME = libnixfetchers libfetchers_DIR := $(d) libfetchers_SOURCES := $(wildcard $(d)/*.cc) +ifdef HOST_UNIX + libfetchers_SOURCES += $(wildcard $(d)/unix/*.cc) +endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libfetchers := -I $(d) +ifdef HOST_UNIX + INCLUDE_libfetchers += -I $(d)/unix +endif libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) diff --git a/src/libmain/local.mk b/src/libmain/local.mk index fde28a820..d41c49dd7 100644 --- a/src/libmain/local.mk +++ b/src/libmain/local.mk @@ -5,8 +5,13 @@ libmain_NAME = libnixmain libmain_DIR := $(d) libmain_SOURCES := $(wildcard $(d)/*.cc) +ifdef HOST_UNIX + libmain_SOURCES += $(wildcard $(d)/unix/*.cc) +endif -libmain_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) +INCLUDE_libmain := -I $(d) + +libmain_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libmain) libmain_LDFLAGS += $(OPENSSL_LIBS) diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 7eaa317e5..ccb7aeee2 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -5,6 +5,9 @@ libstore_NAME = libnixstore libstore_DIR := $(d) libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) +ifdef HOST_UNIX + libstore_SOURCES += $(wildcard $(d)/unix/*.cc) +endif libstore_LIBS = libutil @@ -30,6 +33,9 @@ endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libstore := -I $(d) -I $(d)/build +ifdef HOST_UNIX + INCLUDE_libstore += -I $(d)/unix +endif libstore_CXXFLAGS += \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ diff --git a/src/libutil/local.mk b/src/libutil/local.mk index 4ffb6bd9d..9773ef64f 100644 --- a/src/libutil/local.mk +++ b/src/libutil/local.mk @@ -5,10 +5,22 @@ libutil_NAME = libnixutil libutil_DIR := $(d) libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc) +ifdef HOST_UNIX + libutil_SOURCES += $(wildcard $(d)/unix/*.cc) +endif +ifdef HOST_LINUX + libutil_SOURCES += $(wildcard $(d)/linux/*.cc) +endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libutil := -I $(d) +ifdef HOST_UNIX + INCLUDE_libutil += -I $(d)/unix +endif +ifdef HOST_LINUX + INCLUDE_libutil += -I $(d)/linux +endif libutil_CXXFLAGS += $(INCLUDE_libutil) libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context diff --git a/src/nix/local.mk b/src/nix/local.mk index 55544b564..9f6f31b3a 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -12,9 +12,19 @@ nix_SOURCES := \ $(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-instantiate/*.cc) \ - $(wildcard src/nix-store/*.cc) \ + $(wildcard src/nix-store/*.cc) -nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain -I src/libcmd -I doc/manual +ifdef HOST_UNIX +nix_SOURCES += \ + $(wildcard $(d)/unix/*.cc) +endif + +INCLUDE_nix := -I $(d) +ifdef HOST_UNIX + INCLUDE_nix += -I $(d)/unix +endif + +nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd diff --git a/src/resolve-system-dependencies/local.mk b/src/resolve-system-dependencies/local.mk index f28fdab3b..e138c4080 100644 --- a/src/resolve-system-dependencies/local.mk +++ b/src/resolve-system-dependencies/local.mk @@ -6,7 +6,7 @@ resolve-system-dependencies_DIR := $(d) resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix -resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) -I src/libmain +resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libmain) resolve-system-dependencies_LIBS := libstore libmain libutil From 02fa20622f34e67bc551f4eb5b7c4d387641b9a6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 29 Mar 2024 15:59:14 -0400 Subject: [PATCH 2/2] Start factoring out Unix assumptions This splits files and adds new identifiers in preperation for supporting windows, but no Windows-specific code is actually added yet. Co-authored-by: Robert Hensing --- src/libfetchers/{ => unix}/git.cc | 0 src/libfetchers/{ => unix}/mercurial.cc | 0 src/libstore/build/local-derivation-goal.cc | 26 +-- src/libstore/filetransfer.cc | 7 +- src/libutil/current-process.cc | 4 +- src/libutil/environment-variables.cc | 14 -- src/libutil/file-descriptor.cc | 186 +++---------------- src/libutil/file-descriptor.hh | 58 ++++-- src/libutil/{ => linux}/namespaces.cc | 20 +- src/libutil/{ => linux}/namespaces.hh | 4 - src/libutil/logging.cc | 5 +- src/libutil/serialise.hh | 16 +- src/libutil/unix/environment-variables.cc | 21 +++ src/libutil/unix/file-descriptor.cc | 155 ++++++++++++++++ src/libutil/{ => unix}/monitor-fd.hh | 0 src/libutil/{ => unix}/processes.cc | 0 src/libutil/{ => unix}/processes.hh | 0 src/libutil/{ => unix}/signals.cc | 0 src/libutil/{ => unix}/signals.hh | 0 src/libutil/{ => unix}/unix-domain-socket.cc | 0 src/libutil/{ => unix}/unix-domain-socket.hh | 0 src/libutil/unix/users.cc | 66 +++++++ src/libutil/users.cc | 60 ------ src/libutil/util.cc | 1 - src/nix/main.cc | 5 +- src/nix/{ => unix}/daemon.cc | 0 src/nix/{ => unix}/daemon.md | 0 src/nix/{ => unix}/fmt.cc | 0 src/nix/{ => unix}/fmt.md | 0 src/nix/{ => unix}/run.cc | 0 src/nix/{ => unix}/run.hh | 0 src/nix/{ => unix}/run.md | 0 src/nix/{ => unix}/upgrade-nix.cc | 0 src/nix/{ => unix}/upgrade-nix.md | 0 34 files changed, 352 insertions(+), 296 deletions(-) rename src/libfetchers/{ => unix}/git.cc (100%) rename src/libfetchers/{ => unix}/mercurial.cc (100%) rename src/libutil/{ => linux}/namespaces.cc (95%) rename src/libutil/{ => linux}/namespaces.hh (96%) create mode 100644 src/libutil/unix/environment-variables.cc create mode 100644 src/libutil/unix/file-descriptor.cc rename src/libutil/{ => unix}/monitor-fd.hh (100%) rename src/libutil/{ => unix}/processes.cc (100%) rename src/libutil/{ => unix}/processes.hh (100%) rename src/libutil/{ => unix}/signals.cc (100%) rename src/libutil/{ => unix}/signals.hh (100%) rename src/libutil/{ => unix}/unix-domain-socket.cc (100%) rename src/libutil/{ => unix}/unix-domain-socket.hh (100%) create mode 100644 src/libutil/unix/users.cc rename src/nix/{ => unix}/daemon.cc (100%) rename src/nix/{ => unix}/daemon.md (100%) rename src/nix/{ => unix}/fmt.cc (100%) rename src/nix/{ => unix}/fmt.md (100%) rename src/nix/{ => unix}/run.cc (100%) rename src/nix/{ => unix}/run.hh (100%) rename src/nix/{ => unix}/run.md (100%) rename src/nix/{ => unix}/upgrade-nix.cc (100%) rename src/nix/{ => unix}/upgrade-nix.md (100%) diff --git a/src/libfetchers/git.cc b/src/libfetchers/unix/git.cc similarity index 100% rename from src/libfetchers/git.cc rename to src/libfetchers/unix/git.cc diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/unix/mercurial.cc similarity index 100% rename from src/libfetchers/mercurial.cc rename to src/libfetchers/unix/mercurial.cc diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 914fffd16..f8794e783 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -17,7 +17,6 @@ #include "cgroup.hh" #include "personality.hh" #include "current-process.hh" -#include "namespaces.hh" #include "child.hh" #include "unix-domain-socket.hh" #include "posix-fs-canonicalise.hh" @@ -40,18 +39,19 @@ /* Includes required for chroot support. */ #if __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SECCOMP -#include -#endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +# include +# include +# include +# include +# include +# include +# include +# include +# include "namespaces.hh" +# if HAVE_SECCOMP +# include +# endif +# define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif #if __APPLE__ diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index bab21bf51..0d5379d25 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -1,5 +1,4 @@ #include "filetransfer.hh" -#include "namespaces.hh" #include "globals.hh" #include "store-api.hh" #include "s3.hh" @@ -12,6 +11,10 @@ #include #endif +#if __linux__ +# include "namespaces.hh" +#endif + #include #include @@ -568,7 +571,9 @@ struct curlFileTransfer : public FileTransfer stopWorkerThread(); }); + #if __linux__ unshareFilesystem(); + #endif std::map> items; diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index f80f43ef0..c13e6d567 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -2,7 +2,6 @@ #include #include "current-process.hh" -#include "namespaces.hh" #include "util.hh" #include "finally.hh" #include "file-system.hh" @@ -17,6 +16,7 @@ # include # include # include "cgroup.hh" +# include "namespaces.hh" #endif #include @@ -84,7 +84,9 @@ void restoreProcessContext(bool restoreMounts) { restoreSignals(); if (restoreMounts) { + #if __linux__ restoreMountNamespace(); + #endif } if (savedStackSize) { diff --git a/src/libutil/environment-variables.cc b/src/libutil/environment-variables.cc index 6618d7872..7f4bb2d00 100644 --- a/src/libutil/environment-variables.cc +++ b/src/libutil/environment-variables.cc @@ -32,18 +32,4 @@ std::map getEnv() return env; } - -void clearEnv() -{ - for (auto & name : getEnv()) - unsetenv(name.first.c_str()); -} - -void replaceEnv(const std::map & newEnv) -{ - clearEnv(); - for (auto & newEnvVar : newEnv) - setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); -} - } diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 55d57e29b..95cbb8537 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -8,74 +8,14 @@ namespace nix { -std::string readFile(int fd) -{ - struct stat st; - if (fstat(fd, &st) == -1) - throw SysError("statting file"); - - return drainFD(fd, true, st.st_size); -} - - -void readFull(int fd, char * buf, size_t count) -{ - while (count) { - checkInterrupt(); - ssize_t res = read(fd, buf, count); - if (res == -1) { - if (errno == EINTR) continue; - throw SysError("reading from file"); - } - if (res == 0) throw EndOfFile("unexpected end-of-file"); - count -= res; - buf += res; - } -} - - -void writeFull(int fd, std::string_view s, bool allowInterrupts) -{ - while (!s.empty()) { - if (allowInterrupts) checkInterrupt(); - ssize_t res = write(fd, s.data(), s.size()); - if (res == -1 && errno != EINTR) - throw SysError("writing to file"); - if (res > 0) - s.remove_prefix(res); - } -} - - -std::string readLine(int fd) -{ - std::string s; - while (1) { - checkInterrupt(); - char ch; - // FIXME: inefficient - ssize_t rd = read(fd, &ch, 1); - if (rd == -1) { - if (errno != EINTR) - throw SysError("reading a line"); - } else if (rd == 0) - throw EndOfFile("unexpected EOF reading a line"); - else { - if (ch == '\n') return s; - s += ch; - } - } -} - - -void writeLine(int fd, std::string s) +void writeLine(Descriptor fd, std::string s) { s += '\n'; writeFull(fd, s); } -std::string drainFD(int fd, bool block, const size_t reserveSize) +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. @@ -85,50 +25,18 @@ std::string drainFD(int fd, bool block, const size_t reserveSize) } -void drainFD(int fd, Sink & sink, bool block) -{ - // silence GCC maybe-uninitialized warning in finally - int saved = 0; - - if (!block) { - saved = fcntl(fd, F_GETFL); - if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) - throw SysError("making file descriptor non-blocking"); - } - - Finally finally([&] { - if (!block) { - if (fcntl(fd, F_SETFL, saved) == -1) - throw SysError("making file descriptor blocking"); - } - }); - - std::vector buf(64 * 1024); - while (1) { - checkInterrupt(); - ssize_t rd = read(fd, buf.data(), buf.size()); - if (rd == -1) { - if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) - break; - if (errno != EINTR) - throw SysError("reading from file"); - } - else if (rd == 0) break; - else sink({reinterpret_cast(buf.data()), size_t(rd)}); - } -} - ////////////////////////////////////////////////////////////////////// -AutoCloseFD::AutoCloseFD() : fd{-1} {} + +AutoCloseFD::AutoCloseFD() : fd{INVALID_DESCRIPTOR} {} -AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {} +AutoCloseFD::AutoCloseFD(Descriptor fd) : fd{fd} {} AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd} { - that.fd = -1; + that.fd = INVALID_DESCRIPTOR; } @@ -136,7 +44,7 @@ AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) { close(); fd = that.fd; - that.fd = -1; + that.fd = INVALID_DESCRIPTOR; return *this; } @@ -151,7 +59,7 @@ AutoCloseFD::~AutoCloseFD() } -int AutoCloseFD::get() const +Descriptor AutoCloseFD::get() const { return fd; } @@ -159,56 +67,46 @@ int AutoCloseFD::get() const void AutoCloseFD::close() { - if (fd != -1) { - if (::close(fd) == -1) + if (fd != INVALID_DESCRIPTOR) { + if(::close(fd) == -1) /* This should never happen. */ throw SysError("closing file descriptor %1%", fd); - fd = -1; + fd = INVALID_DESCRIPTOR; } } void AutoCloseFD::fsync() { - if (fd != -1) { - int result; + if (fd != INVALID_DESCRIPTOR) { + int result; + result = #if __APPLE__ - result = ::fcntl(fd, F_FULLFSYNC); + ::fcntl(fd, F_FULLFSYNC) #else - result = ::fsync(fd); + ::fsync(fd) #endif - if (result == -1) - throw SysError("fsync file descriptor %1%", fd); - } + ; + if (result == -1) + throw SysError("fsync file descriptor %1%", fd); + } } AutoCloseFD::operator bool() const { - return fd != -1; + return fd != INVALID_DESCRIPTOR; } -int AutoCloseFD::release() +Descriptor AutoCloseFD::release() { - int oldFD = fd; - fd = -1; + Descriptor oldFD = fd; + fd = INVALID_DESCRIPTOR; return oldFD; } -void Pipe::create() -{ - int fds[2]; -#if HAVE_PIPE2 - if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); -#else - if (pipe(fds) != 0) throw SysError("creating pipe"); - closeOnExec(fds[0]); - closeOnExec(fds[1]); -#endif - readSide = fds[0]; - writeSide = fds[1]; -} +////////////////////////////////////////////////////////////////////// void Pipe::close() @@ -217,38 +115,4 @@ void Pipe::close() writeSide.close(); } -////////////////////////////////////////////////////////////////////// - -void closeMostFDs(const std::set & exceptions) -{ -#if __linux__ - try { - for (auto & s : readDirectory("/proc/self/fd")) { - auto fd = std::stoi(s.name); - if (!exceptions.count(fd)) { - debug("closing leaked FD %d", fd); - close(fd); - } - } - return; - } catch (SystemError &) { - } -#endif - - int maxFD = 0; - maxFD = sysconf(_SC_OPEN_MAX); - for (int fd = 0; fd < maxFD; ++fd) - if (!exceptions.count(fd)) - close(fd); /* ignore result */ -} - - -void closeOnExec(int fd) -{ - int prev; - if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || - fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) - throw SysError("setting close-on-exec flag"); -} - } diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 80ec86135..719e1e444 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -9,53 +9,85 @@ namespace nix { struct Sink; struct Source; +/** + * Operating System capability + */ +typedef int Descriptor; + +const Descriptor INVALID_DESCRIPTOR = -1; + +/** + * Convert a native `Descriptor` to a POSIX file descriptor + * + * This is a no-op except on Windows. + */ +static inline Descriptor toDescriptor(int fd) +{ + return fd; +} + +/** + * Convert a POSIX file descriptor to a native `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline int fromDescriptor(Descriptor fd, int flags) +{ + return fd; +} + /** * Read the contents of a resource into a string. */ -std::string readFile(int fd); +std::string readFile(Descriptor fd); /** * Wrappers arount read()/write() that read/write exactly the * requested number of bytes. */ -void readFull(int fd, char * buf, size_t count); +void readFull(Descriptor fd, char * buf, size_t count); -void writeFull(int fd, std::string_view s, bool allowInterrupts = true); +void writeFull(Descriptor fd, std::string_view s, bool allowInterrupts = true); /** * Read a line from a file descriptor. */ -std::string readLine(int fd); +std::string readLine(Descriptor fd); /** * Write a line to a file descriptor. */ -void writeLine(int fd, std::string s); +void writeLine(Descriptor fd, std::string s); /** * Read a file descriptor until EOF occurs. */ -std::string drainFD(int fd, bool block = true, const size_t reserveSize=0); +std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0); -void drainFD(int fd, Sink & sink, bool block = true); +void drainFD(Descriptor fd, Sink & sink, bool block = true); + +[[gnu::always_inline]] +inline Descriptor getStandardOut() { + return STDOUT_FILENO; +} /** * Automatic cleanup of resources. */ class AutoCloseFD { - int fd; + Descriptor fd; public: AutoCloseFD(); - AutoCloseFD(int fd); + AutoCloseFD(Descriptor fd); AutoCloseFD(const AutoCloseFD & fd) = delete; AutoCloseFD(AutoCloseFD&& fd); ~AutoCloseFD(); AutoCloseFD& operator =(const AutoCloseFD & fd) = delete; AutoCloseFD& operator =(AutoCloseFD&& fd); - int get() const; + Descriptor get() const; explicit operator bool() const; - int release(); + Descriptor release(); void close(); void fsync(); }; @@ -72,12 +104,12 @@ public: * Close all file descriptors except those listed in the given set. * Good practice in child processes. */ -void closeMostFDs(const std::set & exceptions); +void closeMostFDs(const std::set & exceptions); /** * Set the close-on-exec flag for the given file descriptor. */ -void closeOnExec(int fd); +void closeOnExec(Descriptor fd); MakeError(EndOfFile, Error); diff --git a/src/libutil/namespaces.cc b/src/libutil/linux/namespaces.cc similarity index 95% rename from src/libutil/namespaces.cc rename to src/libutil/linux/namespaces.cc index a789b321e..f8289ef39 100644 --- a/src/libutil/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -5,18 +5,14 @@ #include "processes.hh" #include "signals.hh" -#if __linux__ -# include -# include -# include "cgroup.hh" -#endif +#include +#include +#include "cgroup.hh" #include namespace nix { -#if __linux__ - bool userNamespacesSupported() { static auto res = [&]() -> bool @@ -101,19 +97,14 @@ bool mountAndPidNamespacesSupported() return res; } -#endif - ////////////////////////////////////////////////////////////////////// -#if __linux__ static AutoCloseFD fdSavedMountNamespace; static AutoCloseFD fdSavedRoot; -#endif void saveMountNamespace() { -#if __linux__ static std::once_flag done; std::call_once(done, []() { fdSavedMountNamespace = open("/proc/self/ns/mnt", O_RDONLY); @@ -122,12 +113,10 @@ void saveMountNamespace() fdSavedRoot = open("/proc/self/root", O_RDONLY); }); -#endif } void restoreMountNamespace() { -#if __linux__ try { auto savedCwd = absPath("."); @@ -146,15 +135,12 @@ void restoreMountNamespace() } catch (Error & e) { debug(e.msg()); } -#endif } void unshareFilesystem() { -#ifdef __linux__ if (unshare(CLONE_FS) != 0 && errno != EPERM) throw SysError("unsharing filesystem state in download thread"); -#endif } } diff --git a/src/libutil/namespaces.hh b/src/libutil/linux/namespaces.hh similarity index 96% rename from src/libutil/namespaces.hh rename to src/libutil/linux/namespaces.hh index 7e4e921a8..ef3c9123f 100644 --- a/src/libutil/namespaces.hh +++ b/src/libutil/linux/namespaces.hh @@ -26,12 +26,8 @@ void restoreMountNamespace(); */ void unshareFilesystem(); -#if __linux__ - bool userNamespacesSupported(); bool mountAndPidNamespacesSupported(); -#endif - } diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 83db492ca..5024c6081 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -37,8 +37,9 @@ void Logger::warn(const std::string & msg) void Logger::writeToStdout(std::string_view s) { - writeFull(STDOUT_FILENO, s); - writeFull(STDOUT_FILENO, "\n"); + Descriptor standard_out = getStandardOut(); + writeFull(standard_out, s); + writeFull(standard_out, "\n"); } class SimpleLogger : public Logger diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index d9522566f..6249ddaf5 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -119,18 +119,18 @@ protected: */ struct FdSink : BufferedSink { - int fd; + Descriptor fd; size_t written = 0; - FdSink() : fd(-1) { } - FdSink(int fd) : fd(fd) { } + FdSink() : fd(INVALID_DESCRIPTOR) { } + FdSink(Descriptor fd) : fd(fd) { } FdSink(FdSink&&) = default; FdSink & operator=(FdSink && s) { flush(); fd = s.fd; - s.fd = -1; + s.fd = INVALID_DESCRIPTOR; written = s.written; return *this; } @@ -151,18 +151,18 @@ private: */ struct FdSource : BufferedSource { - int fd; + Descriptor fd; size_t read = 0; BackedStringView endOfFileError{"unexpected end-of-file"}; - FdSource() : fd(-1) { } - FdSource(int fd) : fd(fd) { } + FdSource() : fd(INVALID_DESCRIPTOR) { } + FdSource(Descriptor fd) : fd(fd) { } FdSource(FdSource &&) = default; FdSource & operator=(FdSource && s) { fd = s.fd; - s.fd = -1; + s.fd = INVALID_DESCRIPTOR; read = s.read; return *this; } diff --git a/src/libutil/unix/environment-variables.cc b/src/libutil/unix/environment-variables.cc new file mode 100644 index 000000000..c72880896 --- /dev/null +++ b/src/libutil/unix/environment-variables.cc @@ -0,0 +1,21 @@ +#include "util.hh" +#include "environment-variables.hh" + +extern char * * environ __attribute__((weak)); + +namespace nix { + +void clearEnv() +{ + for (auto & name : getEnv()) + unsetenv(name.first.c_str()); +} + +void replaceEnv(const std::map & newEnv) +{ + clearEnv(); + for (auto & newEnvVar : newEnv) + setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); +} + +} diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc new file mode 100644 index 000000000..27c8d821b --- /dev/null +++ b/src/libutil/unix/file-descriptor.cc @@ -0,0 +1,155 @@ +#include "file-system.hh" +#include "signals.hh" +#include "finally.hh" +#include "serialise.hh" + +#include +#include + +namespace nix { + +std::string readFile(int fd) +{ + struct stat st; + if (fstat(fd, &st) == -1) + throw SysError("statting file"); + + return drainFD(fd, true, st.st_size); +} + + +void readFull(int fd, char * buf, size_t count) +{ + while (count) { + checkInterrupt(); + ssize_t res = read(fd, buf, count); + if (res == -1) { + if (errno == EINTR) continue; + throw SysError("reading from file"); + } + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + + +void writeFull(int fd, std::string_view s, bool allowInterrupts) +{ + while (!s.empty()) { + if (allowInterrupts) checkInterrupt(); + ssize_t res = write(fd, s.data(), s.size()); + if (res == -1 && errno != EINTR) + throw SysError("writing to file"); + if (res > 0) + s.remove_prefix(res); + } +} + + +std::string readLine(int fd) +{ + std::string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + ssize_t rd = read(fd, &ch, 1); + if (rd == -1) { + if (errno != EINTR) + throw SysError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; + } + } +} + + +void drainFD(int fd, Sink & sink, bool block) +{ + // silence GCC maybe-uninitialized warning in finally + int saved = 0; + + if (!block) { + saved = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) + throw SysError("making file descriptor non-blocking"); + } + + Finally finally([&]() { + if (!block) { + if (fcntl(fd, F_SETFL, saved) == -1) + throw SysError("making file descriptor blocking"); + } + }); + + std::vector buf(64 * 1024); + while (1) { + checkInterrupt(); + ssize_t rd = read(fd, buf.data(), buf.size()); + if (rd == -1) { + if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) + break; + if (errno != EINTR) + throw SysError("reading from file"); + } + else if (rd == 0) break; + else sink({reinterpret_cast(buf.data()), (size_t) rd}); + } +} + +////////////////////////////////////////////////////////////////////// + +void Pipe::create() +{ + int fds[2]; +#if HAVE_PIPE2 + if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); +#else + if (pipe(fds) != 0) throw SysError("creating pipe"); + closeOnExec(fds[0]); + closeOnExec(fds[1]); +#endif + readSide = fds[0]; + writeSide = fds[1]; +} + + +////////////////////////////////////////////////////////////////////// + +void closeMostFDs(const std::set & exceptions) +{ +#if __linux__ + try { + for (auto & s : readDirectory("/proc/self/fd")) { + auto fd = std::stoi(s.name); + if (!exceptions.count(fd)) { + debug("closing leaked FD %d", fd); + close(fd); + } + } + return; + } catch (SysError &) { + } +#endif + + int maxFD = 0; + maxFD = sysconf(_SC_OPEN_MAX); + for (int fd = 0; fd < maxFD; ++fd) + if (!exceptions.count(fd)) + close(fd); /* ignore result */ +} + + +void closeOnExec(int fd) +{ + int prev; + if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || + fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) + throw SysError("setting close-on-exec flag"); +} + +} diff --git a/src/libutil/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh similarity index 100% rename from src/libutil/monitor-fd.hh rename to src/libutil/unix/monitor-fd.hh diff --git a/src/libutil/processes.cc b/src/libutil/unix/processes.cc similarity index 100% rename from src/libutil/processes.cc rename to src/libutil/unix/processes.cc diff --git a/src/libutil/processes.hh b/src/libutil/unix/processes.hh similarity index 100% rename from src/libutil/processes.hh rename to src/libutil/unix/processes.hh diff --git a/src/libutil/signals.cc b/src/libutil/unix/signals.cc similarity index 100% rename from src/libutil/signals.cc rename to src/libutil/unix/signals.cc diff --git a/src/libutil/signals.hh b/src/libutil/unix/signals.hh similarity index 100% rename from src/libutil/signals.hh rename to src/libutil/unix/signals.hh diff --git a/src/libutil/unix-domain-socket.cc b/src/libutil/unix/unix-domain-socket.cc similarity index 100% rename from src/libutil/unix-domain-socket.cc rename to src/libutil/unix/unix-domain-socket.cc diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix/unix-domain-socket.hh similarity index 100% rename from src/libutil/unix-domain-socket.hh rename to src/libutil/unix/unix-domain-socket.hh diff --git a/src/libutil/unix/users.cc b/src/libutil/unix/users.cc new file mode 100644 index 000000000..58063a953 --- /dev/null +++ b/src/libutil/unix/users.cc @@ -0,0 +1,66 @@ +#include "util.hh" +#include "users.hh" +#include "environment-variables.hh" +#include "file-system.hh" + +#include +#include +#include + +namespace nix { + +std::string getUserName() +{ + auto pw = getpwuid(geteuid()); + std::string name = pw ? pw->pw_name : getEnv("USER").value_or(""); + if (name.empty()) + throw Error("cannot figure out user name"); + return name; +} + +Path getHomeOf(uid_t userId) +{ + std::vector buf(16384); + struct passwd pwbuf; + struct passwd * pw; + if (getpwuid_r(userId, &pwbuf, buf.data(), buf.size(), &pw) != 0 + || !pw || !pw->pw_dir || !pw->pw_dir[0]) + throw Error("cannot determine user's home directory"); + return pw->pw_dir; +} + +Path getHome() +{ + static Path homeDir = []() + { + std::optional unownedUserHomeDir = {}; + auto homeDir = getEnv("HOME"); + if (homeDir) { + // Only use $HOME if doesn't exist or is owned by the current user. + struct stat st; + int result = stat(homeDir->c_str(), &st); + if (result != 0) { + if (errno != ENOENT) { + warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno); + homeDir.reset(); + } + } else if (st.st_uid != geteuid()) { + unownedUserHomeDir.swap(homeDir); + } + } + if (!homeDir) { + homeDir = getHomeOf(geteuid()); + if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) { + warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir); + } + } + return *homeDir; + }(); + return homeDir; +} + +bool isRootUser() { + return getuid() == 0; +} + +} diff --git a/src/libutil/users.cc b/src/libutil/users.cc index 8cb3db434..d546e364f 100644 --- a/src/libutil/users.cc +++ b/src/libutil/users.cc @@ -3,63 +3,8 @@ #include "environment-variables.hh" #include "file-system.hh" -#include -#include -#include - namespace nix { -std::string getUserName() -{ - auto pw = getpwuid(geteuid()); - std::string name = pw ? pw->pw_name : getEnv("USER").value_or(""); - if (name.empty()) - throw Error("cannot figure out user name"); - return name; -} - -Path getHomeOf(uid_t userId) -{ - std::vector buf(16384); - struct passwd pwbuf; - struct passwd * pw; - if (getpwuid_r(userId, &pwbuf, buf.data(), buf.size(), &pw) != 0 - || !pw || !pw->pw_dir || !pw->pw_dir[0]) - throw Error("cannot determine user's home directory"); - return pw->pw_dir; -} - -Path getHome() -{ - static Path homeDir = []() - { - std::optional unownedUserHomeDir = {}; - auto homeDir = getEnv("HOME"); - if (homeDir) { - // Only use $HOME if doesn't exist or is owned by the current user. - struct stat st; - int result = stat(homeDir->c_str(), &st); - if (result != 0) { - if (errno != ENOENT) { - warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno); - homeDir.reset(); - } - } else if (st.st_uid != geteuid()) { - unownedUserHomeDir.swap(homeDir); - } - } - if (!homeDir) { - homeDir = getHomeOf(geteuid()); - if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) { - warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir); - } - } - return *homeDir; - }(); - return homeDir; -} - - Path getCacheDir() { auto cacheDir = getEnv("XDG_CACHE_HOME"); @@ -113,9 +58,4 @@ std::string expandTilde(std::string_view path) return std::string(path); } - -bool isRootUser() { - return getuid() == 0; -} - } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 06124bf15..103ce4232 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/nix/main.cc b/src/nix/main.cc index 36256f3d0..25f81e48b 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -2,7 +2,6 @@ #include "args/root.hh" #include "current-process.hh" -#include "namespaces.hh" #include "command.hh" #include "common-args.hh" #include "eval.hh" @@ -27,6 +26,10 @@ #include +#if __linux__ +# include "namespaces.hh" +#endif + extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); diff --git a/src/nix/daemon.cc b/src/nix/unix/daemon.cc similarity index 100% rename from src/nix/daemon.cc rename to src/nix/unix/daemon.cc diff --git a/src/nix/daemon.md b/src/nix/unix/daemon.md similarity index 100% rename from src/nix/daemon.md rename to src/nix/unix/daemon.md diff --git a/src/nix/fmt.cc b/src/nix/unix/fmt.cc similarity index 100% rename from src/nix/fmt.cc rename to src/nix/unix/fmt.cc diff --git a/src/nix/fmt.md b/src/nix/unix/fmt.md similarity index 100% rename from src/nix/fmt.md rename to src/nix/unix/fmt.md diff --git a/src/nix/run.cc b/src/nix/unix/run.cc similarity index 100% rename from src/nix/run.cc rename to src/nix/unix/run.cc diff --git a/src/nix/run.hh b/src/nix/unix/run.hh similarity index 100% rename from src/nix/run.hh rename to src/nix/unix/run.hh diff --git a/src/nix/run.md b/src/nix/unix/run.md similarity index 100% rename from src/nix/run.md rename to src/nix/unix/run.md diff --git a/src/nix/upgrade-nix.cc b/src/nix/unix/upgrade-nix.cc similarity index 100% rename from src/nix/upgrade-nix.cc rename to src/nix/unix/upgrade-nix.cc diff --git a/src/nix/upgrade-nix.md b/src/nix/unix/upgrade-nix.md similarity index 100% rename from src/nix/upgrade-nix.md rename to src/nix/unix/upgrade-nix.md