libdd_crashtracker/shared/
configuration.rs1use crate::{default_signals, shared::constants, signal_from_signum};
4use libdd_common::Endpoint;
5use serde::{Deserialize, Serialize};
6use std::time::Duration;
7
8#[repr(C)]
14#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum StacktraceCollection {
16 Disabled,
18 WithoutSymbols,
19 EnabledWithInprocessSymbols,
24 EnabledWithSymbolsInReceiver,
25}
26
27#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub struct CrashtrackerConfiguration {
29 additional_files: Vec<String>,
31 create_alt_stack: bool,
32 demangle_names: bool,
34 endpoint: Option<Endpoint>,
35 resolve_frames: StacktraceCollection,
36 signals: Vec<i32>,
37 timeout: Duration,
38 unix_socket_path: Option<String>,
39 use_alt_stack: bool,
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
43pub struct CrashtrackerReceiverConfig {
44 pub args: Vec<String>,
45 pub env: Vec<(String, String)>,
46 pub path_to_receiver_binary: String,
47 pub stderr_filename: Option<String>,
48 pub stdout_filename: Option<String>,
49}
50
51impl CrashtrackerReceiverConfig {
52 pub fn new(
53 args: Vec<String>,
54 env: Vec<(String, String)>,
55 path_to_receiver_binary: String,
56 stderr_filename: Option<String>,
57 stdout_filename: Option<String>,
58 ) -> anyhow::Result<Self> {
59 anyhow::ensure!(
60 stderr_filename.is_none() && stdout_filename.is_none()
61 || stderr_filename != stdout_filename,
62 "Can't give the same filename for stderr ({stderr_filename:?})
63 and stdout ({stdout_filename:?}), they will conflict with each other"
64 );
65
66 Ok(Self {
67 args,
68 env,
69 path_to_receiver_binary,
70 stderr_filename,
71 stdout_filename,
72 })
73 }
74}
75
76impl CrashtrackerConfiguration {
77 #[allow(clippy::too_many_arguments)]
78 pub fn new(
79 additional_files: Vec<String>,
80 create_alt_stack: bool,
81 use_alt_stack: bool,
82 endpoint: Option<Endpoint>,
83 resolve_frames: StacktraceCollection,
84 mut signals: Vec<i32>,
85 timeout: Option<Duration>,
86 unix_socket_path: Option<String>,
87 demangle_names: bool,
88 ) -> anyhow::Result<Self> {
89 anyhow::ensure!(
91 !create_alt_stack || use_alt_stack,
92 "Cannot create an altstack without using it"
93 );
94 let timeout = timeout.unwrap_or(constants::DD_CRASHTRACK_DEFAULT_TIMEOUT);
95 if signals.is_empty() {
96 signals = default_signals();
97 } else {
98 let before_len = signals.len();
100 signals.sort();
101 signals.dedup();
102 anyhow::ensure!(
103 before_len == signals.len(),
104 "Signals contained duplicate elements"
105 );
106 signals
108 .iter()
109 .try_for_each(|x| signal_from_signum(*x).map(|_| ()))?;
110 }
111
112 Ok(Self {
115 additional_files,
116 create_alt_stack,
117 use_alt_stack,
118 endpoint,
119 resolve_frames,
120 signals,
121 timeout,
122 unix_socket_path,
123 demangle_names,
124 })
125 }
126
127 pub fn additional_files(&self) -> &Vec<String> {
128 &self.additional_files
129 }
130
131 pub fn create_alt_stack(&self) -> bool {
132 self.create_alt_stack
133 }
134
135 pub fn use_alt_stack(&self) -> bool {
136 self.use_alt_stack
137 }
138
139 pub fn endpoint(&self) -> &Option<Endpoint> {
140 &self.endpoint
141 }
142
143 pub fn resolve_frames(&self) -> StacktraceCollection {
144 self.resolve_frames
145 }
146
147 pub fn signals(&self) -> &Vec<i32> {
148 &self.signals
149 }
150
151 pub fn timeout(&self) -> Duration {
152 self.timeout
153 }
154
155 pub fn unix_socket_path(&self) -> &Option<String> {
156 &self.unix_socket_path
157 }
158
159 pub fn demangle_names(&self) -> bool {
160 self.demangle_names
161 }
162
163 pub fn set_create_alt_stack(&mut self, create_alt_stack: bool) -> anyhow::Result<()> {
164 anyhow::ensure!(
165 !create_alt_stack || self.use_alt_stack,
166 "Cannot create an altstack without using it"
167 );
168 self.create_alt_stack = create_alt_stack;
169 Ok(())
170 }
171
172 pub fn set_use_alt_stack(&mut self, use_alt_stack: bool) -> anyhow::Result<()> {
173 anyhow::ensure!(
174 !self.create_alt_stack || use_alt_stack,
175 "Cannot create an altstack without using it"
176 );
177 self.use_alt_stack = use_alt_stack;
178 Ok(())
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::CrashtrackerReceiverConfig;
185
186 #[test]
187 fn test_receiver_config_new() -> anyhow::Result<()> {
188 let args = vec!["foo".to_string()];
189 let env = vec![
190 ("bar".to_string(), "baz".to_string()),
191 ("apple".to_string(), "banana".to_string()),
192 ];
193 let path_to_receiver_binary = "/tmp/crashtracker-receiver-binary".to_string();
194 let stderr_filename = None;
195 let stdout_filename = None;
196
197 let config = CrashtrackerReceiverConfig::new(
198 args.clone(),
199 env.clone(),
200 path_to_receiver_binary.clone(),
201 stderr_filename.clone(),
202 stdout_filename.clone(),
203 )?;
204 assert_eq!(config.args, args);
205 assert_eq!(config.env, env);
206 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
207 assert_eq!(config.stderr_filename, stderr_filename);
208 assert_eq!(config.stdout_filename, stdout_filename);
209
210 let stderr_filename = None;
211 let stdout_filename = Some("/tmp/stdout.txt".to_string());
212 let config = CrashtrackerReceiverConfig::new(
213 args.clone(),
214 env.clone(),
215 path_to_receiver_binary.clone(),
216 stderr_filename.clone(),
217 stdout_filename.clone(),
218 )?;
219 assert_eq!(config.args, args);
220 assert_eq!(config.env, env);
221 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
222 assert_eq!(config.stderr_filename, stderr_filename);
223 assert_eq!(config.stdout_filename, stdout_filename);
224
225 let stderr_filename = Some("/tmp/stderr.txt".to_string());
226 let stdout_filename = None;
227 let config = CrashtrackerReceiverConfig::new(
228 args.clone(),
229 env.clone(),
230 path_to_receiver_binary.clone(),
231 stderr_filename.clone(),
232 stdout_filename.clone(),
233 )?;
234 assert_eq!(config.args, args);
235 assert_eq!(config.env, env);
236 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
237 assert_eq!(config.stderr_filename, stderr_filename);
238 assert_eq!(config.stdout_filename, stdout_filename);
239
240 let stderr_filename = Some("/tmp/stderr.txt".to_string());
241 let stdout_filename = Some("/tmp/stdout.txt".to_string());
242 let config = CrashtrackerReceiverConfig::new(
243 args.clone(),
244 env.clone(),
245 path_to_receiver_binary.clone(),
246 stderr_filename.clone(),
247 stdout_filename.clone(),
248 )?;
249 assert_eq!(config.args, args);
250 assert_eq!(config.env, env);
251 assert_eq!(config.path_to_receiver_binary, path_to_receiver_binary);
252 assert_eq!(config.stderr_filename, stderr_filename);
253 assert_eq!(config.stdout_filename, stdout_filename);
254
255 let stderr_filename = Some("/tmp/shared.txt".to_string());
256 let stdout_filename = Some("/tmp/shared.txt".to_string());
257 CrashtrackerReceiverConfig::new(
258 args.clone(),
259 env.clone(),
260 path_to_receiver_binary.clone(),
261 stderr_filename.clone(),
262 stdout_filename.clone(),
263 )
264 .unwrap_err();
265 Ok(())
266 }
267}