Skip to content

Commit

Permalink
Start factoring out Unix assumptions
Browse files Browse the repository at this point in the history
This splits files and adds new identifiers in preperation for supporting
windows, but no Windows-specific code is actually added yet.
  • Loading branch information
Ericson2314 committed Mar 29, 2024
1 parent 19c3e5d commit c2aecc9
Show file tree
Hide file tree
Showing 19 changed files with 330 additions and 275 deletions.
14 changes: 0 additions & 14 deletions src/libutil/environment-variables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,4 @@ std::map<std::string, std::string> getEnv()
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);
}

}
186 changes: 25 additions & 161 deletions src/libutil/file-descriptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -85,58 +25,26 @@ 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}
{
that.fd = -1;
that.fd = INVALID_DESCRIPTOR;
}


AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that)
{
close();
fd = that.fd;
that.fd = -1;
that.fd = INVALID_DESCRIPTOR;
return *this;
}

Expand All @@ -151,64 +59,54 @@ AutoCloseFD::~AutoCloseFD()
}


int AutoCloseFD::get() const
Descriptor AutoCloseFD::get() const
{
return fd;
}


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()
Expand All @@ -217,38 +115,4 @@ void Pipe::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");
}

}
62 changes: 49 additions & 13 deletions src/libutil/file-descriptor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,89 @@ 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 toDesc(int fd)
{
return fd;
}

/**
* Convert a POSIX file descriptor to a native `Descriptor`
*
* This is a no-op except on Windows.
*/
static inline int fromDesc(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();
};
Expand All @@ -72,12 +108,12 @@ public:
* Close all file descriptors except those listed in the given set.
* 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.
*/
void closeOnExec(int fd);
void closeOnExec(Descriptor fd);

MakeError(EndOfFile, Error);

Expand Down
Loading

0 comments on commit c2aecc9

Please sign in to comment.