From 521fb97fe9d693128a34ab1ce26632766f35fff4 Mon Sep 17 00:00:00 2001 From: goodboy Date: Sun, 1 Mar 2026 20:15:13 -0500 Subject: [PATCH] Support UDS on macos (for realz) Though it was a good (vibed) try by @dnks, the previous "fix" was not actually adding unix socket support but merely sidestepping a crash due to `get_peer_info()`'s impl never going to work on MacOS (and it was never intended to). This patch instead solves the underlying issue by implementing a new `get_peer_pid()` helper which does in fact retrieve the peer's PID in a more generic/cross-platform way (:fingers_crossed:); much thanks to the linked SO answer for this solution! Impl deats, - add `get_peer_pid()` and call it from `MsgpackUDSStream.get_stream_addrs()` when we detect a non-'linux' platform, OW use the original soln: `get_stream_addrs()`. - add a new case for the `match (peername, sockname)` with a `case (str(), str()):` which seems to at least work on macos. - drop all the `LOCAL_PEERCRED` dynamic import branching since it was never needed and was never going to work. --- tractor/ipc/_uds.py | 77 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index 637a9a9a..b7612682 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -24,10 +24,12 @@ from contextlib import ( from pathlib import Path import os import sys +import socket as stdlib_socket from socket import ( AF_UNIX, SOCK_STREAM, SOL_SOCKET, + error as socket_error, ) import struct from typing import ( @@ -70,19 +72,19 @@ if sys.platform == 'linux': SO_PEERCRED, ) -# NOTE, macOS uses `LOCAL_PEERCRED` instead of `SO_PEERCRED` and -# doesn't need `SO_PASSCRED` (credential passing is always enabled). -# XXX See code in : `#define LOCAL_PEERCRED 0x001` -# -elif sys.platform == 'darwin': # macOS - LOCAL_PEERCRED: int = 0x0001 - SO_PEERCRED:int|None = LOCAL_PEERCRED # Alias for compatibility - SO_PASSCRED: int|None = None # Not needed/available on macOS else: - # Other Unix platforms - may need additional handling + # Other (Unix) platforms - though further testing is required and + # others may need additional special handling? SO_PASSCRED = None SO_PEERCRED = None + # NOTE, macOS uses `LOCAL_PEERCRED` instead of `SO_PEERCRED` and + # doesn't need `SO_PASSCRED` (credential passing is always enabled). + # See code in : `#define LOCAL_PEERCRED 0x001` + # + # XXX INSTEAD we use the (hopefully) more generic + # `get_peer_pid()` below for other OSes. + log = get_logger() @@ -186,7 +188,11 @@ class UDSAddress( err_on_no_runtime=False, ) if actor: - sockname: str = '::'.join(actor.uid) + f'@{pid}' + sockname: str = f'{actor.aid.name}@{pid}' + # XXX, orig version which broke both macOS (file-name + # length) and `multiaddrs` ('::' invalid separator). + # sockname: str = '::'.join(actor.uid) + f'@{pid}' + # # ?^TODO, for `multiaddr`'s parser we can't use the `::` # above^, SO maybe a `.` or something else here? # sockname: str = '.'.join(actor.uid) + f'@{pid}' @@ -347,6 +353,26 @@ async def open_unix_socket_w_passcred( return trio.SocketStream(sock) +def get_peer_pid(sock) -> int|None: + ''' + Gets the PID of the process connected to the other end of a Unix + domain socket on macOS, or `None` if that fails. + + NOTE, should work on MacOS (and others?). + + ''' + # try to get the peer PID + # https://stackoverflow.com/a/67971484 + try: + pid: int = sock.getsockopt(0, 2) + return pid + except socket_error as e: + log.exception( + f"Failed to get peer PID: {e}" + ) + return None + + def get_peer_info( sock: trio.socket.socket, ) -> tuple[ @@ -358,8 +384,7 @@ def get_peer_info( Deliver the connecting peer's "credentials"-info as defined in a platform-specific way. - On Linux, uses SO_PEERCRED. - On macOS, uses LOCAL_PEERCRED. + Linux-ONLY, uses SO_PEERCRED. For more deats see, - `man accept`, @@ -480,13 +505,31 @@ class MsgpackUDSStream(MsgpackTransport): match (peername, sockname): case (str(), bytes()): sock_path: Path = Path(peername) + case (bytes(), str()): sock_path: Path = Path(sockname) - ( - peer_pid, - _, - _, - ) = get_peer_info(sock) + + case (str(), str()): # XXX, likely macOS + sock_path: Path = Path(peername) + + case _: + raise TypeError( + f'Failed to match (peername, sockname) types?\n' + f'peername: {peername!r}\n' + f'sockname: {sockname!r}\n' + ) + + if sys.platform == 'linux': + ( + peer_pid, + _, + _, + ) = get_peer_info(sock) + + # NOTE known to at least works on, + # - macos + else: + peer_pid: int = get_peer_pid(sock) filedir, filename = unwrap_sockpath(sock_path) laddr = UDSAddress(