use super::cvt;
use crate::api::ensure_supported_api;
use crate::error::{Result, SeccompError};
use crate::{ScmpArch, ScmpFilterContext, ScmpSyscall, ScmpVersion};
use bitflags::bitflags;
use libseccomp_sys::*;
use std::ops::BitOr;
use std::os::unix::io::RawFd;
fn get_errno() -> i32 {
std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
}
fn notify_supported() -> Result<()> {
ensure_supported_api("seccomp notification", 6, ScmpVersion::from((2, 5, 0)))?;
Ok(())
}
pub type ScmpFd = RawFd;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScmpNotifRespFlags: u32 {
const CONTINUE = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
}
}
#[deprecated(
since = "0.3.0",
note = "Use ScmpNotifRespFlags::CONTINUE or ScmpNotifRespFlags::CONTINUE.bits()"
)]
pub const NOTIF_FLAG_CONTINUE: u32 = ScmpNotifRespFlags::CONTINUE.bits();
impl ScmpFilterContext {
pub fn get_notify_fd(&self) -> Result<ScmpFd> {
notify_supported()?;
let ret = unsafe { seccomp_notify_fd(self.as_ptr()) };
if ret < 0 {
return Err(SeccompError::from_errno(ret));
}
Ok(ret)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ScmpNotifData {
pub syscall: ScmpSyscall,
pub arch: ScmpArch,
pub instr_pointer: u64,
pub args: [u64; 6],
}
impl ScmpNotifData {
fn from_sys(data: seccomp_data) -> Result<Self> {
Ok(Self {
syscall: ScmpSyscall::from(data.nr),
arch: ScmpArch::from_sys(data.arch)?,
instr_pointer: data.instruction_pointer,
args: data.args,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ScmpNotifReq {
pub id: u64,
pub pid: u32,
pub flags: u32,
pub data: ScmpNotifData,
}
impl ScmpNotifReq {
fn from_sys(req: seccomp_notif) -> Result<Self> {
Ok(Self {
id: req.id,
pid: req.pid,
flags: req.flags,
data: ScmpNotifData::from_sys(req.data)?,
})
}
pub fn receive(fd: ScmpFd) -> Result<Self> {
notify_supported()?;
let mut req_ptr: *mut seccomp_notif = std::ptr::null_mut();
cvt(unsafe { seccomp_notify_alloc(&mut req_ptr, std::ptr::null_mut()) })?;
loop {
let ret = unsafe { seccomp_notify_receive(fd, req_ptr) };
let errno = get_errno();
if ret == 0 {
break;
} else if errno == libc::EINTR {
continue;
} else {
unsafe { seccomp_notify_free(req_ptr, std::ptr::null_mut()) };
return Err(SeccompError::from_errno(ret));
}
}
let req = seccomp_notif {
id: unsafe { (*req_ptr).id },
pid: unsafe { (*req_ptr).pid },
flags: unsafe { (*req_ptr).flags },
data: unsafe { (*req_ptr).data },
};
unsafe { seccomp_notify_free(req_ptr, std::ptr::null_mut()) };
Self::from_sys(req)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ScmpNotifResp {
pub id: u64,
pub val: i64,
pub error: i32,
pub flags: u32,
}
impl ScmpNotifResp {
unsafe fn to_sys(self, resp: *mut seccomp_notif_resp) {
unsafe {
(*resp).id = self.id;
(*resp).val = self.val;
(*resp).error = self.error;
(*resp).flags = self.flags;
}
}
#[must_use]
pub fn new(id: u64, val: i64, error: i32, flags: u32) -> Self {
Self {
id,
val,
error,
flags,
}
}
#[must_use]
pub fn new_val(id: u64, val: i64, flags: ScmpNotifRespFlags) -> Self {
Self {
id,
val,
error: 0,
flags: flags.bits(),
}
}
#[must_use]
pub fn new_error(id: u64, error: i32, flags: ScmpNotifRespFlags) -> Self {
debug_assert!(error.is_negative());
Self {
id,
val: 0,
error,
flags: flags.bits(),
}
}
#[must_use]
pub fn new_continue(id: u64, flags: ScmpNotifRespFlags) -> Self {
Self {
id,
val: 0,
error: 0,
flags: ScmpNotifRespFlags::CONTINUE.bitor(flags).bits(),
}
}
pub fn respond(&self, fd: ScmpFd) -> Result<()> {
notify_supported()?;
let mut resp_ptr: *mut seccomp_notif_resp = std::ptr::null_mut();
cvt(unsafe { seccomp_notify_alloc(std::ptr::null_mut(), &mut resp_ptr) })?;
unsafe { self.to_sys(resp_ptr) };
loop {
let ret = unsafe { seccomp_notify_respond(fd, resp_ptr) };
let errno = get_errno();
if ret == 0 {
break;
} else if errno == libc::EINTR {
continue;
} else {
unsafe { seccomp_notify_free(std::ptr::null_mut(), resp_ptr) };
return Err(SeccompError::from_errno(ret));
}
}
unsafe { seccomp_notify_free(std::ptr::null_mut(), resp_ptr) };
Ok(())
}
}
pub fn notify_id_valid(fd: ScmpFd, id: u64) -> Result<()> {
notify_supported()?;
loop {
let ret = unsafe { seccomp_notify_id_valid(fd, id) };
let errno = get_errno();
if ret == 0 {
break;
} else if errno == libc::EINTR {
continue;
} else {
return Err(SeccompError::from_errno(ret));
}
}
Ok(())
}