1use std::{io::Cursor, io::Read};
22
23use derive_builder::{Builder, UninitializedFieldError};
24use getset::Getters;
25use log::trace;
26
27use crate::{
28 self as neli,
29 consts::connector::{CnMsgIdx, CnMsgVal, ProcEventType},
30 err::{DeError, MsgError, SerError},
31 FromBytes, FromBytesWithInput, Header, Size, ToBytes,
32};
33
34#[derive(
36 Builder, Getters, Clone, Debug, PartialEq, Eq, Size, ToBytes, FromBytesWithInput, Header,
37)]
38#[neli(from_bytes_bound = "P: Size + FromBytesWithInput<Input = usize>")]
39#[builder(pattern = "owned")]
40pub struct CnMsg<P: Size> {
41 #[getset(get = "pub")]
43 idx: CnMsgIdx,
44 #[getset(get = "pub")]
46 val: CnMsgVal,
47 #[builder(default)]
49 #[getset(get = "pub")]
50 seq: u32,
51 #[builder(default)]
53 #[getset(get = "pub")]
54 ack: u32,
55 #[builder(
57 setter(skip),
58 default = "self.payload.as_ref().ok_or_else(|| UninitializedFieldError::new(\"payload\"))?.unpadded_size() as _"
59 )]
60 #[getset(get = "pub")]
61 len: u16,
62 #[builder(default)]
64 #[getset(get = "pub")]
65 flags: u16,
66 #[neli(size = "len as usize")]
71 #[neli(input = "input - Self::header_size()")]
72 #[getset(get = "pub")]
73 pub(crate) payload: P,
74}
75
76#[derive(Debug, Size)]
80pub struct ProcEventHeader {
81 pub cpu: u32,
83 pub timestamp_ns: u64,
85 pub event: ProcEvent,
87}
88
89#[derive(Debug, Size, Copy, Clone)]
91pub enum ProcEvent {
92 Ack {
94 err: u32,
96 },
97 Fork {
99 parent_pid: i32,
101 parent_tgid: i32,
103 child_pid: i32,
105 child_tgid: i32,
107 },
108 Exec {
110 process_pid: i32,
112 process_tgid: i32,
114 },
115 Uid {
117 process_pid: i32,
119 process_tgid: i32,
121 ruid: u32,
123 euid: u32,
125 },
126 Gid {
128 process_pid: i32,
130 process_tgid: i32,
132 rgid: u32,
134 egid: u32,
136 },
137 Sid {
139 process_pid: i32,
141 process_tgid: i32,
143 },
144 Ptrace {
146 process_pid: i32,
148 process_tgid: i32,
150 tracer_pid: i32,
152 tracer_tgid: i32,
154 },
155 Comm {
157 process_pid: i32,
159 process_tgid: i32,
161 comm: [u8; 16],
163 },
164 Coredump {
166 process_pid: i32,
168 process_tgid: i32,
170 parent_pid: i32,
172 parent_tgid: i32,
174 },
175 Exit {
177 process_pid: i32,
179 process_tgid: i32,
181 exit_code: u32,
183 exit_signal: u32,
185 parent_pid: i32,
187 parent_tgid: i32,
189 },
190}
191
192impl From<&ProcEvent> for ProcEventType {
193 fn from(ev: &ProcEvent) -> Self {
194 match ev {
195 ProcEvent::Ack { .. } => ProcEventType::None,
196 ProcEvent::Fork { .. } => ProcEventType::Fork,
197 ProcEvent::Exec { .. } => ProcEventType::Exec,
198 ProcEvent::Uid { .. } => ProcEventType::Uid,
199 ProcEvent::Gid { .. } => ProcEventType::Gid,
200 ProcEvent::Sid { .. } => ProcEventType::Sid,
201 ProcEvent::Ptrace { .. } => ProcEventType::Ptrace,
202 ProcEvent::Comm { .. } => ProcEventType::Comm,
203 ProcEvent::Coredump { .. } => ProcEventType::Coredump,
204 ProcEvent::Exit { exit_code, .. } => {
205 if *exit_code == 0 {
206 ProcEventType::Exit
207 } else {
208 ProcEventType::NonzeroExit
209 }
210 }
211 }
212 }
213}
214
215impl ToBytes for ProcEventHeader {
216 fn to_bytes(&self, buffer: &mut Cursor<Vec<u8>>) -> Result<(), SerError> {
217 ProcEventType::from(&self.event).to_bytes(buffer)?;
218 self.cpu.to_bytes(buffer)?;
219 self.timestamp_ns.to_bytes(buffer)?;
220
221 match self.event {
222 ProcEvent::Ack { err } => {
223 err.to_bytes(buffer)?;
224 }
225 ProcEvent::Fork {
226 parent_pid,
227 parent_tgid,
228 child_pid,
229 child_tgid,
230 } => {
231 parent_pid.to_bytes(buffer)?;
232 parent_tgid.to_bytes(buffer)?;
233 child_pid.to_bytes(buffer)?;
234 child_tgid.to_bytes(buffer)?;
235 }
236 ProcEvent::Exec {
237 process_pid,
238 process_tgid,
239 } => {
240 process_pid.to_bytes(buffer)?;
241 process_tgid.to_bytes(buffer)?;
242 }
243 ProcEvent::Uid {
244 process_pid,
245 process_tgid,
246 ruid,
247 euid,
248 } => {
249 process_pid.to_bytes(buffer)?;
250 process_tgid.to_bytes(buffer)?;
251 ruid.to_bytes(buffer)?;
252 euid.to_bytes(buffer)?;
253 }
254 ProcEvent::Gid {
255 process_pid,
256 process_tgid,
257 rgid,
258 egid,
259 } => {
260 process_pid.to_bytes(buffer)?;
261 process_tgid.to_bytes(buffer)?;
262 rgid.to_bytes(buffer)?;
263 egid.to_bytes(buffer)?;
264 }
265 ProcEvent::Sid {
266 process_pid,
267 process_tgid,
268 } => {
269 process_pid.to_bytes(buffer)?;
270 process_tgid.to_bytes(buffer)?;
271 }
272 ProcEvent::Ptrace {
273 process_pid,
274 process_tgid,
275 tracer_pid,
276 tracer_tgid,
277 } => {
278 process_pid.to_bytes(buffer)?;
279 process_tgid.to_bytes(buffer)?;
280 tracer_pid.to_bytes(buffer)?;
281 tracer_tgid.to_bytes(buffer)?;
282 }
283 ProcEvent::Comm {
284 process_pid,
285 process_tgid,
286 comm,
287 } => {
288 process_pid.to_bytes(buffer)?;
289 process_tgid.to_bytes(buffer)?;
290 comm.to_bytes(buffer)?;
291 }
292 ProcEvent::Coredump {
293 process_pid,
294 process_tgid,
295 parent_pid,
296 parent_tgid,
297 } => {
298 process_pid.to_bytes(buffer)?;
299 process_tgid.to_bytes(buffer)?;
300 parent_pid.to_bytes(buffer)?;
301 parent_tgid.to_bytes(buffer)?;
302 }
303 ProcEvent::Exit {
304 process_pid,
305 process_tgid,
306 exit_code,
307 exit_signal,
308 parent_pid,
309 parent_tgid,
310 } => {
311 process_pid.to_bytes(buffer)?;
312 process_tgid.to_bytes(buffer)?;
313 exit_code.to_bytes(buffer)?;
314 exit_signal.to_bytes(buffer)?;
315 parent_pid.to_bytes(buffer)?;
316 parent_tgid.to_bytes(buffer)?;
317 }
318 };
319
320 Ok(())
321 }
322}
323
324impl FromBytesWithInput for ProcEventHeader {
325 type Input = usize;
326
327 fn from_bytes_with_input(
328 buffer: &mut Cursor<impl AsRef<[u8]>>,
329 input: Self::Input,
330 ) -> Result<Self, DeError> {
331 let start = buffer.position();
332
333 trace!("Parsing ProcEventHeader at position {start} with input size {input}");
334
335 if input < 16 || buffer.position() as usize + input > buffer.get_ref().as_ref().len() {
337 return Err(DeError::InvalidInput(input));
338 }
339
340 fn parse(buffer: &mut Cursor<impl AsRef<[u8]>>) -> Result<ProcEventHeader, DeError> {
342 let what_val = u32::from_bytes(buffer)?;
343 let what = ProcEventType::from(what_val);
344 let cpu = u32::from_bytes(buffer)?;
345 let timestamp_ns = u64::from_bytes(buffer)?;
346
347 let event = match what {
348 ProcEventType::None => ProcEvent::Ack {
349 err: u32::from_bytes(buffer)?,
350 },
351 ProcEventType::Fork => ProcEvent::Fork {
352 parent_pid: i32::from_bytes(buffer)?,
353 parent_tgid: i32::from_bytes(buffer)?,
354 child_pid: i32::from_bytes(buffer)?,
355 child_tgid: i32::from_bytes(buffer)?,
356 },
357 ProcEventType::Exec => ProcEvent::Exec {
358 process_pid: i32::from_bytes(buffer)?,
359 process_tgid: i32::from_bytes(buffer)?,
360 },
361 ProcEventType::Uid => ProcEvent::Uid {
362 process_pid: i32::from_bytes(buffer)?,
363 process_tgid: i32::from_bytes(buffer)?,
364 ruid: u32::from_bytes(buffer)?,
365 euid: u32::from_bytes(buffer)?,
366 },
367 ProcEventType::Gid => ProcEvent::Gid {
368 process_pid: i32::from_bytes(buffer)?,
369 process_tgid: i32::from_bytes(buffer)?,
370 rgid: u32::from_bytes(buffer)?,
371 egid: u32::from_bytes(buffer)?,
372 },
373 ProcEventType::Sid => ProcEvent::Sid {
374 process_pid: i32::from_bytes(buffer)?,
375 process_tgid: i32::from_bytes(buffer)?,
376 },
377 ProcEventType::Ptrace => ProcEvent::Ptrace {
378 process_pid: i32::from_bytes(buffer)?,
379 process_tgid: i32::from_bytes(buffer)?,
380 tracer_pid: i32::from_bytes(buffer)?,
381 tracer_tgid: i32::from_bytes(buffer)?,
382 },
383 ProcEventType::Comm => {
384 let process_pid = i32::from_bytes(buffer)?;
385 let process_tgid = i32::from_bytes(buffer)?;
386 let mut comm = [0u8; 16];
387 buffer.read_exact(&mut comm)?;
388 ProcEvent::Comm {
389 process_pid,
390 process_tgid,
391 comm,
392 }
393 }
394 ProcEventType::Coredump => ProcEvent::Coredump {
395 process_pid: i32::from_bytes(buffer)?,
396 process_tgid: i32::from_bytes(buffer)?,
397 parent_pid: i32::from_bytes(buffer)?,
398 parent_tgid: i32::from_bytes(buffer)?,
399 },
400 ProcEventType::Exit | ProcEventType::NonzeroExit => ProcEvent::Exit {
401 process_pid: i32::from_bytes(buffer)?,
402 process_tgid: i32::from_bytes(buffer)?,
403 exit_code: u32::from_bytes(buffer)?,
404 exit_signal: u32::from_bytes(buffer)?,
405 parent_pid: i32::from_bytes(buffer)?,
406 parent_tgid: i32::from_bytes(buffer)?,
407 },
408 ProcEventType::UnrecognizedConst(i) => {
409 return Err(DeError::Msg(MsgError::new(format!(
410 "Unrecognized Proc event type: {i} (raw value: {what_val})"
411 ))));
412 }
413 };
414 Ok(ProcEventHeader {
415 cpu,
416 timestamp_ns,
417 event,
418 })
419 }
420
421 let event = match parse(buffer) {
422 Ok(ev) => ev,
423 Err(e) => {
424 buffer.set_position(start);
425 return Err(e);
426 }
427 };
428
429 buffer.set_position(start + input as u64);
430
431 Ok(event)
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 fn build_endian_agnostic_response() -> Vec<u8> {
442 let mut cursor = Cursor::new(vec![]);
443 let msg = CnMsg {
444 idx: CnMsgIdx::Proc,
445 val: CnMsgVal::Proc,
446 seq: 643,
447 ack: 0,
448 len: 40,
449 flags: 0,
450 payload: ProcEventHeader {
451 cpu: 1,
452 timestamp_ns: 2504390882488,
453 event: ProcEvent::Exec {
454 process_pid: 5759,
455 process_tgid: 5759,
456 },
457 },
458 };
459 msg.to_bytes(&mut cursor).unwrap();
460 cursor.into_inner()
461 }
462
463 #[test]
464 fn parse_static_proc_header() {
465 let mut cursor = Cursor::new(build_endian_agnostic_response());
466
467 let len = cursor.get_ref().len();
468 let msg: CnMsg<ProcEventHeader> = CnMsg::from_bytes_with_input(&mut cursor, len).unwrap();
469
470 assert_eq!(msg.idx(), &CnMsgIdx::Proc);
471 assert_eq!(msg.val(), &CnMsgVal::Proc);
472 assert_eq!(msg.payload.cpu, 1);
473 assert_eq!(msg.payload.timestamp_ns, 2504390882488);
474 match &msg.payload.event {
475 ProcEvent::Exec {
476 process_pid,
477 process_tgid,
478 } => {
479 assert_eq!(*process_pid, 5759);
480 assert_eq!(*process_tgid, 5759);
481 }
482 _ => panic!("Expected Exec event"),
483 }
484 }
485
486 #[test]
487 fn parse_static_raw_data() {
488 let mut cursor = Cursor::new(build_endian_agnostic_response());
489
490 let len = cursor.get_ref().len();
491 let msg: CnMsg<Vec<u8>> = CnMsg::from_bytes_with_input(&mut cursor, len).unwrap();
492
493 assert_eq!(msg.idx(), &CnMsgIdx::Proc);
494 assert_eq!(msg.val(), &CnMsgVal::Proc);
495 }
496}