feat(windows): port logos_socket_paths and add a cross target

logos_socket_paths.cpp is the only POSIX-bound file in logos-protocol. All of
it is unix-domain-socket machinery, and on Windows the local transport is named
pipes (QLocalServer maps a name to \\.\pipe\<name>), where none of the
assumptions hold: a pipe has no inode to lstat/chown/chmod -- access comes from
a security descriptor set at CreateNamedPipe time -- and a pipe cannot outlive
its last handle, so a hard-killed process leaves nothing behind.

isSocketDead and reapStaleSockets are therefore not merely unimplemented on
Windows, they are vacuous: the state they detect cannot arise. Both return the
fail-closed answer (false / 0), matching the documented contract that an
endpoint is never reported dead unless certain.

applySocketPerms deliberately does NOT no-op. With no policy requested it
returns true, as on POSIX. But when LOGOS_SOCKET_GROUP or LOGOS_SOCKET_MODE
*are* set it fails with an explanatory error, because silently returning true
would leave the endpoint more permissive than the operator asked for -- the one
direction this file is careful never to go (cf. the chgrp-then-chmod ordering
in the POSIX branch). Granting a pipe to a group needs a DACL plus a
group->SID resolver; until that exists, refuse loudly.

Also gates qt6.wrapQtAppsNoGuiHook behind !isWindows and sets dontWrapQtApps.
Both halves are required: the hook does not even evaluate for a mingw host, it
would be inert anyway (wrap-qt-apps-hook.sh skips anything that is not ELF or
Mach-O), and qtbase's setup hook hard-errors in qtPreHook unless
dontWrapQtApps is set.

Header contract updated per function. POSIX branch unchanged and still compiles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dario Gabriel Lipicar
2026-08-05 22:00:55 -03:00
co-authored by Claude Opus 5
parent 1e9c93434b
commit a26de03070
5 changed files with 102 additions and 8 deletions
+60
View File
@@ -7,6 +7,7 @@
#include <limits>
#include <vector>
#ifndef _WIN32
#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
@@ -15,9 +16,66 @@
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#endif
namespace logos {
#ifdef _WIN32
// On Windows the local transport is backed by NAMED PIPES: QLocalServer maps a
// server name to \\.\pipe\<name>. Every assumption this file is built on is a
// unix-domain-socket assumption, and none of them survives the move:
//
// * a pipe has no filesystem inode, so there is nothing to lstat, chown or
// chmod — access is governed by a security descriptor supplied at
// CreateNamedPipe time, not by mode bits;
// * a pipe instance ceases to exist when its last handle closes, so a
// hard-killed process cannot leave a stale endpoint behind. There is no
// equivalent of the /tmp/logos_<name>_<instance> litter the reaper exists
// to clean up.
//
// So isSocketDead/reapStaleSockets are not merely unimplemented here, they are
// vacuous: the condition they detect cannot arise.
bool applySocketPerms(const std::string& absPath, std::string* errOut)
{
const char* grpEnv = std::getenv("LOGOS_SOCKET_GROUP");
const char* modeEnv = std::getenv("LOGOS_SOCKET_MODE");
const bool wantGroup = grpEnv && *grpEnv;
const bool wantMode = modeEnv && *modeEnv;
// Policy unset is the overwhelmingly common case and is a genuine no-op.
if (!wantGroup && !wantMode) return true;
// Policy SET, though, is a request we cannot honour. Returning true here
// would silently widen access relative to what the operator asked for --
// the one direction this file is careful never to go (see the chgrp-then-
// chmod ordering in the POSIX branch). Granting a Windows pipe to a group
// means building a DACL and resolving the group to a SID; until that
// exists, refuse loudly rather than pretend.
if (errOut) {
*errOut = "LOGOS_SOCKET_GROUP/LOGOS_SOCKET_MODE are not supported on "
"Windows: the local transport uses named pipes, which carry a "
"security descriptor rather than owner/group/mode. Unset them, "
"or run the node per-user (%LOCALAPPDATA%). Path: " + absPath;
}
return false;
}
bool isSocketDead(const std::string& /*absPath*/)
{
// Fail closed, matching the documented contract: never report an endpoint
// dead unless certain. A named pipe that still exists still has an owner.
return false;
}
std::size_t reapStaleSockets(const std::string& /*dir*/, const std::string& /*prefix*/)
{
return 0; // named pipes leave nothing behind to reap
}
#else
namespace {
// Resolve a "group" env value to a gid. Accepts an all-digits string as a
@@ -168,4 +226,6 @@ std::size_t reapStaleSockets(const std::string& dir, const std::string& prefix)
return removed;
}
#endif // _WIN32
} // namespace logos