TRACE("named subsystem %d: %s", k, *it);
}
-static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *container_cgroup)
+static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *path_prune)
{
- if (!container_cgroup || !hierarchies)
+ if (!path_prune || !hierarchies)
return 0;
for (int i = 0; hierarchies[i]; i++) {
if (!h->container_limit_path)
continue;
- ret = lxc_rm_rf(h->container_limit_path);
+ ret = cgroup_tree_prune(h->dfd_base, path_prune);
if (ret < 0)
- WARN("Failed to destroy \"%s\"", h->container_limit_path);
+ SYSWARN("Failed to destroy %d(%s)", h->dfd_base, path_prune);
+ else
+ TRACE("Removed cgroup tree %d(%s)", h->dfd_base, path_prune);
if (h->container_limit_path != h->container_full_path)
free_disarm(h->container_limit_path);
struct generic_userns_exec_data {
struct hierarchy **hierarchies;
- const char *container_cgroup;
+ const char *path_prune;
struct lxc_conf *conf;
uid_t origuid; /* target uid in parent namespace */
char *path;
return log_error_errno(-1, errno, "Failed to setresuid(%d, %d, %d)",
(int)nsuid, (int)nsuid, (int)nsuid);
- return cgroup_tree_remove(arg->hierarchies, arg->container_cgroup);
+ return cgroup_tree_remove(arg->hierarchies, arg->path_prune);
}
__cgfsng_ops static void cgfsng_payload_destroy(struct cgroup_ops *ops,
if (!lxc_list_empty(&handler->conf->id_map)) {
struct generic_userns_exec_data wrap = {
.conf = handler->conf,
- .container_cgroup = ops->container_cgroup,
+ .path_prune = ops->container_limit_cgroup,
.hierarchies = ops->hierarchies,
.origuid = 0,
};
ret = userns_exec_1(handler->conf, cgroup_tree_remove_wrapper,
&wrap, "cgroup_tree_remove_wrapper");
} else {
- ret = cgroup_tree_remove(ops->hierarchies, ops->container_cgroup);
+ ret = cgroup_tree_remove(ops->hierarchies, ops->container_limit_cgroup);
}
if (ret < 0)
SYSWARN("Failed to destroy cgroups");
/* Monitor might have died before we entered the cgroup. */
if (handler->monitor_pid <= 0) {
WARN("No valid monitor process found while destroying cgroups");
- goto try_lxc_rm_rf;
+ goto cgroup_prune_tree;
}
if (conf->cgroup_meta.monitor_pivot_dir)
continue;
}
-try_lxc_rm_rf:
- ret = lxc_rm_rf(h->monitor_full_path);
+cgroup_prune_tree:
+ ret = cgroup_tree_prune(h->dfd_base, ops->monitor_cgroup);
if (ret < 0)
- WARN("Failed to destroy \"%s\"", h->monitor_full_path);
+ SYSWARN("Failed to destroy %d(%s)", h->dfd_base, ops->monitor_cgroup);
+ else
+ TRACE("Removed cgroup tree %d(%s)", h->dfd_base, ops->monitor_cgroup);
}
}
return log_error_errno(false, ERANGE, "Failed to create container cgroup");
ops->container_cgroup = move_ptr(container_cgroup);
+ if (limiting_cgroup)
+ ops->container_limit_cgroup = move_ptr(limiting_cgroup);
+ else
+ ops->container_limit_cgroup = ops->container_cgroup;
INFO("The container process uses \"%s\" as cgroup", ops->container_cgroup);
return true;
}
#include "cgroup_utils.h"
#include "config.h"
#include "file_utils.h"
+#include "log.h"
#include "macro.h"
#include "memory_utils.h"
#include "utils.h"
+lxc_log_define(cgroup_utils, lxc);
+
int get_cgroup_version(char *line)
{
if (is_cgroupfs_v1(line))
return false;
}
+
+int cgroup_tree_prune(int dfd, const char *path)
+{
+ __do_close int dfd_disown = -EBADF, dfd_dup = -EBADF;
+ __do_closedir DIR *dir = NULL;
+ int ret;
+ struct dirent *direntp;
+
+ /*
+ * The unlinkat() syscall doesn't work with empty paths, i.e. it isn't
+ * possible to remove the fd itself.
+ */
+ if (is_empty_string(path) || strequal(path, "."))
+ return ret_errno(EINVAL);
+
+ /*
+ * Note that O_PATH file descriptors can't be used with getdents() and
+ * therefore with readdir().
+ */
+ dfd_disown = open_at(dfd, path, PROTECT_OPEN,
+ PROTECT_LOOKUP_BENEATH_WITH_SYMLINKS, 0);
+ if (dfd_disown < 0)
+ return -errno;
+
+ dfd_dup = dup_cloexec(dfd_disown);
+ if (dfd_dup < 0)
+ return -errno;
+
+ dir = fdopendir(dfd_disown);
+ if (!dir)
+ return -errno;
+
+ /* Transfer ownership to fdopendir(). */
+ move_fd(dfd_disown);
+
+ while ((direntp = readdir(dir))) {
+ struct stat st;
+
+ if (strequal(direntp->d_name, ".") ||
+ strequal(direntp->d_name, ".."))
+ continue;
+
+ ret = fstatat(dfd_dup, direntp->d_name, &st,
+ AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
+ if (ret < 0)
+ continue;
+
+ if (!S_ISDIR(st.st_mode))
+ continue;
+
+ ret = cgroup_tree_prune(dfd_dup, direntp->d_name);
+ if (ret < 0)
+ return -errno;
+ }
+
+ ret = unlinkat(dfd, path, AT_REMOVEDIR);
+ if (ret < 0)
+ return -errno;
+
+ return 0;
+}