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/>.
27 #include "alloc-util.h"
28 #include "dirent-util.h"
33 #include "process-util.h"
34 #include "stat-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
39 #if 0 /// UNNEEDED by elogind
40 static int detect_vm_cpuid(void) {
42 /* CPUID is an x86 specific interface. */
43 #if defined(__i386__) || defined(__x86_64__)
48 } cpuid_vendor_table[] = {
49 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
50 { "KVMKVMKVM", VIRTUALIZATION_KVM },
51 { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU },
52 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
53 { "VMwareVMware", VIRTUALIZATION_VMWARE },
54 /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
55 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
56 /* https://wiki.freebsd.org/bhyve */
57 { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
63 /* http://lwn.net/Articles/301888/ */
65 #if defined (__i386__)
68 #elif defined (__amd64__)
73 /* First detect whether there is a hypervisor */
75 __asm__ __volatile__ (
76 /* ebx/rbx is being used for PIC! */
77 " push %%"REG_b" \n\t"
81 : "=a" (eax), "=c" (ecx)
85 hypervisor = !!(ecx & 0x80000000U);
94 /* There is a hypervisor, see what it is */
96 __asm__ __volatile__ (
97 /* ebx/rbx is being used for PIC! */
98 " push %%"REG_b" \n\t"
100 " mov %%ebx, %1 \n\t"
101 " pop %%"REG_b" \n\t"
103 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
107 log_debug("Virtualization found, CPUID=%s", sig.text);
109 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
110 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
111 return cpuid_vendor_table[j].id;
113 return VIRTUALIZATION_VM_OTHER;
116 log_debug("No virtualization found in CPUID");
118 return VIRTUALIZATION_NONE;
121 static int detect_vm_device_tree(void) {
122 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
123 _cleanup_free_ char *hvtype = NULL;
126 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
128 _cleanup_closedir_ DIR *dir = NULL;
131 dir = opendir("/proc/device-tree");
133 if (errno == ENOENT) {
134 log_debug_errno(errno, "/proc/device-tree: %m");
135 return VIRTUALIZATION_NONE;
140 FOREACH_DIRENT(dent, dir, return -errno)
141 if (strstr(dent->d_name, "fw-cfg")) {
142 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name);
143 return VIRTUALIZATION_QEMU;
146 log_debug("No virtualization found in /proc/device-tree/*");
147 return VIRTUALIZATION_NONE;
151 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype);
152 if (streq(hvtype, "linux,kvm"))
153 return VIRTUALIZATION_KVM;
154 else if (strstr(hvtype, "xen"))
155 return VIRTUALIZATION_XEN;
157 return VIRTUALIZATION_VM_OTHER;
159 log_debug("This platform does not support /proc/device-tree");
160 return VIRTUALIZATION_NONE;
164 static int detect_vm_dmi(void) {
165 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
167 static const char *const dmi_vendors[] = {
168 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
169 "/sys/class/dmi/id/sys_vendor",
170 "/sys/class/dmi/id/board_vendor",
171 "/sys/class/dmi/id/bios_vendor"
174 static const struct {
177 } dmi_vendor_table[] = {
178 { "KVM", VIRTUALIZATION_KVM },
179 { "QEMU", VIRTUALIZATION_QEMU },
180 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
181 { "VMware", VIRTUALIZATION_VMWARE },
182 { "VMW", VIRTUALIZATION_VMWARE },
183 { "innotek GmbH", VIRTUALIZATION_ORACLE },
184 { "Xen", VIRTUALIZATION_XEN },
185 { "Bochs", VIRTUALIZATION_BOCHS },
186 { "Parallels", VIRTUALIZATION_PARALLELS },
187 /* https://wiki.freebsd.org/bhyve */
188 { "BHYVE", VIRTUALIZATION_BHYVE },
193 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
194 _cleanup_free_ char *s = NULL;
197 r = read_one_line_file(dmi_vendors[i], &s);
205 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
206 if (startswith(s, dmi_vendor_table[j].vendor)) {
207 log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]);
208 return dmi_vendor_table[j].id;
213 log_debug("No virtualization found in DMI");
215 return VIRTUALIZATION_NONE;
218 static int detect_vm_xen(void) {
220 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
221 The presence of /proc/xen indicates some form of a Xen domain */
222 if (access("/proc/xen", F_OK) < 0) {
223 log_debug("Virtualization XEN not found, /proc/xen does not exist");
224 return VIRTUALIZATION_NONE;
227 log_debug("Virtualization XEN found (/proc/xen exists)");
228 return VIRTUALIZATION_XEN;
231 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
232 #define PATH_FEATURES "/sys/hypervisor/properties/features"
233 /* Returns -errno, or 0 for domU, or 1 for dom0 */
234 static int detect_vm_xen_dom0(void) {
235 _cleanup_free_ char *domcap = NULL;
239 r = read_one_line_file(PATH_FEATURES, &domcap);
240 if (r < 0 && r != -ENOENT)
243 unsigned long features;
245 r = safe_atolu(domcap, &features);
247 r = !!(features & (1U << XENFEAT_dom0));
248 log_debug("Virtualization XEN, found %s with value %08lx, "
249 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
250 PATH_FEATURES, features, r ? "" : " not");
253 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
254 PATH_FEATURES, domcap);
257 r = read_one_line_file("/proc/xen/capabilities", &domcap);
259 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
266 while ((cap = strsep(&i, ",")))
267 if (streq(cap, "control_d"))
270 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
274 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
278 static int detect_vm_hypervisor(void) {
279 _cleanup_free_ char *hvtype = NULL;
282 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
284 return VIRTUALIZATION_NONE;
288 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype);
290 if (streq(hvtype, "xen"))
291 return VIRTUALIZATION_XEN;
293 return VIRTUALIZATION_VM_OTHER;
296 static int detect_vm_uml(void) {
297 _cleanup_free_ char *cpuinfo_contents = NULL;
300 /* Detect User-Mode Linux by reading /proc/cpuinfo */
301 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
305 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
306 log_debug("UML virtualization found in /proc/cpuinfo");
307 return VIRTUALIZATION_UML;
310 log_debug("No virtualization found in /proc/cpuinfo.");
311 return VIRTUALIZATION_NONE;
314 static int detect_vm_zvm(void) {
316 #if defined(__s390__)
317 _cleanup_free_ char *t = NULL;
320 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
322 return VIRTUALIZATION_NONE;
326 log_debug("Virtualization %s found in /proc/sysinfo", t);
327 if (streq(t, "z/VM"))
328 return VIRTUALIZATION_ZVM;
330 return VIRTUALIZATION_KVM;
332 log_debug("This platform does not support /proc/sysinfo");
333 return VIRTUALIZATION_NONE;
337 /* Returns a short identifier for the various VM implementations */
338 int detect_vm(void) {
339 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
343 if (cached_found >= 0)
346 /* We have to use the correct order here:
348 * -> First try to detect Oracle Virtualbox, even if it uses KVM.
349 * -> Second try to detect from cpuid, this will report KVM for
350 * whatever software is used even if info in dmi is overwritten.
351 * -> Third try to detect from dmi. */
353 dmi = detect_vm_dmi();
354 if (dmi == VIRTUALIZATION_ORACLE) {
359 r = detect_vm_cpuid();
362 if (r != VIRTUALIZATION_NONE) {
363 if (r == VIRTUALIZATION_VM_OTHER)
372 if (r != VIRTUALIZATION_NONE) {
373 if (r == VIRTUALIZATION_VM_OTHER)
379 /* x86 xen will most likely be detected by cpuid. If not (most likely
380 * because we're not an x86 guest), then we should try the /proc/xen
381 * directory next. If that's not found, then we check for the high-level
382 * hypervisor sysfs file.
388 if (r != VIRTUALIZATION_NONE) {
389 if (r == VIRTUALIZATION_VM_OTHER)
395 r = detect_vm_hypervisor();
398 if (r != VIRTUALIZATION_NONE) {
399 if (r == VIRTUALIZATION_VM_OTHER)
405 r = detect_vm_device_tree();
408 if (r != VIRTUALIZATION_NONE) {
409 if (r == VIRTUALIZATION_VM_OTHER)
418 if (r != VIRTUALIZATION_NONE) {
419 if (r == VIRTUALIZATION_VM_OTHER)
430 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
431 * In order to detect the Dom0 as not virtualization we need to
433 if (r == VIRTUALIZATION_XEN) {
434 int ret = detect_vm_xen_dom0();
438 r = VIRTUALIZATION_NONE;
439 } else if (r == VIRTUALIZATION_NONE && other)
440 r = VIRTUALIZATION_VM_OTHER;
443 log_debug("Found VM virtualization %s", virtualization_to_string(r));
448 int detect_container(void) {
450 static const struct {
454 { "lxc", VIRTUALIZATION_LXC },
455 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
456 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
457 { "docker", VIRTUALIZATION_DOCKER },
458 { "rkt", VIRTUALIZATION_RKT },
461 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
462 _cleanup_free_ char *m = NULL;
463 const char *e = NULL;
467 if (cached_found >= 0)
470 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
471 if (access("/proc/vz", F_OK) >= 0 &&
472 access("/proc/bc", F_OK) < 0) {
473 r = VIRTUALIZATION_OPENVZ;
477 if (getpid_cached() == 1) {
478 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
480 e = getenv("container");
482 r = VIRTUALIZATION_NONE;
489 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
490 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
491 r = read_one_line_file("/run/systemd/container", &m);
497 return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
499 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
500 r = getenv_for_pid(1, "container", &m);
505 if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
506 log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
508 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
509 * there is not 1, we know we are in a PID namespace. and hence a container. */
510 r = read_one_line_file("/proc/1/sched", &m);
518 if (!startswith(t, "(1,")) {
519 r = VIRTUALIZATION_CONTAINER_OTHER;
522 } else if (r != -ENOENT)
525 /* If that didn't work, give up, assume no container manager. */
526 r = VIRTUALIZATION_NONE;
530 for (j = 0; j < ELEMENTSOF(value_table); j++)
531 if (streq(e, value_table[j].value)) {
532 r = value_table[j].id;
536 r = VIRTUALIZATION_CONTAINER_OTHER;
539 log_debug("Found container virtualization %s.", virtualization_to_string(r));
544 #if 0 /// UNNEEDED by elogind
545 int detect_virtualization(void) {
548 r = detect_container();
555 static int userns_has_mapping(const char *name) {
556 _cleanup_fclose_ FILE *f = NULL;
557 _cleanup_free_ char *buf = NULL;
558 size_t n_allocated = 0;
563 f = fopen(name, "re");
565 log_debug_errno(errno, "Failed to open %s: %m", name);
566 return errno == ENOENT ? false : -errno;
569 n = getline(&buf, &n_allocated, f);
572 log_debug("%s is empty, we're in an uninitialized user namespace", name);
576 return log_debug_errno(errno, "Failed to read %s: %m", name);
579 r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
581 return log_debug_errno(errno, "Failed to parse %s: %m", name);
583 if (a == 0 && b == 0 && c == UINT32_MAX) {
584 /* The kernel calls mappings_overlap() and does not allow overlaps */
585 log_debug("%s has a full 1:1 mapping", name);
589 /* Anything else implies that we are in a user namespace */
590 log_debug("Mapping found in %s, we're in a user namespace", name);
594 int running_in_userns(void) {
595 _cleanup_free_ char *line = NULL;
598 r = userns_has_mapping("/proc/self/uid_map");
602 r = userns_has_mapping("/proc/self/gid_map");
606 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
607 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
608 * also does not exist. We cannot distinguish those two cases, so assume that
609 * we're running on a stripped-down recent kernel, rather than on an old one,
610 * and if the file is not found, return false.
612 r = read_one_line_file("/proc/self/setgroups", &line);
614 log_debug_errno(r, "/proc/self/setgroups: %m");
615 return r == -ENOENT ? false : r;
619 r = streq(line, "deny");
620 /* See user_namespaces(7) for a description of this "setgroups" contents. */
621 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
626 int running_in_chroot(void) {
629 #if 0 /// elogind does not allow to ignore chroots, we are never init!
630 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
634 ret = files_same("/proc/1/root", "/", 0);
641 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
642 [VIRTUALIZATION_NONE] = "none",
643 [VIRTUALIZATION_KVM] = "kvm",
644 [VIRTUALIZATION_QEMU] = "qemu",
645 [VIRTUALIZATION_BOCHS] = "bochs",
646 [VIRTUALIZATION_XEN] = "xen",
647 [VIRTUALIZATION_UML] = "uml",
648 [VIRTUALIZATION_VMWARE] = "vmware",
649 [VIRTUALIZATION_ORACLE] = "oracle",
650 [VIRTUALIZATION_MICROSOFT] = "microsoft",
651 [VIRTUALIZATION_ZVM] = "zvm",
652 [VIRTUALIZATION_PARALLELS] = "parallels",
653 [VIRTUALIZATION_BHYVE] = "bhyve",
654 [VIRTUALIZATION_VM_OTHER] = "vm-other",
656 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
657 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
658 [VIRTUALIZATION_LXC] = "lxc",
659 [VIRTUALIZATION_OPENVZ] = "openvz",
660 [VIRTUALIZATION_DOCKER] = "docker",
661 [VIRTUALIZATION_RKT] = "rkt",
662 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
665 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);