-
Notifications
You must be signed in to change notification settings - Fork 32
/
lib.rs
115 lines (99 loc) · 3.26 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
#![deny(missing_docs)]
//! Spawns the execsnoop-bpfcc application to watch process executions.
use atoi::atoi;
use bstr::{BStr, ByteSlice};
use bytelines::ByteLines;
use std::io::{self, BufReader};
use std::process::{Command, Stdio};
/// Path to the execsnoop bpfcc binary.
pub const EXECSNOOP_PATH: &str = std::env!(
"EXECSNOOP_PATH",
"must set EXECSNOOP_PATH env to execsnoop-bpfcc path"
);
/// Process info
#[derive(Clone, Debug)]
pub struct Process<'a> {
/// Process name
pub name: &'a [u8],
/// Process cmdline
pub cmd: &'a [u8],
/// Process PID
pub pid: u32,
/// Process parent PID
pub parent_pid: u32,
}
/// Process iterator
pub struct ProcessIterator {
child: std::process::Child,
stream: ByteLines<BufReader<std::process::ChildStdout>>,
name_buffer: Vec<u8>,
cmd_buffer: Vec<u8>,
}
impl ProcessIterator {
/// Get the next process from the iterator
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<Process> {
while let Some(Ok(line)) = self.stream.next() {
let mut fields = BStr::new(line).fields();
if let (Some(name), Some(pid), Some(parent_pid)) =
(fields.next(), fields.next(), fields.next())
{
let cmd = fields.nth(1).unwrap_or_default();
if let (Some(pid), Some(parent_pid)) = (atoi::<u32>(pid), atoi::<u32>(parent_pid)) {
self.name_buffer.clear();
self.name_buffer.extend_from_slice(name);
self.cmd_buffer.clear();
self.cmd_buffer.extend_from_slice(cmd);
return Some(Process {
name: &self.name_buffer,
cmd: &self.cmd_buffer,
pid,
parent_pid,
});
}
} else {
tracing::error!(
"failed to parse execsnoop output: {:?}",
String::from_utf8_lossy(line)
);
}
}
None
}
}
impl Drop for ProcessIterator {
fn drop(&mut self) {
let _res = self.child.kill();
let _res = self.child.wait();
}
}
/// Watches process creation and destruction on Linux.
///
/// # Errors
///
/// Requires the `execsnoop-bpfcc` binary from `bpfcc-tools`
pub fn watch() -> io::Result<ProcessIterator> {
Command::new(EXECSNOOP_PATH)
.env("LC_ALL", "C")
.stdout(Stdio::piped())
.stderr(Stdio::null())
.stdin(Stdio::null())
.spawn()
.and_then(move |mut child| {
let stdout = child.stdout.take().ok_or_else(|| {
let _res = child.kill();
let _res = child.wait();
io::Error::new(io::ErrorKind::Other, "execsnoop-bpfcc lacks stdout pipe")
})?;
tracing::debug!("spawned execsnoop");
let stream = ByteLines::new(BufReader::with_capacity(16 * 1024, stdout));
Ok(ProcessIterator {
child,
stream,
name_buffer: Vec::with_capacity(64),
cmd_buffer: Vec::with_capacity(128),
})
})
}