Skip to main content

proc_connector/
proc_event.rs

1//! Process event types from the Linux Proc Connector.
2//!
3//! This module contains the `ProcEvent` enum and its `Display` implementation.
4
5use std::fmt;
6
7/// A parsed process event from the Linux Proc Connector.
8///
9/// Each variant corresponds to a `PROC_EVENT_*` constant from
10/// `<linux/cn_proc.h>`, with all relevant fields extracted into
11/// named fields.
12///
13/// The `Unknown` variant provides forward compatibility: if the kernel
14/// emits an event type this version of the library does not know about,
15/// it is returned as `Unknown` with the raw payload.
16///
17/// # Example: pattern matching
18///
19/// ```
20/// use proc_connector::ProcEvent;
21///
22/// fn describe(event: &ProcEvent) -> String {
23///     match event {
24///         ProcEvent::Exec { pid, .. } => format!("process {pid} exec'd"),
25///         ProcEvent::Fork { child_pid, .. } => format!("forked child {child_pid}"),
26///         ProcEvent::Exit { pid, exit_code, .. } => {
27///             format!("process {pid} exited with code {exit_code}")
28///         }
29///         ProcEvent::Uid { pid, ruid, euid, .. } => {
30///             format!("process {pid} uid changed {ruid}->{euid}")
31///         }
32///         ProcEvent::Gid { pid, rgid, egid, .. } => {
33///             format!("process {pid} gid changed {rgid}->{egid}")
34///         }
35///         ProcEvent::Sid { pid, .. } => format!("process {pid} session changed"),
36///         ProcEvent::Ptrace { pid, tracer_pid, .. } => {
37///             format!("process {pid} traced by {tracer_pid}")
38///         }
39///         ProcEvent::Comm { pid, comm, .. } => {
40///             let name = String::from_utf8_lossy(comm);
41///             let name = name.trim_end_matches('\0');
42///             format!("process {pid} renamed to {name}")
43///         }
44///         ProcEvent::Coredump { pid, .. } => format!("process {pid} dumped core"),
45///         ProcEvent::Unknown { what, .. } => format!("unknown event 0x{what:08x}"),
46///     }
47/// }
48///
49/// let exec = ProcEvent::Exec { pid: 42, tgid: 42, timestamp_ns: 0 };
50/// assert_eq!(describe(&exec), "process 42 exec'd");
51///
52/// let exit = ProcEvent::Exit { pid: 7, tgid: 7, exit_code: 0, exit_signal: 17, timestamp_ns: 0 };
53/// assert_eq!(describe(&exit), "process 7 exited with code 0");
54/// ```
55///
56/// # Example: Display formatting
57///
58/// ```
59/// use proc_connector::ProcEvent;
60///
61/// let event = ProcEvent::Fork {
62///     parent_pid: 100,
63///     parent_tgid: 100,
64///     child_pid: 200,
65///     child_tgid: 200,
66///     timestamp_ns: 0,
67/// };
68/// assert_eq!(event.to_string(), "FORK parent=(100,100) child=(200,200) ts=0");
69/// ```
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum ProcEvent {
72    /// A process called `execve(2)`.
73    Exec {
74        pid: u32,
75        tgid: u32,
76        /// Kernel timestamp (nanoseconds since boot).
77        timestamp_ns: u64,
78    },
79    /// A new process was created via `fork`/`clone`.
80    Fork {
81        parent_pid: u32,
82        parent_tgid: u32,
83        child_pid: u32,
84        child_tgid: u32,
85        /// Kernel timestamp (nanoseconds since boot).
86        timestamp_ns: u64,
87    },
88    /// A process exited.
89    Exit {
90        pid: u32,
91        tgid: u32,
92        exit_code: u32,
93        exit_signal: u32,
94        /// Kernel timestamp (nanoseconds since boot).
95        timestamp_ns: u64,
96    },
97    /// Real or effective UID changed.
98    Uid {
99        pid: u32,
100        tgid: u32,
101        ruid: u32,
102        euid: u32,
103        /// Kernel timestamp (nanoseconds since boot).
104        timestamp_ns: u64,
105    },
106    /// Real or effective GID changed.
107    Gid {
108        pid: u32,
109        tgid: u32,
110        rgid: u32,
111        egid: u32,
112        /// Kernel timestamp (nanoseconds since boot).
113        timestamp_ns: u64,
114    },
115    /// Session ID changed (`setsid`).
116    Sid {
117        pid: u32,
118        tgid: u32,
119        /// Kernel timestamp (nanoseconds since boot).
120        timestamp_ns: u64,
121    },
122    /// `ptrace` attach or detach.
123    Ptrace {
124        pid: u32,
125        tgid: u32,
126        tracer_pid: u32,
127        tracer_tgid: u32,
128        /// Kernel timestamp (nanoseconds since boot).
129        timestamp_ns: u64,
130    },
131    /// Process name (`comm`) changed (max 16 bytes, may include trailing NUL).
132    Comm {
133        pid: u32,
134        tgid: u32,
135        /// The new process name (up to 16 bytes, usually NUL-terminated).
136        comm: [u8; 16],
137        /// Kernel timestamp (nanoseconds since boot).
138        timestamp_ns: u64,
139    },
140    /// A core dump occurred.
141    Coredump {
142        pid: u32,
143        tgid: u32,
144        /// Kernel timestamp (nanoseconds since boot).
145        timestamp_ns: u64,
146    },
147    /// An unknown event type (forward-compatibility).
148    Unknown {
149        /// The raw `what` field value.
150        what: u32,
151        /// Raw bytes of the `event_data` union (may be empty).
152        raw_data: Vec<u8>,
153    },
154}
155
156impl fmt::Display for ProcEvent {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        match self {
159            ProcEvent::Exec {
160                pid,
161                tgid,
162                timestamp_ns,
163            } => write!(f, "EXEC pid={pid} tgid={tgid} ts={timestamp_ns}"),
164            ProcEvent::Fork {
165                parent_pid,
166                parent_tgid,
167                child_pid,
168                child_tgid,
169                timestamp_ns,
170            } => write!(
171                f,
172                "FORK parent=({parent_pid},{parent_tgid}) child=({child_pid},{child_tgid}) ts={timestamp_ns}"
173            ),
174            ProcEvent::Exit {
175                pid,
176                tgid,
177                exit_code,
178                exit_signal,
179                timestamp_ns,
180            } => write!(
181                f,
182                "EXIT pid={pid} tgid={tgid} code={exit_code} signal={exit_signal} ts={timestamp_ns}"
183            ),
184            ProcEvent::Uid {
185                pid,
186                tgid,
187                ruid,
188                euid,
189                timestamp_ns,
190            } => write!(
191                f,
192                "UID pid={pid} tgid={tgid} ruid={ruid} euid={euid} ts={timestamp_ns}"
193            ),
194            ProcEvent::Gid {
195                pid,
196                tgid,
197                rgid,
198                egid,
199                timestamp_ns,
200            } => write!(
201                f,
202                "GID pid={pid} tgid={tgid} rgid={rgid} egid={egid} ts={timestamp_ns}"
203            ),
204            ProcEvent::Sid {
205                pid,
206                tgid,
207                timestamp_ns,
208            } => write!(f, "SID pid={pid} tgid={tgid} ts={timestamp_ns}"),
209            ProcEvent::Ptrace {
210                pid,
211                tgid,
212                tracer_pid,
213                tracer_tgid,
214                timestamp_ns,
215            } => write!(
216                f,
217                "PTRACE pid={pid} tgid={tgid} tracer=({tracer_pid},{tracer_tgid}) ts={timestamp_ns}"
218            ),
219            ProcEvent::Comm {
220                pid,
221                tgid,
222                comm,
223                timestamp_ns,
224            } => {
225                // Find NUL terminator for clean display
226                let end = comm.iter().position(|&b| b == 0).unwrap_or(16);
227                let name = std::str::from_utf8(&comm[..end]).unwrap_or("<invalid>");
228                write!(
229                    f,
230                    "COMM pid={pid} tgid={tgid} name=\"{name}\" ts={timestamp_ns}"
231                )
232            }
233            ProcEvent::Coredump {
234                pid,
235                tgid,
236                timestamp_ns,
237            } => {
238                write!(f, "COREDUMP pid={pid} tgid={tgid} ts={timestamp_ns}")
239            }
240            ProcEvent::Unknown { what, raw_data } => {
241                write!(f, "UNKNOWN what=0x{what:08x} len={}", raw_data.len())
242            }
243        }
244    }
245}