use super::errors;
use super::nr;
use errno;
#[cfg(feature = "async")]
use futures::prelude::*;
use libc;
#[cfg(feature = "async")]
use tokio::timer;
#[derive(Clone, Debug)]
pub struct ModUnloader {
force: bool,
}
impl Default for ModUnloader {
fn default() -> Self {
Self { force: false }
}
}
impl ModUnloader {
pub fn new() -> Self {
Self::default()
}
pub fn forced(mut self, force_unload: bool) -> Self {
self.force = force_unload;
self
}
pub fn unload_sync<S: AsRef<str>>(&self, modname: S, blocking: bool) -> errors::Result<()> {
let flags = match (self.force, blocking) {
(false, false) => 0,
(true, false) => libc::O_TRUNC,
(true, true) => libc::O_TRUNC | libc::O_NONBLOCK,
(false, true) => libc::O_NONBLOCK,
};
let r = unsafe { libc::syscall(nr::DELETE_MODULE, modname.as_ref().as_ptr(), flags) };
match r {
0 => Ok(()),
_ => Err(
errors::Error::from_kind(errors::ErrorKind::Sys(errno::errno()))
.chain_err(|| "blocking delete_module error"),
),
}
}
#[cfg(feature = "async")]
pub fn unload_async<S: AsRef<str>>(
&self,
modname: S,
pause_millis: ::std::num::NonZeroU64,
) -> Box<Future<Item = (), Error = errors::Error>> {
let flags = {
let ff = if self.force { libc::O_TRUNC } else { 0 };
ff | libc::O_NONBLOCK
};
let pause = ::std::time::Duration::from_millis(pause_millis.get());
let unloader = UnloadTask {
flags,
interval: timer::Interval::new_interval(pause),
modname: modname.as_ref().to_string(),
};
Box::new(unloader)
}
}
#[cfg(feature = "async")]
pub(crate) struct UnloadTask {
flags: libc::c_int,
interval: timer::Interval,
modname: String,
}
#[cfg(feature = "async")]
impl Future for UnloadTask {
type Item = ();
type Error = errors::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
use futures::task;
match self.interval.poll() {
Ok(Async::Ready(_)) => {}
Ok(Async::NotReady) => {
task::current().notify();
return Ok(Async::NotReady);
}
Err(e) => bail!("failed rate-limiting interval: {}", e),
};
let r = unsafe { libc::syscall(nr::DELETE_MODULE, self.modname.as_ptr(), self.flags) };
if r == 0 {
return Ok(Async::Ready(()));
}
let num = errno::errno();
if num.0 == libc::EWOULDBLOCK {
task::current().notify();
return Ok(Async::NotReady);
}
Err(errors::Error::from_kind(errors::ErrorKind::Sys(num))
.chain_err(|| "async delete_module error"))
}
}