From f006b41a3c2d14f312a07abbacaa19c1ca1ad70e Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Thu, 26 Mar 2026 16:38:01 +0100 Subject: [PATCH] libutil: transfer bind/connect errors from libexec helper this should improve error messages when connecting to long socket paths, and make it at all possible to detect ENOTDIR from connect helpers. this will be needed to handle daemon sockets that live in directories we have not previously considered like the protocol name directories we need for rpc purposes, all of which would have resulted in hard errors previously Change-Id: I4e16cf09fedd07c93cfdf7b0f151106984507315 --- lix/libexec/unix-bind-connect.cc | 24 ++++++++++++++++++++---- lix/libstore/uds-remote-store.cc | 4 +++- lix/libutil/unix-domain-socket.cc | 26 ++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/lix/libexec/unix-bind-connect.cc b/lix/libexec/unix-bind-connect.cc index 0d6d8f022..8e5ddc95a 100644 --- a/lix/libexec/unix-bind-connect.cc +++ b/lix/libexec/unix-bind-connect.cc @@ -2,7 +2,17 @@ #include #include -LIBEXEC_HELPER(4) +LIBEXEC_HELPER(5) + +static int resultFd = -1; + +static void sendResult(int result) +{ + // NOTE posix says pipe writes smaller than PIPE_BUF must be atomic, so this either + // succeeds or fails (pipe bufs of four bytes make no sense at all for our systems) + int error = result; + DIE_UNLESS_SYS("writing result", ::write(resultFd, &error, sizeof(error))); +} int helperMain(const char *, std::span args) noexcept { @@ -10,8 +20,12 @@ int helperMain(const char *, std::span args) noexcept std::string_view method = args[1]; const auto dir = args[2]; const auto name = args[3]; + resultFd = argToInt("result-fd", args[4]); - DIE_UNLESS_SYS("chdir", chdir(dir)); + if (chdir(dir)) { + sendResult(errno); + return 0; + } struct sockaddr_un addr; addr.sun_family = AF_UNIX; @@ -23,9 +37,11 @@ int helperMain(const char *, std::span args) noexcept } if (method == "bind") { - DIE_UNLESS_SYS("bind", bind(socket, reinterpret_cast(&addr), sizeof(addr))); + auto result = bind(socket, reinterpret_cast(&addr), sizeof(addr)) ? errno : 0; + sendResult(result); } else if (method == "connect") { - DIE_UNLESS_SYS("connect", connect(socket, reinterpret_cast(&addr), sizeof(addr))); + auto result = connect(socket, reinterpret_cast(&addr), sizeof(addr)) ? errno : 0; + sendResult(result); } else { die(std::format("invalid method %s", method)); } diff --git a/lix/libstore/uds-remote-store.cc b/lix/libstore/uds-remote-store.cc index fb76b0fae..1929aa50f 100644 --- a/lix/libstore/uds-remote-store.cc +++ b/lix/libstore/uds-remote-store.cc @@ -66,7 +66,9 @@ static void connectToFirstAvailableSocket(AutoCloseFD & sockFD, const std::list< nix::connect(sockFD.get(), socket); return; } catch (SysError & e) { - if (e.errNo == EACCES || e.errNo == EPERM || e.errNo == ECONNREFUSED || e.errNo == ENOENT) { + if (e.errNo == EACCES || e.errNo == EPERM || e.errNo == ECONNREFUSED || e.errNo == ENOENT + || e.errNo == ENOTDIR) + { debug("skipping socket %s: %s", socket, strerror(e.errNo)); } else { throw; diff --git a/lix/libutil/unix-domain-socket.cc b/lix/libutil/unix-domain-socket.cc index 3adc601a2..9f098fffd 100644 --- a/lix/libutil/unix-domain-socket.cc +++ b/lix/libutil/unix-domain-socket.cc @@ -64,13 +64,35 @@ static void bindConnectProcHelper( // special case. auto * psaddr = reinterpret_cast(&addr); + Pipe resultFd; + resultFd.create(); + if (path.size() + 1 >= sizeof(addr.sun_path)) { runHelper( "unix-bind-connect", {.args = - {std::to_string(fd), std::string(operationName), dirOf(path), std::string(baseNameOf(path))}, - .redirections = {{.dup = fd, .from = fd}}} + { + std::to_string(fd), + std::string(operationName), + dirOf(path), + std::string(baseNameOf(path)), + std::to_string(resultFd.writeSide.get()), + }, + .redirections = { + {.dup = fd, .from = fd}, + {.dup = resultFd.writeSide.get(), .from = resultFd.writeSide.get()}, + }} ).waitAndCheck(); + resultFd.writeSide.close(); + auto resultRaw = drainFD(resultFd.readSide.get()); + int result; + if (resultRaw.size() != sizeof(result)) { + throw Error("bind-connect helper returned bad result! bailing"); + } + memcpy(&result, resultRaw.data(), sizeof(result)); + if (result != 0) { + throw SysError(result, "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)