Merge pull request #10364 from obsidiansystems/split-out-unix

Start factoring out Unix-assuming code
This commit is contained in:
John Ericson 2024-04-02 15:07:36 -04:00 committed by GitHub
commit 478c05308c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 402 additions and 302 deletions

View file

@ -5,6 +5,11 @@ ERROR_SWITCH_ENUM = -Werror=switch-enum
$(foreach i, config.h $(wildcard src/lib*/*.hh), \ $(foreach i, config.h $(wildcard src/lib*/*.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) $(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): src/libutil/util.hh config.h
GCH_CXXFLAGS = $(INCLUDE_libutil) GCH_CXXFLAGS = $(INCLUDE_libutil)

View file

@ -6,7 +6,7 @@ libcmd_DIR := $(d)
libcmd_SOURCES := $(wildcard $(d)/*.cc) 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) libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS)

View file

@ -15,7 +15,7 @@ libexpr_SOURCES := \
INCLUDE_libexpr := -I $(d) 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 libexpr_LIBS = libutil libstore libfetchers

View file

@ -5,10 +5,16 @@ libfetchers_NAME = libnixfetchers
libfetchers_DIR := $(d) libfetchers_DIR := $(d)
libfetchers_SOURCES := $(wildcard $(d)/*.cc) 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 # Not just for this library itself, but also for downstream libraries using this library
INCLUDE_libfetchers := -I $(d) INCLUDE_libfetchers := -I $(d)
ifdef HOST_UNIX
INCLUDE_libfetchers += -I $(d)/unix
endif
libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers)

View file

@ -5,8 +5,13 @@ libmain_NAME = libnixmain
libmain_DIR := $(d) libmain_DIR := $(d)
libmain_SOURCES := $(wildcard $(d)/*.cc) 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) libmain_LDFLAGS += $(OPENSSL_LIBS)

View file

@ -17,7 +17,6 @@
#include "cgroup.hh" #include "cgroup.hh"
#include "personality.hh" #include "personality.hh"
#include "current-process.hh" #include "current-process.hh"
#include "namespaces.hh"
#include "child.hh" #include "child.hh"
#include "unix-domain-socket.hh" #include "unix-domain-socket.hh"
#include "posix-fs-canonicalise.hh" #include "posix-fs-canonicalise.hh"
@ -40,18 +39,19 @@
/* Includes required for chroot support. */ /* Includes required for chroot support. */
#if __linux__ #if __linux__
#include <sys/ioctl.h> # include <sys/ioctl.h>
#include <net/if.h> # include <net/if.h>
#include <netinet/ip.h> # include <netinet/ip.h>
#include <sys/mman.h> # include <sys/mman.h>
#include <sched.h> # include <sched.h>
#include <sys/param.h> # include <sys/param.h>
#include <sys/mount.h> # include <sys/mount.h>
#include <sys/syscall.h> # include <sys/syscall.h>
#if HAVE_SECCOMP # include "namespaces.hh"
#include <seccomp.h> # if HAVE_SECCOMP
#endif # include <seccomp.h>
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) # endif
# define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
#endif #endif
#if __APPLE__ #if __APPLE__

View file

@ -1,5 +1,4 @@
#include "filetransfer.hh" #include "filetransfer.hh"
#include "namespaces.hh"
#include "globals.hh" #include "globals.hh"
#include "store-api.hh" #include "store-api.hh"
#include "s3.hh" #include "s3.hh"
@ -12,6 +11,10 @@
#include <aws/core/client/ClientConfiguration.h> #include <aws/core/client/ClientConfiguration.h>
#endif #endif
#if __linux__
# include "namespaces.hh"
#endif
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
@ -568,7 +571,9 @@ struct curlFileTransfer : public FileTransfer
stopWorkerThread(); stopWorkerThread();
}); });
#if __linux__
unshareFilesystem(); unshareFilesystem();
#endif
std::map<CURL *, std::shared_ptr<TransferItem>> items; std::map<CURL *, std::shared_ptr<TransferItem>> items;

View file

