likemod/
unload.rs

1use super::errors;
2use super::nr;
3use errno;
4#[cfg(feature = "async")]
5use futures::prelude::*;
6use libc;
7#[cfg(feature = "async")]
8use tokio::timer;
9
10/// Module unloader.
11///
12/// Asynchronous methods can be enabled via the optional `async` feature.
13#[derive(Clone, Debug)]
14pub struct ModUnloader {
15    force: bool,
16}
17
18impl Default for ModUnloader {
19    fn default() -> Self {
20        Self { force: false }
21    }
22}
23
24impl ModUnloader {
25    /// Create a new default `ModLoader`.
26    pub fn new() -> Self {
27        Self::default()
28    }
29
30    /// Set whether a forced unload should be performed.
31    ///
32    /// A force unload will taint the kernel and can leave the
33    /// host in an unstable state, or cause data loss.
34    pub fn forced(mut self, force_unload: bool) -> Self {
35        self.force = force_unload;
36        self
37    }
38
39    /// Unload module by name, synchronously.
40    ///
41    /// If `blocking` is enabled, this can block at syscall level (putting
42    /// the process in D state) while waiting for module reference count
43    /// to be 0 for clean unloading (unless forced).
44    ///
45    /// It is usually recommended not to set `blocking`, as the process
46    /// cannot be killed while blocked on syscall. Consider using
47    /// `unload_async` instead.
48    pub fn unload_sync<S: AsRef<str>>(&self, modname: S, blocking: bool) -> errors::Result<()> {
49        let flags = match (self.force, blocking) {
50            (false, false) => 0,
51            (true, false) => libc::O_TRUNC,
52            (true, true) => libc::O_TRUNC | libc::O_NONBLOCK,
53            (false, true) => libc::O_NONBLOCK,
54        };
55        // UNSAFE(lucab): required syscall, all parameters are immutable.
56        let r = unsafe { libc::syscall(nr::DELETE_MODULE, modname.as_ref().as_ptr(), flags) };
57        match r {
58            0 => Ok(()),
59            _ => Err(
60                errors::Error::from_kind(errors::ErrorKind::Sys(errno::errno()))
61                    .chain_err(|| "blocking delete_module error"),
62            ),
63        }
64    }
65
66    /// Unload module by name, asynchronously.
67    ///
68    /// If the module is currently in use, this will continuously retry
69    /// unloading at fixed intervals after pausing for the specified
70    /// amount of milliseconds.
71    /// This requires enabling the `async` optional feature.
72    #[cfg(feature = "async")]
73    pub fn unload_async<S: AsRef<str>>(
74        &self,
75        modname: S,
76        pause_millis: ::std::num::NonZeroU64,
77    ) -> Box<Future<Item = (), Error = errors::Error>> {
78        let flags = {
79            let ff = if self.force { libc::O_TRUNC } else { 0 };
80            ff | libc::O_NONBLOCK
81        };
82        let pause = ::std::time::Duration::from_millis(pause_millis.get());
83        let unloader = UnloadTask {
84            flags,
85            interval: timer::Interval::new_interval(pause),
86            modname: modname.as_ref().to_string(),
87        };
88        Box::new(unloader)
89    }
90}
91
92#[cfg(feature = "async")]
93pub(crate) struct UnloadTask {
94    flags: libc::c_int,
95    interval: timer::Interval,
96    modname: String,
97}
98
99#[cfg(feature = "async")]
100impl Future for UnloadTask {
101    type Item = ();
102    type Error = errors::Error;
103    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
104        use futures::task;
105
106        // Rate-limit to a sane interval, as `delete_module(2)`
107        // has no feedback mechanism.
108        match self.interval.poll() {
109            Ok(Async::Ready(_)) => {}
110            Ok(Async::NotReady) => {
111                task::current().notify();
112                return Ok(Async::NotReady);
113            }
114            Err(e) => bail!("failed rate-limiting interval: {}", e),
115        };
116
117        // UNSAFE(lucab): required syscall, all parameters are immutable.
118        let r = unsafe { libc::syscall(nr::DELETE_MODULE, self.modname.as_ptr(), self.flags) };
119
120        // Successfully unloaded.
121        if r == 0 {
122            return Ok(Async::Ready(()));
123        }
124
125        // Module is busy, keep polling later.
126        let num = errno::errno();
127        if num.0 == libc::EWOULDBLOCK {
128            task::current().notify();
129            return Ok(Async::NotReady);
130        }
131
132        // Any other generic error, bubble this up.
133        Err(errors::Error::from_kind(errors::ErrorKind::Sys(num))
134            .chain_err(|| "async delete_module error"))
135    }
136}