//===----------------------------------------------------------------------===// // Copyright © 2026 Apple Inc. and the Containerization project authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //===----------------------------------------------------------------------===// #if canImport(Darwin) import Darwin #elseif canImport(Glibc) import Glibc #elseif canImport(Musl) import Musl #else #error("Signal not supported on this platform.") #endif /// A unix signal. public struct Signal: RawRepresentable, Hashable, Sendable { public let rawValue: Int32 public init(rawValue: Int32) { self.rawValue = rawValue } /// Parse a signal from a string representation (e.g. "SIGKILL", "KILL", "9"). public init(_ name: String, from map: [String: Int32] = Signal.linux) throws { var signalUpper = name.uppercased() signalUpper.trimPrefix("SIG") if let sig = Int32(signalUpper) { if !map.values.contains(sig) { throw SignalError.invalidSignal(name) } self.rawValue = sig return } guard let sig = map[signalUpper] else { throw SignalError.invalidSignal(name) } self.rawValue = sig } // Signals that are commonly sent to containers and share the same // number across macOS/Linux. public static let hup = Signal(rawValue: 1) public static let int = Signal(rawValue: 2) public static let quit = Signal(rawValue: 3) public static let kill = Signal(rawValue: 9) public static let term = Signal(rawValue: 15) public static let winch = Signal(rawValue: 28) /// Linux signals. public enum Linux { public static let hup = Signal(rawValue: 1) public static let int = Signal(rawValue: 2) public static let quit = Signal(rawValue: 3) public static let ill = Signal(rawValue: 4) public static let trap = Signal(rawValue: 5) public static let abrt = Signal(rawValue: 6) public static let bus = Signal(rawValue: 7) public static let fpe = Signal(rawValue: 8) public static let kill = Signal(rawValue: 9) public static let usr1 = Signal(rawValue: 10) public static let segv = Signal(rawValue: 11) public static let usr2 = Signal(rawValue: 12) public static let pipe = Signal(rawValue: 13) public static let alrm = Signal(rawValue: 14) public static let term = Signal(rawValue: 15) public static let stkflt = Signal(rawValue: 16) public static let chld = Signal(rawValue: 17) public static let cont = Signal(rawValue: 18) public static let stop = Signal(rawValue: 19) public static let tstp = Signal(rawValue: 20) public static let ttin = Signal(rawValue: 21) public static let ttou = Signal(rawValue: 22) public static let urg = Signal(rawValue: 23) public static let xcpu = Signal(rawValue: 24) public static let xfsz = Signal(rawValue: 25) public static let vtalrm = Signal(rawValue: 26) public static let prof = Signal(rawValue: 27) public static let winch = Signal(rawValue: 28) public static let io = Signal(rawValue: 29) public static let poll = Signal(rawValue: 29) public static let pwr = Signal(rawValue: 30) public static let sys = Signal(rawValue: 31) public static func rtmin(offset: Int32 = 0) -> Signal { Signal(rawValue: 34 + offset) } public static let rtmax = Signal(rawValue: 64) } /// Darwin signals. public enum Darwin { public static let hup = Signal(rawValue: 1) public static let int = Signal(rawValue: 2) public static let quit = Signal(rawValue: 3) public static let ill = Signal(rawValue: 4) public static let trap = Signal(rawValue: 5) public static let abrt = Signal(rawValue: 6) public static let emt = Signal(rawValue: 7) public static let fpe = Signal(rawValue: 8) public static let kill = Signal(rawValue: 9) public static let bus = Signal(rawValue: 10) public static let segv = Signal(rawValue: 11) public static let sys = Signal(rawValue: 12) public static let pipe = Signal(rawValue: 13) public static let alrm = Signal(rawValue: 14) public static let term = Signal(rawValue: 15) public static let urg = Signal(rawValue: 16) public static let stop = Signal(rawValue: 17) public static let tstp = Signal(rawValue: 18) public static let cont = Signal(rawValue: 19) public static let chld = Signal(rawValue: 20) public static let ttin = Signal(rawValue: 21) public static let ttou = Signal(rawValue: 22) public static let io = Signal(rawValue: 23) public static let xcpu = Signal(rawValue: 24) public static let xfsz = Signal(rawValue: 25) public static let vtalrm = Signal(rawValue: 26) public static let prof = Signal(rawValue: 27) public static let winch = Signal(rawValue: 28) public static let info = Signal(rawValue: 29) public static let usr1 = Signal(rawValue: 30) public static let usr2 = Signal(rawValue: 31) } /// All Linux signals including real-time signals (RTMIN through RTMAX). public static let linux: [String: Int32] = [ "ABRT": 6, "ALRM": 14, "BUS": 7, "CHLD": 17, "CLD": 17, "CONT": 18, "FPE": 8, "HUP": 1, "ILL": 4, "INT": 2, "IO": 29, "IOT": 6, "KILL": 9, "PIPE": 13, "POLL": 29, "PROF": 27, "PWR": 30, "QUIT": 3, "SEGV": 11, "STKFLT": 16, "STOP": 19, "SYS": 31, "TERM": 15, "TRAP": 5, "TSTP": 20, "TTIN": 21, "TTOU": 22, "URG": 23, "USR1": 10, "USR2": 12, "VTALRM": 26, "WINCH": 28, "XCPU": 24, "XFSZ": 25, "RTMIN": 34, "RTMIN+1": 35, "RTMIN+2": 36, "RTMIN+3": 37, "RTMIN+4": 38, "RTMIN+5": 39, "RTMIN+6": 40, "RTMIN+7": 41, "RTMIN+8": 42, "RTMIN+9": 43, "RTMIN+10": 44, "RTMIN+11": 45, "RTMIN+12": 46, "RTMIN+13": 47, "RTMIN+14": 48, "RTMIN+15": 49, "RTMIN+16": 50, "RTMIN+17": 51, "RTMIN+18": 52, "RTMIN+19": 53, "RTMIN+20": 54, "RTMIN+21": 55, "RTMIN+22": 56, "RTMIN+23": 57, "RTMIN+24": 58, "RTMIN+25": 59, "RTMIN+26": 60, "RTMIN+27": 61, "RTMIN+28": 62, "RTMIN+29": 63, "RTMAX": 64, ] } #if os(macOS) extension Signal { /// All signals for the macOS host. public static let platform: [String: Int32] = [ "ABRT": SIGABRT, "ALRM": SIGALRM, "BUS": SIGBUS, "CHLD": SIGCHLD, "CONT": SIGCONT, "EMT": SIGEMT, "FPE": SIGFPE, "HUP": SIGHUP, "ILL": SIGILL, "INFO": SIGINFO, "INT": SIGINT, "IO": SIGIO, "IOT": SIGIOT, "KILL": SIGKILL, "PIPE": SIGPIPE, "PROF": SIGPROF, "QUIT": SIGQUIT, "SEGV": SIGSEGV, "STOP": SIGSTOP, "SYS": SIGSYS, "TERM": SIGTERM, "TRAP": SIGTRAP, "TSTP": SIGTSTP, "TTIN": SIGTTIN, "TTOU": SIGTTOU, "URG": SIGURG, "USR1": SIGUSR1, "USR2": SIGUSR2, "VTALRM": SIGVTALRM, "WINCH": SIGWINCH, "XCPU": SIGXCPU, "XFSZ": SIGXFSZ, ] } #elseif os(Linux) extension Signal { /// All signals for the Linux host. public static let platform = linux } #endif extension Signal { private static let platformToName: [Int32: String] = Dictionary(Signal.platform.map { ($0.value, $0.key) }, uniquingKeysWith: { first, _ in first }) /// Returns the canonical name for this signal on the current platform. public func platformName() -> String? { Self.platformName(self.rawValue) } /// Returns the canonical name for a signal number on the current platform. public static func platformName(_ signal: Int32) -> String? { platformToName[signal] } } #if os(macOS) extension Signal { /// Converts a macOS signal to the equivalent Linux signal. public func linuxSignal() -> Signal? { guard let name = Self.platformToName[self.rawValue], let linuxNumber = Signal.linux[name] else { return nil } return Signal(rawValue: linuxNumber) } } #endif extension Signal: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int32) { self.rawValue = value } } /// Errors that can be encountered for converting signals. public enum SignalError: Error, CustomStringConvertible { case invalidSignal(String) public var description: String { switch self { case .invalidSignal(let sig): return "invalid signal: \(sig)" } } }