Browse Source

Merge pull request #643 from aya-rs/procfs

Remove procfs dependency
Tamir Duberstein 1 year ago
parent
commit
6e9aba55fe

+ 1 - 1
aya-obj/src/btf/btf.rs

@@ -135,7 +135,7 @@ pub enum BtfError {
         #[source]
         io_error: std::io::Error,
         /// The error log produced by the kernel verifier.
-        verifier_log: Cow<'static, str>,
+        verifier_log: String,
     },
 
     /// offset not found for symbol

+ 1 - 1
aya/Cargo.toml

@@ -24,6 +24,7 @@ object = { version = "0.31", default-features = false, features = [
     "elf",
 ] }
 parking_lot = { version = "0.12.0", features = ["send_guard"] }
+text_io = "0.1.12"
 thiserror = "1"
 tokio = { version = "1.24.0", features = [
     "macros",
@@ -31,7 +32,6 @@ tokio = { version = "1.24.0", features = [
     "rt-multi-thread",
     "net",
 ], optional = true }
-procfs = { version = "0.15.1", default-features = false }
 
 [dev-dependencies]
 futures = { version = "0.3.12", default-features = false, features = ["std"] }

+ 1 - 1
aya/src/maps/mod.rs

@@ -47,9 +47,9 @@ use std::{
     ptr,
 };
 
+use crate::util::KernelVersion;
 use libc::{getrlimit, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY};
 use log::warn;
-use procfs::KernelVersion;
 use thiserror::Error;
 
 use crate::{

+ 1 - 1
aya/src/programs/cgroup_device.rs

@@ -1,6 +1,6 @@
 //! Cgroup device programs.
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::os::fd::{AsRawFd, RawFd};
 
 use crate::{

+ 1 - 1
aya/src/programs/cgroup_skb.rs

@@ -1,6 +1,6 @@
 //! Cgroup skb programs.
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::{
     hash::Hash,
     os::fd::{AsRawFd, RawFd},

+ 1 - 1
aya/src/programs/cgroup_sock.rs

@@ -2,7 +2,7 @@
 
 pub use aya_obj::programs::CgroupSockAttachType;
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::{
     hash::Hash,
     os::fd::{AsRawFd, RawFd},

+ 1 - 1
aya/src/programs/cgroup_sock_addr.rs

@@ -2,7 +2,7 @@
 
 pub use aya_obj::programs::CgroupSockAddrAttachType;
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::{
     hash::Hash,
     os::fd::{AsRawFd, RawFd},

+ 1 - 1
aya/src/programs/cgroup_sockopt.rs

@@ -2,7 +2,7 @@
 
 pub use aya_obj::programs::CgroupSockoptAttachType;
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::{
     hash::Hash,
     os::fd::{AsRawFd, RawFd},

+ 1 - 1
aya/src/programs/cgroup_sysctl.rs

@@ -1,6 +1,6 @@
 //! Cgroup sysctl programs.
 
-use procfs::KernelVersion;
+use crate::util::KernelVersion;
 use std::{
     hash::Hash,
     os::fd::{AsRawFd, RawFd},

+ 2 - 3
aya/src/programs/mod.rs

@@ -64,10 +64,9 @@ pub mod uprobe;
 mod utils;
 pub mod xdp;
 
+use crate::util::KernelVersion;
 use libc::ENOSPC;
-use procfs::KernelVersion;
 use std::{
-    borrow::Cow,
     ffi::CString,
     io,
     os::unix::io::{AsRawFd, RawFd},
@@ -143,7 +142,7 @@ pub enum ProgramError {
         #[source]
         io_error: io::Error,
         /// The error log produced by the kernel verifier.
-        verifier_log: Cow<'static, str>,
+        verifier_log: String,
     },
 
     /// A syscall failed.

+ 2 - 2
aya/src/programs/probe.rs

@@ -1,5 +1,5 @@
+use crate::util::KernelVersion;
 use libc::pid_t;
-use procfs::KernelVersion;
 use std::{
     fs::{self, OpenOptions},
     io::{self, Write},
@@ -50,7 +50,7 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
 ) -> Result<T::Id, ProgramError> {
     // https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
     // Use debugfs to create probe
-    if KernelVersion::current().unwrap() >= KernelVersion::new(4, 17, 0) {
+    if KernelVersion::current().unwrap() < KernelVersion::new(4, 17, 0) {
         let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?;
         let link = T::from(perf_attach_debugfs(
             program_data.fd_or_err()?,

+ 1 - 1
aya/src/programs/xdp.rs

@@ -1,8 +1,8 @@
 //! eXpress Data Path (XDP) programs.
 
+use crate::util::KernelVersion;
 use bitflags;
 use libc::if_nametoindex;
-use procfs::KernelVersion;
 use std::{convert::TryFrom, ffi::CString, hash::Hash, io, mem, os::unix::io::RawFd};
 use thiserror::Error;
 

+ 5 - 13
aya/src/sys/bpf.rs

@@ -1,5 +1,4 @@
 use std::{
-    borrow::Cow,
     cmp::{self, min},
     ffi::{CStr, CString},
     io,
@@ -8,12 +7,12 @@ use std::{
     slice,
 };
 
+use crate::util::KernelVersion;
 use libc::{c_char, c_long, close, ENOENT, ENOSPC};
 use obj::{
     maps::{bpf_map_def, LegacyMap},
     BpfSectionKind,
 };
-use procfs::KernelVersion;
 
 use crate::{
     generated::{
@@ -993,13 +992,10 @@ pub(crate) fn bpf_prog_get_next_id(id: u32) -> Result<Option<u32>, (c_long, io::
     }
 }
 
-pub(crate) fn retry_with_verifier_logs<F>(
+pub(crate) fn retry_with_verifier_logs(
     max_retries: usize,
-    f: F,
-) -> (SysResult, Cow<'static, str>)
-where
-    F: Fn(&mut [u8]) -> SysResult,
-{
+    f: impl Fn(&mut [u8]) -> SysResult,
+) -> (SysResult, String) {
     const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
     const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
 
@@ -1023,11 +1019,7 @@ where
         if let Some(pos) = log_buf.iter().position(|b| *b == 0) {
             log_buf.truncate(pos);
         }
-        let log_buf = if log_buf.is_empty() {
-            "none".into()
-        } else {
-            String::from_utf8(log_buf).unwrap().into()
-        };
+        let log_buf = String::from_utf8(log_buf).unwrap();
 
         break (ret, log_buf);
     }

+ 133 - 5
aya/src/util.rs

@@ -1,11 +1,12 @@
 //! Utility functions.
 use std::{
     collections::BTreeMap,
-    ffi::CString,
+    error::Error,
+    ffi::{CStr, CString},
     fs::{self, File},
-    io::{self, BufReader},
+    io::{self, BufRead, BufReader},
     mem, slice,
-    str::FromStr,
+    str::{FromStr, Utf8Error},
 };
 
 use crate::{
@@ -13,9 +14,136 @@ use crate::{
     Pod,
 };
 
-use libc::{if_nametoindex, sysconf, _SC_PAGESIZE};
+use libc::{if_nametoindex, sysconf, uname, utsname, _SC_PAGESIZE};
 
-use io::BufRead;
+/// Represents a kernel version, in major.minor.release version.
+// Adapted from https://docs.rs/procfs/latest/procfs/sys/kernel/struct.Version.html.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd)]
+pub struct KernelVersion {
+    pub(crate) major: u8,
+    pub(crate) minor: u8,
+    pub(crate) patch: u16,
+}
+
+/// An error encountered while fetching the current kernel version.
+#[derive(thiserror::Error, Debug)]
+pub enum CurrentKernelVersionError {
+    /// The kernel version string could not be read.
+    #[error("failed to read kernel version")]
+    IOError(#[from] io::Error),
+    /// The kernel version string could not be parsed.
+    #[error("failed to parse kernel version")]
+    ParseError(#[from] text_io::Error),
+    /// The kernel version string was not valid UTF-8.
+    #[error("kernel version string is not valid UTF-8")]
+    Utf8Error(#[from] Utf8Error),
+}
+
+impl KernelVersion {
+    /// Constructor.
+    pub fn new(major: u8, minor: u8, patch: u16) -> Self {
+        Self {
+            major,
+            minor,
+            patch,
+        }
+    }
+
+    /// Returns the kernel version of the currently running kernel.
+    pub fn current() -> Result<Self, impl Error> {
+        let kernel_version = Self::get_kernel_version();
+
+        // The kernel version is clamped to 4.19.255 on kernels 4.19.222 and above.
+        //
+        // See https://github.com/torvalds/linux/commit/a256aac.
+        const CLAMPED_KERNEL_MAJOR: u8 = 4;
+        const CLAMPED_KERNEL_MINOR: u8 = 19;
+        if let Ok(Self {
+            major: CLAMPED_KERNEL_MAJOR,
+            minor: CLAMPED_KERNEL_MINOR,
+            patch: 222..,
+        }) = kernel_version
+        {
+            return Ok(Self::new(CLAMPED_KERNEL_MAJOR, CLAMPED_KERNEL_MINOR, 255));
+        }
+
+        kernel_version
+    }
+
+    // This is ported from https://github.com/torvalds/linux/blob/3f01e9f/tools/lib/bpf/libbpf_probes.c#L21-L101.
+
+    fn get_ubuntu_kernel_version() -> Result<Option<Self>, CurrentKernelVersionError> {
+        const UBUNTU_KVER_FILE: &str = "/proc/version_signature";
+        let s = match fs::read(UBUNTU_KVER_FILE) {
+            Ok(s) => s,
+            Err(e) => {
+                if e.kind() == io::ErrorKind::NotFound {
+                    return Ok(None);
+                }
+                return Err(e.into());
+            }
+        };
+        let ubuntu: String;
+        let ubuntu_version: String;
+        let major: u8;
+        let minor: u8;
+        let patch: u16;
+        text_io::try_scan!(s.iter().copied() => "{} {} {}.{}.{}\n", ubuntu, ubuntu_version, major, minor, patch);
+        Ok(Some(Self::new(major, minor, patch)))
+    }
+
+    fn get_debian_kernel_version(
+        info: &utsname,
+    ) -> Result<Option<Self>, CurrentKernelVersionError> {
+        // Safety: man 2 uname:
+        //
+        // The length of the arrays in a struct utsname is unspecified (see NOTES); the fields are
+        // terminated by a null byte ('\0').
+        let p = unsafe { CStr::from_ptr(info.version.as_ptr()) };
+        let p = p.to_str()?;
+        let p = match p.split_once("Debian ") {
+            Some((_prefix, suffix)) => suffix,
+            None => return Ok(None),
+        };
+        let major: u8;
+        let minor: u8;
+        let patch: u16;
+        text_io::try_scan!(p.bytes() => "{}.{}.{}", major, minor, patch);
+        Ok(Some(Self::new(major, minor, patch)))
+    }
+
+    fn get_kernel_version() -> Result<Self, CurrentKernelVersionError> {
+        if let Some(v) = Self::get_ubuntu_kernel_version()? {
+            return Ok(v);
+        }
+
+        let mut info = unsafe { mem::zeroed::<utsname>() };
+        if unsafe { uname(&mut info) } != 0 {
+            return Err(io::Error::last_os_error().into());
+        }
+
+        if let Some(v) = Self::get_debian_kernel_version(&info)? {
+            return Ok(v);
+        }
+
+        // Safety: man 2 uname:
+        //
+        // The length of the arrays in a struct utsname is unspecified (see NOTES); the fields are
+        // terminated by a null byte ('\0').
+        let p = unsafe { CStr::from_ptr(info.release.as_ptr()) };
+        let p = p.to_str()?;
+        // Unlike sscanf, text_io::try_scan! does not stop at the first non-matching character.
+        let p = match p.split_once(|c: char| c != '.' && !c.is_ascii_digit()) {
+            Some((prefix, _suffix)) => prefix,
+            None => p,
+        };
+        let major: u8;
+        let minor: u8;
+        let patch: u16;
+        text_io::try_scan!(p.bytes() => "{}.{}.{}", major, minor, patch);
+        Ok(Self::new(major, minor, patch))
+    }
+}
 
 const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online";
 pub(crate) const POSSIBLE_CPUS: &str = "/sys/devices/system/cpu/possible";

+ 0 - 1
test/integration-test/Cargo.toml

@@ -12,7 +12,6 @@ aya-obj = { path = "../../aya-obj" }
 libc = { version = "0.2.105" }
 log = "0.4"
 object = { version = "0.31", default-features = false, features = ["std", "read_core", "elf"] }
-procfs = "0.15.1"
 rbpf = "0.2.0"
 tempfile = "3.3.0"
 tokio = { version = "1.24", features = ["rt", "rt-multi-thread", "sync", "time"] }

+ 1 - 2
test/integration-test/tests/btf_relocations.rs

@@ -1,9 +1,8 @@
 use anyhow::{bail, Context as _, Result};
-use procfs::KernelVersion;
 use std::{path::PathBuf, process::Command, thread::sleep, time::Duration};
 use tempfile::TempDir;
 
-use aya::{maps::Array, programs::TracePoint, BpfLoader, Btf, Endianness};
+use aya::{maps::Array, programs::TracePoint, util::KernelVersion, BpfLoader, Btf, Endianness};
 
 // In the tests below we often use values like 0xAAAAAAAA or -0x7AAAAAAA. Those values have no
 // special meaning, they just have "nice" bit patterns that can be helpful while debugging.

+ 1 - 1
test/integration-test/tests/load.rs

@@ -1,4 +1,3 @@
-use procfs::KernelVersion;
 use std::{convert::TryInto as _, thread, time};
 
 use aya::{
@@ -8,6 +7,7 @@ use aya::{
         links::{FdLink, PinnedLink},
         loaded_programs, KProbe, TracePoint, Xdp, XdpFlags,
     },
+    util::KernelVersion,
     Bpf,
 };
 

+ 1 - 2
test/integration-test/tests/smoke.rs

@@ -1,8 +1,7 @@
-use procfs::KernelVersion;
-
 use aya::{
     include_bytes_aligned,
     programs::{Extension, Xdp, XdpFlags},
+    util::KernelVersion,
     Bpf, BpfLoader,
 };