1use std::{
2 collections::BTreeMap,
3 fmt::Debug,
4 io::Write,
5 sync::{Arc, atomic::AtomicU64},
6};
7
8use crate::{
9 cache::ArcStr,
10 printer::ListPrinter,
11 proc::{Cred, CredInspectError},
12 timestamp::Timestamp,
13};
14use chrono::{DateTime, Local};
15use clap::ValueEnum;
16use crossterm::event::KeyEvent;
17use enumflags2::BitFlags;
18use filterable_enum::FilterableEnum;
19use nix::{errno::Errno, libc::c_int, unistd::Pid};
20use strum::Display;
21use tokio::sync::mpsc;
22
23use crate::{
24 breakpoint::BreakPointHit,
25 proc::{EnvDiff, FileDescriptorInfoCollection, Interpreter},
26 tracer::ProcessExit,
27 tracer::{InspectError, Signal},
28};
29
30mod id;
31mod message;
32mod parent;
33pub use id::*;
34pub use message::*;
35pub use parent::*;
36
37#[derive(Debug, Clone, Display, PartialEq, Eq)]
38pub enum Event {
39 ShouldQuit,
40 Key(KeyEvent),
41 Tracer(TracerMessage),
42 Render,
43 Resize { width: u16, height: u16 },
44 Init,
45 Error,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum TracerMessage {
50 Event(TracerEvent),
52 StateUpdate(ProcessStateUpdateEvent),
55 FatalError(String),
56}
57
58impl From<TracerEvent> for TracerMessage {
59 fn from(event: TracerEvent) -> Self {
60 Self::Event(event)
61 }
62}
63
64impl From<ProcessStateUpdateEvent> for TracerMessage {
65 fn from(update: ProcessStateUpdateEvent) -> Self {
66 Self::StateUpdate(update)
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct TracerEvent {
72 pub details: TracerEventDetails,
73 pub id: EventId,
74}
75
76static ID: AtomicU64 = AtomicU64::new(0);
78
79impl TracerEvent {
80 pub fn allocate_id() -> EventId {
81 EventId::new(ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
82 }
83}
84
85impl From<TracerEventDetails> for TracerEvent {
86 fn from(details: TracerEventDetails) -> Self {
87 Self {
88 details,
89 id: Self::allocate_id(),
90 }
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, FilterableEnum)]
95#[filterable_enum(kind_extra_derive=ValueEnum, kind_extra_derive=Display, kind_extra_attrs="strum(serialize_all = \"kebab-case\")")]
96pub enum TracerEventDetails {
97 Info(TracerEventMessage),
98 Warning(TracerEventMessage),
99 Error(TracerEventMessage),
100 NewChild {
101 timestamp: Timestamp,
102 ppid: Pid,
103 pcomm: ArcStr,
104 pid: Pid,
105 },
106 Exec(Box<ExecEvent>),
107 TraceeSpawn {
108 pid: Pid,
109 timestamp: Timestamp,
110 },
111 TraceeExit {
112 timestamp: Timestamp,
113 signal: Option<Signal>,
114 exit_code: i32,
115 },
116}
117
118impl TracerEventDetails {
119 pub fn into_event_with_id(self, id: EventId) -> TracerEvent {
120 TracerEvent { details: self, id }
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct TracerEventMessage {
126 pub pid: Option<Pid>,
127 pub timestamp: Option<DateTime<Local>>,
128 pub msg: String,
129}
130
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct ExecEvent {
133 pub pid: Pid,
134 pub cwd: OutputMsg,
135 pub comm: ArcStr,
136 pub filename: OutputMsg,
137 pub argv: Arc<Result<Vec<OutputMsg>, InspectError>>,
138 pub envp: Arc<Result<BTreeMap<OutputMsg, OutputMsg>, InspectError>>,
139 pub has_dash_env: bool,
141 pub cred: Result<Cred, CredInspectError>,
142 pub interpreter: Option<Vec<Interpreter>>,
143 pub env_diff: Result<EnvDiff, InspectError>,
144 pub fdinfo: Arc<FileDescriptorInfoCollection>,
145 pub result: i64,
146 pub timestamp: Timestamp,
147 pub parent: Option<ParentEventId>,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub struct RuntimeModifier {
152 pub show_env: bool,
153 pub show_cwd: bool,
154}
155
156impl Default for RuntimeModifier {
157 fn default() -> Self {
158 Self {
159 show_env: true,
160 show_cwd: true,
161 }
162 }
163}
164
165impl TracerEventDetails {
166 pub fn into_tracer_msg(self) -> TracerMessage {
167 TracerMessage::Event(self.into())
168 }
169
170 pub fn timestamp(&self) -> Option<Timestamp> {
171 match self {
172 Self::Info(m) | Self::Warning(m) | Self::Error(m) => m.timestamp,
173 Self::Exec(exec_event) => Some(exec_event.timestamp),
174 Self::NewChild { timestamp, .. }
175 | Self::TraceeSpawn { timestamp, .. }
176 | Self::TraceeExit { timestamp, .. } => Some(*timestamp),
177 }
178 }
179}
180
181impl TracerEventDetails {
182 pub fn argv_to_string(argv: &Result<Vec<OutputMsg>, InspectError>) -> String {
183 let Ok(argv) = argv else {
184 return "[failed to read argv]".into();
185 };
186 let mut result =
187 Vec::with_capacity(argv.iter().map(|s| s.as_ref().len() + 3).sum::<usize>() + 2);
188 let list_printer = ListPrinter::new(crate::printer::ColorLevel::Less);
189 list_printer.print_string_list(&mut result, argv).unwrap();
190 unsafe { String::from_utf8_unchecked(result) }
192 }
193
194 pub fn interpreters_to_string(interpreters: &[Interpreter]) -> String {
195 let mut result = Vec::new();
196 let list_printer = ListPrinter::new(crate::printer::ColorLevel::Less);
197 match interpreters.len() {
198 0 => {
199 write!(result, "{}", Interpreter::None).unwrap();
200 }
201 1 => {
202 write!(result, "{}", interpreters[0]).unwrap();
203 }
204 _ => {
205 list_printer.begin(&mut result).unwrap();
206 for (idx, interpreter) in interpreters.iter().enumerate() {
207 if idx != 0 {
208 list_printer.comma(&mut result).unwrap();
209 }
210 write!(result, "{interpreter}").unwrap();
211 }
212 list_printer.end(&mut result).unwrap();
213 }
214 }
215 unsafe { String::from_utf8_unchecked(result) }
217 }
218}
219
220impl FilterableTracerEventDetails {
221 pub fn send_if_match(
222 self,
223 tx: &mpsc::UnboundedSender<TracerMessage>,
224 filter: BitFlags<TracerEventDetailsKind>,
225 ) -> Result<(), mpsc::error::SendError<TracerMessage>> {
226 if let Some(evt) = self.filter_and_take(filter) {
227 tx.send(TracerMessage::from(TracerEvent::from(evt)))?;
228 }
229 Ok(())
230 }
231}
232
233#[macro_export]
234macro_rules! filterable_event {
235 ($($t:tt)*) => {
236 tracexec_core::event::FilterableTracerEventDetails::from(tracexec_core::event::TracerEventDetails::$($t)*)
237 };
238}
239
240pub use filterable_event;
241
242#[derive(Debug, Clone, PartialEq, Eq)]
243pub enum ProcessStateUpdate {
244 Exit {
245 status: ProcessExit,
246 timestamp: Timestamp,
247 },
248 BreakPointHit(BreakPointHit),
249 Resumed,
250 Detached {
251 hid: u64,
252 timestamp: Timestamp,
253 },
254 ResumeError {
255 hit: BreakPointHit,
256 error: Errno,
257 },
258 DetachError {
259 hit: BreakPointHit,
260 error: Errno,
261 },
262}
263
264impl ProcessStateUpdate {
265 pub fn termination_timestamp(&self) -> Option<Timestamp> {
266 match self {
267 Self::Exit { timestamp, .. } | Self::Detached { timestamp, .. } => Some(*timestamp),
268 _ => None,
269 }
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
274pub struct ProcessStateUpdateEvent {
275 pub update: ProcessStateUpdate,
276 pub pid: Pid,
277 pub ids: Vec<EventId>,
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub enum EventStatus {
282 ExecENOENT,
284 ExecFailure,
285 ProcessRunning,
287 ProcessExitedNormally,
288 ProcessExitedAbnormally(c_int),
289 ProcessPaused,
290 ProcessDetached,
291 ProcessKilled,
293 ProcessTerminated,
294 ProcessInterrupted,
295 ProcessSegfault,
296 ProcessAborted,
297 ProcessIllegalInstruction,
298 ProcessSignaled(Signal),
299 InternalError,
301}
302
303impl From<EventStatus> for &'static str {
304 fn from(value: EventStatus) -> Self {
305 match value {
306 EventStatus::ExecENOENT => "β οΈ",
307 EventStatus::ExecFailure => "β",
308 EventStatus::ProcessRunning => "π’",
309 EventStatus::ProcessExitedNormally => "π",
310 EventStatus::ProcessExitedAbnormally(_) => "π‘",
311 EventStatus::ProcessKilled => "π΅",
312 EventStatus::ProcessTerminated => "π€¬",
313 EventStatus::ProcessInterrupted => "π₯Ί",
314 EventStatus::ProcessSegfault => "π₯",
315 EventStatus::ProcessAborted => "π±",
316 EventStatus::ProcessIllegalInstruction => "πΏ",
317 EventStatus::ProcessSignaled(_) => "π",
318 EventStatus::ProcessPaused => "βΈοΈ",
319 EventStatus::ProcessDetached => "πΈ",
320 EventStatus::InternalError => "β",
321 }
322 }
323}
324
325impl std::fmt::Display for EventStatus {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 let icon: &str = <&'static str>::from(*self);
328 write!(f, "{icon} ")?;
329 use EventStatus::*;
330 match self {
331 ExecENOENT | ExecFailure => write!(
332 f,
333 "Exec failed. Further process state is not available for this event."
334 )?,
335 ProcessRunning => write!(f, "Running")?,
336 ProcessTerminated => write!(f, "Terminated")?,
337 ProcessAborted => write!(f, "Aborted")?,
338 ProcessSegfault => write!(f, "Segmentation fault")?,
339 ProcessIllegalInstruction => write!(f, "Illegal instruction")?,
340 ProcessKilled => write!(f, "Killed")?,
341 ProcessInterrupted => write!(f, "Interrupted")?,
342 ProcessExitedNormally => write!(f, "Exited(0)")?,
343 ProcessExitedAbnormally(code) => write!(f, "Exited({code})")?,
344 ProcessSignaled(signal) => write!(f, "Signaled({signal})")?,
345 ProcessPaused => write!(f, "Paused due to breakpoint hit")?,
346 ProcessDetached => write!(f, "Detached from tracexec")?,
347 InternalError => write!(f, "An internal error occurred in tracexec")?,
348 }
349 Ok(())
350 }
351}