1#![cfg(target_os = "linux")]
18#![expect(missing_docs)]
19#![forbid(unsafe_code)]
20
21use anyhow::Context;
22use std::fs::File;
23use std::io::ErrorKind;
24use std::os::unix::fs::OpenOptionsExt;
25use std::path::Path;
26use tracing::Level;
27
28const KMSG_NOTE_BYTES: usize = 1024 * 256; pub fn main() -> ! {
31 if let Err(e) = do_main() {
32 tracing::error!(?e, "core dump error");
33 std::process::exit(libc::EXIT_FAILURE)
34 }
35
36 std::process::exit(libc::EXIT_SUCCESS)
37}
38
39pub fn do_main() -> anyhow::Result<()> {
40 let mut args = std::env::args().skip(1).peekable();
41
42 let level = if args.peek().is_some_and(|x| x == "-v") {
43 args.next();
44 Level::DEBUG
45 } else {
46 Level::INFO
47 };
48
49 tracing_subscriber::fmt()
50 .with_writer(std::io::stderr)
51 .log_internal_errors(true)
52 .with_max_level(level)
53 .with_timer(tracing_subscriber::fmt::time::uptime())
54 .compact()
55 .with_ansi(false)
56 .init();
57
58 if underhill_confidentiality::confidential_filtering_enabled() {
60 tracing::info!("crash reporting disabled due to CVM");
61 std::process::exit(libc::EXIT_FAILURE);
62 }
63
64 let pid: i32 = args
65 .next()
66 .context("missing pid")?
67 .parse()
68 .context("failed to parse pid")?;
69
70 if args.next().is_some() {
71 anyhow::bail!("unexpected extra arguments");
72 }
73
74 let mut builder = elfcore::CoreDumpBuilder::new(pid)?;
75
76 let mut kmsg_file = NonBlockingFile::new("/dev/kmsg");
77 match kmsg_file.as_mut() {
78 Ok(kmsg_file) => _ = builder.add_custom_file_note("KMSG", kmsg_file, KMSG_NOTE_BYTES),
79 Err(e) => tracing::error!("Failed to open KMSG file: {:?}", e),
80 }
81
82 let n = builder
83 .write(std::io::stdout().lock())
84 .context("failed to write core dump")?;
85
86 tracing::info!("dump: {} bytes", n);
87
88 Ok(())
89}
90
91struct NonBlockingFile(File);
92
93impl NonBlockingFile {
94 fn new<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
95 Ok(Self(
96 std::fs::OpenOptions::new()
97 .read(true)
98 .custom_flags(libc::O_NONBLOCK)
99 .open(path)?,
100 ))
101 }
102}
103
104impl std::io::Read for NonBlockingFile {
105 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
106 match self.0.read(buf) {
107 Ok(len) => Ok(len),
109 Err(ref err) if err.kind() == ErrorKind::WouldBlock => Ok(0),
111 Err(ref err)
115 if err.kind() == ErrorKind::Interrupted || err.kind() == ErrorKind::BrokenPipe =>
116 {
117 self.read(buf)
118 }
119 Err(e) => Err(e),
120 }
121 }
122}