@ -5,6 +5,9 @@ libstore_NAME = libnixstore
libstore_DIR := $(d) libstore_DIR := $(d)
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
ifdef HOST_UNIX
libstore_SOURCES += $(wildcard $(d)/unix/*.cc)
endif
libstore_LIBS = libutil libstore_LIBS = libutil
@ -30,6 +33,9 @@ endif
# Not just for this library itself, but also for downstream libraries using this library # Not just for this library itself, but also for downstream libraries using this library
INCLUDE_libstore := -I $(d) -I $(d)/build INCLUDE_libstore := -I $(d) -I $(d)/build
ifdef HOST_UNIX
INCLUDE_libstore += -I $(d)/unix
endif
libstore_CXXFLAGS += \ libstore_CXXFLAGS += \
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \

View file

@ -2,7 +2,6 @@
#include <cstring> #include <cstring>
#include "current-process.hh" #include "current-process.hh"
#include "namespaces.hh"
#include "util.hh" #include "util.hh"
#include "finally.hh" #include "finally.hh"
#include "file-system.hh" #include "file-system.hh"
@ -17,6 +16,7 @@
# include <mutex> # include <mutex>
# include <sys/resource.h> # include <sys/resource.h>
# include "cgroup.hh" # include "cgroup.hh"
# include "namespaces.hh"
#endif #endif
#include <sys/mount.h> #include <sys/mount.h>
@ -84,7 +84,9 @@ void restoreProcessContext(bool restoreMounts)
{ {
restoreSignals(); restoreSignals();
if (restoreMounts) { if (restoreMounts) {
#if __linux__
restoreMountNamespace(); restoreMountNamespace();
#endif
} }
if (savedStackSize) { if (savedStackSize) {

View file

@ -32,18 +32,4 @@ std::map<std::string, std::string> getEnv()
return env; return env;
} }
void clearEnv()
{
for (auto & name : getEnv())
unsetenv(name.first.c_str());
}
void replaceEnv(const std::map<std::string, std::string> & newEnv)
{
clearEnv();
for (auto & newEnvVar : newEnv)
setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1);
}
} }

View file

@ -8,74 +8,14 @@
namespace nix { namespace nix {
std::string readFile(int fd) void writeLine(Descriptor fd, std::string s)
{
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)
{ {
s += '\n'; s += '\n';
writeFull(fd, s); 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 // the parser needs two extra bytes to append terminating characters, other users will
// not care very much about the extra memory. // not care very much about the extra memory.
@ -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<unsigned char> 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<char *>(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} AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd}
{ {
that.fd = -1; that.fd = INVALID_DESCRIPTOR;
} }
@ -136,7 +44,7 @@ AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that)
{ {
close(); close();
fd = that.fd; fd = that.fd;
that.fd = -1; that.fd = INVALID_DESCRIPTOR;
return *this; return *this;
} }
@ -151,7 +59,7 @@ AutoCloseFD::~AutoCloseFD()
} }
int AutoCloseFD::get() const Descriptor AutoCloseFD::get() const
{ {
return fd; return fd;
} }
@ -159,56 +67,46 @@ int AutoCloseFD::get() const
void AutoCloseFD::close() void AutoCloseFD::close()
{ {
if (fd != -1) { if (fd != INVALID_DESCRIPTOR) {
if (::close(fd) == -1) if(::close(fd) == -1)
/* This should never happen. */ /* This should never happen. */
throw SysError("closing file descriptor %1%", fd); throw SysError("closing file descriptor %1%", fd);
fd = -1; fd = INVALID_DESCRIPTOR;
} }
} }
void AutoCloseFD::fsync() void AutoCloseFD::fsync()
{ {
if (fd != -1) { if (fd != INVALID_DESCRIPTOR) {
int result; int result;
result =
#if __APPLE__ #if __APPLE__
result = ::fcntl(fd, F_FULLFSYNC); ::fcntl(fd, F_FULLFSYNC)
#else #else
result = ::fsync(fd); ::fsync(fd)
#endif #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 AutoCloseFD::operator bool() const
{ {
return fd != -1; return fd != INVALID_DESCRIPTOR;
} }
int AutoCloseFD::release() Descriptor AutoCloseFD::release()
{ {
int oldFD = fd; Descriptor oldFD = fd;
fd = -1; fd = INVALID_DESCRIPTOR;
return oldFD; 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() void Pipe::close()
@ -217,38 +115,4 @@ void Pipe::close()
writeSide.close(); writeSide.close();
} }
//////////////////////////////////////////////////////////////////////
void closeMostFDs(const std::set<int> & 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");
}
} }

View file

@ -9,53 +9,85 @@ namespace nix {
struct Sink; struct Sink;
struct Source; 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. * 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 * Wrappers arount read()/write() that read/write exactly the
* requested number of bytes. * 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. * Read a line from a file descriptor.
*/ */
std::string readLine(int fd); std::string readLine(Descriptor fd);
/** /**
* Write a line to a file descriptor. * 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. * 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. * Automatic cleanup of resources.
*/ */
class AutoCloseFD class AutoCloseFD
{ {
int fd; Descriptor fd;
public: public:
AutoCloseFD(); AutoCloseFD();
AutoCloseFD(int fd); AutoCloseFD(Descriptor fd);
AutoCloseFD(const AutoCloseFD & fd) = delete; AutoCloseFD(const AutoCloseFD & fd) = delete;
AutoCloseFD(AutoCloseFD&& fd); AutoCloseFD(AutoCloseFD&& fd);
~AutoCloseFD(); ~AutoCloseFD();
AutoCloseFD& operator =(const AutoCloseFD & fd) = delete; AutoCloseFD& operator =(const AutoCloseFD & fd) = delete;
AutoCloseFD& operator =(AutoCloseFD&& fd); AutoCloseFD& operator =(AutoCloseFD&& fd);
int get() const; Descriptor get() const;
explicit operator bool() const; explicit operator bool() const;
int release(); Descriptor release();
void close(); void close();
void fsync(); void fsync();
}; };
@ -72,12 +104,12 @@ public:
* Close all file descriptors except those listed in the given set. * Close all file descriptors except those listed in the given set.
* Good practice in child processes. * Good practice in child processes.
*/ */
void closeMostFDs(const std::set<int> & exceptions); void closeMostFDs(const std::set<Descriptor> & exceptions);
/** /**
* Set the close-on-exec flag for the given file descriptor. * Set the close-on-exec flag for the given file descriptor.
*/ */
void closeOnExec(int fd); void closeOnExec(Descriptor fd);
MakeError(EndOfFile, Error); MakeError(EndOfFile, Error);

View file

@ -5,18 +5,14 @@
#include "processes.hh" #include "processes.hh"
#include "signals.hh" #include "signals.hh"
#if __linux__ #include <mutex>
# include <mutex> #include <sys/resource.h>
# include <sys/resource.h> #include "cgroup.hh"
# include "cgroup.hh"
#endif
#include <sys/mount.h> #include <sys/mount.h>
namespace nix { namespace nix {
#if __linux__
bool userNamespacesSupported() bool userNamespacesSupported()
{ {
static auto res = [&]() -> bool static auto res = [&]() -> bool
@ -101,19 +97,14 @@ bool mountAndPidNamespacesSupported()
return res; return res;
} }
#endif
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#if __linux__
static AutoCloseFD fdSavedMountNamespace; static AutoCloseFD fdSavedMountNamespace;
static AutoCloseFD fdSavedRoot; static AutoCloseFD fdSavedRoot;
#endif
void saveMountNamespace() void saveMountNamespace()
{ {
#if __linux__
static std::once_flag done; static std::once_flag done;
std::call_once(done, []() { std::call_once(done, []() {
fdSavedMountNamespace = open("/proc/self/ns/mnt", O_RDONLY); fdSavedMountNamespace = open("/proc/self/ns/mnt", O_RDONLY);
@ -122,12 +113,10 @@ void saveMountNamespace()
fdSavedRoot = open("/proc/self/root", O_RDONLY); fdSavedRoot = open("/proc/self/root", O_RDONLY);
}); });
#endif
} }
void restoreMountNamespace() void restoreMountNamespace()
{ {
#if __linux__
try { try {
auto savedCwd = absPath("."); auto savedCwd = absPath(".");
@ -146,15 +135,12 @@ void restoreMountNamespace()
} catch (Error & e) { } catch (Error & e) {
debug(e.msg()); debug(e.msg());
} }
#endif
} }
void unshareFilesystem() void unshareFilesystem()
{ {
#ifdef __linux__
if (unshare(CLONE_FS) != 0 && errno != EPERM) if (unshare(CLONE_FS) != 0 && errno != EPERM)
throw SysError("unsharing filesystem state in download thread"); throw SysError("unsharing filesystem state in download thread");
#endif
} }
} }

View file

@ -26,12 +26,8 @@ void restoreMountNamespace();
*/ */
void unshareFilesystem(); void unshareFilesystem();
#if __linux__
bool userNamespacesSupported(); bool userNamespacesSupported();
bool mountAndPidNamespacesSupported(); bool mountAndPidNamespacesSupported();
#endif
} }

View file

@ -5,10 +5,22 @@ libutil_NAME = libnixutil
libutil_DIR := $(d) libutil_DIR := $(d)
libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc) 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 # Not just for this library itself, but also for downstream libraries using this library
INCLUDE_libutil := -I $(d) 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_CXXFLAGS += $(INCLUDE_libutil)
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context

