Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Sources/ContainerCommands/Container/ContainerKill.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,10 @@ extension Application {
containers = containerIds
}

let signalNumber = try Signal(signal).rawValue

var errors: [any Error] = []
for container in containers {
do {
try await client.kill(id: container, signal: signalNumber)
try await client.kill(id: container, signal: signal)
print(container)
} catch {
errors.append(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,12 @@ public struct ContainerClient: Sendable {
}

/// Send a signal to the container.
public func kill(id: String, signal: Int32) async throws {
public func kill(id: String, signal: String) async throws {
do {
let request = XPCMessage(route: .containerKill)
request.set(key: .id, value: id)
request.set(key: .processIdentifier, value: id)
request.set(key: .signal, value: Int64(signal))
request.set(key: .signal, value: signal)

try await xpcClient.send(request)
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ public actor ContainersService {
}

/// Send a signal to the container.
public func kill(id: String, processID: String, signal: Int64) async throws {
public func kill(id: String, processID: String, signal: String) async throws {
log.debug(
"ContainersService: enter",
metadata: [
Expand All @@ -604,6 +604,13 @@ public actor ContainersService {
let state = try self._getContainerState(id: id)
let client = try state.getClient()
try await client.kill(processID, signal: signal)

// SIGKILL is guaranteed to terminate the target. When directed at the
// container's init process, follow up with the same API-server cleanup
// that `stop` performs.
if processID == id, (try? Signal(signal)) == .kill {
try await handleContainerExit(id: id)
}
}

/// Stop all containers inside the sandbox, aborting any processes currently
Expand Down Expand Up @@ -1156,8 +1163,11 @@ public actor ContainersService {
}

extension XPCMessage {
func signal() throws -> Int64 {
self.int64(key: .signal)
func signal() throws -> String {
guard let signal = self.string(key: .signal) else {
throw ContainerizationError(.invalidArgument, message: "missing signal in xpc message")
}
return signal
}

func stopOptions() throws -> ContainerStopOptions {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ extension RuntimeClient {
}
}

public func kill(_ id: String, signal: Int64) async throws {
public func kill(_ id: String, signal: String) async throws {
let request = XPCMessage(route: RuntimeRoutes.kill.rawValue)
request.set(key: RuntimeKeys.id.rawValue, value: id)
request.set(key: RuntimeKeys.signal.rawValue, value: signal)
Expand Down
31 changes: 22 additions & 9 deletions Sources/Services/RuntimeLinux/Server/RuntimeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -553,11 +553,13 @@ public actor RuntimeService {
self.log.debug("enter", metadata: ["func": "\(#function)"])
defer { self.log.debug("exit", metadata: ["func": "\(#function)"]) }

return try await self.lock.withLock { [self] _ in
let id = try message.id()
let signal = try Signal(message.signal())

try await self.lock.withLock { [self] _ in
switch await self.state {
case .running:
let ctr = try await getContainer()
let id = try message.id()
if id != ctr.container.id {
guard let processInfo = await self.processes[id] else {
throw ContainerizationError(.invalidState, message: "process \(id) does not exist")
Expand All @@ -566,20 +568,28 @@ public actor RuntimeService {
guard let proc = processInfo.process else {
throw ContainerizationError(.invalidState, message: "process \(id) not started")
}
try await proc.kill(Signal(rawValue: Int32(try message.signal())))
return message.reply()
try await proc.kill(signal)
return
}

// TODO: fix underlying signal value to int64
try await ctr.container.kill(Signal(rawValue: Int32(try message.signal())))
return message.reply()
try await ctr.container.kill(signal)
default:
throw ContainerizationError(
.invalidState,
message: "cannot kill: container is not running"
)
}
}

// SIGKILL is guaranteed by the kernel to terminate the target, so block
// until we observe the exit.
if signal == .kill {
_ = await withCheckedContinuation { cc in
self.waitForExit(id: id, cont: cc)
}
}

return message.reply()
}

/// Resize the terminal for a process.
Expand Down Expand Up @@ -1250,8 +1260,11 @@ public actor RuntimeService {
}

extension XPCMessage {
fileprivate func signal() throws -> Int64 {
self.int64(key: RuntimeKeys.signal.rawValue)
fileprivate func signal() throws -> String {
guard let signal = self.string(key: RuntimeKeys.signal.rawValue) else {
throw ContainerizationError(.invalidArgument, message: "missing signal in xpc message")
}
return signal
}

fileprivate func stopOptions() throws -> ContainerStopOptions {
Expand Down