Skip to main content

libcontainer/container/
container_kill.rs

1use libcgroups::common::{CgroupManager, get_cgroup_setup};
2use nix::sys::signal::{self};
3
4use super::{Container, ContainerStatus};
5use crate::error::LibcontainerError;
6use crate::signal::Signal;
7
8impl Container {
9    /// Sends the specified signal to the container init process
10    ///
11    /// # Example
12    ///
13    /// ```no_run
14    /// use libcontainer::container::builder::ContainerBuilder;
15    /// use libcontainer::syscall::syscall::SyscallType;
16    /// use nix::sys::signal::Signal;
17    ///
18    /// # fn main() -> anyhow::Result<()> {
19    /// let mut container = ContainerBuilder::new(
20    ///     "74f1a4cb3801".to_owned(),
21    ///     SyscallType::default(),
22    /// )
23    /// .as_init("/var/run/docker/bundle")
24    /// .build()?;
25    ///
26    /// container.kill(Signal::SIGKILL, false)?;
27    /// # Ok(())
28    /// # }
29    /// ```
30    pub fn kill<S: Into<Signal>>(&mut self, signal: S, all: bool) -> Result<(), LibcontainerError> {
31        self.refresh_status()?;
32        match self.can_kill() {
33            true => {
34                self.do_kill(signal, all)?;
35            }
36            false if all && self.status() == ContainerStatus::Stopped => {
37                self.do_kill(signal, all)?;
38            }
39            false => {
40                tracing::error!(id = ?self.id(), status = ?self.status(), "cannot kill container due to incorrect state");
41                return Err(LibcontainerError::IncorrectStatus(self.status()));
42            }
43        }
44        self.set_status(ContainerStatus::Stopped).save()?;
45        Ok(())
46    }
47
48    pub(crate) fn do_kill<S: Into<Signal>>(
49        &self,
50        signal: S,
51        all: bool,
52    ) -> Result<(), LibcontainerError> {
53        if all {
54            self.kill_all_processes(signal)
55        } else {
56            self.kill_one_process(signal)
57        }
58    }
59
60    fn kill_one_process<S: Into<Signal>>(&self, signal: S) -> Result<(), LibcontainerError> {
61        let signal = signal.into().into_raw();
62        let pid = self.pid().ok_or(LibcontainerError::Other(
63            "container process pid not found in state".into(),
64        ))?;
65
66        tracing::debug!("kill signal {} to {}", signal, pid);
67
68        match signal::kill(pid, signal) {
69            Ok(_) => {}
70            Err(nix::errno::Errno::ESRCH) => {
71                // the process does not exist, which is what we want
72            }
73            Err(err) => {
74                tracing::error!(id = ?self.id(), err = ?err, ?pid, ?signal, "failed to kill process");
75                return Err(LibcontainerError::OtherSyscall(err));
76            }
77        }
78
79        // For cgroup V1, a frozon process cannot respond to signals,
80        // so we need to thaw it. Only thaw the cgroup for SIGKILL.
81        if self.status() == ContainerStatus::Paused && signal == signal::Signal::SIGKILL {
82            match get_cgroup_setup()? {
83                libcgroups::common::CgroupSetup::Legacy
84                | libcgroups::common::CgroupSetup::Hybrid => {
85                    let cmanager = libcgroups::common::create_cgroup_manager(
86                        libcgroups::common::CgroupConfig {
87                            cgroup_path: self.spec()?.cgroup_path,
88                            systemd_cgroup: self.systemd(),
89                            container_name: self.id().to_string(),
90                        },
91                    )?;
92                    cmanager.freeze(libcgroups::common::FreezerState::Thawed)?;
93                }
94                libcgroups::common::CgroupSetup::Unified => {}
95            }
96        }
97        Ok(())
98    }
99
100    fn kill_all_processes<S: Into<Signal>>(&self, signal: S) -> Result<(), LibcontainerError> {
101        let signal = signal.into().into_raw();
102        let cmanager =
103            libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
104                cgroup_path: self.spec()?.cgroup_path,
105                systemd_cgroup: self.systemd(),
106                container_name: self.id().to_string(),
107            })?;
108
109        if let Err(e) = cmanager.freeze(libcgroups::common::FreezerState::Frozen) {
110            tracing::warn!(
111                err = ?e,
112                id = ?self.id(),
113                "failed to freeze container",
114            );
115        }
116
117        let pids = cmanager.get_all_pids()?;
118        pids.iter()
119            .try_for_each(|&pid| {
120                tracing::debug!("kill signal {} to {}", signal, pid);
121                let res = signal::kill(pid, signal);
122                match res {
123                    Err(nix::errno::Errno::ESRCH) => {
124                        // the process does not exist, which is what we want
125                        Ok(())
126                    }
127                    _ => res,
128                }
129            })
130            .map_err(LibcontainerError::OtherSyscall)?;
131        if let Err(err) = cmanager.freeze(libcgroups::common::FreezerState::Thawed) {
132            tracing::warn!(
133                err = ?err,
134                id = ?self.id(),
135                "failed to thaw container",
136            );
137        }
138
139        Ok(())
140    }
141}