1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #if defined(__i386__) || defined(__x86_64__)
30 #include "alloc-util.h"
31 #include "dirent-util.h"
36 #include "process-util.h"
37 #include "stat-util.h"
38 #include "string-table.h"
39 #include "string-util.h"
42 #if 0 /// UNNEEDED by elogind
43 static int detect_vm_cpuid(void) {
45 /* CPUID is an x86 specific interface. */
46 #if defined(__i386__) || defined(__x86_64__)
51 } cpuid_vendor_table[] = {
52 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
53 { "KVMKVMKVM", VIRTUALIZATION_KVM },
54 { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU },
55 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
56 { "VMwareVMware", VIRTUALIZATION_VMWARE },
57 /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
58 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
59 /* https://wiki.freebsd.org/bhyve */
60 { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
61 { "QNXQVMBSQG", VIRTUALIZATION_QNX },
64 uint32_t eax, ebx, ecx, edx;
67 /* http://lwn.net/Articles/301888/ */
69 /* First detect whether there is a hypervisor */
70 if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
71 return VIRTUALIZATION_NONE;
73 hypervisor = !!(ecx & 0x80000000U);
82 /* There is a hypervisor, see what it is */
83 __cpuid(0x40000000U, eax, ebx, ecx, edx);
89 log_debug("Virtualization found, CPUID=%s", sig.text);
91 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
92 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
93 return cpuid_vendor_table[j].id;
95 return VIRTUALIZATION_VM_OTHER;
98 log_debug("No virtualization found in CPUID");
100 return VIRTUALIZATION_NONE;
103 static int detect_vm_device_tree(void) {
104 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
105 _cleanup_free_ char *hvtype = NULL;
108 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
110 _cleanup_closedir_ DIR *dir = NULL;
113 dir = opendir("/proc/device-tree");
115 if (errno == ENOENT) {
116 log_debug_errno(errno, "/proc/device-tree: %m");
117 return VIRTUALIZATION_NONE;
122 FOREACH_DIRENT(dent, dir, return -errno)
123 if (strstr(dent->d_name, "fw-cfg")) {
124 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name);
125 return VIRTUALIZATION_QEMU;
128 log_debug("No virtualization found in /proc/device-tree/*");
129 return VIRTUALIZATION_NONE;
133 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype);
134 if (streq(hvtype, "linux,kvm"))
135 return VIRTUALIZATION_KVM;
136 else if (strstr(hvtype, "xen"))
137 return VIRTUALIZATION_XEN;
139 return VIRTUALIZATION_VM_OTHER;
141 log_debug("This platform does not support /proc/device-tree");
142 return VIRTUALIZATION_NONE;
146 static int detect_vm_dmi(void) {
147 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
149 static const char *const dmi_vendors[] = {
150 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
151 "/sys/class/dmi/id/sys_vendor",
152 "/sys/class/dmi/id/board_vendor",
153 "/sys/class/dmi/id/bios_vendor"
156 static const struct {
159 } dmi_vendor_table[] = {
160 { "KVM", VIRTUALIZATION_KVM },
161 { "QEMU", VIRTUALIZATION_QEMU },
162 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
163 { "VMware", VIRTUALIZATION_VMWARE },
164 { "VMW", VIRTUALIZATION_VMWARE },
165 { "innotek GmbH", VIRTUALIZATION_ORACLE },
166 { "Xen", VIRTUALIZATION_XEN },
167 { "Bochs", VIRTUALIZATION_BOCHS },
168 { "Parallels", VIRTUALIZATION_PARALLELS },
169 /* https://wiki.freebsd.org/bhyve */
170 { "BHYVE", VIRTUALIZATION_BHYVE },
175 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
176 _cleanup_free_ char *s = NULL;
179 r = read_one_line_file(dmi_vendors[i], &s);
187 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
188 if (startswith(s, dmi_vendor_table[j].vendor)) {
189 log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]);
190 return dmi_vendor_table[j].id;
195 log_debug("No virtualization found in DMI");
197 return VIRTUALIZATION_NONE;
200 static int detect_vm_xen(void) {
202 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
203 The presence of /proc/xen indicates some form of a Xen domain */
204 if (access("/proc/xen", F_OK) < 0) {
205 log_debug("Virtualization XEN not found, /proc/xen does not exist");
206 return VIRTUALIZATION_NONE;
209 log_debug("Virtualization XEN found (/proc/xen exists)");
210 return VIRTUALIZATION_XEN;
213 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
214 #define PATH_FEATURES "/sys/hypervisor/properties/features"
215 /* Returns -errno, or 0 for domU, or 1 for dom0 */
216 static int detect_vm_xen_dom0(void) {
217 _cleanup_free_ char *domcap = NULL;
221 r = read_one_line_file(PATH_FEATURES, &domcap);
222 if (r < 0 && r != -ENOENT)
225 unsigned long features;
227 /* Here, we need to use sscanf() instead of safe_atoul()
228 * as the string lacks the leading "0x". */
229 r = sscanf(domcap, "%lx", &features);
231 r = !!(features & (1U << XENFEAT_dom0));
232 log_debug("Virtualization XEN, found %s with value %08lx, "
233 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
234 PATH_FEATURES, features, r ? "" : " not");
237 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
238 PATH_FEATURES, domcap);
241 r = read_one_line_file("/proc/xen/capabilities", &domcap);
243 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
250 while ((cap = strsep(&i, ",")))
251 if (streq(cap, "control_d"))
254 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
258 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
262 static int detect_vm_hypervisor(void) {
263 _cleanup_free_ char *hvtype = NULL;
266 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
268 return VIRTUALIZATION_NONE;
272 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype);
274 if (streq(hvtype, "xen"))
275 return VIRTUALIZATION_XEN;
277 return VIRTUALIZATION_VM_OTHER;
280 static int detect_vm_uml(void) {
281 _cleanup_free_ char *cpuinfo_contents = NULL;
284 /* Detect User-Mode Linux by reading /proc/cpuinfo */
285 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
287 log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
288 return VIRTUALIZATION_NONE;
293 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
294 log_debug("UML virtualization found in /proc/cpuinfo");
295 return VIRTUALIZATION_UML;
298 log_debug("UML virtualization not found in /proc/cpuinfo.");
299 return VIRTUALIZATION_NONE;
302 static int detect_vm_zvm(void) {
304 #if defined(__s390__)
305 _cleanup_free_ char *t = NULL;
308 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
310 return VIRTUALIZATION_NONE;
314 log_debug("Virtualization %s found in /proc/sysinfo", t);
315 if (streq(t, "z/VM"))
316 return VIRTUALIZATION_ZVM;
318 return VIRTUALIZATION_KVM;
320 log_debug("This platform does not support /proc/sysinfo");
321 return VIRTUALIZATION_NONE;
325 /* Returns a short identifier for the various VM implementations */
326 int detect_vm(void) {
327 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
331 if (cached_found >= 0)
334 /* We have to use the correct order here:
336 * → First, try to detect Oracle Virtualbox, even if it uses KVM, as well as Xen even if it cloaks as Microsoft
339 * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is
342 * → Third, try to detect from DMI. */
344 dmi = detect_vm_dmi();
345 if (IN_SET(dmi, VIRTUALIZATION_ORACLE, VIRTUALIZATION_XEN)) {
350 r = detect_vm_cpuid();
353 if (r != VIRTUALIZATION_NONE) {
354 if (r == VIRTUALIZATION_VM_OTHER)
363 if (r != VIRTUALIZATION_NONE) {
364 if (r == VIRTUALIZATION_VM_OTHER)
370 /* x86 xen will most likely be detected by cpuid. If not (most likely
371 * because we're not an x86 guest), then we should try the /proc/xen
372 * directory next. If that's not found, then we check for the high-level
373 * hypervisor sysfs file.
379 if (r != VIRTUALIZATION_NONE) {
380 if (r == VIRTUALIZATION_VM_OTHER)
386 r = detect_vm_hypervisor();
389 if (r != VIRTUALIZATION_NONE) {
390 if (r == VIRTUALIZATION_VM_OTHER)
396 r = detect_vm_device_tree();
399 if (r != VIRTUALIZATION_NONE) {
400 if (r == VIRTUALIZATION_VM_OTHER)
409 if (r != VIRTUALIZATION_NONE) {
410 if (r == VIRTUALIZATION_VM_OTHER)
421 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
422 * In order to detect the Dom0 as not virtualization we need to
424 if (r == VIRTUALIZATION_XEN) {
425 int ret = detect_vm_xen_dom0();
429 r = VIRTUALIZATION_NONE;
430 } else if (r == VIRTUALIZATION_NONE && other)
431 r = VIRTUALIZATION_VM_OTHER;
434 log_debug("Found VM virtualization %s", virtualization_to_string(r));
439 int detect_container(void) {
441 static const struct {
445 { "lxc", VIRTUALIZATION_LXC },
446 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
447 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
448 { "docker", VIRTUALIZATION_DOCKER },
449 { "rkt", VIRTUALIZATION_RKT },
452 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
453 _cleanup_free_ char *m = NULL;
454 const char *e = NULL;
458 if (cached_found >= 0)
461 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
462 if (access("/proc/vz", F_OK) >= 0 &&
463 access("/proc/bc", F_OK) < 0) {
464 r = VIRTUALIZATION_OPENVZ;
468 if (getpid_cached() == 1) {
469 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
471 e = getenv("container");
473 r = VIRTUALIZATION_NONE;
480 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
481 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
482 r = read_one_line_file("/run/systemd/container", &m);
488 return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
490 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
491 r = getenv_for_pid(1, "container", &m);
496 if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
497 log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
499 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
500 * there is not 1, we know we are in a PID namespace. and hence a container. */
501 r = read_one_line_file("/proc/1/sched", &m);
509 if (!startswith(t, "(1,")) {
510 r = VIRTUALIZATION_CONTAINER_OTHER;
513 } else if (r != -ENOENT)
516 /* If that didn't work, give up, assume no container manager. */
517 r = VIRTUALIZATION_NONE;
521 for (j = 0; j < ELEMENTSOF(value_table); j++)
522 if (streq(e, value_table[j].value)) {
523 r = value_table[j].id;
527 r = VIRTUALIZATION_CONTAINER_OTHER;
530 log_debug("Found container virtualization %s.", virtualization_to_string(r));
535 #if 0 /// UNNEEDED by elogind
536 int detect_virtualization(void) {
539 r = detect_container();
546 static int userns_has_mapping(const char *name) {
547 _cleanup_fclose_ FILE *f = NULL;
548 _cleanup_free_ char *buf = NULL;
549 size_t n_allocated = 0;
554 f = fopen(name, "re");
556 log_debug_errno(errno, "Failed to open %s: %m", name);
557 return errno == ENOENT ? false : -errno;
560 n = getline(&buf, &n_allocated, f);
563 log_debug("%s is empty, we're in an uninitialized user namespace", name);
567 return log_debug_errno(errno, "Failed to read %s: %m", name);
570 r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
572 return log_debug_errno(errno, "Failed to parse %s: %m", name);
574 if (a == 0 && b == 0 && c == UINT32_MAX) {
575 /* The kernel calls mappings_overlap() and does not allow overlaps */
576 log_debug("%s has a full 1:1 mapping", name);
580 /* Anything else implies that we are in a user namespace */
581 log_debug("Mapping found in %s, we're in a user namespace", name);
585 int running_in_userns(void) {
586 _cleanup_free_ char *line = NULL;
589 r = userns_has_mapping("/proc/self/uid_map");
593 r = userns_has_mapping("/proc/self/gid_map");
597 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
598 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
599 * also does not exist. We cannot distinguish those two cases, so assume that
600 * we're running on a stripped-down recent kernel, rather than on an old one,
601 * and if the file is not found, return false.
603 r = read_one_line_file("/proc/self/setgroups", &line);
605 log_debug_errno(r, "/proc/self/setgroups: %m");
606 return r == -ENOENT ? false : r;
610 r = streq(line, "deny");
611 /* See user_namespaces(7) for a description of this "setgroups" contents. */
612 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
617 int running_in_chroot(void) {
620 #if 0 /// elogind does not allow to ignore chroots, we are never init!
621 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
625 r = files_same("/proc/1/root", "/", 0);
632 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
633 [VIRTUALIZATION_NONE] = "none",
634 [VIRTUALIZATION_KVM] = "kvm",
635 [VIRTUALIZATION_QEMU] = "qemu",
636 [VIRTUALIZATION_BOCHS] = "bochs",
637 [VIRTUALIZATION_XEN] = "xen",
638 [VIRTUALIZATION_UML] = "uml",
639 [VIRTUALIZATION_VMWARE] = "vmware",
640 [VIRTUALIZATION_ORACLE] = "oracle",
641 [VIRTUALIZATION_MICROSOFT] = "microsoft",
642 [VIRTUALIZATION_ZVM] = "zvm",
643 [VIRTUALIZATION_PARALLELS] = "parallels",
644 [VIRTUALIZATION_BHYVE] = "bhyve",
645 [VIRTUALIZATION_QNX] = "qnx",
646 [VIRTUALIZATION_VM_OTHER] = "vm-other",
648 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
649 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
650 [VIRTUALIZATION_LXC] = "lxc",
651 [VIRTUALIZATION_OPENVZ] = "openvz",
652 [VIRTUALIZATION_DOCKER] = "docker",
653 [VIRTUALIZATION_RKT] = "rkt",
654 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
657 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);