use bcc::perf_event::PerfMapBuilder;
use bcc::BccError;
use bcc::{Kprobe, Kretprobe, BPF};
use clap::{App, Arg};
use core::sync::atomic::{AtomicBool, Ordering};
use std::ptr;
use std::sync::Arc;
#[repr(C)]
struct data_t {
id: u64,
ts: u64,
ret: libc::c_int,
comm: [u8; 16], fname: [u8; 255], }
fn do_main(runnable: Arc<AtomicBool>) -> Result<(), BccError> {
let matches = App::new("opensnoop")
.about("Prints out filename + PID every time a file is opened")
.arg(
Arg::with_name("duration")
.long("duration")
.value_name("Seconds")
.help("The total duration to run this tool")
.takes_value(true),
)
.get_matches();
let duration: Option<std::time::Duration> = matches
.value_of("duration")
.map(|v| std::time::Duration::new(v.parse().expect("Invalid argument for duration"), 0));
let code = include_str!("opensnoop.c");
let mut module = BPF::new(code)?;
Kprobe::new()
.handler("trace_entry")
.function("do_sys_open")
.attach(&mut module)?;
Kretprobe::new()
.handler("trace_return")
.function("do_sys_open")
.attach(&mut module)?;
let table = module.table("events")?;
let mut perf_map = PerfMapBuilder::new(table, perf_data_callback).build()?;
println!("{:-7} {:-16} {}", "PID", "COMM", "FILENAME");
let start = std::time::Instant::now();
while runnable.load(Ordering::SeqCst) {
perf_map.poll(200);
if let Some(d) = duration {
if std::time::Instant::now() - start >= d {
break;
}
}
}
Ok(())
}
fn perf_data_callback() -> Box<dyn FnMut(&[u8]) + Send> {
Box::new(|x| {
let data = parse_struct(x);
println!(
"{:-7} {:-16} {}",
data.id >> 32,
get_string(&data.comm),
get_string(&data.fname)
);
})
}
fn parse_struct(x: &[u8]) -> data_t {
unsafe { ptr::read_unaligned(x.as_ptr() as *const data_t) }
}
fn get_string(x: &[u8]) -> String {
match x.iter().position(|&r| r == 0) {
Some(zero_pos) => String::from_utf8_lossy(&x[0..zero_pos]).to_string(),
None => String::from_utf8_lossy(x).to_string(),
}
}
fn main() {
let runnable = Arc::new(AtomicBool::new(true));
let r = runnable.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})
.expect("Failed to set handler for SIGINT / SIGTERM");
match do_main(runnable) {
Err(x) => {
eprintln!("Error: {}", x);
std::process::exit(1);
}
_ => {}
}
}