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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use core::fmt;
use bun_core::output;
// PORT NOTE: Zig `enum(u8) { ..., _ }` is non-exhaustive — any u8 is a valid
// inhabitant. A Rust `#[repr(u8)] enum` with only the named variants would be
// UB for the `from()` path (which accepts arbitrary bytes), so this is ported
// as a transparent newtype with associated consts.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct SignalCode(pub u8);
// Associated-const generator fed by the canonical X-macro in `bun_core`.
macro_rules! __sys_signal_consts {
($($name:ident = $n:literal),* $(,)?) => { $(pub const $name: Self = Self($n);)* };
}
impl SignalCode {
bun_core::for_each_signal!(__sys_signal_consts);
// The `subprocess.kill()` method sends a signal to the child process. If no
// argument is given, the process will be sent the 'SIGTERM' signal.
pub const DEFAULT: Self = Self::SIGTERM;
pub fn name(self) -> Option<&'static str> {
match self.0 {
1..=31 => Some(bun_core::SIGNAL_NAMES[self.0 as usize]),
_ => None,
}
}
pub fn valid(self) -> bool {
self.0 <= Self::SIGSYS.0 && self.0 >= Self::SIGHUP.0
}
/// Shell scripts use exit codes 128 + signal number
/// https://tldp.org/LDP/abs/html/exitcodes.html
pub fn to_exit_code(self) -> Option<u8> {
match self.0 {
1..=31 => Some(128u8.wrapping_add(self.0)),
_ => None,
}
}
pub fn description(self) -> Option<&'static str> {
// Description names copied from fish
// https://github.com/fish-shell/fish-shell/blob/00ffc397b493f67e28f18640d3de808af29b1434/fish-rust/src/signal.rs#L420
match self {
Self::SIGHUP => Some("Terminal hung up"),
Self::SIGINT => Some("Quit request"),
Self::SIGQUIT => Some("Quit request"),
Self::SIGILL => Some("Illegal instruction"),
Self::SIGTRAP => Some("Trace or breakpoint trap"),
Self::SIGABRT => Some("Abort"),
Self::SIGBUS => Some("Misaligned address error"),
Self::SIGFPE => Some("Floating point exception"),
Self::SIGKILL => Some("Forced quit"),
Self::SIGUSR1 => Some("User defined signal 1"),
Self::SIGUSR2 => Some("User defined signal 2"),
Self::SIGSEGV => Some("Address boundary error"),
Self::SIGPIPE => Some("Broken pipe"),
Self::SIGALRM => Some("Timer expired"),
Self::SIGTERM => Some("Polite quit request"),
Self::SIGCHLD => Some("Child process status changed"),
Self::SIGCONT => Some("Continue previously stopped process"),
Self::SIGSTOP => Some("Forced stop"),
Self::SIGTSTP => Some("Stop request from job control (^Z)"),
Self::SIGTTIN => Some("Stop from terminal input"),
Self::SIGTTOU => Some("Stop from terminal output"),
Self::SIGURG => Some("Urgent socket condition"),
Self::SIGXCPU => Some("CPU time limit exceeded"),
Self::SIGXFSZ => Some("File size limit exceeded"),
Self::SIGVTALRM => Some("Virtual timefr expired"),
Self::SIGPROF => Some("Profiling timer expired"),
Self::SIGWINCH => Some("Window size change"),
Self::SIGIO => Some("I/O on asynchronous file descriptor is possible"),
Self::SIGSYS => Some("Bad system call"),
Self::SIGPWR => Some("Power failure"),
_ => None,
}
}
pub fn from<T: bytemuck::NoUninit>(value: T) -> SignalCode {
// Zig `std.mem.asBytes(&value)[0]` — view `value` as bytes and read the
// first one. `NoUninit` guarantees `T` is `Copy` with no padding/uninit
// bytes, so `bytemuck::bytes_of` is the safe equivalent of the raw
// `*(&raw const value).cast::<u8>()` reinterpret. A ZST `T` panics on
// the `[0]` index (was UB before); all callers pass integer types.
SignalCode(bytemuck::bytes_of(&value)[0])
}
pub fn fmt(self, enable_ansi_colors: bool) -> Fmt {
Fmt {
signal: self,
enable_ansi_colors,
}
}
}
/// `bun.ComptimeEnumMap(SignalCode)` — name-bytes → open newtype.
#[inline]
pub fn from_name(s: &[u8]) -> Option<SignalCode> {
bun_core::SignalCode::from_name(s).map(|c| SignalCode(c as u8))
}
// This wrapper struct is lame, what if bun's color formatter was more versatile
pub struct Fmt {
signal: SignalCode,
enable_ansi_colors: bool,
}
impl fmt::Display for Fmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let signal = self.signal;
// PERF(port): was comptime bool dispatch (`switch inline else`) — profile if it shows up on a hot path
if let Some(str_) = signal.name() {
if let Some(desc) = signal.description() {
// TODO(port): Output.prettyFmt("{s} <d>({s})<r>", enable_ansi_colors) —
// use bun_core::output's pretty-fmt helper once available.
if self.enable_ansi_colors {
return write!(f, "{} {}({}){}", str_, output::DIM, desc, output::RESET);
} else {
return write!(f, "{} ({})", str_, desc);
}
}
}
write!(f, "code {}", signal.0)
}
}
// NOTE: `pub const fromJS = @import("../sys_jsc/signal_code_jsc.zig").fromJS;`
// deleted per porting guide — `from_js` lives as an extension-trait method in
// the `bun_sys_jsc` crate.
// ported from: src/sys/SignalCode.zig