From a4a9a6f7c6e9cd9e219c56d08434a04bc2f395ff Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sat, 19 Dec 2020 21:00:22 +0300 Subject: [PATCH 1/2] virt: detect Docker and Podman containers Docker doesn't set $container, so it cannot be detected that way. Instead, we check for presence of /.dockerinit, which it creates. Podman does set $container, but some Red Hat images (in particular, Fedora images) override $container to equal "oci". So to correctly detect Podman containers, we check for presence of /run/.containerenv, which is created by Podman and is now the official way to get information about the container from within the container. Fixes https://github.com/systemd/systemd/issues/15393 --- src/basic/virt.c | 50 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/basic/virt.c b/src/basic/virt.c index 02e7fbf1f7..f625328e58 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -453,6 +453,34 @@ static const char *const container_table[_VIRTUALIZATION_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int); +static int detect_container_files(void) { + unsigned i; + + static const struct { + const char *file_path; + int id; + } container_file_table[] = { + /* https://github.com/containers/podman/issues/6192 */ + /* https://github.com/containers/podman/issues/3586#issuecomment-661918679 */ + { "/run/.containerenv", VIRTUALIZATION_PODMAN }, + /* https://github.com/moby/moby/issues/18355 */ + /* Docker must be the last in this table, see below. */ + { "/.dockerenv", VIRTUALIZATION_DOCKER }, + }; + + for (i = 0; i < ELEMENTSOF(container_file_table); i++) { + if (access(container_file_table[i].file_path, F_OK) >= 0) + return container_file_table[i].id; + + if (errno != ENOENT) + log_debug_errno(errno, + "Checking if %s exists failed, ignoring: %m", + container_file_table[i].file_path); + } + + return VIRTUALIZATION_NONE; +} + int detect_container(void) { static thread_local int cached_found = _VIRTUALIZATION_INVALID; _cleanup_free_ char *m = NULL, *o = NULL, *p = NULL; @@ -530,7 +558,7 @@ int detect_container(void) { */ e = getenv("container"); if (!e) - goto none; + goto check_files; if (isempty(e)) { r = VIRTUALIZATION_NONE; goto finish; @@ -558,12 +586,28 @@ int detect_container(void) { if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */ log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m"); -none: - /* If that didn't work, give up, assume no container manager. */ +check_files: + /* Check for existence of some well-known files. We only do this after checking + * for other specific container managers, otherwise we risk mistaking another + * container manager for Docker: the /.dockerenv file could inadvertently end up + * in a file system image. */ + r = detect_container_files(); + if (r) + goto finish; + + /* If none of that worked, give up, assume no container manager. */ r = VIRTUALIZATION_NONE; goto finish; translate_name: + if (streq(e, "oci")) { + /* Some images hardcode container=oci, but OCI is not a specific container manager. + * Try to detect one based on well-known files. */ + r = detect_container_files(); + if (!r) + r = VIRTUALIZATION_CONTAINER_OTHER; + goto finish; + } r = container_from_string(e); if (r < 0) r = VIRTUALIZATION_CONTAINER_OTHER; From 0e13779d376a63af571ec28da44519824997b30d Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 20 Dec 2020 14:43:01 +0300 Subject: [PATCH 2/2] virt: detect cgroups namespaces detect_container() is now able to detect if we're running in a cgroup namespace. --- src/basic/virt.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/basic/virt.c b/src/basic/virt.c index f625328e58..6160b852dc 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -9,6 +9,7 @@ #include #include "alloc-util.h" +#include "cgroup-util.h" #include "dirent-util.h" #include "env-util.h" #include "fd-util.h" @@ -453,6 +454,72 @@ static const char *const container_table[_VIRTUALIZATION_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int); +static int running_in_cgroupns(void) { + int r; + + if (!cg_ns_supported()) + return false; + + r = cg_all_unified(); + if (r < 0) + return r; + + if (r) { + /* cgroup v2 */ + + r = access("/sys/fs/cgroup/cgroup.events", F_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + /* All kernel versions have cgroup.events in nested cgroups. */ + return false; + } + + /* There's no cgroup.type in the root cgroup, and future kernel versions + * are unlikely to add it since cgroup.type is something that makes no sense + * whatsoever in the root cgroup. */ + r = access("/sys/fs/cgroup/cgroup.type", F_OK); + if (r == 0) + return true; + if (r < 0 && errno != ENOENT) + return -errno; + + /* On older kernel versions, there's no cgroup.type */ + r = access("/sys/kernel/cgroup/features", F_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + /* This is an old kernel that we know for sure has cgroup.events + * only in nested cgroups. */ + return true; + } + + /* This is a recent kernel, and cgroup.type doesn't exist, so we must be + * in the root cgroup. */ + return false; + } else { + /* cgroup v1 */ + + /* If systemd controller is not mounted, do not even bother. */ + r = access("/sys/fs/cgroup/systemd", F_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + return false; + } + + /* release_agent only exists in the root cgroup. */ + r = access("/sys/fs/cgroup/systemd/release_agent", F_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + return true; + } + + return false; + } +} + static int detect_container_files(void) { unsigned i; @@ -595,6 +662,14 @@ check_files: if (r) goto finish; + r = running_in_cgroupns(); + if (r > 0) { + r = VIRTUALIZATION_CONTAINER_OTHER; + goto finish; + } + if (r < 0) + log_debug_errno(r, "Failed to detect cgroup namespace: %m"); + /* If none of that worked, give up, assume no container manager. */ r = VIRTUALIZATION_NONE; goto finish;