From 5b5a75979a0b954a5deefe79c8040bac1ad9c76a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 May 2024 14:41:48 +0200 Subject: [PATCH] util.{hh,cc}: Split out unix-domain-socket.{hh,cc} Change-Id: I3f9a628e0f8998b6146f5caa8ae9842361a66b8b --- src/libstore/build/local-derivation-goal.cc | 1 + src/libstore/gc.cc | 1 + src/libstore/uds-remote-store.cc | 1 + src/libutil/file-descriptor.hh | 9 -- src/libutil/meson.build | 2 + src/libutil/unix-domain-socket.cc | 105 ++++++++++++++++++++ src/libutil/unix-domain-socket.hh | 31 ++++++ src/libutil/util.cc | 94 ------------------ src/libutil/util.hh | 11 -- src/nix/daemon.cc | 1 + 10 files changed, 142 insertions(+), 114 deletions(-) create mode 100644 src/libutil/unix-domain-socket.cc create mode 100644 src/libutil/unix-domain-socket.hh diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index d385ffb34..fd871e8a4 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -15,6 +15,7 @@ #include "personality.hh" #include "namespaces.hh" #include "child.hh" +#include "unix-domain-socket.hh" #include #include diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 60d88f3b2..722452b8d 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -4,6 +4,7 @@ #include "processes.hh" #include "signals.hh" #include "finally.hh" +#include "unix-domain-socket.hh" #include #include diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 99589f8b2..226cdf717 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -1,4 +1,5 @@ #include "uds-remote-store.hh" +#include "unix-domain-socket.hh" #include "worker-protocol.hh" #include diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 68324b9d9..f59baa7a0 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -77,13 +77,4 @@ void closeOnExec(int fd); MakeError(EndOfFile, Error); -/** - * Create a Unix domain socket. - */ -AutoCloseFD createUnixDomainSocket(); - -/** - * Create a Unix domain socket in listen mode. - */ -AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); } diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ef4270066..73c520116 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -35,6 +35,7 @@ libutil_sources = files( 'tarfile.cc', 'terminal.cc', 'thread-pool.cc', + 'unix-domain-socket.cc', 'url.cc', 'url-name.cc', 'util.cc', @@ -102,6 +103,7 @@ libutil_headers = files( 'thread-pool.hh', 'topo-sort.hh', 'types.hh', + 'unix-domain-socket.hh', 'url-parts.hh', 'url-name.hh', 'url.hh', diff --git a/src/libutil/unix-domain-socket.cc b/src/libutil/unix-domain-socket.cc new file mode 100644 index 000000000..9fefcbe1c --- /dev/null +++ b/src/libutil/unix-domain-socket.cc @@ -0,0 +1,105 @@ +#include "file-system.hh" +#include "processes.hh" +#include "unix-domain-socket.hh" +#include "util.hh" + +#include +#include +#include + +namespace nix { + +AutoCloseFD createUnixDomainSocket() +{ + AutoCloseFD fdSocket{socket(PF_UNIX, SOCK_STREAM + #ifdef SOCK_CLOEXEC + | SOCK_CLOEXEC + #endif + , 0)}; + if (!fdSocket) + throw SysError("cannot create Unix domain socket"); + closeOnExec(fdSocket.get()); + return fdSocket; +} + + +AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) +{ + auto fdSocket = nix::createUnixDomainSocket(); + + bind(fdSocket.get(), path); + + chmodPath(path.c_str(), mode); + + if (listen(fdSocket.get(), 100) == -1) + throw SysError("cannot listen on socket '%1%'", path); + + return fdSocket; +} + +static void bindConnectProcHelper( + std::string_view operationName, auto && operation, + int fd, const std::string & path) +{ + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + + // Casting between types like these legacy C library interfaces + // require is forbidden in C++. To maintain backwards + // compatibility, the implementation of the bind/connect functions + // contains some hints to the compiler that allow for this + // special case. + auto * psaddr = reinterpret_cast(&addr); + + if (path.size() + 1 >= sizeof(addr.sun_path)) { + Pipe pipe; + pipe.create(); + Pid pid = startProcess([&] { + try { + pipe.readSide.close(); + Path dir = dirOf(path); + if (chdir(dir.c_str()) == -1) + throw SysError("chdir to '%s' failed", dir); + std::string base(baseNameOf(path)); + if (base.size() + 1 >= sizeof(addr.sun_path)) + throw Error("socket path '%s' is too long", base); + memcpy(addr.sun_path, base.c_str(), base.size() + 1); + if (operation(fd, psaddr, sizeof(addr)) == -1) + throw SysError("cannot %s to socket at '%s'", operationName, path); + writeFull(pipe.writeSide.get(), "0\n"); + } catch (SysError & e) { + writeFull(pipe.writeSide.get(), fmt("%d\n", e.errNo)); + } catch (...) { + writeFull(pipe.writeSide.get(), "-1\n"); + } + }); + pipe.writeSide.close(); + auto errNo = string2Int(chomp(drainFD(pipe.readSide.get()))); + if (!errNo || *errNo == -1) + throw Error("cannot %s to socket at '%s'", operationName, path); + else if (*errNo > 0) { + errno = *errNo; + throw SysError("cannot %s to socket at '%s'", operationName, path); + } + } else { + memcpy(addr.sun_path, path.c_str(), path.size() + 1); + if (operation(fd, psaddr, sizeof(addr)) == -1) + throw SysError("cannot %s to socket at '%s'", operationName, path); + } +} + + +void bind(int fd, const std::string & path) +{ + unlink(path.c_str()); + + bindConnectProcHelper("bind", ::bind, fd, path); +} + + +void connect(int fd, const std::string & path) +{ + bindConnectProcHelper("connect", ::connect, fd, path); +} + +} diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh new file mode 100644 index 000000000..692ad2627 --- /dev/null +++ b/src/libutil/unix-domain-socket.hh @@ -0,0 +1,31 @@ +#pragma once +///@file + +#include "file-descriptor.hh" +#include "types.hh" + +#include + +namespace nix { + +/** + * Create a Unix domain socket. + */ +AutoCloseFD createUnixDomainSocket(); + +/** + * Create a Unix domain socket in listen mode. + */ +AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); + +/** + * Bind a Unix domain socket to a path. + */ +void bind(int fd, const std::string & path); + +/** + * Connect to a Unix domain socket. + */ +void connect(int fd, const std::string & path); + +} diff --git a/src/libutil/util.cc b/src/libutil/util.cc index aff26e32b..f580ef038 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -459,100 +459,6 @@ void unshareFilesystem() #endif } -AutoCloseFD createUnixDomainSocket() -{ - AutoCloseFD fdSocket{socket(PF_UNIX, SOCK_STREAM - #ifdef SOCK_CLOEXEC - | SOCK_CLOEXEC - #endif - , 0)}; - if (!fdSocket) - throw SysError("cannot create Unix domain socket"); - closeOnExec(fdSocket.get()); - return fdSocket; -} - - -AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) -{ - auto fdSocket = nix::createUnixDomainSocket(); - - bind(fdSocket.get(), path); - - chmodPath(path.c_str(), mode); - - if (listen(fdSocket.get(), 100) == -1) - throw SysError("cannot listen on socket '%1%'", path); - - return fdSocket; -} - - -static void bindConnectProcHelper( - std::string_view operationName, auto && operation, - int fd, const std::string & path) -{ - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - - // Casting between types like these legacy C library interfaces - // require is forbidden in C++. To maintain backwards - // compatibility, the implementation of the bind/connect functions - // contains some hints to the compiler that allow for this - // special case. - auto * psaddr = reinterpret_cast(&addr); - - if (path.size() + 1 >= sizeof(addr.sun_path)) { - Pipe pipe; - pipe.create(); - Pid pid = startProcess([&] { - try { - pipe.readSide.close(); - Path dir = dirOf(path); - if (chdir(dir.c_str()) == -1) - throw SysError("chdir to '%s' failed", dir); - std::string base(baseNameOf(path)); - if (base.size() + 1 >= sizeof(addr.sun_path)) - throw Error("socket path '%s' is too long", base); - memcpy(addr.sun_path, base.c_str(), base.size() + 1); - if (operation(fd, psaddr, sizeof(addr)) == -1) - throw SysError("cannot %s to socket at '%s'", operationName, path); - writeFull(pipe.writeSide.get(), "0\n"); - } catch (SysError & e) { - writeFull(pipe.writeSide.get(), fmt("%d\n", e.errNo)); - } catch (...) { - writeFull(pipe.writeSide.get(), "-1\n"); - } - }); - pipe.writeSide.close(); - auto errNo = string2Int(chomp(drainFD(pipe.readSide.get()))); - if (!errNo || *errNo == -1) - throw Error("cannot %s to socket at '%s'", operationName, path); - else if (*errNo > 0) { - errno = *errNo; - throw SysError("cannot %s to socket at '%s'", operationName, path); - } - } else { - memcpy(addr.sun_path, path.c_str(), path.size() + 1); - if (operation(fd, psaddr, sizeof(addr)) == -1) - throw SysError("cannot %s to socket at '%s'", operationName, path); - } -} - - -void bind(int fd, const std::string & path) -{ - unlink(path.c_str()); - - bindConnectProcHelper("bind", ::bind, fd, path); -} - - -void connect(int fd, const std::string & path) -{ - bindConnectProcHelper("connect", ::connect, fd, path); -} - std::string showBytes(uint64_t bytes) { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index e704cd709..216693635 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -406,17 +406,6 @@ struct MaintainCount }; -/** - * Bind a Unix domain socket to a path. - */ -void bind(int fd, const std::string & path); - -/** - * Connect to a Unix domain socket. - */ -void connect(int fd, const std::string & path); - - /** * A Rust/Python-like enumerate() iterator adapter. * diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 9d4afb6d9..113e23bd1 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -14,6 +14,7 @@ #include "legacy.hh" #include "signals.hh" #include "daemon.hh" +#include "unix-domain-socket.hh" #include #include