View file

@ -37,8 +37,9 @@ void Logger::warn(const std::string & msg)
void Logger::writeToStdout(std::string_view s) void Logger::writeToStdout(std::string_view s)
{ {
writeFull(STDOUT_FILENO, s); Descriptor standard_out = getStandardOut();
writeFull(STDOUT_FILENO, "\n"); writeFull(standard_out, s);
writeFull(standard_out, "\n");
} }
class SimpleLogger : public Logger class SimpleLogger : public Logger

View file

@ -119,18 +119,18 @@ protected:
*/ */
struct FdSink : BufferedSink struct FdSink : BufferedSink
{ {
int fd; Descriptor fd;
size_t written = 0; size_t written = 0;
FdSink() : fd(-1) { } FdSink() : fd(INVALID_DESCRIPTOR) { }
FdSink(int fd) : fd(fd) { } FdSink(Descriptor fd) : fd(fd) { }
FdSink(FdSink&&) = default; FdSink(FdSink&&) = default;
FdSink & operator=(FdSink && s) FdSink & operator=(FdSink && s)
{ {
flush(); flush();
fd = s.fd; fd = s.fd;
s.fd = -1; s.fd = INVALID_DESCRIPTOR;
written = s.written; written = s.written;
return *this; return *this;
} }
@ -151,18 +151,18 @@ private:
*/ */
struct FdSource : BufferedSource struct FdSource : BufferedSource
{ {
int fd; Descriptor fd;
size_t read = 0; size_t read = 0;
BackedStringView endOfFileError{"unexpected end-of-file"}; BackedStringView endOfFileError{"unexpected end-of-file"};
FdSource() : fd(-1) { } FdSource() : fd(INVALID_DESCRIPTOR) { }
FdSource(int fd) : fd(fd) { } FdSource(Descriptor fd) : fd(fd) { }
FdSource(FdSource &&) = default; FdSource(FdSource &&) = default;
FdSource & operator=(FdSource && s) FdSource & operator=(FdSource && s)
{ {
fd = s.fd; fd = s.fd;
s.fd = -1; s.fd = INVALID_DESCRIPTOR;
read = s.read; read = s.read;
return *this; return *this;
} }

View file

@ -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<std::string, std::string> & newEnv)
{
clearEnv();
for (auto & newEnvVar : newEnv)
setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1);
}
}

View file

@ -0,0 +1,155 @@
#include "file-system.hh"
#include "signals.hh"
#include "finally.hh"
#include "serialise.hh"
#include <fcntl.h>
#include <unistd.h>
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<unsigned char> 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<char *>(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<int> & 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");
}
}

