libutil: move unix chdir+bind/connect to libexec

Change-Id: Ie07ece701454153d3d5c6c34e5613c0d1d5fae03
This commit is contained in:
eldritch horrors
2026-01-20 22:42:53 +00:00
parent 41a68f206f
commit be27e9696b
4 changed files with 80 additions and 30 deletions
+32
View File
@@ -1,15 +1,19 @@
#pragma once
///@file common setup/utility header for libexec helpers
#include <cctype>
#include <cerrno>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <format> // IWYU pragma: keep
#include <limits>
#include <span>
#include <string> // IWYU pragma: keep
#include <string_view>
#include <type_traits>
#include <unistd.h>
/// file descriptor of the error reporting pipe. anything written to this pipe
@@ -37,6 +41,34 @@ inline void die(std::string_view msg)
exit(252);
}
/// converts an argument to an integer or dies with a message.
template<typename T, size_t N>
requires std::is_integral_v<T>
T argToInt(const char (&argName)[N], const char * str)
{
// this should really just wrap std::from_chars, but macos doesn't have it.
for (const auto c : std::string_view(str)) {
if (c != '-' && !std::isdigit(c)) {
die(std::format("invalid {} argument", argName));
}
}
char * end = nullptr;
const auto tmp = [&] {
if constexpr (std::is_signed_v<T>) {
return std::strtoimax(str, &end, 10); // NOLINT(lix-unsafe-c-calls): str is a C string
} else {
return std::strtoumax(str, &end, 10); // NOLINT(lix-unsafe-c-calls): str is a C string
}
}();
if (!end || *end || tmp < std::numeric_limits<T>::min() || tmp > std::numeric_limits<T>::max()) {
die(std::format("invalid {} argument", argName));
}
return tmp;
}
/// check syscall result and immediately terminate with a message on failure.
#define DIE_UNLESS_SYS(name, expr) \
([&] { \
+7
View File
@@ -11,3 +11,10 @@ run_pager = executable(
install : true,
install_dir : libexecdir / 'lix',
)
unix_bind_connect = executable(
'unix-bind-connect',
files('unix-bind-connect.cc'),
install : true,
install_dir : libexecdir / 'lix',
)
+34
View File
@@ -0,0 +1,34 @@
#include "common.hh"
#include <sys/socket.h>
#include <sys/un.h>
LIBEXEC_HELPER(4)
int helperMain(const char *, std::span<char *> args) noexcept
{
int socket = argToInt<int>("socket", args[0]);
std::string_view method = args[1];
const auto dir = args[2];
const auto name = args[3];
DIE_UNLESS_SYS("chdir", chdir(dir));
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (auto nameLen = strlen(name); nameLen + 1 >= sizeof(addr.sun_path)) {
die(std::format("socket path {}/{} is too long", dir, name));
} else {
memcpy(addr.sun_path, name, nameLen + 1);
}
if (method == "bind") {
DIE_UNLESS_SYS("bind", bind(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
} else if (method == "connect") {
DIE_UNLESS_SYS("connect", connect(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
} else {
die(std::format("invalid method %s", method));
}
return 0;
}
+7 -30
View File
@@ -4,6 +4,7 @@
#include "lix/libutil/unix-domain-socket.hh"
#include "lix/libutil/strings.hh"
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
@@ -64,36 +65,12 @@ static void bindConnectProcHelper(
auto * psaddr = reinterpret_cast<struct sockaddr *>(&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 (sys::chdir(dir) == -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<int>(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);
}
runHelper(
"unix-bind-connect",
{.args =
{std::to_string(fd), std::string(operationName), dirOf(path), std::string(baseNameOf(path))},
.redirections = {{.dup = fd, .from = fd}}}
).waitAndCheck();
} else {
memcpy(addr.sun_path, path.c_str(), path.size() + 1);
if (operation(fd, psaddr, sizeof(addr)) == -1)