X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fbasic%2Fvirt.c;h=35beebe7fa4116f5dedfd2b2a0e50fd0cc97486c;hp=7c0666cc244445a22d69e0229fd90c0ee75dce39;hb=2a4a438b7c7306da5f698d2247e646263f3c45c0;hpb=166f4e4fb53a82900ff9ae080e0ff37d35d4dacd diff --git a/src/basic/virt.c b/src/basic/virt.c index 7c0666cc2..35beebe7f 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -20,6 +18,8 @@ ***/ #include +#include +#include #include #include @@ -27,13 +27,14 @@ #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" +#include "macro.h" #include "process-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" -#include "util.h" #include "virt.h" +#if 0 /// UNNEEDED by elogind static int detect_vm_cpuid(void) { /* CPUID is an x86 specific interface. */ @@ -49,6 +50,8 @@ static int detect_vm_cpuid(void) { { "VMwareVMware", VIRTUALIZATION_VMWARE }, /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, + /* https://wiki.freebsd.org/bhyve */ + { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, }; uint32_t eax, ecx; @@ -98,6 +101,8 @@ static int detect_vm_cpuid(void) { : "0" (eax) ); + log_debug("Virtualization found, CPUID=%s", sig.text); + for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++) if (streq(sig.text, cpuid_vendor_table[j].cpuid)) return cpuid_vendor_table[j].id; @@ -105,6 +110,7 @@ static int detect_vm_cpuid(void) { return VIRTUALIZATION_VM_OTHER; } #endif + log_debug("No virtualization found in CPUID"); return VIRTUALIZATION_NONE; } @@ -121,19 +127,25 @@ static int detect_vm_device_tree(void) { dir = opendir("/proc/device-tree"); if (!dir) { - if (errno == ENOENT) + if (errno == ENOENT) { + log_debug_errno(errno, "/proc/device-tree: %m"); return VIRTUALIZATION_NONE; + } return -errno; } FOREACH_DIRENT(dent, dir, return -errno) - if (strstr(dent->d_name, "fw-cfg")) + if (strstr(dent->d_name, "fw-cfg")) { + log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name); return VIRTUALIZATION_QEMU; + } + log_debug("No virtualization found in /proc/device-tree/*"); return VIRTUALIZATION_NONE; } else if (r < 0) return r; + log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); if (streq(hvtype, "linux,kvm")) return VIRTUALIZATION_KVM; else if (strstr(hvtype, "xen")) @@ -141,6 +153,7 @@ static int detect_vm_device_tree(void) { else return VIRTUALIZATION_VM_OTHER; #else + log_debug("This platform does not support /proc/device-tree"); return VIRTUALIZATION_NONE; #endif } @@ -168,6 +181,8 @@ static int detect_vm_dmi(void) { { "Xen", VIRTUALIZATION_XEN }, { "Bochs", VIRTUALIZATION_BOCHS }, { "Parallels", VIRTUALIZATION_PARALLELS }, + /* https://wiki.freebsd.org/bhyve */ + { "BHYVE", VIRTUALIZATION_BHYVE }, }; unsigned i; int r; @@ -179,47 +194,77 @@ static int detect_vm_dmi(void) { r = read_one_line_file(dmi_vendors[i], &s); if (r < 0) { if (r == -ENOENT) - continue; + continue; return r; - } + } + + for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++) - if (startswith(s, dmi_vendor_table[j].vendor)) + if (startswith(s, dmi_vendor_table[j].vendor)) { + log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]); return dmi_vendor_table[j].id; + } } #endif + log_debug("No virtualization found in DMI"); + return VIRTUALIZATION_NONE; } static int detect_vm_xen(void) { + /* Check for Dom0 will be executed later in detect_vm_xen_dom0 + Thats why we dont check the content of /proc/xen/capabilities here. */ + if (access("/proc/xen/capabilities", F_OK) < 0) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return VIRTUALIZATION_NONE; + } + + log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); + return VIRTUALIZATION_XEN; + +} + +static bool detect_vm_xen_dom0(void) { _cleanup_free_ char *domcap = NULL; char *cap, *i; int r; r = read_one_line_file("/proc/xen/capabilities", &domcap); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; + if (r == -ENOENT) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return false; + } + if (r < 0) + return r; i = domcap; - while ((cap = strsep(&i, ","))) - if (streq(cap, "control_d")) - break; + while ((cap = strsep(&i, ","))) + if (streq(cap, "control_d")) + break; + if (!cap) { + log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); + return false; + } - return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN; - } + log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); + return true; +} static int detect_vm_hypervisor(void) { - _cleanup_free_ char *hvtype = NULL; + _cleanup_free_ char *hvtype = NULL; int r; - r = read_one_line_file("/sys/hypervisor/type", &hvtype); + r = read_one_line_file("/sys/hypervisor/type", &hvtype); if (r == -ENOENT) return VIRTUALIZATION_NONE; if (r < 0) return r; + log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); + if (streq(hvtype, "xen")) return VIRTUALIZATION_XEN; else @@ -234,9 +279,13 @@ static int detect_vm_uml(void) { r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); if (r < 0) return r; - if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) + + if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { + log_debug("UML virtualization found in /proc/cpuinfo"); return VIRTUALIZATION_UML; + } + log_debug("No virtualization found in /proc/cpuinfo."); return VIRTUALIZATION_NONE; } @@ -252,14 +301,16 @@ static int detect_vm_zvm(void) { if (r < 0) return r; + log_debug("Virtualization %s found in /proc/sysinfo", t); if (streq(t, "z/VM")) return VIRTUALIZATION_ZVM; else return VIRTUALIZATION_KVM; #else + log_debug("This platform does not support /proc/sysinfo"); return VIRTUALIZATION_NONE; #endif - } +} /* Returns a short identifier for the various VM implementations */ int detect_vm(void) { @@ -269,13 +320,20 @@ int detect_vm(void) { if (cached_found >= 0) return cached_found; - r = detect_vm_cpuid(); + /* We have to use the correct order here: + * Some virtualization technologies do use KVM hypervisor but are + * expected to be detected as something else. So detect DMI first. + * + * An example is Virtualbox since version 5.0, which uses KVM backend. + * Detection via DMI works corretly, the CPU ID would find KVM + * only. */ + r = detect_vm_dmi(); if (r < 0) return r; if (r != VIRTUALIZATION_NONE) goto finish; - r = detect_vm_dmi(); + r = detect_vm_cpuid(); if (r < 0) return r; if (r != VIRTUALIZATION_NONE) @@ -310,16 +368,24 @@ int detect_vm(void) { if (r < 0) return r; if (r != VIRTUALIZATION_NONE) - goto finish; + goto finish; r = detect_vm_zvm(); if (r < 0) return r; finish: + /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others. + * In order to detect the Dom0 as not virtualization we need to + * double-check it */ + if (r == VIRTUALIZATION_XEN && detect_vm_xen_dom0()) + r = VIRTUALIZATION_NONE; + cached_found = r; + log_debug("Found VM virtualization %s", virtualization_to_string(r)); return r; } +#endif // 0 int detect_container(void) { @@ -405,6 +471,7 @@ int detect_container(void) { r = VIRTUALIZATION_CONTAINER_OTHER; finish: + log_debug("Found container virtualization %s", virtualization_to_string(r)); cached_found = r; return r; } @@ -414,16 +481,91 @@ int detect_virtualization(void) { int r; r = detect_container(); + if (r == 0) + r = detect_vm(); + + return r; +} + +static int userns_has_mapping(const char *name) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *buf = NULL; + size_t n_allocated = 0; + ssize_t n; + uint32_t a, b, c; + int r; + + f = fopen(name, "re"); + if (!f) { + log_debug_errno(errno, "Failed to open %s: %m", name); + return errno == -ENOENT ? false : -errno; + } + + n = getline(&buf, &n_allocated, f); + if (n < 0) { + if (feof(f)) { + log_debug("%s is empty, we're in an uninitialized user namespace", name); + return true; + } + + return log_debug_errno(errno, "Failed to read %s: %m", name); + } + + r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c); + if (r < 3) + return log_debug_errno(errno, "Failed to parse %s: %m", name); + + if (a == 0 && b == 0 && c == UINT32_MAX) { + /* The kernel calls mappings_overlap() and does not allow overlaps */ + log_debug("%s has a full 1:1 mapping", name); + return false; + } + + /* Anything else implies that we are in a user namespace */ + log_debug("Mapping found in %s, we're in a user namespace", name); + return true; +} + +int running_in_userns(void) { + _cleanup_free_ char *line = NULL; + int r; + + r = userns_has_mapping("/proc/self/uid_map"); if (r != 0) return r; - return detect_vm(); + r = userns_has_mapping("/proc/self/gid_map"); + if (r != 0) + return r; + + /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also + * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups" + * also does not exist. We cannot distinguish those two cases, so assume that + * we're running on a stripped-down recent kernel, rather than on an old one, + * and if the file is not found, return false. + */ + r = read_one_line_file("/proc/self/setgroups", &line); + if (r < 0) { + log_debug_errno(r, "/proc/self/setgroups: %m"); + return r == -ENOENT ? false : r; + } + + truncate_nl(line); + r = streq(line, "deny"); + /* See user_namespaces(7) for a description of this "setgroups" contents. */ + log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in"); + return r; } #endif // 0 int running_in_chroot(void) { int ret; +#if 0 /// elogind does not allow to ignore chroots, we are never init! + if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) + return 0; +#endif // 0 + ret = files_same("/proc/1/root", "/"); if (ret < 0) return ret; @@ -443,6 +585,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_MICROSOFT] = "microsoft", [VIRTUALIZATION_ZVM] = "zvm", [VIRTUALIZATION_PARALLELS] = "parallels", + [VIRTUALIZATION_BHYVE] = "bhyve", [VIRTUALIZATION_VM_OTHER] = "vm-other", [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",