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)]
65pub enum ProcEvent {
66 None,
68
69 Fork {
71 parent_pid: u32,
73 parent_tgid: u32,
75 child_pid: u32,
77 child_tgid: u32,
79 },
80
81 Exec {
83 pid: u32,
85 tgid: u32,
87 },
88
89 Uid {
91 pid: u32,
93 tgid: u32,
95 ruid: u32,
97 euid: u32,
99 },
100
101 Gid {
103 pid: u32,
105 tgid: u32,
107 rgid: u32,
109 egid: u32,
111 },
112
113 Sid {
115 pid: u32,
117 tgid: u32,
119 },
120
121 Comm {
123 pid: u32,
125 tgid: u32,
127 comm: String,
129 },
130
131 Ptrace {
133 pid: u32,
135 tgid: u32,
137 tracer_pid: u32,
139 tracer_tgid: u32,
141 },
142
143 Coredump {
145 pid: u32,
147 tgid: u32,
149 parent_pid: u32,
151 parent_tgid: u32,
153 },
154
155 Exit {
157 pid: u32,
159 tgid: u32,
161 exit_code: u32,
163 exit_signal: u32,
165 parent_pid: u32,
167 parent_tgid: u32,
169 },
170
171 Unknown {
173 what: u32,
175 },
176}
177
178impl ProcEvent {
179 pub fn pid(&self) -> Option<u32> {
181 match self {
182 ProcEvent::Fork { child_pid, .. } => Some(*child_pid),
183 ProcEvent::Exec { pid, .. } => Some(*pid),
184 ProcEvent::Uid { pid, .. } => Some(*pid),
185 ProcEvent::Gid { pid, .. } => Some(*pid),
186 ProcEvent::Sid { pid, .. } => Some(*pid),
187 ProcEvent::Comm { pid, .. } => Some(*pid),
188 ProcEvent::Ptrace { pid, .. } => Some(*pid),
189 ProcEvent::Coredump { pid, .. } => Some(*pid),
190 ProcEvent::Exit { pid, .. } => Some(*pid),
191 ProcEvent::None | ProcEvent::Unknown { .. } => None,
192 }
193 }
194
195 pub fn tgid(&self) -> Option<u32> {
197 match self {
198 ProcEvent::Fork { child_tgid, .. } => Some(*child_tgid),
199 ProcEvent::Exec { tgid, .. } => Some(*tgid),
200 ProcEvent::Uid { tgid, .. } => Some(*tgid),
201 ProcEvent::Gid { tgid, .. } => Some(*tgid),
202 ProcEvent::Sid { tgid, .. } => Some(*tgid),
203 ProcEvent::Comm { tgid, .. } => Some(*tgid),
204 ProcEvent::Ptrace { tgid, .. } => Some(*tgid),
205 ProcEvent::Coredump { tgid, .. } => Some(*tgid),
206 ProcEvent::Exit { tgid, .. } => Some(*tgid),
207 ProcEvent::None | ProcEvent::Unknown { .. } => None,
208 }
209 }
210
211 pub fn parse_from_bytes(input: &[u8]) -> Option<Self> {
216 let mut input = input;
217
218 let header = ProcEventHeader::parse(&mut input).ok()?;
220
221 match header.what {
223 PROC_EVENT_NONE => Some(ProcEvent::None),
224
225 PROC_EVENT_FORK => {
226 let parent_pid = parse_u32_ne(&mut input).ok()?;
227 let parent_tgid = parse_u32_ne(&mut input).ok()?;
228 let child_pid = parse_u32_ne(&mut input).ok()?;
229 let child_tgid = parse_u32_ne(&mut input).ok()?;
230 Some(ProcEvent::Fork {
231 parent_pid,
232 parent_tgid,
233 child_pid,
234 child_tgid,
235 })
236 }
237
238 PROC_EVENT_EXEC => {
239 let pid = parse_u32_ne(&mut input).ok()?;
240 let tgid = parse_u32_ne(&mut input).ok()?;
241 Some(ProcEvent::Exec { pid, tgid })
242 }
243
244 PROC_EVENT_UID => {
245 let pid = parse_u32_ne(&mut input).ok()?;
246 let tgid = parse_u32_ne(&mut input).ok()?;
247 let ruid = parse_u32_ne(&mut input).ok()?;
248 let euid = parse_u32_ne(&mut input).ok()?;
249 Some(ProcEvent::Uid {
250 pid,
251 tgid,
252 ruid,
253 euid,
254 })
255 }
256
257 PROC_EVENT_GID => {
258 let pid = parse_u32_ne(&mut input).ok()?;
259 let tgid = parse_u32_ne(&mut input).ok()?;
260 let rgid = parse_u32_ne(&mut input).ok()?;
261 let egid = parse_u32_ne(&mut input).ok()?;
262 Some(ProcEvent::Gid {
263 pid,
264 tgid,
265 rgid,
266 egid,
267 })
268 }
269
270 PROC_EVENT_SID => {
271 let pid = parse_u32_ne(&mut input).ok()?;
272 let tgid = parse_u32_ne(&mut input).ok()?;
273 Some(ProcEvent::Sid { pid, tgid })
274 }
275
276 PROC_EVENT_COMM => {
277 let pid = parse_u32_ne(&mut input).ok()?;
278 let tgid = parse_u32_ne(&mut input).ok()?;
279 if input.len() < 16 {
281 return None;
282 }
283 let comm = parse_string_from_bytes(&input[..16]);
284 Some(ProcEvent::Comm { pid, tgid, comm })
285 }
286
287 PROC_EVENT_PTRACE => {
288 let pid = parse_u32_ne(&mut input).ok()?;
289 let tgid = parse_u32_ne(&mut input).ok()?;
290 let tracer_pid = parse_u32_ne(&mut input).ok()?;
291 let tracer_tgid = parse_u32_ne(&mut input).ok()?;
292 Some(ProcEvent::Ptrace {
293 pid,
294 tgid,
295 tracer_pid,
296 tracer_tgid,
297 })
298 }
299
300 PROC_EVENT_COREDUMP => {
301 let pid = parse_u32_ne(&mut input).ok()?;
302 let tgid = parse_u32_ne(&mut input).ok()?;
303 let parent_pid = parse_u32_ne(&mut input).ok()?;
304 let parent_tgid = parse_u32_ne(&mut input).ok()?;
305 Some(ProcEvent::Coredump {
306 pid,
307 tgid,
308 parent_pid,
309 parent_tgid,
310 })
311 }
312
313 PROC_EVENT_EXIT => {
314 let pid = parse_u32_ne(&mut input).ok()?;
315 let tgid = parse_u32_ne(&mut input).ok()?;
316 let exit_code = parse_u32_ne(&mut input).ok()?;
317 let exit_signal = parse_u32_ne(&mut input).ok()?;
318 let parent_pid = parse_u32_ne(&mut input).ok()?;
319 let parent_tgid = parse_u32_ne(&mut input).ok()?;
320 Some(ProcEvent::Exit {
321 pid,
322 tgid,
323 exit_code,
324 exit_signal,
325 parent_pid,
326 parent_tgid,
327 })
328 }
329
330 _ => Some(ProcEvent::Unknown { what: header.what }),
331 }
332 }
333}
334
335#[repr(C)]
337#[derive(Debug, Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)]
338struct CnMsg {
339 idx: u32,
341 val: u32,
342 seq: u32,
344 ack: u32,
346 len: u16,
348 flags: u16,
350}
351
352impl CnMsg {
353 fn as_bytes(&self) -> &[u8] {
355 <Self as IntoBytes>::as_bytes(self)
356 }
357
358 fn from_bytes(data: &[u8]) -> Option<(&Self, &[u8])> {
361 Self::ref_from_prefix(data).ok()
362 }
363}
364
365#[derive(Debug, Clone, Copy)]
367struct ProcEventHeader {
368 what: u32,
369 #[allow(dead_code)]
370 cpu: u32,
371 #[allow(dead_code)]
372 timestamp_ns: u64,
373}
374
375impl ProcEventHeader {
376 fn parse(input: &mut &[u8]) -> PResult<Self> {
377 let what = parse_u32_ne(input)?;
378 let cpu = parse_u32_ne(input)?;
379 let timestamp_ns = parse_u64_ne(input)?;
380 Ok(Self {
381 what,
382 cpu,
383 timestamp_ns,
384 })
385 }
386}
387
388impl Connection<Connector> {
389 pub async fn new() -> Result<Self> {
401 let mut socket = NetlinkSocket::new(Connector::PROTOCOL)?;
402
403 socket.add_membership(CN_IDX_PROC)?;
405
406 let conn = Self::from_parts(socket, Connector);
407
408 conn.send_proc_control(PROC_CN_MCAST_LISTEN).await?;
410
411 Ok(conn)
412 }
413
414 pub async fn unregister(&self) -> Result<()> {
418 self.send_proc_control(PROC_CN_MCAST_IGNORE).await
419 }
420
421 async fn send_proc_control(&self, op: u32) -> Result<()> {
423 let seq = self.socket().next_seq();
424 let pid = self.socket().pid();
425
426 let mut buf = Vec::with_capacity(64);
428
429 let msg_len = NLMSG_HDRLEN + std::mem::size_of::<CnMsg>() + 4;
431 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 {
439 idx: CN_IDX_PROC,
440 val: CN_VAL_PROC,
441 seq: 0,
442 ack: 0,
443 len: 4,
444 flags: 0,
445 };
446 buf.extend_from_slice(cn_msg.as_bytes());
447
448 buf.extend_from_slice(&op.to_ne_bytes());
450
451 self.socket().send(&buf).await?;
452 Ok(())
453 }
454
455 pub async fn recv(&self) -> Result<ProcEvent> {
475 loop {
476 let data = self.socket().recv_msg().await?;
477
478 if let Some(event) = self.parse_proc_event(&data) {
479 return Ok(event);
480 }
481 }
483 }
484
485 fn parse_proc_event(&self, data: &[u8]) -> Option<ProcEvent> {
487 if data.len() < NLMSG_HDRLEN {
489 return None;
490 }
491 let after_nlhdr = &data[NLMSG_HDRLEN..];
492
493 let (_cn_msg, mut input) = CnMsg::from_bytes(after_nlhdr)?;
495
496 let header = ProcEventHeader::parse(&mut input).ok()?;
498
499 match header.what {
501 PROC_EVENT_NONE => Some(ProcEvent::None),
502
503 PROC_EVENT_FORK => {
504 let parent_pid = parse_u32_ne(&mut input).ok()?;
505 let parent_tgid = parse_u32_ne(&mut input).ok()?;
506 let child_pid = parse_u32_ne(&mut input).ok()?;
507 let child_tgid = parse_u32_ne(&mut input).ok()?;
508 Some(ProcEvent::Fork {
509 parent_pid,
510 parent_tgid,
511 child_pid,
512 child_tgid,
513 })
514 }
515
516 PROC_EVENT_EXEC => {
517 let pid = parse_u32_ne(&mut input).ok()?;
518 let tgid = parse_u32_ne(&mut input).ok()?;
519 Some(ProcEvent::Exec { pid, tgid })
520 }
521
522 PROC_EVENT_UID => {
523 let pid = parse_u32_ne(&mut input).ok()?;
524 let tgid = parse_u32_ne(&mut input).ok()?;
525 let ruid = parse_u32_ne(&mut input).ok()?;
526 let euid = parse_u32_ne(&mut input).ok()?;
527 Some(ProcEvent::Uid {
528 pid,
529 tgid,
530 ruid,
531 euid,
532 })
533 }
534
535 PROC_EVENT_GID => {
536 let pid = parse_u32_ne(&mut input).ok()?;
537 let tgid = parse_u32_ne(&mut input).ok()?;
538 let rgid = parse_u32_ne(&mut input).ok()?;
539 let egid = parse_u32_ne(&mut input).ok()?;
540 Some(ProcEvent::Gid {
541 pid,
542 tgid,
543 rgid,
544 egid,
545 })
546 }
547
548 PROC_EVENT_SID => {
549 let pid = parse_u32_ne(&mut input).ok()?;
550 let tgid = parse_u32_ne(&mut input).ok()?;
551 Some(ProcEvent::Sid { pid, tgid })
552 }
553
554 PROC_EVENT_COMM => {
555 let pid = parse_u32_ne(&mut input).ok()?;
556 let tgid = parse_u32_ne(&mut input).ok()?;
557 if input.len() < 16 {
559 return None;
560 }
561 let comm = parse_string_from_bytes(&input[..16]);
562 Some(ProcEvent::Comm { pid, tgid, comm })
563 }
564
565 PROC_EVENT_PTRACE => {
566 let pid = parse_u32_ne(&mut input).ok()?;
567 let tgid = parse_u32_ne(&mut input).ok()?;
568 let tracer_pid = parse_u32_ne(&mut input).ok()?;
569 let tracer_tgid = parse_u32_ne(&mut input).ok()?;
570 Some(ProcEvent::Ptrace {
571 pid,
572 tgid,
573 tracer_pid,
574 tracer_tgid,
575 })
576 }
577
578 PROC_EVENT_COREDUMP => {
579 let pid = parse_u32_ne(&mut input).ok()?;
580 let tgid = parse_u32_ne(&mut input).ok()?;
581 let parent_pid = parse_u32_ne(&mut input).ok()?;
582 let parent_tgid = parse_u32_ne(&mut input).ok()?;
583 Some(ProcEvent::Coredump {
584 pid,
585 tgid,
586 parent_pid,
587 parent_tgid,
588 })
589 }
590
591 PROC_EVENT_EXIT => {
592 let pid = parse_u32_ne(&mut input).ok()?;
593 let tgid = parse_u32_ne(&mut input).ok()?;
594 let exit_code = parse_u32_ne(&mut input).ok()?;
595 let exit_signal = parse_u32_ne(&mut input).ok()?;
596 let parent_pid = parse_u32_ne(&mut input).ok()?;
597 let parent_tgid = parse_u32_ne(&mut input).ok()?;
598 Some(ProcEvent::Exit {
599 pid,
600 tgid,
601 exit_code,
602 exit_signal,
603 parent_pid,
604 parent_tgid,
605 })
606 }
607
608 _ => Some(ProcEvent::Unknown { what: header.what }),
609 }
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn proc_event_pid() {
619 let fork = ProcEvent::Fork {
620 parent_pid: 1,
621 parent_tgid: 1,
622 child_pid: 100,
623 child_tgid: 100,
624 };
625 assert_eq!(fork.pid(), Some(100));
626 assert_eq!(fork.tgid(), Some(100));
627
628 let exit = ProcEvent::Exit {
629 pid: 200,
630 tgid: 200,
631 exit_code: 0,
632 exit_signal: 17,
633 parent_pid: 1,
634 parent_tgid: 1,
635 };
636 assert_eq!(exit.pid(), Some(200));
637 }
638}