Last active
October 22, 2024 09:32
-
-
Save trvswgnr/6d0dda4595dda708627cd9076841cbd3 to your computer and use it in GitHub Desktop.
typescript linux exit signals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Unix/POSIX Signal implementation with architecture-specific handling. | |
| * @module Signal | |
| */ | |
| type Prettify<T> = { | |
| [K in keyof T]: T[K]; | |
| } & {}; | |
| type IsNever<T> = [T] extends [never] ? true : false; | |
| const validSignalNumbers = [ | |
| 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, | |
| 25, 26, 27, 28, 29, 30, 31, | |
| ] as const; | |
| export type SignalNumber = (typeof validSignalNumbers)[number]; | |
| export const isValidSignalNumber = (value: unknown): value is SignalNumber => | |
| typeof value === "number" && validSignalNumbers.includes(value as SignalNumber); | |
| export const isValidSignalValue = ( | |
| value: unknown, | |
| ): value is { | |
| alpha_sparc: SignalNumber; | |
| x86_arm: SignalNumber; | |
| mips: SignalNumber; | |
| } => | |
| typeof value === "object" && | |
| value !== null && | |
| "alpha_sparc" in value && | |
| isValidSignalNumber(value.alpha_sparc) && | |
| "x86_arm" in value && | |
| isValidSignalNumber(value.x86_arm) && | |
| "mips" in value && | |
| isValidSignalNumber(value.mips); | |
| const hasValidSignalValue = <X extends { value: unknown }>( | |
| value: X, | |
| ): value is X & { value: SignalNumber } => isValidSignalValue(value.value); | |
| /** Supported CPU architecture types */ | |
| export const ArchType = { | |
| /** ARM 32-bit */ | |
| ARM: "arm", | |
| /** ARM 64-bit */ | |
| ARM64: "arm64", | |
| /** x86 32-bit */ | |
| X86: "ia32", | |
| /** LoongArch 64-bit */ | |
| LOONG64: "loong64", | |
| /** MIPS */ | |
| MIPS: "mips", | |
| /** MIPS little-endian */ | |
| MIPSEL: "mipsel", | |
| /** PowerPC */ | |
| PPC: "ppc", | |
| /** PowerPC 64-bit */ | |
| PPC64: "ppc64", | |
| /** RISC-V 64-bit */ | |
| RISCV64: "riscv64", | |
| /** S390 */ | |
| S390: "s390", | |
| /** S390x */ | |
| S390X: "s390x", | |
| /** x86 64-bit */ | |
| X64: "x64", | |
| } as const; | |
| export type ArchType = (typeof ArchType)[keyof typeof ArchType]; | |
| const validSignalNames = [ | |
| "SIGHUP", | |
| "SIGINT", | |
| "SIGQUIT", | |
| "SIGILL", | |
| "SIGABRT", | |
| "SIGFPE", | |
| "SIGKILL", | |
| "SIGSEGV", | |
| "SIGPIPE", | |
| "SIGALRM", | |
| "SIGTERM", | |
| "SIGUSR1", | |
| "SIGUSR2", | |
| "SIGCHLD", | |
| "SIGCONT", | |
| "SIGSTOP", | |
| "SIGTSTP", | |
| "SIGTTIN", | |
| "SIGTTOU", | |
| "SIGBUS", | |
| "SIGPOLL", | |
| "SIGPROF", | |
| "SIGSYS", | |
| "SIGTRAP", | |
| "SIGURG", | |
| "SIGVTALRM", | |
| "SIGXCPU", | |
| "SIGXFSZ", | |
| "SIGIOT", | |
| "SIGEMT", | |
| "SIGSTKFLT", | |
| "SIGIO", | |
| "SIGCLD", | |
| "SIGPWR", | |
| "SIGINFO", | |
| "SIGLOST", | |
| "SIGWINCH", | |
| "SIGUNUSED", | |
| ] as const; | |
| export type SignalName = (typeof validSignalNames)[number]; | |
| export const isValidSignalName = (value: unknown): value is SignalName => | |
| typeof value === "string" && validSignalNames.includes(value as SignalName); | |
| export const hasValidSignalName = <X extends { name: unknown }>( | |
| value: X, | |
| ): value is X & { name: SignalName } => isValidSignalName(value.name); | |
| export const ActionType = { | |
| /** Terminate the process */ | |
| Term: "Term", | |
| /** Generate core dump */ | |
| Core: "Core", | |
| /** Ignore the signal */ | |
| Ign: "Ign", | |
| /** Continue if stopped */ | |
| Cont: "Cont", | |
| /** Stop the process */ | |
| Stop: "Stop", | |
| } as const; | |
| export type ActionType = (typeof ActionType)[keyof typeof ActionType]; | |
| export const isValidActionType = (value: unknown): value is ActionType => | |
| typeof value === "string" && Object.values(ActionType).includes(value as ActionType); | |
| const hasValidActionType = <X extends { action: unknown }>( | |
| value: X, | |
| ): value is X & { action: ActionType } => isValidActionType(value.action); | |
| type ArchValue = SignalNumber | null; | |
| function archTupleToObject< | |
| const A extends ArchValue, | |
| const B extends ArchValue, | |
| const C extends ArchValue, | |
| >(value: readonly [A, B, C]) { | |
| return { | |
| alpha_sparc: value[0], | |
| x86_arm: value[1], | |
| mips: value[2], | |
| } as const; | |
| } | |
| type ArchValues<A extends ArchValue, B extends ArchValue, C extends ArchValue> = { | |
| /** Signal number for Alpha and SPARC architectures */ | |
| readonly alpha_sparc: A; | |
| /** Signal number for x86 and ARM architectures */ | |
| readonly x86_arm: B; | |
| /** Signal number for MIPS architecture */ | |
| readonly mips: C; | |
| }; | |
| export type Signal<S extends SignalName, V, A extends ActionType> = { | |
| readonly name: S; | |
| readonly value: V; | |
| readonly action: A; | |
| }; | |
| const createSignal = < | |
| const S extends SignalName, | |
| const A extends ArchValue, | |
| const B extends ArchValue, | |
| const C extends ArchValue, | |
| const Ac extends ActionType, | |
| >( | |
| name: S, | |
| value: readonly [A, B, C], | |
| action: Ac, | |
| ) => | |
| ({ | |
| name, | |
| value: archTupleToObject(value), | |
| action, | |
| } as const); | |
| export namespace Signal { | |
| /** Hangup detected on controlling terminal or death of controlling process */ | |
| export const SIGHUP = createSignal("SIGHUP", [1, 1, 1], ActionType.Term); | |
| export type SIGHUP = typeof SIGHUP; | |
| /** Interrupt from keyboard */ | |
| export const SIGINT = createSignal("SIGINT", [2, 2, 2], ActionType.Term); | |
| export type SIGINT = typeof SIGINT; | |
| /** Quit from keyboard */ | |
| export const SIGQUIT = createSignal("SIGQUIT", [3, 3, 3], ActionType.Core); | |
| export type SIGQUIT = typeof SIGQUIT; | |
| /** Illegal Instruction */ | |
| export const SIGILL = createSignal("SIGILL", [4, 4, 4], ActionType.Core); | |
| export type SIGILL = typeof SIGILL; | |
| /** Abort signal from abort(3) */ | |
| export const SIGABRT = createSignal("SIGABRT", [6, 6, 6], ActionType.Core); | |
| export type SIGABRT = typeof SIGABRT; | |
| /** Floating-point exception */ | |
| export const SIGFPE = createSignal("SIGFPE", [8, 8, 8], ActionType.Core); | |
| export type SIGFPE = typeof SIGFPE; | |
| /** Kill signal, cannot be caught, blocked or ignored */ | |
| export const SIGKILL = createSignal("SIGKILL", [9, 9, 9], ActionType.Term); | |
| export type SIGKILL = typeof SIGKILL; | |
| /** Invalid memory reference */ | |
| export const SIGSEGV = createSignal("SIGSEGV", [11, 11, 11], ActionType.Core); | |
| export type SIGSEGV = typeof SIGSEGV; | |
| /** Broken pipe: write to pipe with no readers; see pipe(7) */ | |
| export const SIGPIPE = createSignal("SIGPIPE", [13, 13, 13], ActionType.Term); | |
| export type SIGPIPE = typeof SIGPIPE; | |
| /** Timer signal from alarm(2) */ | |
| export const SIGALRM = createSignal("SIGALRM", [14, 14, 14], ActionType.Term); | |
| export type SIGALRM = typeof SIGALRM; | |
| /** Termination signal */ | |
| export const SIGTERM = createSignal("SIGTERM", [15, 15, 15], ActionType.Term); | |
| export type SIGTERM = typeof SIGTERM; | |
| /** User-defined signal 1 */ | |
| export const SIGUSR1 = createSignal("SIGUSR1", [30, 10, 16], ActionType.Term); | |
| export type SIGUSR1 = typeof SIGUSR1; | |
| /** User-defined signal 2 */ | |
| export const SIGUSR2 = createSignal("SIGUSR2", [31, 12, 17], ActionType.Term); | |
| export type SIGUSR2 = typeof SIGUSR2; | |
| /** Child stopped or terminated */ | |
| export const SIGCHLD = createSignal("SIGCHLD", [20, 17, 18], ActionType.Ign); | |
| export type SIGCHLD = typeof SIGCHLD; | |
| /** Continue if stopped */ | |
| export const SIGCONT = createSignal("SIGCONT", [19, 18, 25], ActionType.Cont); | |
| export type SIGCONT = typeof SIGCONT; | |
| /** Stop process, cannot be caught, blocked or ignored */ | |
| export const SIGSTOP = createSignal("SIGSTOP", [17, 19, 23], ActionType.Stop); | |
| export type SIGSTOP = typeof SIGSTOP; | |
| /** Stop typed at terminal */ | |
| export const SIGTSTP = createSignal("SIGTSTP", [18, 20, 24], ActionType.Stop); | |
| export type SIGTSTP = typeof SIGTSTP; | |
| /** Terminal input for background process */ | |
| export const SIGTTIN = createSignal("SIGTTIN", [21, 21, 26], ActionType.Stop); | |
| export type SIGTTIN = typeof SIGTTIN; | |
| /** Terminal output for background process */ | |
| export const SIGTTOU = createSignal("SIGTTOU", [22, 22, 27], ActionType.Stop); | |
| export type SIGTTOU = typeof SIGTTOU; | |
| /** Bus error (bad memory access) */ | |
| export const SIGBUS = createSignal("SIGBUS", [10, 7, 10], ActionType.Core); | |
| export type SIGBUS = typeof SIGBUS; | |
| /** Pollable event (Sys V). Synonym for SIGIO */ | |
| export const SIGPOLL = createSignal("SIGPOLL", [29, 29, 22], ActionType.Term); | |
| export type SIGPOLL = typeof SIGPOLL; | |
| /** Profiling timer expired */ | |
| export const SIGPROF = createSignal("SIGPROF", [27, 27, 29], ActionType.Term); | |
| export type SIGPROF = typeof SIGPROF; | |
| /** Bad system call (SVr4); see also seccomp(2) */ | |
| export const SIGSYS = createSignal("SIGSYS", [12, 31, 12], ActionType.Core); | |
| export type SIGSYS = typeof SIGSYS; | |
| /** Trace/breakpoint trap */ | |
| export const SIGTRAP = createSignal("SIGTRAP", [5, 5, 5], ActionType.Core); | |
| export type SIGTRAP = typeof SIGTRAP; | |
| /** Urgent condition on socket (4.2BSD) */ | |
| export const SIGURG = createSignal("SIGURG", [16, 23, 21], ActionType.Ign); | |
| export type SIGURG = typeof SIGURG; | |
| /** Virtual alarm clock (4.2BSD) */ | |
| export const SIGVTALRM = createSignal("SIGVTALRM", [26, 26, 28], ActionType.Term); | |
| export type SIGVTALRM = typeof SIGVTALRM; | |
| /** CPU time limit exceeded (4.2BSD); see setrlimit(2) */ | |
| export const SIGXCPU = createSignal("SIGXCPU", [24, 24, 30], ActionType.Core); | |
| export type SIGXCPU = typeof SIGXCPU; | |
| /** File size limit exceeded (4.2BSD); see setrlimit(2) */ | |
| export const SIGXFSZ = createSignal("SIGXFSZ", [25, 25, 31], ActionType.Core); | |
| export type SIGXFSZ = typeof SIGXFSZ; | |
| /** IOT trap. A synonym for SIGABRT */ | |
| export const SIGIOT = createSignal("SIGIOT", [6, 6, 6], ActionType.Core); | |
| export type SIGIOT = typeof SIGIOT; | |
| /** Emulator trap */ | |
| export const SIGEMT = createSignal("SIGEMT", [7, null, 7], ActionType.Term); | |
| export type SIGEMT = typeof SIGEMT; | |
| /** Stack fault on coprocessor (unused) */ | |
| export const SIGSTKFLT = createSignal("SIGSTKFLT", [null, 16, null], ActionType.Term); | |
| export type SIGSTKFLT = typeof SIGSTKFLT; | |
| /** I/O now possible (4.2BSD) */ | |
| export const SIGIO = createSignal("SIGIO", [23, 29, 22], ActionType.Term); | |
| export type SIGIO = typeof SIGIO; | |
| /** A synonym for SIGCHLD */ | |
| export const SIGCLD = createSignal("SIGCLD", [null, null, 18], ActionType.Ign); | |
| export type SIGCLD = typeof SIGCLD; | |
| /** Power failure (System V) */ | |
| export const SIGPWR = createSignal("SIGPWR", [29, 30, 19], ActionType.Term); | |
| export type SIGPWR = typeof SIGPWR; | |
| /** A synonym for SIGPWR */ | |
| export const SIGINFO = createSignal("SIGINFO", [29, null, null], ActionType.Term); | |
| export type SIGINFO = typeof SIGINFO; | |
| /** File lock lost (unused) */ | |
| export const SIGLOST = createSignal("SIGLOST", [null, null, null], ActionType.Term); | |
| export type SIGLOST = typeof SIGLOST; | |
| /** Window resize signal (4.3BSD, Sun) */ | |
| export const SIGWINCH = createSignal("SIGWINCH", [28, 28, 20], ActionType.Ign); | |
| export type SIGWINCH = typeof SIGWINCH; | |
| /** Synonymous with SIGSYS */ | |
| export const SIGUNUSED = createSignal("SIGUNUSED", [31, null, null], ActionType.Core); | |
| export type SIGUNUSED = typeof SIGUNUSED; | |
| } | |
| type SignalNS = typeof Signal; | |
| type AnySignal = SignalNS[keyof SignalNS]; | |
| type _SignalFromValue<S extends number | ArchValues<ArchValue, ArchValue, ArchValue>> = | |
| S extends number | |
| ? Extract<AnySignal, { value: readonly [S, S, S] }> | |
| : Extract<AnySignal, { value: S }>; | |
| type SignalFromValue<S extends number | ArchValues<ArchValue, ArchValue, ArchValue>> = | |
| IsNever<_SignalFromValue<S>> extends true | |
| ? S extends SignalNumber | |
| ? FilterSignalsByNumber<S> | |
| : never | |
| : _SignalFromValue<S>; | |
| type SignalFromName<N extends keyof SignalNS> = Extract<AnySignal, { signal: N }>; | |
| export function fromValue< | |
| const S extends SignalNumber | ArchValues<ArchValue, ArchValue, ArchValue>, | |
| >(value: S): SignalFromValue<S extends SignalNumber ? ArchValues<S, S, S> : S> { | |
| const archValue = toArch(value); | |
| for (const signal of Object.values(Signal)) { | |
| if (compareArch(signal.value, archValue)) { | |
| return signal as any; | |
| } | |
| } | |
| throw new Error("Invalid signal value"); | |
| } | |
| export function fromName<const S extends keyof SignalNS>(name: S): SignalFromName<S> { | |
| return Signal[name] as unknown as SignalFromName<S>; | |
| } | |
| function toArch<S extends number | ArchValues<ArchValue, ArchValue, ArchValue>>( | |
| v: S, | |
| ): ArchValues<ArchValue, ArchValue, ArchValue> { | |
| if (isValidSignalNumber(v)) { | |
| return archTupleToObject([v, v, v]); | |
| } | |
| if (typeof v === "object") { | |
| return v; | |
| } | |
| throw new Error("Invalid signal value"); | |
| } | |
| function compareArch( | |
| a: ArchValues<ArchValue, ArchValue, ArchValue>, | |
| b: ArchValues<ArchValue, ArchValue, ArchValue>, | |
| ): boolean { | |
| const { alpha_sparc: a1, x86_arm: a2, mips: a3 } = a; | |
| const { alpha_sparc: b1, x86_arm: b2, mips: b3 } = b; | |
| return a1 === b1 && a2 === b2 && a3 === b3; | |
| } | |
| export function isSignal(x: unknown): x is Signal<SignalName, number, ActionType> { | |
| return ( | |
| typeof x === "object" && | |
| x !== null && | |
| "name" in x && | |
| hasValidSignalName(x) && | |
| "value" in x && | |
| hasValidSignalValue(x) && | |
| "action" in x && | |
| hasValidActionType(x) && | |
| isValidSignal(x) | |
| ); | |
| } | |
| function isValidSignal< | |
| const X extends { | |
| name: SignalName; | |
| value: SignalNumber; | |
| action: ActionType; | |
| }, | |
| >(x: X): x is X & Signal<X["name"], X["value"], X["action"]> { | |
| for (const signal of Object.values(Signal)) { | |
| if (objectsAreEqual(signal, x)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| function objectsAreEqual< | |
| A extends Record<PropertyKey, unknown>, | |
| B extends Record<PropertyKey, unknown>, | |
| >(a: A, b: B): boolean { | |
| const aEntries = Object.entries(a); | |
| const bEntries = Object.entries(b); | |
| return ( | |
| aEntries.length === bEntries.length && | |
| aEntries.every(([key, value]) => b[key] === value) | |
| ); | |
| } | |
| type ArchField<A extends ArchType> = A extends "arm" | "arm64" | "ia32" | "x64" | |
| ? "x86_arm" | |
| : A extends "mips" | "mipsel" | |
| ? "mips" | |
| : A extends "ppc" | "ppc64" | "s390" | "s390x" | "loong64" | "riscv64" | |
| ? "alpha_sparc" | |
| : never; | |
| type _SignalForArch<N extends number, A extends ArchType> = Extract< | |
| AnySignal, | |
| { value: { [K in ArchField<A>]: N } } | |
| >; | |
| type SignalForArch<N extends number, A extends ArchType> = IsNever< | |
| _SignalForArch<N, A> | |
| > extends true | |
| ? N extends SignalNumber | |
| ? FilterSignalsByNumber<N> | |
| : never | |
| : _SignalForArch<N, A>; | |
| type FilterSignalsByNumber<N extends SignalNumber> = Extract< | |
| AnySignal, | |
| { value: { alpha_sparc: N } } | { value: { x86_arm: N } } | { value: { mips: N } } | |
| >; | |
| export function getSignalForArchitecture< | |
| const N extends number, | |
| const A extends ArchType, | |
| >(signalNumber: N, arch: A): Prettify<SignalForArch<N, A>> { | |
| const valueField = ["arm", "arm64", "ia32", "x64"].includes(arch) | |
| ? "x86_arm" | |
| : ["mips", "mipsel"].includes(arch) | |
| ? "mips" | |
| : ["ppc", "ppc64", "s390", "s390x", "loong64", "riscv64"].includes(arch) | |
| ? "alpha_sparc" | |
| : null; | |
| if (valueField === null) { | |
| throw new Error(`Unsupported architecture: ${arch}`); | |
| } | |
| for (const signal of Object.values(Signal)) { | |
| if (signal.value[valueField] === signalNumber) { | |
| return signal as SignalForArch<N, A>; | |
| } | |
| } | |
| throw new Error(`No signal found for number ${signalNumber} on architecture ${arch}`); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment