1use std::error::Error;
35use std::fmt::Write;
36use std::io;
37use std::panic;
38use std::process;
39
40#[derive(Debug, Clone, Copy)]
41enum Fork {
42 Parent,
43 Child,
44}
45
46fn fork() -> io::Result<Fork> {
48 let r = unsafe { libc::fork() };
50 handle_err(r).map(|r| match r {
51 0 => Fork::Child,
52 _ => Fork::Parent,
53 })
54}
55
56fn close_std_fds() -> io::Result<()> {
58 handle_err(unsafe { libc::close(libc::STDOUT_FILENO) })?;
60 handle_err(unsafe { libc::close(libc::STDERR_FILENO) })?;
61 handle_err(unsafe { libc::close(libc::STDIN_FILENO) })?;
62 Ok(())
63}
64
65fn handle_err(res: i32) -> io::Result<i32> {
66 match res {
67 -1 => Err(io::Error::last_os_error()),
68 r => Ok(r),
69 }
70}
71
72#[allow(deprecated)]
73fn panic_hook(info: &panic::PanicInfo<'_>) {
74 let msg = match info.payload().downcast_ref::<&'static str>() {
75 Some(s) => *s,
76 None => match info.payload().downcast_ref::<String>() {
77 Some(s) => &s[..],
78 None => "Box<Any>",
79 },
80 };
81 log::error!("child panicked at '{}', {}", msg, info.location().unwrap());
82}
83
84pub fn spawn<F>(f: F) -> io::Result<()>
88where
89 F: FnOnce(),
90{
91 match fork()? {
92 Fork::Parent => Ok(()),
93 Fork::Child => match exec_child(f) {
94 Ok(()) => {
95 process::exit(0);
96 }
97 Err(err) => {
98 log::error!("{}", format_err(&err));
99 process::exit(1);
100 }
101 },
102 }
103}
104
105fn exec_child<F>(f: F) -> io::Result<()>
106where
107 F: FnOnce(),
108{
109 close_std_fds()?;
110 panic::set_hook(Box::new(panic_hook));
111 f();
112 Ok(())
113}
114
115#[doc(hidden)]
116pub fn format_err(err: &(dyn Error + 'static)) -> String {
117 let mut out = err.to_string();
118 let mut tmp = String::new();
119 let mut source = err.source();
120 while let Some(err) = source {
121 match write!(&mut tmp, "{err}") {
122 Ok(_) => {
123 if !out.contains(&tmp) {
124 out.push_str(": ");
125 out.push_str(&tmp);
126 }
127 }
128 Err(_) => {
129 out.push_str(": <unknown>");
130 }
131 }
132 source = err.source();
133 tmp.clear();
134 }
135 out
136}