1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fs;

use libcgroups::common::CgroupManager;
use libcgroups::{self};
use nix::sys::signal;

use super::{Container, ContainerStatus};
use crate::config::YoukiConfig;
use crate::error::LibcontainerError;
use crate::hooks;
use crate::process::intel_rdt::delete_resctrl_subdirectory;

impl Container {
    /// Deletes the container
    ///
    /// # Example
    ///
    /// ```no_run
    /// use libcontainer::container::builder::ContainerBuilder;
    /// use libcontainer::syscall::syscall::SyscallType;
    ///
    /// # fn main() -> anyhow::Result<()> {
    /// let mut container = ContainerBuilder::new(
    ///     "74f1a4cb3801".to_owned(),
    ///     SyscallType::default(),
    /// )
    /// .as_init("/var/run/docker/bundle")
    /// .build()?;
    ///
    /// container.delete(true)?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn delete(&mut self, force: bool) -> Result<(), LibcontainerError> {
        self.refresh_status()?;

        tracing::debug!("container status: {:?}", self.status());

        // Check if container is allowed to be deleted based on container status.
        match self.status() {
            ContainerStatus::Stopped => {}
            ContainerStatus::Created => {
                // Here, we differ from the OCI spec, but matches the same
                // behavior as `runc` and `crun`. The OCI spec does not allow
                // deletion of status `created` without `force` flag. But both
                // `runc` and `crun` allows deleting `created`. Therefore we
                // decided to follow `runc` and `crun`.
                self.do_kill(signal::Signal::SIGKILL, true)?;
                self.set_status(ContainerStatus::Stopped).save()?;
            }
            ContainerStatus::Creating | ContainerStatus::Running | ContainerStatus::Paused => {
                // Containers can't be deleted while in these status, unless
                // force flag is set. In the force case, we need to clean up any
                // processes associated with containers.
                if force {
                    self.do_kill(signal::Signal::SIGKILL, true)?;
                    self.set_status(ContainerStatus::Stopped).save()?;
                } else {
                    tracing::error!(
                        id = ?self.id(),
                        status = ?self.status(),
                        "delete requires the container state to be stopped or created",
                    );
                    return Err(LibcontainerError::IncorrectStatus);
                }
            }
        }

        // Once reached here, the container is verified that it can be deleted.
        debug_assert!(self.status().can_delete());

        if let Some(true) = &self.clean_up_intel_rdt_subdirectory() {
            if let Err(err) = delete_resctrl_subdirectory(self.id()) {
                tracing::warn!(
                    "failed to delete resctrl subdirectory due to: {err:?}, continue to delete"
                );
            }
        }

        if self.root.exists() {
            match YoukiConfig::load(&self.root) {
                Ok(config) => {
                    tracing::debug!("config: {:?}", config);

                    // remove the cgroup created for the container
                    // check https://man7.org/linux/man-pages/man7/cgroups.7.html
                    // creating and removing cgroups section for more information on cgroups
                    let cmanager = libcgroups::common::create_cgroup_manager(
                        libcgroups::common::CgroupConfig {
                            cgroup_path: config.cgroup_path.to_owned(),
                            systemd_cgroup: self.systemd(),
                            container_name: self.id().to_string(),
                        },
                    )?;
                    cmanager.remove().map_err(|err| {
                        tracing::error!(cgroup_path = ?config.cgroup_path, "failed to remove cgroup due to: {err:?}");
                        err
                    })?;

                    if let Some(hooks) = config.hooks.as_ref() {
                        hooks::run_hooks(hooks.poststop().as_ref(), Some(self), None).map_err(
                            |err| {
                                tracing::error!(err = ?err, "failed to run post stop hooks");
                                err
                            },
                        )?;
                    }
                }
                Err(err) => {
                    // There is a brief window where the container state is
                    // created, but the container config is not yet generated
                    // from the OCI spec. In this case, we assume as if we
                    // successfully deleted the config and moving on.
                    tracing::warn!(
                        "skipping loading youki config due to: {err:?}, continue to delete"
                    );
                }
            }

            // remove the directory storing container state
            tracing::debug!("remove dir {:?}", self.root);
            fs::remove_dir_all(&self.root).map_err(|err| {
                tracing::error!(?err, path = ?self.root, "failed to remove container dir");
                LibcontainerError::OtherIO(err)
            })?;
        }

        Ok(())
    }
}