extern crate bindgen;
use std::{
env, fs,
io::{self, BufRead, Write},
path, process,
string::String,
};
use anyhow::Result;
use libbpf_cargo::SkeletonBuilder;
use tempfile::tempdir;
static CLANG_DEFAULT: &str = "/usr/bin/clang";
static HEADER_MAP_STRUCTS: &str = "src/bpf/map_structs.h";
static VMLINUX_URL: &str =
"https://raw.githubusercontent.com/libbpf/libbpf-bootstrap/master/vmlinux/vmlinux_508.h";
fn download_btf(mut f: fs::File) -> Result<()> {
let mut res = reqwest::blocking::get(VMLINUX_URL)?;
io::copy(&mut res, &mut f)?;
Ok(())
}
fn generate_btf<P: AsRef<path::Path>>(out_path: P) -> Result<()> {
let vmlinux_path = out_path.as_ref().join("vmlinux.h");
let mut f = fs::File::create(vmlinux_path)?;
match process::Command::new("bpftool")
.arg("btf")
.arg("dump")
.arg("file")
.arg("/sys/kernel/btf/vmlinux")
.arg("format")
.arg("c")
.output()
{
Ok(output) => {
if output.status.success() {
f.write_all(&output.stdout)?;
} else {
download_btf(f)?;
}
}
Err(_) => download_btf(f)?,
};
Ok(())
}
fn generate_bpf_skel<P: AsRef<path::Path>>(out_path: P) -> Result<()> {
let bpf_dir = path::Path::new("src").join("bpf");
let src = bpf_dir.join("lockc.bpf.c");
let tmp_dir = tempdir()?;
let skel_unfiltered = tmp_dir.path().join("lockc.skel.rs");
let clang = match env::var("CLANG") {
Ok(val) => val,
Err(_) => String::from(CLANG_DEFAULT),
};
SkeletonBuilder::new(&src)
.clang(clang)
.clang_args(format!("-I{}", out_path.as_ref().display()))
.generate(&skel_unfiltered)?;
let skel_filtered = out_path.as_ref().join("lockc.skel.rs");
let f_src = fs::File::open(skel_unfiltered)?;
let f_src_buf = io::BufReader::new(f_src);
let f_dest = fs::File::create(skel_filtered)?;
let mut f_dest_buf = io::LineWriter::new(f_dest);
for line_r in f_src_buf.lines() {
let line = line_r.unwrap();
if !line.contains("#![allow(") {
f_dest_buf.write_all(line.as_bytes())?;
f_dest_buf.write_all(b"\n")?;
}
}
f_dest_buf.flush()?;
println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
Ok(())
}
fn generate_bindings<P: AsRef<path::Path>>(out_path: P) -> Result<()> {
println!("cargo:rerun-if-changed={}", HEADER_MAP_STRUCTS);
let bindings = bindgen::Builder::default()
.header(HEADER_MAP_STRUCTS)
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.unwrap();
bindings.write_to_file(out_path.as_ref().join("bindings.rs"))?;
Ok(())
}
fn main() -> Result<()> {
let out_path = path::PathBuf::from(env::var("OUT_DIR")?);
generate_btf(out_path.clone())?;
generate_bpf_skel(out_path.clone())?;
generate_bindings(out_path)?;
Ok(())
}