libdd_crashtracker/shared/configuration/
mod.rs1mod builder;
5pub use builder::CrashtrackerConfigurationBuilder;
6use core::time::Duration;
7use libdd_common::Endpoint;
8use serde::{Deserialize, Serialize};
9
10#[repr(C)]
16#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
17pub enum StacktraceCollection {
18 #[default]
19 Disabled,
20 WithoutSymbols,
21 EnabledWithInprocessSymbols,
26 EnabledWithSymbolsInReceiver,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct CrashtrackerConfiguration {
31 additional_files: Vec<String>,
33 #[serde(default)]
34 collect_all_threads: bool,
35 create_alt_stack: bool,
36 demangle_names: bool,
38 endpoint: Option<Endpoint>,
39 #[serde(default = "default_max_threads")]
40 max_threads: usize,
41 resolve_frames: StacktraceCollection,
42 signals: Vec<i32>,
43 timeout: Duration,
44 unix_socket_path: Option<String>,
45 #[serde(skip, default = "default_unix_socket_connector_value")]
46 unix_socket_connector: fn(&str) -> std::os::fd::RawFd,
47 use_alt_stack: bool,
48}
49
50impl PartialEq for CrashtrackerConfiguration {
51 fn eq(&self, other: &Self) -> bool {
52 self.additional_files == other.additional_files
53 && self.collect_all_threads == other.collect_all_threads
54 && self.create_alt_stack == other.create_alt_stack
55 && self.demangle_names == other.demangle_names
56 && self.endpoint == other.endpoint
57 && self.max_threads == other.max_threads
58 && self.resolve_frames == other.resolve_frames
59 && self.signals == other.signals
60 && self.timeout == other.timeout
61 && self.unix_socket_path == other.unix_socket_path
62 && self.use_alt_stack == other.use_alt_stack
63 }
64}
65
66pub const fn default_max_threads() -> usize {
67 256
68}
69
70pub fn default_unix_socket_connector(unix_socket_path: &str) -> std::os::fd::RawFd {
71 use std::os::fd::IntoRawFd;
72 use std::os::unix::net::UnixStream;
73 #[cfg(target_os = "linux")]
74 let stream = if unix_socket_path.starts_with(['.', '/']) {
75 UnixStream::connect(unix_socket_path)
76 } else {
77 use std::os::linux::net::SocketAddrExt;
78 match std::os::unix::net::SocketAddr::from_abstract_name(unix_socket_path) {
79 Ok(addr) => UnixStream::connect_addr(&addr),
80 Err(e) => Err(e),
81 }
82 };
83 #[cfg(not(target_os = "linux"))]
84 let stream = UnixStream::connect(unix_socket_path);
85 match stream {
86 Ok(s) => s.into_raw_fd(),
87 Err(_) => -1,
88 }
89}
90
91fn default_unix_socket_connector_value() -> fn(&str) -> std::os::fd::RawFd {
92 default_unix_socket_connector
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
96pub struct CrashtrackerReceiverConfig {
97 pub args: Vec<String>,
98 pub env: Vec<(String, String)>,
99 pub path_to_receiver_binary: String,
100 pub stderr_filename: Option<String>,
101 pub stdout_filename: Option<String>,
102}
103
104impl CrashtrackerReceiverConfig {
105 pub fn new(
106 args: Vec<String>,
107 env: Vec<(String, String)>,
108 path_to_receiver_binary: String,
109 stderr_filename: Option<String>,
110 stdout_filename: Option<String>,
111 ) -> anyhow::Result<Self> {
112 anyhow::ensure!(
113 stderr_filename.is_none() && stdout_filename.is_none()
114 || stderr_filename != stdout_filename,
115 "Can't give the same filename for stderr ({stderr_filename:?})
116 and stdout ({stdout_filename:?}), they will conflict with each other"
117 );
118
119 Ok(Self {
120 args,
121 env,
122 path_to_receiver_binary,
123 stderr_filename,
124 stdout_filename,
125 })
126 }
127}
128
129impl CrashtrackerConfiguration {
130 pub fn builder() -> CrashtrackerConfigurationBuilder {
131 CrashtrackerConfigurationBuilder::default()
132 }
133
134 pub fn additional_files(&self) -> &Vec<String> {
135 &self.additional_files
136 }
137
138 pub fn collect_all_threads(&self) -> bool {
139 self.collect_all_threads
140 }
141
142 pub fn create_alt_stack(&self) -> bool {
143 self.create_alt_stack
144 }
145
146 pub fn max_threads(&self) -> usize {
147 self.max_threads
148 }
149
150 pub fn use_alt_stack(&self) -> bool {
151 self.use_alt_stack
152 }
153
154 pub(crate) fn endpoint(&self) -> &Option<Endpoint> {
155 &self.endpoint
156 }
157
158 pub fn resolve_frames(&self) -> StacktraceCollection {
159 self.resolve_frames
160 }
161
162 pub fn signals(&self) -> &Vec<i32> {
163 &self.signals
164 }
165
166 pub fn timeout(&self) -> Duration {
167 self.timeout
168 }
169
170 pub fn unix_socket_path(&self) -> &Option<String> {
171 &self.unix_socket_path
172 }
173
174 pub fn unix_socket_connector(&self) -> fn(&str) -> std::os::fd::RawFd {
175 self.unix_socket_connector
176 }
177
178 pub fn demangle_names(&self) -> bool {
179 self.demangle_names
180 }
181
182 pub fn set_collect_all_threads(&mut self, collect: bool) {
183 self.collect_all_threads = collect;
184 }
185
186 pub fn set_max_threads(&mut self, max: usize) {
187 self.max_threads = max;
188 }
189
190 pub fn set_create_alt_stack(&mut self, create_alt_stack: bool) -> anyhow::Result<()> {
191 anyhow::ensure!(
192 !create_alt_stack || self.use_alt_stack,
193 "Cannot create an altstack without using it"
194 );
195 self.create_alt_stack = create_alt_stack;
196 Ok(())
197 }
198
199 pub fn set_use_alt_stack(&mut self, use_alt_stack: bool) -> anyhow::Result<()> {
200 anyhow::ensure!(
201 !self.create_alt_stack || use_alt_stack,
202 "Cannot create an altstack without using it"
203 );
204 self.use_alt_stack = use_alt_stack;
205 Ok(())
206 }
207
208 pub fn set_unix_socket_path(&mut self, path: String) {
209 self.unix_socket_path = Some(path);
210 }
211
212 pub fn set_unix_socket_connector(&mut self, connector: fn(&str) -> std::os::fd::RawFd) {
213 self.unix_socket_connector = connector;
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::CrashtrackerReceiverConfig;
220
221 #[test]
222 fn test_receiver_config_new() -> anyhow::Result<()> {
223 let args = vec!["foo".to_string()];
224 let env = vec![
225 ("bar".to_string(), "baz".to_string()),
226 ("apple".to_string(), "banana".to_string()),
227 ];
228 let path_to_receiver_binary = "/tmp/crashtracker-receiver-binary".to_string();
229 let stderr_filename = None;
230 let stdout_filename = None;
231
232 let config = CrashtrackerReceiverConfig::new(
233 args.clone(),
234 env.clone(),
235 path_to_receiver_binary.clone(),
236 stderr_filename.clone(),
237 stdout_filename.clone(),
238 )?;
239 assert_eq!(config.args, args);
240 assert_eq!(config.env, env);
241 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
242 assert_eq!(config.stderr_filename, stderr_filename);
243 assert_eq!(config.stdout_filename, stdout_filename);
244
245 let stderr_filename = None;
246 let stdout_filename = Some("/tmp/stdout.txt".to_string());
247 let config = CrashtrackerReceiverConfig::new(
248 args.clone(),
249 env.clone(),
250 path_to_receiver_binary.clone(),
251 stderr_filename.clone(),
252 stdout_filename.clone(),
253 )?;
254 assert_eq!(config.args, args);
255 assert_eq!(config.env, env);
256 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
257 assert_eq!(config.stderr_filename, stderr_filename);
258 assert_eq!(config.stdout_filename, stdout_filename);
259
260 let stderr_filename = Some("/tmp/stderr.txt".to_string());
261 let stdout_filename = None;
262 let config = CrashtrackerReceiverConfig::new(
263 args.clone(),
264 env.clone(),
265 path_to_receiver_binary.clone(),
266 stderr_filename.clone(),
267 stdout_filename.clone(),
268 )?;
269 assert_eq!(config.args, args);
270 assert_eq!(config.env, env);
271 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
272 assert_eq!(config.stderr_filename, stderr_filename);
273 assert_eq!(config.stdout_filename, stdout_filename);
274
275 let stderr_filename = Some("/tmp/stderr.txt".to_string());
276 let stdout_filename = Some("/tmp/stdout.txt".to_string());
277 let config = CrashtrackerReceiverConfig::new(
278 args.clone(),
279 env.clone(),
280 path_to_receiver_binary.clone(),
281 stderr_filename.clone(),
282 stdout_filename.clone(),
283 )?;
284 assert_eq!(config.args, args);
285 assert_eq!(config.env, env);
286 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
287 assert_eq!(config.stderr_filename, stderr_filename);
288 assert_eq!(config.stdout_filename, stdout_filename);
289
290 let stderr_filename = Some("/tmp/shared.txt".to_string());
291 let stdout_filename = Some("/tmp/shared.txt".to_string());
292 CrashtrackerReceiverConfig::new(
293 args.clone(),
294 env.clone(),
295 path_to_receiver_binary.clone(),
296 stderr_filename.clone(),
297 stdout_filename.clone(),
298 )
299 .unwrap_err();
300 Ok(())
301 }
302}