From 044877a4357df7a495d7ea9825e2e46f9760e78b Mon Sep 17 00:00:00 2001
From: Davide Bertola <dade@dadeb.it>
Date: Fri, 3 Jun 2022 17:10:13 +0200
Subject: [PATCH] aya-gen: run bindgen as a child process

---
 aya-gen/Cargo.toml       |  1 +
 aya-gen/src/btf_types.rs | 60 ++++++++++++++++++++++++++++++----------
 2 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/aya-gen/Cargo.toml b/aya-gen/Cargo.toml
index dc38497f..5944e443 100644
--- a/aya-gen/Cargo.toml
+++ b/aya-gen/Cargo.toml
@@ -13,3 +13,4 @@ syn = "1"
 quote = "1"
 proc-macro2 = "1"
 indexmap = "1.6"
+tempfile = "3"
diff --git a/aya-gen/src/btf_types.rs b/aya-gen/src/btf_types.rs
index a1c55443..d23fe2bc 100644
--- a/aya-gen/src/btf_types.rs
+++ b/aya-gen/src/btf_types.rs
@@ -1,10 +1,13 @@
 use std::{
-    fs, io,
+    fs::{self, File},
+    io::{self, Write},
     path::{Path, PathBuf},
     process::Command,
     str::from_utf8,
 };
 
+use tempfile::tempdir;
+
 use thiserror::Error;
 
 use crate::bindgen;
@@ -18,7 +21,10 @@ pub enum Error {
     BpfToolExit { code: i32, stderr: String },
 
     #[error("bindgen failed")]
-    Bindgen,
+    Bindgen(#[source] io::Error),
+
+    #[error("{stderr}\nbindgen failed with exit code {code}")]
+    BindgenExit { code: i32, stderr: String },
 
     #[error("rustfmt failed")]
     Rustfmt(#[source] io::Error),
@@ -35,25 +41,49 @@ pub enum InputFile {
 pub fn generate<T: AsRef<str>>(input_file: InputFile, types: &[T]) -> Result<String, Error> {
     let mut bindgen = bindgen::bpf_builder();
 
-    match input_file {
-        InputFile::Btf(path) => {
-            let c_header = c_header_from_btf(&path)?;
-            bindgen = bindgen.header_contents("kernel_types.h", &c_header);
-        }
-        InputFile::Header(header) => {
-            let c_header = fs::read_to_string(&header).map_err(|_| Error::ReadHeaderFile)?;
-            let name = Path::new(&header).file_name().unwrap().to_str().unwrap();
-            bindgen = bindgen.header_contents(name, &c_header);
-        }
-    }
+    let (c_header, name) = match input_file {
+        InputFile::Btf(path) => (c_header_from_btf(&path)?, "kernel_types.h".to_string()),
+        InputFile::Header(header) => (
+            fs::read_to_string(&header).map_err(|_| Error::ReadHeaderFile)?,
+            header.file_name().unwrap().to_str().unwrap().to_owned(),
+        ),
+    };
 
     for ty in types {
         bindgen = bindgen.allowlist_type(ty);
     }
 
-    let bindings = bindgen.generate().or(Err(Error::Bindgen))?.to_string();
+    // TODO: check if this part should be moved to bindgen::generate()
+    let dir = tempdir().unwrap();
+    let file_path = dir.path().join(name);
+    let mut file = File::create(&file_path).unwrap();
+    let _ = file.write(c_header.as_bytes()).unwrap();
+
+    let flags = bindgen.command_line_flags();
+
+    // TODO: check proper logging; it seems useful to see what command is
+    // launched but we can't disturb the normal output of the aya-gen tool
+    println!(
+        "Launching bindgen {} {}",
+        file_path.to_str().unwrap(),
+        flags.join(" ")
+    );
+
+    let output = Command::new("bindgen")
+        .arg(file_path)
+        .args(flags)
+        // TODO: pass additional arguments after --
+        .output()
+        .map_err(Error::Bindgen)?;
 
-    Ok(bindings)
+    if !output.status.success() {
+        return Err(Error::BindgenExit {
+            code: output.status.code().unwrap(),
+            stderr: from_utf8(&output.stderr).unwrap().to_owned(),
+        });
+    }
+
+    Ok(from_utf8(&output.stdout).unwrap().to_owned())
 }
 
 fn c_header_from_btf(path: &Path) -> Result<String, Error> {