libutil: move unix chdir+bind/connect to libexec
Change-Id: Ie07ece701454153d3d5c6c34e5613c0d1d5fae03
This commit is contained in:
@@ -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) \
|
||||
([&] { \
|
||||
|
||||
@@ -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',
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user