use crate::error::ErrorKind::*;
use crate::error::{Result, SeccompError};
use libseccomp_sys::*;
use std::convert::TryInto;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ScmpAction {
KillProcess,
KillThread,
Trap,
Notify,
Errno(i32),
Trace(u16),
Log,
Allow,
}
impl ScmpAction {
pub(crate) fn to_sys(self) -> u32 {
match self {
Self::KillProcess => SCMP_ACT_KILL_PROCESS,
Self::KillThread => SCMP_ACT_KILL_THREAD,
Self::Trap => SCMP_ACT_TRAP,
Self::Notify => SCMP_ACT_NOTIFY,
Self::Errno(x) => SCMP_ACT_ERRNO(x as u16),
Self::Trace(x) => SCMP_ACT_TRACE(x),
Self::Log => SCMP_ACT_LOG,
Self::Allow => SCMP_ACT_ALLOW,
}
}
pub(crate) fn from_sys(val: u32) -> Result<Self> {
match val & SCMP_ACT_MASK {
SCMP_ACT_KILL_PROCESS => Ok(Self::KillProcess),
SCMP_ACT_KILL_THREAD => Ok(Self::KillThread),
SCMP_ACT_TRAP => Ok(Self::Trap),
SCMP_ACT_NOTIFY => Ok(Self::Notify),
SCMP_ACT_ERRNO_MASK => Ok(Self::Errno(val as u16 as i32)),
SCMP_ACT_TRACE_MASK => Ok(Self::Trace(val as u16)),
SCMP_ACT_LOG => Ok(Self::Log),
SCMP_ACT_ALLOW => Ok(Self::Allow),
inv => Err(SeccompError::new(InvalidAction(inv))),
}
}
pub fn from_str(action: &str, val: Option<i32>) -> Result<Self> {
match action {
"SCMP_ACT_KILL_PROCESS" => Ok(Self::KillProcess),
"SCMP_ACT_KILL_THREAD" | "SCMP_ACT_KILL" => Ok(Self::KillThread),
"SCMP_ACT_TRAP" => Ok(Self::Trap),
"SCMP_ACT_NOTIFY" => Ok(Self::Notify),
"SCMP_ACT_ERRNO" => match val {
Some(v) => Ok(Self::Errno(v)),
None => Err(SeccompError::new(Common(
"Missing an errno value of SCMP_ACT_ERRNO in ScmpAction::from_str".into(),
))),
},
"SCMP_ACT_TRACE" => match val {
Some(v) => Ok(Self::Trace(v.try_into()?)),
None => Err(SeccompError::new(Common(
"Missing a message value of SCMP_ACT_TRACE in ScmpAction::from_str".into(),
))),
},
"SCMP_ACT_LOG" => Ok(Self::Log),
"SCMP_ACT_ALLOW" => Ok(Self::Allow),
_ => Err(SeccompError::new(FromStr(action.to_string()))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_action() {
let test_data = &[
("SCMP_ACT_KILL_PROCESS", ScmpAction::KillProcess),
("SCMP_ACT_KILL_THREAD", ScmpAction::KillThread),
("SCMP_ACT_KILL", ScmpAction::KillThread),
("SCMP_ACT_TRAP", ScmpAction::Trap),
("SCMP_ACT_NOTIFY", ScmpAction::Notify),
("SCMP_ACT_ERRNO", ScmpAction::Errno(10)),
("SCMP_ACT_TRACE", ScmpAction::Trace(10)),
("SCMP_ACT_LOG", ScmpAction::Log),
("SCMP_ACT_ALLOW", ScmpAction::Allow),
];
for data in test_data {
if data.0 == "SCMP_ACT_ERRNO" || data.0 == "SCMP_ACT_TRACE" {
assert_eq!(
ScmpAction::from_sys(ScmpAction::from_str(data.0, Some(10)).unwrap().to_sys())
.unwrap(),
data.1
);
} else {
assert_eq!(
ScmpAction::from_sys(ScmpAction::from_str(data.0, None).unwrap().to_sys())
.unwrap(),
data.1
);
}
}
assert!(ScmpAction::from_str("SCMP_ACT_ERRNO", None).is_err());
assert!(ScmpAction::from_str("SCMP_ACT_TRACE", None).is_err());
assert!(ScmpAction::from_str("SCMP_INVALID_FLAG", None).is_err());
assert!(ScmpAction::from_sys(0x00010000).is_err());
}
}