mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-23 22:46:16 +02:00
ac89bb064a
All OS and IO operations should be moved out, leaving only some misc portable pure functions. This is useful to avoid copious CPP when doing things like Windows and Emscripten ports. Newly exposed functions to break cycles: - `restoreSignals` - `updateWindowSize`
254 lines
5.1 KiB
C++
254 lines
5.1 KiB
C++
#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 writeLine(int fd, std::string s)
|
|
{
|
|
s += '\n';
|
|
writeFull(fd, s);
|
|
}
|
|
|
|
|
|
std::string drainFD(int 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);
|
|
drainFD(fd, sink, block);
|
|
return std::move(sink.s);
|
|
}
|
|
|
|
|
|
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({(char *) buf.data(), (size_t) rd});
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
AutoCloseFD::AutoCloseFD() : fd{-1} {}
|
|
|
|
|
|
AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {}
|
|
|
|
|
|
AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd}
|
|
{
|
|
that.fd = -1;
|
|
}
|
|
|
|
|
|
AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that)
|
|
{
|
|
close();
|
|
fd = that.fd;
|
|
that.fd = -1;
|
|
return *this;
|
|
}
|
|
|
|
|
|
AutoCloseFD::~AutoCloseFD()
|
|
{
|
|
try {
|
|
close();
|
|
} catch (...) {
|
|
ignoreException();
|
|
}
|
|
}
|
|
|
|
|
|
int AutoCloseFD::get() const
|
|
{
|
|
return fd;
|
|
}
|
|
|
|
|
|
void AutoCloseFD::close()
|
|
{
|
|
if (fd != -1) {
|
|
if (::close(fd) == -1)
|
|
/* This should never happen. */
|
|
throw SysError("closing file descriptor %1%", fd);
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
void AutoCloseFD::fsync()
|
|
{
|
|
if (fd != -1) {
|
|
int result;
|
|
#if __APPLE__
|
|
result = ::fcntl(fd, F_FULLFSYNC);
|
|
#else
|
|
result = ::fsync(fd);
|
|
#endif
|
|
if (result == -1)
|
|
throw SysError("fsync file descriptor %1%", fd);
|
|
}
|
|
}
|
|
|
|
|
|
AutoCloseFD::operator bool() const
|
|
{
|
|
return fd != -1;
|
|
}
|
|
|
|
|
|
int AutoCloseFD::release()
|
|
{
|
|
int oldFD = fd;
|
|
fd = -1;
|
|
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()
|
|
{
|
|
readSide.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 (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");
|
|
}
|
|
|
|
}
|