use crate::error::{Result, SeccompError};
use libseccomp_sys::*;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ScmpVersion {
pub major: u32,
pub minor: u32,
pub micro: u32,
}
impl ScmpVersion {
pub fn current() -> Result<Self> {
if let Some(version) = unsafe { seccomp_version().as_ref() } {
Ok(Self {
major: version.major,
minor: version.minor,
micro: version.micro,
})
} else {
Err(SeccompError::with_msg("Could not get libseccomp version"))
}
}
}
impl From<(u32, u32, u32)> for ScmpVersion {
fn from(version: (u32, u32, u32)) -> Self {
Self {
major: version.0,
minor: version.1,
micro: version.2,
}
}
}
impl fmt::Display for ScmpVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.micro)
}
}
pub fn check_version(expected: ScmpVersion) -> Result<bool> {
let current = ScmpVersion::current()?;
if current.major > expected.major
|| (current.major == expected.major && current.minor > expected.minor)
|| (current.major == expected.major
&& current.minor == expected.minor
&& current.micro >= expected.micro)
{
Ok(true)
} else {
Ok(false)
}
}
pub(crate) fn ensure_supported_version(msg: &str, expected: ScmpVersion) -> Result<()> {
if check_version(expected)? {
Ok(())
} else {
let current = ScmpVersion::current()?;
Err(SeccompError::with_msg(format!(
"{} requires libseccomp >= {} (current version: {})",
msg, expected, current,
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct ScmpVersionTest {
pub msg: &'static str,
pub ver: ScmpVersion,
pub is_ok: bool,
}
impl ScmpVersionTest {
pub fn new(msg: &'static str, ver: ScmpVersion, is_ok: bool) -> Self {
Self { msg, ver, is_ok }
}
}
#[test]
fn test_ensure_supported_version() {
let ver = ScmpVersion::current().unwrap();
let tests = &[
ScmpVersionTest::new(
"VerCurrent",
ScmpVersion::from((ver.major, ver.minor, ver.micro)),
true,
),
ScmpVersionTest::new("VerMajor-1", ScmpVersion::from((ver.major - 1, 0, 0)), true),
ScmpVersionTest::new(
"If VerMinor != 0 then VerMinor-1, otherwise VerMinor=0",
ScmpVersion::from((ver.major, if ver.minor != 0 { ver.minor - 1 } else { 0 }, 0)),
true,
),
ScmpVersionTest::new(
"If VerMicro != 0 then VerMicro-1, otherwise VerMicro=0",
ScmpVersion::from((
ver.major,
ver.minor,
if ver.micro != 0 { ver.micro - 1 } else { 0 },
)),
true,
),
ScmpVersionTest::new(
"VerNew",
ScmpVersion::from((ver.major + 1, ver.minor + 1, ver.micro + 1)),
false,
),
ScmpVersionTest::new(
"VerMajor+1",
ScmpVersion::from((ver.major + 1, 0, 0)),
false,
),
ScmpVersionTest::new(
"VerMinor+1",
ScmpVersion::from((ver.major, ver.minor + 1, 0)),
false,
),
ScmpVersionTest::new(
"VerMicro+1",
ScmpVersion::from((ver.major, ver.minor, ver.micro + 1)),
false,
),
];
for test in tests {
assert_eq!(
ensure_supported_version(test.msg, test.ver).is_ok(),
test.is_ok
);
}
}
}