aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/file-descriptor.cc
diff options
context:
space:
mode:
authorTom Hubrecht <github@mail.hubrecht.ovh>2024-05-28 12:25:49 +0200
committerTom Hubrecht <github@mail.hubrecht.ovh>2024-05-29 09:54:47 +0200
commit8cd9aa24a8d48caf22225ce32dff3c5aaa111687 (patch)
tree27ea642636e1e47d37053f5fd4d209e29f4a9149 /src/libutil/file-descriptor.cc
parent6b5078c81554ddb36547f8c41805cc94b7738396 (diff)
util.{hh,cc}: Split out file-descriptor.{hh,cc}
Change-Id: I0dd0f9a9c2003fb887e076127e7f825fd3289c76
Diffstat (limited to 'src/libutil/file-descriptor.cc')
-rw-r--r--src/libutil/file-descriptor.cc251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc
new file mode 100644
index 000000000..ec22f17ab
--- /dev/null
+++ b/src/libutil/file-descriptor.cc
@@ -0,0 +1,251 @@
+#include "file-system.hh"
+#include "finally.hh"
+#include "serialise.hh"
+#include "signals.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);
+}
+
+
+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);
+}
+
+
+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 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::array<unsigned char, 64 * 1024> buf;
+ 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 = AutoCloseFD{fds[0]};
+ writeSide = AutoCloseFD{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");
+}
+
+}