minidumper_child/
server.rs

1use minidumper::{LoopAction, MinidumpBinary, Server, ServerHandler};
2use std::{
3    fs::{self, File},
4    io::{self, Read, Write},
5    path::{Path, PathBuf},
6    sync::atomic::AtomicBool,
7    time::Duration,
8};
9
10use crate::Error;
11
12struct Handler<Minidump, Message>
13where
14    Minidump: Fn(Vec<u8>, &Path) + Send + Sync + 'static,
15    Message: Fn(u32, Vec<u8>) + Send + Sync + 'static,
16{
17    crashes_dir: PathBuf,
18    on_minidump: Option<Minidump>,
19    on_message: Option<Message>,
20}
21
22impl<Minidump, Message> Handler<Minidump, Message>
23where
24    Minidump: Fn(Vec<u8>, &Path) + Send + Sync + 'static,
25    Message: Fn(u32, Vec<u8>) + Send + Sync + 'static,
26{
27    pub fn new(
28        crashes_dir: PathBuf,
29        on_minidump: Option<Minidump>,
30        on_message: Option<Message>,
31    ) -> Self {
32        Handler {
33            crashes_dir,
34            on_minidump,
35            on_message,
36        }
37    }
38}
39
40impl<Minidump, Message> ServerHandler for Handler<Minidump, Message>
41where
42    Minidump: Fn(Vec<u8>, &Path) + Send + Sync + 'static,
43    Message: Fn(u32, Vec<u8>) + Send + Sync + 'static,
44{
45    /// Called when a crash has been received and a backing file needs to be
46    /// created to store it.
47    fn create_minidump_file(&self) -> Result<(File, PathBuf), io::Error> {
48        fs::create_dir_all(&self.crashes_dir)?;
49        let file_name = format!("{}.dmp", uuid::Uuid::new_v4());
50        let path = self.crashes_dir.join(file_name);
51        Ok((File::create(&path)?, path))
52    }
53
54    /// Called when a crash has been fully written as a minidump to the provided
55    /// file. Also returns the full heap buffer as well.
56    fn on_minidump_created(&self, result: Result<MinidumpBinary, minidumper::Error>) -> LoopAction {
57        if let Ok(mut minidump) = result {
58            if let Some(buffer) = minidump.contents.or_else(|| {
59                minidump.file.flush().ok().and_then(|_| {
60                    let mut buf = Vec::new();
61                    File::open(&minidump.path)
62                        .unwrap()
63                        .read_to_end(&mut buf)
64                        .map(|_| buf)
65                        .ok()
66                })
67            }) {
68                if let Some(on_minidump) = &self.on_minidump {
69                    on_minidump(buffer, &minidump.path)
70                }
71            }
72
73            fs::remove_file(minidump.path).ok();
74        }
75
76        // Tells the server to exit, which will in turn exit the process
77        LoopAction::Exit
78    }
79
80    fn on_message(&self, kind: u32, buffer: Vec<u8>) {
81        if let Some(on_message) = &self.on_message {
82            on_message(kind, buffer);
83        }
84    }
85
86    fn on_client_disconnected(&self, _num_clients: usize) -> minidumper::LoopAction {
87        LoopAction::Exit
88    }
89}
90
91pub fn start<Minidump, Message>(
92    socket_name: &str,
93    crashes_dir: PathBuf,
94    stale_timeout: u64,
95    on_minidump: Option<Minidump>,
96    on_message: Option<Message>,
97) -> Result<(), Error>
98where
99    Minidump: Fn(Vec<u8>, &Path) + Send + Sync + 'static,
100    Message: Fn(u32, Vec<u8>) + Send + Sync + 'static,
101{
102    Server::with_name(socket_name)
103        .map_err(Error::from)
104        .and_then(|mut server| {
105            let handler = Box::new(Handler::new(crashes_dir, on_minidump, on_message));
106            let shutdown = AtomicBool::new(false);
107            let stale_timeout = Some(Duration::from_millis(stale_timeout));
108
109            server
110                .run(handler, &shutdown, stale_timeout)
111                .map_err(Error::from)
112        })
113}