From fe1ddf30b566d9d1dc7c41f1fc17286a7bb3853d Mon Sep 17 00:00:00 2001 From: Nikolay Edigaryev Date: Thu, 5 Feb 2026 14:28:46 +0100 Subject: [PATCH] Bind and connect to Unix domain sockets using relative paths --- Sources/tart/Commands/Exec.swift | 10 +++++++++- Sources/tart/Commands/IP.swift | 10 +++++++++- Sources/tart/ControlSocket.swift | 10 +++++++++- Sources/tart/MACAddressResolver/AgentResolver.swift | 8 ++++---- Sources/tart/VMDirectory.swift | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Sources/tart/Commands/Exec.swift b/Sources/tart/Commands/Exec.swift index 20952c9..88f1d13 100644 --- a/Sources/tart/Commands/Exec.swift +++ b/Sources/tart/Commands/Exec.swift @@ -47,8 +47,16 @@ struct Exec: AsyncParsableCommand { try! group.syncShutdownGracefully() } + // Change the current working directory to a VM's base directory + // to work around Unix domain socket 104 byte limitation [1] + // + // [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/ + if let baseURL = vmDir.controlSocketURL.baseURL { + FileManager.default.changeCurrentDirectoryPath(baseURL.path()) + } + let channel = try GRPCChannelPool.with( - target: .unixDomainSocket(vmDir.controlSocketURL.path()), + target: .unixDomainSocket(vmDir.controlSocketURL.relativePath), transportSecurity: .plaintext, eventLoopGroup: group, ) diff --git a/Sources/tart/Commands/IP.swift b/Sources/tart/Commands/IP.swift index ab142a2..b4e116e 100644 --- a/Sources/tart/Commands/IP.swift +++ b/Sources/tart/Commands/IP.swift @@ -68,7 +68,15 @@ struct IP: AsyncParsableCommand { throw RuntimeError.Generic("Cannot perform IP resolution via Tart Guest Agent when control socket URL is not set") } - if let ip = try await AgentResolver.ResolveIP(controlSocketURL) { + // Change the current working directory to a VM's base directory + // to work around Unix domain socket 104 byte limitation [1] + // + // [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/ + if let baseURL = controlSocketURL.baseURL { + FileManager.default.changeCurrentDirectoryPath(baseURL.path()) + } + + if let ip = try await AgentResolver.ResolveIP(controlSocketURL.relativePath) { return ip } } diff --git a/Sources/tart/ControlSocket.swift b/Sources/tart/ControlSocket.swift index 443a7db..fbf1ce4 100644 --- a/Sources/tart/ControlSocket.swift +++ b/Sources/tart/ControlSocket.swift @@ -21,8 +21,16 @@ class ControlSocket { // if any, otherwise we may get the "address already in use" error try? FileManager.default.removeItem(atPath: controlSocketURL.path()) + // Change the current working directory to a VM's base directory + // to work around Unix domain socket 104 byte limitation [1] + // + // [1]: https://blog.8-p.info/en/2020/06/11/unix-domain-socket-length/ + if let baseURL = controlSocketURL.baseURL { + FileManager.default.changeCurrentDirectoryPath(baseURL.path()) + } + let serverChannel = try await ServerBootstrap(group: eventLoopGroup) - .bind(unixDomainSocketPath: controlSocketURL.path()) { childChannel in + .bind(unixDomainSocketPath: controlSocketURL.relativePath) { childChannel in childChannel.eventLoop.makeCompletedFuture { return try NIOAsyncChannel( wrappingChannelSynchronously: childChannel diff --git a/Sources/tart/MACAddressResolver/AgentResolver.swift b/Sources/tart/MACAddressResolver/AgentResolver.swift index 856af3e..2978ce9 100644 --- a/Sources/tart/MACAddressResolver/AgentResolver.swift +++ b/Sources/tart/MACAddressResolver/AgentResolver.swift @@ -6,15 +6,15 @@ import Cirruslabs_TartGuestAgent_Apple_Swift import Cirruslabs_TartGuestAgent_Grpc_Swift class AgentResolver { - static func ResolveIP(_ controlSocketURL: URL) async throws -> IPv4Address? { + static func ResolveIP(_ controlSocketPath: String) async throws -> IPv4Address? { do { - return try await resolveIP(controlSocketURL) + return try await resolveIP(controlSocketPath) } catch let error as GRPCConnectionPoolError { return nil } } - private static func resolveIP(_ controlSocketURL: URL) async throws -> IPv4Address? { + private static func resolveIP(_ controlSocketPath: String) async throws -> IPv4Address? { // Create a gRPC channel connected to the VM's control socket let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { @@ -22,7 +22,7 @@ class AgentResolver { } let channel = try GRPCChannelPool.with( - target: .unixDomainSocket(controlSocketURL.path()), + target: .unixDomainSocket(controlSocketPath), transportSecurity: .plaintext, eventLoopGroup: group, ) diff --git a/Sources/tart/VMDirectory.swift b/Sources/tart/VMDirectory.swift index c1ca268..6319632 100644 --- a/Sources/tart/VMDirectory.swift +++ b/Sources/tart/VMDirectory.swift @@ -27,7 +27,7 @@ struct VMDirectory: Prunable { baseURL.appendingPathComponent("manifest.json") } var controlSocketURL: URL { - baseURL.appendingPathComponent("control.sock") + URL(fileURLWithPath: "control.sock", relativeTo: baseURL) } var explicitlyPulledMark: URL {