Skip to main content

agentsight_capture/
binary_extractor.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 eunomia-bpf org.
3
4use std::fs;
5use std::io::Write;
6use std::os::unix::fs::PermissionsExt;
7use std::path::{Path, PathBuf};
8use std::sync::{Mutex, OnceLock};
9use tempfile::TempDir;
10use tokio::time::{Duration, sleep};
11
12const PROCESS_BINARY: &[u8] = include_bytes!("../vendor/bpf/process");
13const SSLSNIFF_BINARY: &[u8] = include_bytes!("../vendor/bpf/sslsniff");
14const STDIOCAP_BINARY: &[u8] = include_bytes!("../vendor/bpf/stdiocap");
15
16pub struct BinaryExtractor {
17    _temp_dir: TempDir, // Keep alive to prevent cleanup
18    pub process_path: PathBuf,
19    pub sslsniff_path: PathBuf,
20    stdiocap_init_lock: Mutex<()>,
21    stdiocap_path: OnceLock<PathBuf>,
22}
23
24impl BinaryExtractor {
25    pub async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
26        let temp_dir = TempDir::new()?;
27        let temp_path = temp_dir.path();
28
29        log::debug!("Created temporary directory: {}", temp_path.display());
30
31        // Extract and setup the process binary
32        let process_path = temp_path.join("process");
33        Self::extract_binary(&process_path, PROCESS_BINARY, "process").await?;
34
35        // Extract and setup the sslsniff binary
36        let sslsniff_path = temp_path.join("sslsniff");
37        Self::extract_binary(&sslsniff_path, SSLSNIFF_BINARY, "sslsniff").await?;
38
39        // Small delay to ensure files are fully written
40        sleep(Duration::from_millis(100)).await;
41
42        Ok(Self {
43            _temp_dir: temp_dir,
44            process_path,
45            sslsniff_path,
46            stdiocap_init_lock: Mutex::new(()),
47            stdiocap_path: OnceLock::new(),
48        })
49    }
50
51    async fn extract_binary(
52        path: &Path,
53        binary_data: &[u8],
54        name: &str,
55    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
56        {
57            let mut file = fs::File::create(path)?;
58            file.write_all(binary_data)?;
59            file.flush()?;
60        } // File is closed here
61
62        // Make the binary executable
63        let mut perms = fs::metadata(path)?.permissions();
64        perms.set_mode(0o755);
65        fs::set_permissions(path, perms)?;
66
67        log::debug!("Extracted {} binary to: {}", name, path.display());
68
69        Ok(())
70    }
71
72    pub fn get_process_path(&self) -> &Path {
73        &self.process_path
74    }
75
76    pub fn get_sslsniff_path(&self) -> &Path {
77        &self.sslsniff_path
78    }
79
80    pub fn get_stdiocap_path(&self) -> Result<&Path, Box<dyn std::error::Error + Send + Sync>> {
81        if let Some(path) = self.stdiocap_path.get() {
82            return Ok(path.as_path());
83        }
84
85        let _guard = self
86            .stdiocap_init_lock
87            .lock()
88            .map_err(|_| std::io::Error::other("stdiocap extraction lock poisoned"))?;
89
90        if let Some(path) = self.stdiocap_path.get() {
91            return Ok(path.as_path());
92        }
93
94        let stdiocap_path = self._temp_dir.path().join("stdiocap");
95        {
96            let mut file = fs::File::create(&stdiocap_path)?;
97            file.write_all(STDIOCAP_BINARY)?;
98            file.flush()?;
99        }
100
101        let mut perms = fs::metadata(&stdiocap_path)?.permissions();
102        perms.set_mode(0o755);
103        fs::set_permissions(&stdiocap_path, perms)?;
104        log::debug!("Extracted stdiocap binary to: {}", stdiocap_path.display());
105
106        self.stdiocap_path
107            .set(stdiocap_path)
108            .map_err(|_| std::io::Error::other("stdiocap path initialized concurrently"))?;
109
110        Ok(self
111            .stdiocap_path
112            .get()
113            .expect("stdiocap path should be initialized")
114            .as_path())
115    }
116}