1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
33
34use super::connection::Connection;
35use super::error::Result;
36use super::parse::{PResult, parse_string_from_bytes, parse_u32_ne, parse_u64_ne};
37use super::protocol::{Connector, ProtocolState};
38use super::socket::NetlinkSocket;
39
40const CN_IDX_PROC: u32 = 1;
42const CN_VAL_PROC: u32 = 1;
43
44const PROC_EVENT_NONE: u32 = 0x00000000;
46const PROC_EVENT_FORK: u32 = 0x00000001;
47const PROC_EVENT_EXEC: u32 = 0x00000002;
48const PROC_EVENT_UID: u32 = 0x00000004;
49const PROC_EVENT_GID: u32 = 0x00000040;
50const PROC_EVENT_SID: u32 = 0x00000080;
51const PROC_EVENT_PTRACE: u32 = 0x00000100;
52const PROC_EVENT_COMM: u32 = 0x00000200;
53const PROC_EVENT_COREDUMP: u32 = 0x40000000;
54const PROC_EVENT_EXIT: u32 = 0x80000000;
55
56const PROC_CN_MCAST_LISTEN: u32 = 1;
58const PROC_CN_MCAST_IGNORE: u32 = 2;
59
60const NLMSG_HDRLEN: usize = 16;
62
63#[derive(Debug, Clone)]
65#[non_exhaustive]
66pub enum ProcEvent {
67 None,
69
70 Fork {
72 parent_pid: u32,
74 parent_tgid: u32,
76 child_pid: u32,
78 child_tgid: u32,
80 },
81
82 Exec {
84 pid: u32,
86 tgid: u32,
88 },
89
90 Uid {
92 pid: u32,
94 tgid: u32,
96 ruid: u32,
98 euid: u32,
100 },
101
102 Gid {
104 pid: u32,
106 tgid: u32,
108 rgid: u32,
110 egid: u32,
112 },
113
114 Sid {
116 pid: u32,
118 tgid: u32,
120 },
121
122 Comm {
124 pid: u32,
126 tgid: u32,
128 comm: String,
130 },
131
132 Ptrace {
134 pid: u32,
136 tgid: u32,
138 tracer_pid: u32,
140 tracer_tgid: u32,
142 },
143
144 Coredump {
146 pid: u32,
148 tgid: u32,
150 parent_pid: u32,
152 parent_tgid: u32,
154 },
155
156 Exit {
158 pid: u32,
160 tgid: u32,
162 exit_code: u32,
164 exit_signal: u32,
166 parent_pid: u32,
168 parent_tgid: u32,
170 },
171
172 Unknown {
174 what: u32,
176 },
177}
178
179impl ProcEvent {
180 pub fn pid(&self) -> Option<u32> {
182 match self {
183 ProcEvent::Fork { child_pid, .. } => Some(*child_pid),
184 ProcEvent::Exec { pid, .. } => Some(*pid),
185 ProcEvent::Uid { pid, .. } => Some(*pid),
186 ProcEvent::Gid { pid, .. } => Some(*pid),
187 ProcEvent::Sid { pid, .. } => Some(*pid),
188 ProcEvent::Comm { pid, .. } => Some(*pid),
189 ProcEvent::Ptrace { pid, .. } => Some(*pid),
190 ProcEvent::Coredump { pid, .. } => Some(*pid),
191 ProcEvent::Exit { pid, .. } => Some(*pid),
192 ProcEvent::None | ProcEvent::Unknown { .. } => None,
193 }
194 }
195
196 pub fn tgid(&self) -> Option<u32> {
198 match self {
199 ProcEvent::Fork { child_tgid, .. } => Some(*child_tgid),
200 ProcEvent::Exec { tgid, .. } => Some(*tgid),
201 ProcEvent::Uid { tgid, .. } => Some(*tgid),
202 ProcEvent::Gid { tgid, .. } => Some(*tgid),
203 ProcEvent::Sid { tgid, .. } => Some(*tgid),
204 ProcEvent::Comm { tgid, .. } => Some(*tgid),
205 ProcEvent::Ptrace { tgid, .. } => Some(*tgid),
206 ProcEvent::Coredump { tgid, .. } => Some(*tgid),
207 ProcEvent::Exit { tgid, .. } => Some(*tgid),
208 ProcEvent::None | ProcEvent::Unknown { .. } => None,
209 }
210 }
211
212 pub fn parse_from_bytes(input: &[u8]) -> Option<Self> {
217 let mut input = input;
218
219 let header = ProcEventHeader::parse(&mut input).ok()?;
221
222 match header.what {
224 PROC_EVENT_NONE => Some(ProcEvent::None),
225
226 PROC_EVENT_FORK => {
227 let parent_pid = parse_u32_ne(&mut input).ok()?;
228 let parent_tgid = parse_u32_ne(&mut input).ok()?;
229 let child_pid = parse_u32_ne(&mut input).ok()?;
230 let child_tgid = parse_u32_ne(&mut input).ok()?;
231 Some(ProcEvent::Fork {
232 parent_pid,
233 parent_tgid,
234 child_pid,
235 child_tgid,
236 })
237 }
238
239 PROC_EVENT_EXEC => {
240 let pid = parse_u32_ne(&mut input).ok()?;
241 let tgid = parse_u32_ne(&mut input).ok()?;
242 Some(ProcEvent::Exec { pid, tgid })
243 }
244
245 PROC_EVENT_UID => {
246 let pid = parse_u32_ne(&mut input).ok()?;
247 let tgid = parse_u32_ne(&mut input).ok()?;
248 let ruid = parse_u32_ne(&mut input).ok()?;
249 let euid = parse_u32_ne(&mut input).ok()?;
250 Some(ProcEvent::Uid {
251 pid,
252 tgid,
253 ruid,
254 euid,
255 })
256 }
257
258 PROC_EVENT_GID => {
259 let pid = parse_u32_ne(&mut input).ok()?;
260 let tgid = parse_u32_ne(&mut input).ok()?;
261 let rgid = parse_u32_ne(&mut input).ok()?;
262 let egid = parse_u32_ne(&mut input).ok()?;
263 Some(ProcEvent::Gid {
264 pid,
265 tgid,
266 rgid,
267 egid,
268 })
269 }
270
271 PROC_EVENT_SID => {
272 let pid = parse_u32_ne(&mut input).ok()?;
273 let tgid = parse_u32_ne(&mut input).ok()?;
274 Some(ProcEvent::Sid { pid, tgid })
275 }
276
277 PROC_EVENT_COMM => {
278 let pid = parse_u32_ne(&mut input).ok()?;
279 let tgid = parse_u32_ne(&mut input).ok()?;
280 if input.len() < 16 {
282 return None;
283 }
284 let comm = parse_string_from_bytes(&input[..16]);
285 Some(ProcEvent::Comm { pid, tgid, comm })
286 }
287
288 PROC_EVENT_PTRACE => {
289 let pid = parse_u32_ne(&mut input).ok()?;
290 let tgid = parse_u32_ne(&mut input).ok()?;
291 let tracer_pid = parse_u32_ne(&mut input).ok()?;
292 let tracer_tgid = parse_u32_ne(&mut input).ok()?;
293 Some(ProcEvent::Ptrace {
294 pid,
295 tgid,
296 tracer_pid,
297 tracer_tgid,
298 })
299 }
300
301 PROC_EVENT_COREDUMP => {
302 let pid = parse_u32_ne(&mut input).ok()?;
303 let tgid = parse_u32_ne(&mut input).ok()?;
304 let parent_pid = parse_u32_ne(&mut input).ok()?;
305 let parent_tgid = parse_u32_ne(&mut input).ok()?;
306 Some(ProcEvent::Coredump {
307 pid,
308 tgid,
309 parent_pid,
310 parent_tgid,
311 })
312 }
313
314 PROC_EVENT_EXIT => {
315 let pid = parse_u32_ne(&mut input).ok()?;
316 let tgid = parse_u32_ne(&mut input).ok()?;
317 let exit_code = parse_u32_ne(&mut input).ok()?;
318 let exit_signal = parse_u32_ne(&mut input).ok()?;
319 let parent_pid = parse_u32_ne(&mut input).ok()?;
320 let parent_tgid = parse_u32_ne(&mut input).ok()?;
321 Some(ProcEvent::Exit {
322 pid,
323 tgid,
324 exit_code,
325 exit_signal,
326 parent_pid,
327 parent_tgid,
328 })
329 }
330
331 _ => Some(ProcEvent::Unknown { what: header.what }),
332 }
333 }
334}
335
336#[repr(C)]
338#[derive(Debug, Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)]
339struct CnMsg {
340 idx: u32,
342 val: u32,
343 seq: u32,
345 ack: u32,
347 len: u16,
349 flags: u16,
351}
352
353impl CnMsg {
354 fn as_bytes(&self) -> &[u8] {
356 <Self as IntoBytes>::as_bytes(self)
357 }
358
359 fn from_bytes(data: &[u8]) -> Option<(&Self, &[u8])> {
362 Self::ref_from_prefix(data).ok()
363 }
364}
365
366#[derive(Debug, Clone, Copy)]
368struct ProcEventHeader {
369 what: u32,
370 #[allow(dead_code)]
371 cpu: u32,
372 #[allow(dead_code)]
373 timestamp_ns: u64,
374}
375
376impl ProcEventHeader {
377 fn parse(input: &mut &[u8]) -> PResult<Self> {
378 let what = parse_u32_ne(input)?;
379 let cpu = parse_u32_ne(input)?;
380 let timestamp_ns = parse_u64_ne(input)?;
381 Ok(Self {
382 what,
383 cpu,
384 timestamp_ns,
385 })
386 }
387}
388
389impl Connection<Connector> {
390 pub async fn new() -> Result<Self> {
402 let mut socket = NetlinkSocket::new(Connector::PROTOCOL)?;
403
404 socket.add_membership(CN_IDX_PROC)?;
406
407 let conn = Self::from_parts(socket, Connector);
408
409 conn.send_proc_control(PROC_CN_MCAST_LISTEN).await?;
411
412 Ok(conn)
413 }
414
415 pub async fn unregister(&self) -> Result<()> {
419 self.send_proc_control(PROC_CN_MCAST_IGNORE).await
420 }
421
422 async fn send_proc_control(&self, op: u32) -> Result<()> {
424 let seq = self.socket().next_seq();
425 let pid = self.socket().pid();
426
427 let mut buf = Vec::with_capacity(64);
429
430 let msg_len = NLMSG_HDRLEN + std::mem::size_of::<CnMsg>() + 4;
432 buf.extend_from_slice(&(msg_len as u32).to_ne_bytes()); buf.extend_from_slice(&0x0u16.to_ne_bytes()); buf.extend_from_slice(&0x0u16.to_ne_bytes()); buf.extend_from_slice(&seq.to_ne_bytes()); buf.extend_from_slice(&pid.to_ne_bytes()); let cn_msg = CnMsg {
440 idx: CN_IDX_PROC,
441 val: CN_VAL_PROC,
442 seq: 0,
443 ack: 0,
444 len: 4,
445 flags: 0,
446 };
447 buf.extend_from_slice(cn_msg.as_bytes());
448
449 buf.extend_from_slice(&op.to_ne_bytes());
451
452 self.socket().send(&buf).await?;
453 Ok(())
454 }
455
456 pub async fn recv(&self) -> Result<ProcEvent> {
476 loop {
477 let data = self.socket().recv_msg().await?;
478
479 if let Some(event) = self.parse_proc_event(&data) {
480 return Ok(event);
481 }
482 }
484 }
485
486 fn parse_proc_event(&self, data: &[u8]) -> Option<ProcEvent> {
488 if data.len() < NLMSG_HDRLEN {
490 return None;
491 }
492 let after_nlhdr = &data[NLMSG_HDRLEN..];
493
494 let (_cn_msg, mut input) = CnMsg::from_bytes(after_nlhdr)?;
496
497 let header = ProcEventHeader::parse(&mut input).ok()?;
499
500 match header.what {
502 PROC_EVENT_NONE => Some(ProcEvent::None),
503
504 PROC_EVENT_FORK => {
505 let parent_pid = parse_u32_ne(&mut input).ok()?;
506 let parent_tgid = parse_u32_ne(&mut input).ok()?;
507 let child_pid = parse_u32_ne(&mut input).ok()?;
508 let child_tgid = parse_u32_ne(&mut input).ok()?;
509 Some(ProcEvent::Fork {
510 parent_pid,
511 parent_tgid,
512 child_pid,
513 child_tgid,
514 })
515 }
516
517 PROC_EVENT_EXEC => {
518 let pid = parse_u32_ne(&mut input).ok()?;
519 let tgid = parse_u32_ne(&mut input).ok()?;
520 Some(ProcEvent::Exec { pid, tgid })
521 }
522
523 PROC_EVENT_UID => {
524 let pid = parse_u32_ne(&mut input).ok()?;
525 let tgid = parse_u32_ne(&mut input).ok()?;
526 let ruid = parse_u32_ne(&mut input).ok()?;
527 let euid = parse_u32_ne(&mut input).ok()?;
528 Some(ProcEvent::Uid {
529 pid,
530 tgid,
531 ruid,
532 euid,
533 })
534 }
535
536 PROC_EVENT_GID => {
537 let pid = parse_u32_ne(&mut input).ok()?;
538 let tgid = parse_u32_ne(&mut input).ok()?;
539 let rgid = parse_u32_ne(&mut input).ok()?;
540 let egid = parse_u32_ne(&mut input).ok()?;
541 Some(ProcEvent::Gid {
542 pid,
543 tgid,
544 rgid,
545 egid,
546 })
547 }
548
549 PROC_EVENT_SID => {
550 let pid = parse_u32_ne(&mut input).ok()?;
551 let tgid = parse_u32_ne(&mut input).ok()?;
552 Some(ProcEvent::Sid { pid, tgid })
553 }
554
555 PROC_EVENT_COMM => {
556 let pid = parse_u32_ne(&mut input).ok()?;
557 let tgid = parse_u32_ne(&mut input).ok()?;
558 if input.len() < 16 {
560 return None;
561 }
562 let comm = parse_string_from_bytes(&input[..16]);
563 Some(ProcEvent::Comm { pid, tgid, comm })
564 }
565
566 PROC_EVENT_PTRACE => {
567 let pid = parse_u32_ne(&mut input).ok()?;
568 let tgid = parse_u32_ne(&mut input).ok()?;
569 let tracer_pid = parse_u32_ne(&mut input).ok()?;
570 let tracer_tgid = parse_u32_ne(&mut input).ok()?;
571 Some(ProcEvent::Ptrace {
572 pid,
573 tgid,
574 tracer_pid,
575 tracer_tgid,
576 })
577 }
578
579 PROC_EVENT_COREDUMP => {
580 let pid = parse_u32_ne(&mut input).ok()?;
581 let tgid = parse_u32_ne(&mut input).ok()?;
582 let parent_pid = parse_u32_ne(&mut input).ok()?;
583 let parent_tgid = parse_u32_ne(&mut input).ok()?;
584 Some(ProcEvent::Coredump {
585 pid,
586 tgid,
587 parent_pid,
588 parent_tgid,
589 })
590 }
591
592 PROC_EVENT_EXIT => {
593 let pid = parse_u32_ne(&mut input).ok()?;
594 let tgid = parse_u32_ne(&mut input).ok()?;
595 let exit_code = parse_u32_ne(&mut input).ok()?;
596 let exit_signal = parse_u32_ne(&mut input).ok()?;
597 let parent_pid = parse_u32_ne(&mut input).ok()?;
598 let parent_tgid = parse_u32_ne(&mut input).ok()?;
599 Some(ProcEvent::Exit {
600 pid,
601 tgid,
602 exit_code,
603 exit_signal,
604 parent_pid,
605 parent_tgid,
606 })
607 }
608
609 _ => Some(ProcEvent::Unknown { what: header.what }),
610 }
611 }
612}
613
614#[cfg(test)]
615mod tests {
616 use super::*;
617
618 #[test]
619 fn proc_event_pid() {
620 let fork = ProcEvent::Fork {
621 parent_pid: 1,
622 parent_tgid: 1,
623 child_pid: 100,
624 child_tgid: 100,
625 };
626 assert_eq!(fork.pid(), Some(100));
627 assert_eq!(fork.tgid(), Some(100));
628
629 let exit = ProcEvent::Exit {
630 pid: 200,
631 tgid: 200,
632 exit_code: 0,
633 exit_signal: 17,
634 parent_pid: 1,
635 parent_tgid: 1,
636 };
637 assert_eq!(exit.pid(), Some(200));
638 }
639}