66
src/libutil/unix/users.cc Normal file
View file

@ -0,0 +1,66 @@
#include "util.hh"
#include "users.hh"
#include "environment-variables.hh"
#include "file-system.hh"
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
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<char> 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<std::string> 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;
}
}

View file

@ -3,63 +3,8 @@
#include "environment-variables.hh" #include "environment-variables.hh"
#include "file-system.hh" #include "file-system.hh"
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
namespace nix { 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<char> 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<std::string> 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() Path getCacheDir()
{ {
auto cacheDir = getEnv("XDG_CACHE_HOME"); auto cacheDir = getEnv("XDG_CACHE_HOME");
@ -113,9 +58,4 @@ std::string expandTilde(std::string_view path)
return std::string(path); return std::string(path);
} }
bool isRootUser() {
return getuid() == 0;
}
} }

View file

@ -4,7 +4,6 @@
#include <array> #include <array>
#include <cctype> #include <cctype>
#include <iostream> #include <iostream>
#include <grp.h>
#include <regex> #include <regex>
#include <sodium.h> #include <sodium.h>

View file

@ -12,9 +12,19 @@ nix_SOURCES := \
$(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-daemon/*.cc) \
$(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-env/*.cc) \
$(wildcard src/nix-instantiate/*.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 nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd

View file

@ -2,7 +2,6 @@
#include "args/root.hh" #include "args/root.hh"
#include "current-process.hh" #include "current-process.hh"
#include "namespaces.hh"
#include "command.hh" #include "command.hh"
#include "common-args.hh" #include "common-args.hh"
#include "eval.hh" #include "eval.hh"
@ -27,6 +26,10 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#if __linux__
# include "namespaces.hh"
#endif
extern std::string chrootHelperName; extern std::string chrootHelperName;
void chrootHelper(int argc, char * * argv); void chrootHelper(int argc, char * * argv);

View file

@ -6,7 +6,7 @@ resolve-system-dependencies_DIR := $(d)
resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix 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 resolve-system-dependencies_LIBS := libstore libmain libutil