use neli::{
connector::{CnMsg, ProcEvent, ProcEventHeader},
consts::{
connector::{CnMsgIdx, CnMsgVal, ProcCnMcastOp},
nl::{NlmF, Nlmsg},
socket::NlFamily,
},
nl::{NlPayload, NlmsghdrBuilder},
socket::synchronous::NlSocketHandle,
utils::Groups,
};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let pid = std::process::id();
let socket = NlSocketHandle::connect(
NlFamily::Connector,
Some(pid),
Groups::new_bitmask(CnMsgIdx::Proc.into()),
)?;
let subscribe = NlmsghdrBuilder::default()
.nl_type(Nlmsg::Done)
.nl_flags(NlmF::empty())
.nl_pid(pid)
.nl_payload(NlPayload::Payload(
neli::connector::CnMsgBuilder::default()
.idx(CnMsgIdx::Proc)
.val(CnMsgVal::Proc)
.payload(ProcCnMcastOp::Listen)
.build()?,
))
.build()?;
socket.send(&subscribe)?;
loop {
for event in socket.recv::<Nlmsg, CnMsg<ProcEventHeader>>()?.0 {
let ProcEvent::Exec { process_pid, .. } = event?
.get_payload()
.ok_or("Failed to extract payload")?
.payload()
.event
else {
continue;
};
let exe = fs::read_link(format!("/proc/{process_pid}/exe"))
.map(|p| p.display().to_string())
.unwrap_or_else(|_| "unknown".to_string());
let cmdline = cmdline_to_string(process_pid).unwrap_or_else(|_| "unknown".to_string());
println!("Process created: PID: {process_pid}, Exe: {exe}, Cmdline: {cmdline}");
}
}
}
fn cmdline_to_string(pid: i32) -> std::io::Result<String> {
let mut data = fs::read(format!("/proc/{pid}/cmdline"))?;
for b in &mut data {
if *b == 0 {
*b = b' ';
}
}
let s = unsafe { String::from_utf8_unchecked(data) };
Ok(s)
}