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
This commit is contained in:
eldritch horrors
2026-04-20 08:19:17 +00:00
parent a5205265c0
commit f006b41a3c
3 changed files with 47 additions and 7 deletions
+20 -4
View File
@@ -2,7 +2,17 @@
#include <sys/socket.h>
#include <sys/un.h>
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<char *> args) noexcept
{
@@ -10,8 +20,12 @@ int helperMain(const char *, std::span<char *> args) noexcept
std::string_view method = args[1];
const auto dir = args[2];
const auto name = args[3];
resultFd = argToInt<int>("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<char *> args) noexcept
}
if (method == "bind") {
DIE_UNLESS_SYS("bind", bind(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
auto result = bind(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ? errno : 0;
sendResult(result);
} else if (method == "connect") {
DIE_UNLESS_SYS("connect", connect(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
auto result = connect(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ? errno : 0;
sendResult(result);
} else {
die(std::format("invalid method %s", method));
}
+3 -1
View File
@@ -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;
+24 -2
View File
@@ -64,13 +64,35 @@ static void bindConnectProcHelper(
// special case.
auto * psaddr = reinterpret_cast<struct sockaddr *>(&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)