#![warn(missing_docs)]
use std::{ffi::CStr, io, ptr};
pub use libnv::{
libnv::{NvFlag, NvList},
NvError,
};
#[doc(hidden)]
pub mod sys {
pub use casper_sys::{cap_limit_get, cap_limit_set, cap_xfer_nvlist, service_register};
pub use ctor::ctor;
pub use libnv_sys::nvlist_t;
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub struct ServiceRegisterFlags(u64);
impl ServiceRegisterFlags {
pub const FD: ServiceRegisterFlags = ServiceRegisterFlags(casper_sys::CASPER_SERVICE_FD);
pub const NONE: ServiceRegisterFlags = ServiceRegisterFlags(0);
pub const NO_UNIQ_LIMITS: ServiceRegisterFlags =
ServiceRegisterFlags(casper_sys::CASPER_SERVICE_NO_UNIQ_LIMITS);
pub const STDIO: ServiceRegisterFlags = ServiceRegisterFlags(casper_sys::CASPER_SERVICE_STDIO);
}
impl From<ServiceRegisterFlags> for u64 {
fn from(f: ServiceRegisterFlags) -> Self {
f.0
}
}
#[derive(Debug)]
#[doc(hidden)]
pub struct CapChannel(ptr::NonNull<casper_sys::cap_channel_t>);
impl CapChannel {
pub fn as_mut_ptr(&mut self) -> *mut casper_sys::cap_channel_t {
self.0.as_ptr()
}
pub fn as_ptr(&self) -> *const casper_sys::cap_channel_t {
self.0.as_ptr() as *const _
}
fn from_raw_ptr(chan: *mut casper_sys::cap_channel_t) -> Option<Self> {
ptr::NonNull::new(chan).map(Self)
}
}
impl Drop for CapChannel {
fn drop(&mut self) {
unsafe { casper_sys::cap_close(self.0.as_ptr()) }
}
}
unsafe impl Send for CapChannel {}
unsafe impl Sync for CapChannel {}
#[derive(Debug)]
pub struct Casper(CapChannel);
impl Casper {
pub unsafe fn new() -> io::Result<Self> {
let chan = unsafe { casper_sys::cap_init() };
CapChannel::from_raw_ptr(chan)
.map(Casper)
.ok_or(io::Error::last_os_error())
}
#[doc(hidden)]
pub fn service_open(&mut self, name: &CStr) -> io::Result<CapChannel> {
let chan = unsafe { casper_sys::cap_service_open(self.0.as_ptr(), name.as_ptr()) };
CapChannel::from_raw_ptr(chan).ok_or(io::Error::last_os_error())
}
pub fn try_clone(&mut self) -> io::Result<Self> {
let chan2 = unsafe { casper_sys::cap_clone(self.0.as_ptr()) };
CapChannel::from_raw_ptr(chan2)
.map(Casper)
.ok_or(io::Error::last_os_error())
}
}
mod macros {
#[macro_export]
macro_rules! service_connection {
(
$(#[$attr:meta])*
$vis:vis $astruct:ident, $cname:expr, $meth:ident
) => {
$(#[$attr])*
$vis struct $astruct($crate::casper::CapChannel);
impl $astruct {
fn limit_get(&mut self) -> ::std::io::Result<$crate::casper::NvList> {
let mut nvlp: *mut $crate::casper::sys::nvlist_t = ::std::ptr::null_mut();
let r = unsafe {
$crate::casper::sys::cap_limit_get(self.0.as_ptr(), &mut nvlp as *mut *mut $crate::casper::sys::nvlist_t)
};
if r < 0 {
return Err(::std::io::Error::last_os_error());
}
if nvlp.is_null() {
Ok($crate::casper::NvList::new($crate::casper::NvFlag::None).unwrap())
} else {
unsafe{ Ok($crate::casper::NvList::from_ptr(nvlp)) }
}
}
fn limit_set(&mut self, limits: $crate::casper::NvList) -> ::std::io::Result<()> {
let raw_nvl : *mut $crate::casper::sys::nvlist_t = limits.into();
let r = unsafe {
$crate::casper::sys::cap_limit_set(self.0.as_ptr(), raw_nvl)
};
if r < 0 {
Err(::std::io::Error::last_os_error())
} else {
Ok(())
}
}
fn xfer_nvlist(&mut self, invl: $crate::casper::NvList) -> ::std::io::Result<$crate::casper::NvList> {
let r = unsafe {
$crate::casper::sys::cap_xfer_nvlist(self.0.as_ptr(), invl.into())
};
if r.is_null() {
Err(::std::io::Error::last_os_error())
} else {
let nvl = unsafe{ $crate::casper::NvList::from_ptr(r) };
let key = ::std::ffi::CStr::from_bytes_until_nul(b"error\0").unwrap();
match nvl.get_number(key) {
Ok(Some(0)) => Ok(nvl),
Ok(Some(e)) => Err(::std::io::Error::from_raw_os_error(e as i32)),
Ok(None) => panic!("zygote did not return error code"),
Err($crate::casper::NvError::NativeError(e)) => Err(::std::io::Error::from_raw_os_error(e)),
Err($crate::casper::NvError::Io(e)) => Err(e),
_ => unimplemented!()
}
}
}
}
$vis trait CasperExt {
#[doc = stringify!($meth)]
fn $meth(&mut self) -> ::std::io::Result<$astruct>;
}
impl CasperExt for ::capsicum::casper::Casper {
fn $meth(&mut self) -> ::std::io::Result<$astruct> {
self.service_open($cname)
.map($astruct)
}
}
}
}
#[macro_export]
macro_rules! service {
(
$(#[$attr:meta])*
$vis:vis $sstruct:ident, $astruct:ident, $meth:ident, $flags:expr
) => {
$crate::casper::service_connection!{
$(#[$attr])*
$vis $astruct,
<$sstruct as $crate::casper::Service>::SERVICE_NAME, $meth
}
#[$crate::casper::sys::ctor]
unsafe fn casper_service_init() {
use $crate::casper::Service;
$crate::casper::sys::service_register(
$sstruct::SERVICE_NAME.as_ptr(),
Some($sstruct::c_limit),
Some($sstruct::c_cmd),
$flags.into()
);
}
}
}
pub use service;
pub use service_connection;
}
pub use macros::{service, service_connection};
pub trait Service {
const SERVICE_NAME: &'static CStr;
fn cmd(
cmd: &str,
limits: Option<&NvList>,
nvin: Option<&mut NvList>,
nvout: &mut NvList,
) -> io::Result<()>;
fn limit(_oldlimits: Option<&NvList>, _newlimits: Option<&NvList>) -> io::Result<()> {
unimplemented!()
}
#[doc(hidden)]
unsafe extern "C" fn c_cmd(
cmd: *const ::std::os::raw::c_char,
limits: *const sys::nvlist_t,
nvlin: *mut sys::nvlist_t,
nvlout: *mut sys::nvlist_t,
) -> i32 {
let cmd = unsafe { CStr::from_ptr(cmd).to_string_lossy() };
let limits = if limits.is_null() {
None
} else {
unsafe { Some(NvList::from_ptr(limits as *mut sys::nvlist_t)) }
};
let mut nvlin = unsafe { NvList::from_ptr(nvlin) };
let mut nvlout = unsafe { NvList::from_ptr(nvlout) };
let r = match Self::cmd(&cmd, limits.as_ref(), Some(&mut nvlin), &mut nvlout) {
Ok(()) => 0,
Err(e) => e.raw_os_error().unwrap(),
};
let _: *mut sys::nvlist_t = nvlin.into();
let _: *mut sys::nvlist_t = nvlout.into();
let _ = limits.map(<*mut sys::nvlist_t>::from);
r
}
#[doc(hidden)]
extern "C" fn c_limit(oldlimits: *const sys::nvlist_t, newlimits: *const sys::nvlist_t) -> i32 {
let oldlimits = if oldlimits.is_null() {
None
} else {
unsafe { Some(NvList::from_ptr(oldlimits as *mut sys::nvlist_t)) }
};
let newlimits = if newlimits.is_null() {
None
} else {
unsafe { Some(NvList::from_ptr(newlimits as *mut sys::nvlist_t)) }
};
let r = match Self::limit(oldlimits.as_ref(), newlimits.as_ref()) {
Ok(()) => 0,
Err(e) => e.raw_os_error().unwrap(),
};
let _ = oldlimits.map(<*mut sys::nvlist_t>::from);
let _ = newlimits.map(<*mut sys::nvlist_t>::from);
r
}
}