From e01ae1f45305166feeaa222806f4eed817c586f9 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Mon, 14 Jul 2025 17:17:12 +0200 Subject: [PATCH] libutil: add generic unix socketpair wrapper previously we used this only for SSH, but other uses may appear soon. Change-Id: Ibe9666d63aaea07525ebad57decda88b11964cc0 --- lix/libstore/ssh.cc | 19 +------------------ lix/libutil/file-descriptor.cc | 23 +++++++++++++++++++++++ lix/libutil/file-descriptor.hh | 12 ++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lix/libstore/ssh.cc b/lix/libstore/ssh.cc index 2b329f231..ab3ec5a20 100644 --- a/lix/libstore/ssh.cc +++ b/lix/libstore/ssh.cc @@ -51,24 +51,7 @@ void SSH::addCommonSSHOpts(Strings & args) std::unique_ptr SSH::startCommand(const std::string & command) { - int sp[2]; - // only linux and bsd support SOCK_CLOEXEC in socketpair type. -#if __linux__ || __FreeBSD__ - constexpr int sock_type = SOCK_STREAM | SOCK_CLOEXEC; -#else - constexpr int sock_type = SOCK_STREAM; -#endif - if (socketpair(AF_UNIX, sock_type, 0, sp) < 0) { - throw SysError("socketpair() for ssh"); - } - - AutoCloseFD parent(sp[0]), child(sp[1]); -#if !(__linux__ || __FreeBSD__) - if (fcntl(parent.get(), F_SETFD, O_CLOEXEC) < 0 || fcntl(child.get(), F_SETFD, O_CLOEXEC) < 0) { - throw SysError("making socketpair O_CLOEXEC"); - } -#endif - + auto [parent, child] = SocketPair::stream(); auto conn = std::make_unique(); ProcessOptions options; options.dieWithParent = false; diff --git a/lix/libutil/file-descriptor.cc b/lix/libutil/file-descriptor.cc index 5a2658406..2a8dab3e2 100644 --- a/lix/libutil/file-descriptor.cc +++ b/lix/libutil/file-descriptor.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -327,4 +328,26 @@ void resetBlockingState(int fd, FdBlockingState prevState) throw SysError("resetBlockingState"); } } + +SocketPair SocketPair::stream() +{ + int sp[2]; +#ifdef SOCK_CLOEXEC + constexpr int sock_type = SOCK_STREAM | SOCK_CLOEXEC; +#else + constexpr int sock_type = SOCK_STREAM; +#endif + if (socketpair(AF_UNIX, sock_type, 0, sp) < 0) { + throw SysError("socketpair()"); + } + + AutoCloseFD a(sp[0]), b(sp[1]); +#ifndef SOCK_CLOEXEC + closeOnExec(a.get()); + closeOnExec(b.get()); +#endif + + return {std::move(a), std::move(b)}; +} + } diff --git a/lix/libutil/file-descriptor.hh b/lix/libutil/file-descriptor.hh index 77e1239ea..8bd5bd2e5 100644 --- a/lix/libutil/file-descriptor.hh +++ b/lix/libutil/file-descriptor.hh @@ -83,6 +83,18 @@ public: void close(); }; +struct SocketPair +{ + /** The two sides of the socket pair. */ + AutoCloseFD a, b; + + /** Create a unix stream socket pair with the `O_CLOEXEC` set on both ends. */ + static SocketPair stream(); + +private: + SocketPair(AutoCloseFD a, AutoCloseFD b) : a(std::move(a)), b(std::move(b)) {} +}; + /** * Close all file descriptors except stdio fds (ie 0, 1, 2). * Good practice in child processes.