1pub fn signal_name(sig: i32) -> &'static str {
5 match sig {
6 1 => "SIGHUP",
7 2 => "SIGINT",
8 3 => "SIGQUIT",
9 4 => "SIGILL",
10 6 => "SIGABRT",
11 7 => "SIGBUS",
12 8 => "SIGFPE",
13 9 => "SIGKILL",
14 11 => "SIGSEGV",
15 13 => "SIGPIPE",
16 14 => "SIGALRM",
17 15 => "SIGTERM",
18 32..=64 => "a real-time signal",
19 _ => "an unknown signal",
20 }
21}
22
23pub fn exit_report(
30 code: Option<i32>,
31 signal: Option<i32>,
32 oom_kills: u64,
33 memory_max: Option<u64>,
34) -> (u8, Vec<String>) {
35 let exit = code
36 .map(|c| (c & 0xff) as u8)
37 .or_else(|| signal.map(|s| (128 + s).clamp(0, 255) as u8))
38 .unwrap_or(1);
39 let mut messages = Vec::new();
40 if let Some(s) = signal {
41 messages.push(format!(
42 "the command was killed by signal {s} ({})",
43 signal_name(s)
44 ));
45 }
46 if oom_kills > 0 {
47 let limit = memory_max
48 .map(common::format_bytes)
49 .unwrap_or_else(|| "memory.max".to_string());
50 let noun = if oom_kills == 1 {
51 "process"
52 } else {
53 "processes"
54 };
55 messages.push(format!(
56 "the memory limit ({limit}) was reached; the kernel killed {oom_kills} {noun} in this cgroup"
57 ));
58 }
59 (exit, messages)
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 const M150: u64 = 150 * 1024 * 1024;
66
67 #[test]
68 fn exit_codes_and_messages() {
69 assert_eq!(exit_report(Some(0), None, 0, None), (0, vec![]));
70 assert_eq!(exit_report(Some(3), None, 0, None), (3, vec![]));
71 let (code, msgs) = exit_report(None, Some(9), 1, Some(M150));
72 assert_eq!(code, 137);
73 assert_eq!(msgs[0], "the command was killed by signal 9 (SIGKILL)");
74 assert_eq!(
75 msgs[1],
76 "the memory limit (150.0M) was reached; the kernel killed 1 process in this cgroup"
77 );
78 let (code, msgs) = exit_report(Some(1), None, 2, Some(M150));
79 assert_eq!(code, 1);
80 assert_eq!(msgs, vec!["the memory limit (150.0M) was reached; the kernel killed 2 processes in this cgroup".to_string()]);
81 assert_eq!(exit_report(None, Some(15), 0, None).0, 143);
82 assert_eq!(exit_report(None, None, 0, None).0, 1);
83 }
84
85 #[test]
86 fn unknown_signals_have_a_generic_name() {
87 assert_eq!(signal_name(11), "SIGSEGV");
88 assert_eq!(signal_name(40), "a real-time signal");
89 }
90}