1use crate::{
21 self as neli, FromBytesWithInput, Header, Size, ToBytes,
22 consts::connector::{CnMsgIdx, CnMsgVal, ProcEventType},
23 err::{DeError, MsgError},
24};
25use byteorder::{NativeEndian, ReadBytesExt};
26use derive_builder::Builder;
27use getset::Getters;
28use log::trace;
29use std::{io::Cursor, io::Read};
30
31#[derive(
33 Builder, Getters, Clone, Debug, PartialEq, Eq, Size, ToBytes, FromBytesWithInput, Header,
34)]
35#[neli(from_bytes_bound = "P: Size + FromBytesWithInput<Input = usize>")]
36#[builder(pattern = "owned")]
37pub struct CnMsg<P: Size> {
38 #[getset(get = "pub")]
40 idx: CnMsgIdx,
41 #[getset(get = "pub")]
43 val: CnMsgVal,
44 #[builder(default)]
46 #[getset(get = "pub")]
47 seq: u32,
48 #[builder(default)]
50 #[getset(get = "pub")]
51 ack: u32,
52 #[builder(
54 setter(skip),
55 default = "self.payload.as_ref().unwrap().unpadded_size() as _"
56 )]
57 #[getset(get = "pub")]
58 len: u16,
59 #[builder(default)]
61 #[getset(get = "pub")]
62 flags: u16,
63 #[neli(size = "len as usize")]
68 #[neli(input = "(len as usize)")]
69 #[getset(get = "pub")]
70 pub(crate) payload: P,
71}
72
73#[derive(Debug, Size)]
77pub struct ProcEventHeader {
78 pub cpu: u32,
80 pub timestamp_ns: u64,
82 pub event: ProcEvent,
84}
85
86#[derive(Debug, Size, Copy, Clone)]
88pub enum ProcEvent {
89 Ack {
91 err: u32,
93 },
94 Fork {
96 parent_pid: i32,
98 parent_tgid: i32,
100 child_pid: i32,
102 child_tgid: i32,
104 },
105 Exec {
107 process_pid: i32,
109 process_tgid: i32,
111 },
112 Uid {
114 process_pid: i32,
116 process_tgid: i32,
118 ruid: u32,
120 euid: u32,
122 },
123 Gid {
125 process_pid: i32,
127 process_tgid: i32,
129 rgid: u32,
131 egid: u32,
133 },
134 Sid {
136 process_pid: i32,
138 process_tgid: i32,
140 },
141 Ptrace {
143 process_pid: i32,
145 process_tgid: i32,
147 tracer_pid: i32,
149 tracer_tgid: i32,
151 },
152 Comm {
154 process_pid: i32,
156 process_tgid: i32,
158 comm: [u8; 16],
160 },
161 Coredump {
163 process_pid: i32,
165 process_tgid: i32,
167 parent_pid: i32,
169 parent_tgid: i32,
171 },
172 Exit {
174 process_pid: i32,
176 process_tgid: i32,
178 exit_code: u32,
180 exit_signal: u32,
182 parent_pid: i32,
184 parent_tgid: i32,
186 },
187}
188
189impl FromBytesWithInput for ProcEventHeader {
190 type Input = usize;
191
192 fn from_bytes_with_input(
193 buffer: &mut Cursor<impl AsRef<[u8]>>,
194 input: Self::Input,
195 ) -> Result<Self, DeError> {
196 let start = buffer.position() as usize;
197 let bytes = buffer.get_ref().as_ref();
198
199 trace!("Parsing ProcEventHeader at position {start} with input size {input}");
200
201 if input < 16 || bytes.len() < start + input {
203 return Err(DeError::InvalidInput(input));
204 }
205
206 let what_val = buffer.read_u32::<NativeEndian>()?;
208 let what = ProcEventType::from(what_val);
209 let cpu = buffer.read_u32::<NativeEndian>()?;
210 let timestamp_ns = buffer.read_u64::<NativeEndian>()?;
211
212 let event = match what {
213 ProcEventType::None => ProcEvent::Ack {
214 err: buffer.read_u32::<NativeEndian>()?,
215 },
216 ProcEventType::Fork => ProcEvent::Fork {
217 parent_pid: buffer.read_i32::<NativeEndian>()?,
218 parent_tgid: buffer.read_i32::<NativeEndian>()?,
219 child_pid: buffer.read_i32::<NativeEndian>()?,
220 child_tgid: buffer.read_i32::<NativeEndian>()?,
221 },
222 ProcEventType::Exec => ProcEvent::Exec {
223 process_pid: buffer.read_i32::<NativeEndian>()?,
224 process_tgid: buffer.read_i32::<NativeEndian>()?,
225 },
226 ProcEventType::Uid => ProcEvent::Uid {
227 process_pid: buffer.read_i32::<NativeEndian>()?,
228 process_tgid: buffer.read_i32::<NativeEndian>()?,
229 ruid: buffer.read_u32::<NativeEndian>()?,
230 euid: buffer.read_u32::<NativeEndian>()?,
231 },
232 ProcEventType::Gid => ProcEvent::Gid {
233 process_pid: buffer.read_i32::<NativeEndian>()?,
234 process_tgid: buffer.read_i32::<NativeEndian>()?,
235 rgid: buffer.read_u32::<NativeEndian>()?,
236 egid: buffer.read_u32::<NativeEndian>()?,
237 },
238 ProcEventType::Sid => ProcEvent::Sid {
239 process_pid: buffer.read_i32::<NativeEndian>()?,
240 process_tgid: buffer.read_i32::<NativeEndian>()?,
241 },
242 ProcEventType::Ptrace => ProcEvent::Ptrace {
243 process_pid: buffer.read_i32::<NativeEndian>()?,
244 process_tgid: buffer.read_i32::<NativeEndian>()?,
245 tracer_pid: buffer.read_i32::<NativeEndian>()?,
246 tracer_tgid: buffer.read_i32::<NativeEndian>()?,
247 },
248 ProcEventType::Comm => {
249 let process_pid = buffer.read_i32::<NativeEndian>()?;
250 let process_tgid = buffer.read_i32::<NativeEndian>()?;
251 let mut comm = [0u8; 16];
252 buffer.read_exact(&mut comm)?;
253 ProcEvent::Comm {
254 process_pid,
255 process_tgid,
256 comm,
257 }
258 }
259 ProcEventType::Coredump => ProcEvent::Coredump {
260 process_pid: buffer.read_i32::<NativeEndian>()?,
261 process_tgid: buffer.read_i32::<NativeEndian>()?,
262 parent_pid: buffer.read_i32::<NativeEndian>()?,
263 parent_tgid: buffer.read_i32::<NativeEndian>()?,
264 },
265 ProcEventType::Exit | ProcEventType::NonzeroExit => ProcEvent::Exit {
266 process_pid: buffer.read_i32::<NativeEndian>()?,
267 process_tgid: buffer.read_i32::<NativeEndian>()?,
268 exit_code: buffer.read_u32::<NativeEndian>()?,
269 exit_signal: buffer.read_u32::<NativeEndian>()?,
270 parent_pid: buffer.read_i32::<NativeEndian>()?,
271 parent_tgid: buffer.read_i32::<NativeEndian>()?,
272 },
273 ProcEventType::UnrecognizedConst(i) => {
274 return Err(DeError::Msg(MsgError::new(format!(
275 "Unrecognized Proc event type: {i} (raw value: {what_val})"
276 ))));
277 }
278 };
279
280 buffer.set_position(start as u64 + input as u64);
282
283 Ok(ProcEventHeader {
284 cpu,
285 timestamp_ns,
286 event,
287 })
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 static RAW_RESPONSE: &[u8] = &[
296 1, 0, 0, 0, 1, 0, 0, 0, 122, 2, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 184,
297 52, 84, 25, 71, 2, 0, 0, 127, 22, 0, 0, 127, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
298 0, 0, 0, 0,
299 ];
300
301 #[test]
302 fn parse_static_proc_header() {
303 let mut cursor = Cursor::new(&RAW_RESPONSE);
304
305 let msg: CnMsg<ProcEventHeader> =
306 CnMsg::from_bytes_with_input(&mut cursor, RAW_RESPONSE.len()).unwrap();
307
308 assert_eq!(msg.idx(), &CnMsgIdx::Proc);
309 assert_eq!(msg.val(), &CnMsgVal::Proc);
310 assert_eq!(msg.payload.cpu, 1);
311 assert_eq!(msg.payload.timestamp_ns, 2504390882488);
312 match &msg.payload.event {
313 ProcEvent::Exec {
314 process_pid,
315 process_tgid,
316 } => {
317 assert_eq!(*process_pid, 5759);
318 assert_eq!(*process_tgid, 5759);
319 }
320 _ => panic!("Expected Exec event"),
321 }
322 }
323
324 #[test]
325 fn parse_static_raw_data() {
326 let mut cursor = Cursor::new(&RAW_RESPONSE);
327
328 let msg: CnMsg<Vec<u8>> =
329 CnMsg::from_bytes_with_input(&mut cursor, RAW_RESPONSE.len()).unwrap();
330
331 assert_eq!(msg.idx(), &CnMsgIdx::Proc);
332 assert_eq!(msg.val(), &CnMsgVal::Proc);
333 assert_eq!(msg.payload, RAW_RESPONSE[CnMsg::<Vec<u8>>::header_size()..]);
334 }
335}