1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2011 Lennart Poettering
6 #if defined(__i386__) || defined(__x86_64__)
15 #include "alloc-util.h"
16 #include "dirent-util.h"
21 #include "process-util.h"
22 #include "stat-util.h"
23 #include "string-table.h"
24 #include "string-util.h"
27 #if 0 /// UNNEEDED by elogind
28 static int detect_vm_cpuid(void) {
30 /* CPUID is an x86 specific interface. */
31 #if defined(__i386__) || defined(__x86_64__)
36 } cpuid_vendor_table[] = {
37 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
38 { "KVMKVMKVM", VIRTUALIZATION_KVM },
39 { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU },
40 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
41 { "VMwareVMware", VIRTUALIZATION_VMWARE },
42 /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
43 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
44 /* https://wiki.freebsd.org/bhyve */
45 { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
46 { "QNXQVMBSQG", VIRTUALIZATION_QNX },
49 uint32_t eax, ebx, ecx, edx;
52 /* http://lwn.net/Articles/301888/ */
54 /* First detect whether there is a hypervisor */
55 if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
56 return VIRTUALIZATION_NONE;
58 hypervisor = ecx & 0x80000000U;
67 /* There is a hypervisor, see what it is */
68 __cpuid(0x40000000U, eax, ebx, ecx, edx);
74 log_debug("Virtualization found, CPUID=%s", sig.text);
76 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
77 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
78 return cpuid_vendor_table[j].id;
80 return VIRTUALIZATION_VM_OTHER;
83 log_debug("No virtualization found in CPUID");
85 return VIRTUALIZATION_NONE;
88 static int detect_vm_device_tree(void) {
89 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
90 _cleanup_free_ char *hvtype = NULL;
93 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
95 _cleanup_closedir_ DIR *dir = NULL;
98 dir = opendir("/proc/device-tree");
100 if (errno == ENOENT) {
101 log_debug_errno(errno, "/proc/device-tree: %m");
102 return VIRTUALIZATION_NONE;
107 FOREACH_DIRENT(dent, dir, return -errno)
108 if (strstr(dent->d_name, "fw-cfg")) {
109 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name);
110 return VIRTUALIZATION_QEMU;
113 log_debug("No virtualization found in /proc/device-tree/*");
114 return VIRTUALIZATION_NONE;
118 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype);
119 if (streq(hvtype, "linux,kvm"))
120 return VIRTUALIZATION_KVM;
121 else if (strstr(hvtype, "xen"))
122 return VIRTUALIZATION_XEN;
124 return VIRTUALIZATION_VM_OTHER;
126 log_debug("This platform does not support /proc/device-tree");
127 return VIRTUALIZATION_NONE;
131 static int detect_vm_dmi(void) {
132 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
134 static const char *const dmi_vendors[] = {
135 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
136 "/sys/class/dmi/id/sys_vendor",
137 "/sys/class/dmi/id/board_vendor",
138 "/sys/class/dmi/id/bios_vendor"
141 static const struct {
144 } dmi_vendor_table[] = {
145 { "KVM", VIRTUALIZATION_KVM },
146 { "QEMU", VIRTUALIZATION_QEMU },
147 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
148 { "VMware", VIRTUALIZATION_VMWARE },
149 { "VMW", VIRTUALIZATION_VMWARE },
150 { "innotek GmbH", VIRTUALIZATION_ORACLE },
151 { "Xen", VIRTUALIZATION_XEN },
152 { "Bochs", VIRTUALIZATION_BOCHS },
153 { "Parallels", VIRTUALIZATION_PARALLELS },
154 /* https://wiki.freebsd.org/bhyve */
155 { "BHYVE", VIRTUALIZATION_BHYVE },
160 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
161 _cleanup_free_ char *s = NULL;
164 r = read_one_line_file(dmi_vendors[i], &s);
172 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
173 if (startswith(s, dmi_vendor_table[j].vendor)) {
174 log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]);
175 return dmi_vendor_table[j].id;
180 log_debug("No virtualization found in DMI");
182 return VIRTUALIZATION_NONE;
185 static int detect_vm_xen(void) {
187 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
188 The presence of /proc/xen indicates some form of a Xen domain */
189 if (access("/proc/xen", F_OK) < 0) {
190 log_debug("Virtualization XEN not found, /proc/xen does not exist");
191 return VIRTUALIZATION_NONE;
194 log_debug("Virtualization XEN found (/proc/xen exists)");
195 return VIRTUALIZATION_XEN;
198 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
199 #define PATH_FEATURES "/sys/hypervisor/properties/features"
200 /* Returns -errno, or 0 for domU, or 1 for dom0 */
201 static int detect_vm_xen_dom0(void) {
202 _cleanup_free_ char *domcap = NULL;
206 r = read_one_line_file(PATH_FEATURES, &domcap);
207 if (r < 0 && r != -ENOENT)
210 unsigned long features;
212 /* Here, we need to use sscanf() instead of safe_atoul()
213 * as the string lacks the leading "0x". */
214 r = sscanf(domcap, "%lx", &features);
216 r = !!(features & (1U << XENFEAT_dom0));
217 log_debug("Virtualization XEN, found %s with value %08lx, "
218 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
219 PATH_FEATURES, features, r ? "" : " not");
222 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
223 PATH_FEATURES, domcap);
226 r = read_one_line_file("/proc/xen/capabilities", &domcap);
228 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
235 while ((cap = strsep(&i, ",")))
236 if (streq(cap, "control_d"))
239 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
243 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
247 static int detect_vm_hypervisor(void) {
248 _cleanup_free_ char *hvtype = NULL;
251 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
253 return VIRTUALIZATION_NONE;
257 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype);
259 if (streq(hvtype, "xen"))
260 return VIRTUALIZATION_XEN;
262 return VIRTUALIZATION_VM_OTHER;
265 static int detect_vm_uml(void) {
266 _cleanup_free_ char *cpuinfo_contents = NULL;
269 /* Detect User-Mode Linux by reading /proc/cpuinfo */
270 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
272 log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
273 return VIRTUALIZATION_NONE;
278 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
279 log_debug("UML virtualization found in /proc/cpuinfo");
280 return VIRTUALIZATION_UML;
283 log_debug("UML virtualization not found in /proc/cpuinfo.");
284 return VIRTUALIZATION_NONE;
287 static int detect_vm_zvm(void) {
289 #if defined(__s390__)
290 _cleanup_free_ char *t = NULL;
293 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
295 return VIRTUALIZATION_NONE;
299 log_debug("Virtualization %s found in /proc/sysinfo", t);
300 if (streq(t, "z/VM"))
301 return VIRTUALIZATION_ZVM;
303 return VIRTUALIZATION_KVM;
305 log_debug("This platform does not support /proc/sysinfo");
306 return VIRTUALIZATION_NONE;
310 /* Returns a short identifier for the various VM implementations */
311 int detect_vm(void) {
312 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
316 if (cached_found >= 0)
319 /* We have to use the correct order here:
321 * → First, try to detect Oracle Virtualbox, even if it uses KVM, as well as Xen even if it cloaks as Microsoft
324 * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is
327 * → Third, try to detect from DMI. */
329 dmi = detect_vm_dmi();
330 if (IN_SET(dmi, VIRTUALIZATION_ORACLE, VIRTUALIZATION_XEN)) {
335 r = detect_vm_cpuid();
338 if (r == VIRTUALIZATION_VM_OTHER)
340 else if (r != VIRTUALIZATION_NONE)
343 /* Now, let's get back to DMI */
346 if (dmi == VIRTUALIZATION_VM_OTHER)
348 else if (dmi != VIRTUALIZATION_NONE) {
353 /* x86 xen will most likely be detected by cpuid. If not (most likely
354 * because we're not an x86 guest), then we should try the /proc/xen
355 * directory next. If that's not found, then we check for the high-level
356 * hypervisor sysfs file.
362 if (r == VIRTUALIZATION_VM_OTHER)
364 else if (r != VIRTUALIZATION_NONE)
367 r = detect_vm_hypervisor();
370 if (r == VIRTUALIZATION_VM_OTHER)
372 else if (r != VIRTUALIZATION_NONE)
375 r = detect_vm_device_tree();
378 if (r == VIRTUALIZATION_VM_OTHER)
380 else if (r != VIRTUALIZATION_NONE)
386 if (r == VIRTUALIZATION_VM_OTHER)
388 else if (r != VIRTUALIZATION_NONE)
396 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
397 * In order to detect the Dom0 as not virtualization we need to
399 if (r == VIRTUALIZATION_XEN) {
402 dom0 = detect_vm_xen_dom0();
406 r = VIRTUALIZATION_NONE;
407 } else if (r == VIRTUALIZATION_NONE && other)
408 r = VIRTUALIZATION_VM_OTHER;
411 log_debug("Found VM virtualization %s", virtualization_to_string(r));
416 int detect_container(void) {
418 static const struct {
422 { "lxc", VIRTUALIZATION_LXC },
423 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
424 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
425 { "docker", VIRTUALIZATION_DOCKER },
426 { "rkt", VIRTUALIZATION_RKT },
429 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
430 _cleanup_free_ char *m = NULL;
431 const char *e = NULL;
435 if (cached_found >= 0)
438 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
439 if (access("/proc/vz", F_OK) >= 0 &&
440 access("/proc/bc", F_OK) < 0) {
441 r = VIRTUALIZATION_OPENVZ;
445 if (getpid_cached() == 1) {
446 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
448 e = getenv("container");
450 r = VIRTUALIZATION_NONE;
457 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
458 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
459 r = read_one_line_file("/run/systemd/container", &m);
465 return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
467 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
468 r = getenv_for_pid(1, "container", &m);
473 if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
474 log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
476 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
477 * there is not 1, we know we are in a PID namespace. and hence a container. */
478 r = read_one_line_file("/proc/1/sched", &m);
486 if (!startswith(t, "(1,")) {
487 r = VIRTUALIZATION_CONTAINER_OTHER;
490 } else if (r != -ENOENT)
493 /* If that didn't work, give up, assume no container manager. */
494 r = VIRTUALIZATION_NONE;
498 for (j = 0; j < ELEMENTSOF(value_table); j++)
499 if (streq(e, value_table[j].value)) {
500 r = value_table[j].id;
504 r = VIRTUALIZATION_CONTAINER_OTHER;
507 log_debug("Found container virtualization %s.", virtualization_to_string(r));
512 #if 0 /// UNNEEDED by elogind
513 int detect_virtualization(void) {
516 r = detect_container();
523 static int userns_has_mapping(const char *name) {
524 _cleanup_fclose_ FILE *f = NULL;
525 _cleanup_free_ char *buf = NULL;
526 size_t n_allocated = 0;
531 f = fopen(name, "re");
533 log_debug_errno(errno, "Failed to open %s: %m", name);
534 return errno == ENOENT ? false : -errno;
537 n = getline(&buf, &n_allocated, f);
540 log_debug("%s is empty, we're in an uninitialized user namespace", name);
544 return log_debug_errno(errno, "Failed to read %s: %m", name);
547 r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
549 return log_debug_errno(errno, "Failed to parse %s: %m", name);
551 if (a == 0 && b == 0 && c == UINT32_MAX) {
552 /* The kernel calls mappings_overlap() and does not allow overlaps */
553 log_debug("%s has a full 1:1 mapping", name);
557 /* Anything else implies that we are in a user namespace */
558 log_debug("Mapping found in %s, we're in a user namespace", name);
562 int running_in_userns(void) {
563 _cleanup_free_ char *line = NULL;
566 r = userns_has_mapping("/proc/self/uid_map");
570 r = userns_has_mapping("/proc/self/gid_map");
574 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
575 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
576 * also does not exist. We cannot distinguish those two cases, so assume that
577 * we're running on a stripped-down recent kernel, rather than on an old one,
578 * and if the file is not found, return false.
580 r = read_one_line_file("/proc/self/setgroups", &line);
582 log_debug_errno(r, "/proc/self/setgroups: %m");
583 return r == -ENOENT ? false : r;
587 r = streq(line, "deny");
588 /* See user_namespaces(7) for a description of this "setgroups" contents. */
589 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
594 int running_in_chroot(void) {
597 #if 0 /// elogind does not allow to ignore chroots, we are never init!
598 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
602 r = files_same("/proc/1/root", "/", 0);
609 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
610 [VIRTUALIZATION_NONE] = "none",
611 [VIRTUALIZATION_KVM] = "kvm",
612 [VIRTUALIZATION_QEMU] = "qemu",
613 [VIRTUALIZATION_BOCHS] = "bochs",
614 [VIRTUALIZATION_XEN] = "xen",
615 [VIRTUALIZATION_UML] = "uml",
616 [VIRTUALIZATION_VMWARE] = "vmware",
617 [VIRTUALIZATION_ORACLE] = "oracle",
618 [VIRTUALIZATION_MICROSOFT] = "microsoft",
619 [VIRTUALIZATION_ZVM] = "zvm",
620 [VIRTUALIZATION_PARALLELS] = "parallels",
621 [VIRTUALIZATION_BHYVE] = "bhyve",
622 [VIRTUALIZATION_QNX] = "qnx",
623 [VIRTUALIZATION_VM_OTHER] = "vm-other",
625 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
626 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
627 [VIRTUALIZATION_LXC] = "lxc",
628 [VIRTUALIZATION_OPENVZ] = "openvz",
629 [VIRTUALIZATION_DOCKER] = "docker",
630 [VIRTUALIZATION_RKT] = "rkt",
631 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
634 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);