From 356b55701c7597ef6110e836b65c5f6b1ef73659 Mon Sep 17 00:00:00 2001 From: wygud Date: Tue, 7 Oct 2025 15:37:11 -0400 Subject: [PATCH] Add macOS compatibility for Unix socket credential passing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make socket credential imports platform-conditional in `.ipc._uds`. - Linux: use `SO_PASSCRED`/`SO_PEERCRED` from socket module - macOS: use `LOCAL_PEERCRED` (0x0001) instead, no need for `SO_PASSCRED` - Conditionally call `setsockopt(SO_PASSCRED)` only on Linux Fixes AttributeError on macOS where SO_PASSCRED doesn't exist. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tractor/ipc/_uds.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index e23fd8d2..95c9fa79 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -23,14 +23,29 @@ from contextlib import ( ) from pathlib import Path import os +import sys from socket import ( AF_UNIX, SOCK_STREAM, - SO_PASSCRED, - SO_PEERCRED, SOL_SOCKET, ) import struct + +# Platform-specific credential passing constants +# See: https://stackoverflow.com/a/7982749 +if sys.platform == 'linux': + from socket import SO_PASSCRED, SO_PEERCRED +elif sys.platform == 'darwin': # macOS + # macOS uses LOCAL_PEERCRED instead of SO_PEERCRED + # and doesn't need SO_PASSCRED (credential passing is always enabled) + # Value from : #define LOCAL_PEERCRED 0x001 + LOCAL_PEERCRED = 0x0001 + SO_PEERCRED = LOCAL_PEERCRED # Alias for compatibility + SO_PASSCRED = None # Not needed/available on macOS +else: + # Other Unix platforms - may need additional handling + SO_PASSCRED = None + SO_PEERCRED = None from typing import ( Type, TYPE_CHECKING, @@ -306,7 +321,11 @@ async def open_unix_socket_w_passcred( # much more simplified logic vs tcp sockets - one socket type and only one # possible location to connect to sock = trio.socket.socket(AF_UNIX, SOCK_STREAM) - sock.setsockopt(SOL_SOCKET, SO_PASSCRED, 1) + + # Only set SO_PASSCRED on Linux (not needed/available on macOS) + if SO_PASSCRED is not None: + sock.setsockopt(SOL_SOCKET, SO_PASSCRED, 1) + with close_on_error(sock): await sock.connect(os.fspath(filename)) @@ -320,7 +339,10 @@ def get_peer_info(sock: trio.socket.socket) -> tuple[ ]: ''' Deliver the connecting peer's "credentials"-info as defined in - a very Linux specific way.. + a platform-specific way. + + On Linux, uses SO_PEERCRED. + On macOS, uses LOCAL_PEERCRED. For more deats see, - `man accept`, @@ -333,6 +355,11 @@ def get_peer_info(sock: trio.socket.socket) -> tuple[ - https://stackoverflow.com/a/7982749 ''' + if SO_PEERCRED is None: + raise RuntimeError( + f'Peer credential retrieval not supported on {sys.platform}!' + ) + creds: bytes = sock.getsockopt( SOL_SOCKET, SO_PEERCRED,