use std::fmt;
use crate::sys;
macro_rules! define_arch {
($($name:tt,)*) => {
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
#[repr(u32)]
pub enum Arch {
NATIVE = sys::SCMP_ARCH::NATIVE,
$(
$name = sys::SCMP_ARCH::$name,
)*
}
static ALL_ARCHES: &[Arch] = &[
$(
Arch::$name,
)*
];
static ALL_ARCH_NAMES: &[&str] = &[
$(
stringify!($name),
)*
];
};
}
define_arch!(
X86,
X86_64,
X32,
ARM,
AARCH64,
MIPS,
MIPS64,
MIPS64N32,
MIPSEL,
MIPSEL64,
MIPSEL64N32,
PPC,
PPC64,
PPC64LE,
S390,
S390X,
PARISC,
PARISC64,
RISCV64,
);
impl Arch {
pub fn all() -> &'static [Self] {
if cfg!(feature = "libseccomp-2-4") {
debug_assert!(Arch::PARISC.is_supported());
debug_assert!(Arch::PARISC64.is_supported());
} else if !Arch::PARISC64.is_supported() {
debug_assert!(!Arch::PARISC.is_supported());
debug_assert_eq!(ALL_ARCHES[16], Arch::PARISC);
return &ALL_ARCHES[..16];
}
if cfg!(feature = "libseccomp-2-5") {
debug_assert!(Arch::RISCV64.is_supported());
} else if !Arch::RISCV64.is_supported() {
debug_assert_eq!(ALL_ARCHES[18], Arch::RISCV64);
return &ALL_ARCHES[..18];
}
ALL_ARCHES
}
pub fn is_supported(self) -> bool {
unsafe {
sys::seccomp_syscall_resolve_name_arch(
self as u32,
"read\0".as_ptr() as *const libc::c_char,
) != sys::NR_SCMP_ERROR
}
}
pub fn native() -> Self {
if cfg!(target_arch = "arm") {
Self::ARM
} else if cfg!(target_arch = "aarch64") {
Self::AARCH64
} else if cfg!(target_arch = "x86") {
Self::X86
} else if cfg!(target_arch = "x86_64") {
if cfg!(target_pointer_width = "64") {
Self::X86_64
} else {
Self::X32
}
} else {
Self::get_arch(unsafe { sys::seccomp_arch_native() })
.expect("Unrecognized architecture returned from libseccomp")
}
}
#[inline]
pub(crate) fn get_arch(arch_raw: u32) -> Option<Self> {
ALL_ARCHES
.iter()
.cloned()
.find(|&arch| arch as u32 == arch_raw)
}
}
impl fmt::Display for Arch {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl std::str::FromStr for Arch {
type Err = ParseArchError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for (&arch, &arch_name) in ALL_ARCHES.iter().zip(ALL_ARCH_NAMES.iter()) {
if s.eq_ignore_ascii_case(arch_name) {
return Ok(arch);
}
}
Err(ParseArchError(()))
}
}
pub struct ParseArchError(());
impl ParseArchError {
#[inline]
fn desc(&self) -> &str {
"Unknown architecture"
}
}
impl fmt::Debug for ParseArchError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ParseArchError")
.field("message", &self.desc())
.finish()
}
}
impl fmt::Display for ParseArchError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.desc())
}
}
impl std::error::Error for ParseArchError {}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_arch_string() {
for bad_name in [
"NATIVE",
"native",
"NOEXIST",
"noexist",
"very-very-very-long-name",
]
.iter()
{
Arch::from_str(bad_name).unwrap_err();
}
assert_eq!(Arch::from_str("x86").unwrap(), Arch::X86);
assert_eq!(Arch::from_str("X86").unwrap(), Arch::X86);
assert_eq!(Arch::from_str("x86_64").unwrap(), Arch::X86_64);
for &arch in Arch::all().iter() {
let arch_name = arch.to_string();
assert_eq!(Arch::from_str(&arch_name).unwrap(), arch);
assert_eq!(
Arch::from_str(&arch_name.to_ascii_lowercase()).unwrap(),
arch
);
}
}
#[allow(deprecated)]
#[test]
fn test_arch_parse_error() {
let err = ParseArchError(());
assert_eq!(err.to_string(), "Unknown architecture");
assert_eq!(err.desc(), "Unknown architecture");
assert_eq!(
format!("{:?}", err),
"ParseArchError { message: \"Unknown architecture\" }"
);
}
#[test]
fn test_native_arch() {
let native_arch = Arch::native();
assert!(Arch::all().contains(&native_arch));
assert_eq!(native_arch as u32, unsafe { sys::seccomp_arch_native() });
}
#[test]
fn test_arch_supported() {
let all_arches = Arch::all();
assert_eq!(all_arches, &ALL_ARCHES[..all_arches.len()]);
for arch in all_arches {
assert!(arch.is_supported());
}
for arch in ALL_ARCHES[all_arches.len()..].iter() {
assert!(!arch.is_supported());
}
}
#[test]
fn test_get_arch() {
for &arch in ALL_ARCHES {
assert_eq!(Arch::get_arch(arch as u32), Some(arch));
}
assert_eq!(Arch::get_arch(Arch::NATIVE as u32), None);
}
}