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 static int detect_vm_cpuid(void) {
44 /* CPUID is an x86 specific interface. */
45 #if defined(__i386__) || defined(__x86_64__)
50 } cpuid_vendor_table
[] = {
51 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
52 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
53 { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU
},
54 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
55 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
56 /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
57 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
58 /* https://wiki.freebsd.org/bhyve */
59 { "bhyve bhyve ", VIRTUALIZATION_BHYVE
},
60 { "QNXQVMBSQG", VIRTUALIZATION_QNX
},
63 uint32_t eax
, ebx
, ecx
, edx
;
66 /* http://lwn.net/Articles/301888/ */
68 /* First detect whether there is a hypervisor */
69 if (__get_cpuid(1, &eax
, &ebx
, &ecx
, &edx
) == 0)
70 return VIRTUALIZATION_NONE
;
72 hypervisor
= !!(ecx
& 0x80000000U
);
81 /* There is a hypervisor, see what it is */
82 __cpuid(0x40000000U
, eax
, ebx
, ecx
, edx
);
88 log_debug("Virtualization found, CPUID=%s", sig
.text
);
90 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
91 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
92 return cpuid_vendor_table
[j
].id
;
94 return VIRTUALIZATION_VM_OTHER
;
97 log_debug("No virtualization found in CPUID");
99 return VIRTUALIZATION_NONE
;
102 static int detect_vm_device_tree(void) {
103 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
104 _cleanup_free_
char *hvtype
= NULL
;
107 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
109 _cleanup_closedir_
DIR *dir
= NULL
;
112 dir
= opendir("/proc/device-tree");
114 if (errno
== ENOENT
) {
115 log_debug_errno(errno
, "/proc/device-tree: %m");
116 return VIRTUALIZATION_NONE
;
121 FOREACH_DIRENT(dent
, dir
, return -errno
)
122 if (strstr(dent
->d_name
, "fw-cfg")) {
123 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent
->d_name
);
124 return VIRTUALIZATION_QEMU
;
127 log_debug("No virtualization found in /proc/device-tree/*");
128 return VIRTUALIZATION_NONE
;
132 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype
);
133 if (streq(hvtype
, "linux,kvm"))
134 return VIRTUALIZATION_KVM
;
135 else if (strstr(hvtype
, "xen"))
136 return VIRTUALIZATION_XEN
;
138 return VIRTUALIZATION_VM_OTHER
;
140 log_debug("This platform does not support /proc/device-tree");
141 return VIRTUALIZATION_NONE
;
145 static int detect_vm_dmi(void) {
146 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
148 static const char *const dmi_vendors
[] = {
149 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
150 "/sys/class/dmi/id/sys_vendor",
151 "/sys/class/dmi/id/board_vendor",
152 "/sys/class/dmi/id/bios_vendor"
155 static const struct {
158 } dmi_vendor_table
[] = {
159 { "KVM", VIRTUALIZATION_KVM
},
160 { "QEMU", VIRTUALIZATION_QEMU
},
161 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
162 { "VMware", VIRTUALIZATION_VMWARE
},
163 { "VMW", VIRTUALIZATION_VMWARE
},
164 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
165 { "Xen", VIRTUALIZATION_XEN
},
166 { "Bochs", VIRTUALIZATION_BOCHS
},
167 { "Parallels", VIRTUALIZATION_PARALLELS
},
168 /* https://wiki.freebsd.org/bhyve */
169 { "BHYVE", VIRTUALIZATION_BHYVE
},
174 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
175 _cleanup_free_
char *s
= NULL
;
178 r
= read_one_line_file(dmi_vendors
[i
], &s
);
186 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
187 if (startswith(s
, dmi_vendor_table
[j
].vendor
)) {
188 log_debug("Virtualization %s found in DMI (%s)", s
, dmi_vendors
[i
]);
189 return dmi_vendor_table
[j
].id
;
194 log_debug("No virtualization found in DMI");
196 return VIRTUALIZATION_NONE
;
199 static int detect_vm_xen(void) {
201 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
202 The presence of /proc/xen indicates some form of a Xen domain */
203 if (access("/proc/xen", F_OK
) < 0) {
204 log_debug("Virtualization XEN not found, /proc/xen does not exist");
205 return VIRTUALIZATION_NONE
;
208 log_debug("Virtualization XEN found (/proc/xen exists)");
209 return VIRTUALIZATION_XEN
;
212 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
213 #define PATH_FEATURES "/sys/hypervisor/properties/features"
214 /* Returns -errno, or 0 for domU, or 1 for dom0 */
215 static int detect_vm_xen_dom0(void) {
216 _cleanup_free_
char *domcap
= NULL
;
220 r
= read_one_line_file(PATH_FEATURES
, &domcap
);
221 if (r
< 0 && r
!= -ENOENT
)
224 unsigned long features
;
226 /* Here, we need to use sscanf() instead of safe_atoul()
227 * as the string lacks the leading "0x". */
228 r
= sscanf(domcap
, "%lx", &features
);
230 r
= !!(features
& (1U << XENFEAT_dom0
));
231 log_debug("Virtualization XEN, found %s with value %08lx, "
232 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
233 PATH_FEATURES
, features
, r
? "" : " not");
236 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
237 PATH_FEATURES
, domcap
);
240 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
242 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
249 while ((cap
= strsep(&i
, ",")))
250 if (streq(cap
, "control_d"))
253 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
257 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
261 static int detect_vm_hypervisor(void) {
262 _cleanup_free_
char *hvtype
= NULL
;
265 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
267 return VIRTUALIZATION_NONE
;
271 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype
);
273 if (streq(hvtype
, "xen"))
274 return VIRTUALIZATION_XEN
;
276 return VIRTUALIZATION_VM_OTHER
;
279 static int detect_vm_uml(void) {
280 _cleanup_free_
char *cpuinfo_contents
= NULL
;
283 /* Detect User-Mode Linux by reading /proc/cpuinfo */
284 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
286 log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
287 return VIRTUALIZATION_NONE
;
292 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n")) {
293 log_debug("UML virtualization found in /proc/cpuinfo");
294 return VIRTUALIZATION_UML
;
297 log_debug("UML virtualization not found in /proc/cpuinfo.");
298 return VIRTUALIZATION_NONE
;
301 static int detect_vm_zvm(void) {
303 #if defined(__s390__)
304 _cleanup_free_
char *t
= NULL
;
307 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
309 return VIRTUALIZATION_NONE
;
313 log_debug("Virtualization %s found in /proc/sysinfo", t
);
314 if (streq(t
, "z/VM"))
315 return VIRTUALIZATION_ZVM
;
317 return VIRTUALIZATION_KVM
;
319 log_debug("This platform does not support /proc/sysinfo");
320 return VIRTUALIZATION_NONE
;
324 /* Returns a short identifier for the various VM implementations */
325 int detect_vm(void) {
326 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
330 if (cached_found
>= 0)
333 /* We have to use the correct order here:
335 * -> First try to detect Oracle Virtualbox, even if it uses KVM.
336 * -> Second try to detect from cpuid, this will report KVM for
337 * whatever software is used even if info in dmi is overwritten.
338 * -> Third try to detect from dmi. */
340 dmi
= detect_vm_dmi();
341 if (dmi
== VIRTUALIZATION_ORACLE
) {
346 r
= detect_vm_cpuid();
349 if (r
!= VIRTUALIZATION_NONE
) {
350 if (r
== VIRTUALIZATION_VM_OTHER
)
359 if (r
!= VIRTUALIZATION_NONE
) {
360 if (r
== VIRTUALIZATION_VM_OTHER
)
366 /* x86 xen will most likely be detected by cpuid. If not (most likely
367 * because we're not an x86 guest), then we should try the /proc/xen
368 * directory next. If that's not found, then we check for the high-level
369 * hypervisor sysfs file.
375 if (r
!= VIRTUALIZATION_NONE
) {
376 if (r
== VIRTUALIZATION_VM_OTHER
)
382 r
= detect_vm_hypervisor();
385 if (r
!= VIRTUALIZATION_NONE
) {
386 if (r
== VIRTUALIZATION_VM_OTHER
)
392 r
= detect_vm_device_tree();
395 if (r
!= VIRTUALIZATION_NONE
) {
396 if (r
== VIRTUALIZATION_VM_OTHER
)
405 if (r
!= VIRTUALIZATION_NONE
) {
406 if (r
== VIRTUALIZATION_VM_OTHER
)
417 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
418 * In order to detect the Dom0 as not virtualization we need to
420 if (r
== VIRTUALIZATION_XEN
) {
421 int ret
= detect_vm_xen_dom0();
425 r
= VIRTUALIZATION_NONE
;
426 } else if (r
== VIRTUALIZATION_NONE
&& other
)
427 r
= VIRTUALIZATION_VM_OTHER
;
430 log_debug("Found VM virtualization %s", virtualization_to_string(r
));
434 int detect_container(void) {
436 static const struct {
440 { "lxc", VIRTUALIZATION_LXC
},
441 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
442 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
443 { "docker", VIRTUALIZATION_DOCKER
},
444 { "rkt", VIRTUALIZATION_RKT
},
447 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
448 _cleanup_free_
char *m
= NULL
;
449 const char *e
= NULL
;
453 if (cached_found
>= 0)
456 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
457 if (access("/proc/vz", F_OK
) >= 0 &&
458 access("/proc/bc", F_OK
) < 0) {
459 r
= VIRTUALIZATION_OPENVZ
;
463 if (getpid_cached() == 1) {
464 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
466 e
= getenv("container");
468 r
= VIRTUALIZATION_NONE
;
475 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
476 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
477 r
= read_one_line_file("/run/systemd/container", &m
);
483 return log_debug_errno(r
, "Failed to read /run/systemd/container: %m");
485 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
486 r
= getenv_for_pid(1, "container", &m
);
491 if (r
< 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
492 log_debug_errno(r
, "Failed to read $container of PID 1, ignoring: %m");
494 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
495 * there is not 1, we know we are in a PID namespace. and hence a container. */
496 r
= read_one_line_file("/proc/1/sched", &m
);
504 if (!startswith(t
, "(1,")) {
505 r
= VIRTUALIZATION_CONTAINER_OTHER
;
508 } else if (r
!= -ENOENT
)
511 /* If that didn't work, give up, assume no container manager. */
512 r
= VIRTUALIZATION_NONE
;
516 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
517 if (streq(e
, value_table
[j
].value
)) {
518 r
= value_table
[j
].id
;
522 r
= VIRTUALIZATION_CONTAINER_OTHER
;
525 log_debug("Found container virtualization %s.", virtualization_to_string(r
));
530 int detect_virtualization(void) {
533 r
= detect_container();
540 static int userns_has_mapping(const char *name
) {
541 _cleanup_fclose_
FILE *f
= NULL
;
542 _cleanup_free_
char *buf
= NULL
;
543 size_t n_allocated
= 0;
548 f
= fopen(name
, "re");
550 log_debug_errno(errno
, "Failed to open %s: %m", name
);
551 return errno
== ENOENT
? false : -errno
;
554 n
= getline(&buf
, &n_allocated
, f
);
557 log_debug("%s is empty, we're in an uninitialized user namespace", name
);
561 return log_debug_errno(errno
, "Failed to read %s: %m", name
);
564 r
= sscanf(buf
, "%"PRIu32
" %"PRIu32
" %"PRIu32
, &a
, &b
, &c
);
566 return log_debug_errno(errno
, "Failed to parse %s: %m", name
);
568 if (a
== 0 && b
== 0 && c
== UINT32_MAX
) {
569 /* The kernel calls mappings_overlap() and does not allow overlaps */
570 log_debug("%s has a full 1:1 mapping", name
);
574 /* Anything else implies that we are in a user namespace */
575 log_debug("Mapping found in %s, we're in a user namespace", name
);
579 int running_in_userns(void) {
580 _cleanup_free_
char *line
= NULL
;
583 r
= userns_has_mapping("/proc/self/uid_map");
587 r
= userns_has_mapping("/proc/self/gid_map");
591 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
592 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
593 * also does not exist. We cannot distinguish those two cases, so assume that
594 * we're running on a stripped-down recent kernel, rather than on an old one,
595 * and if the file is not found, return false.
597 r
= read_one_line_file("/proc/self/setgroups", &line
);
599 log_debug_errno(r
, "/proc/self/setgroups: %m");
600 return r
== -ENOENT
? false : r
;
604 r
= streq(line
, "deny");
605 /* See user_namespaces(7) for a description of this "setgroups" contents. */
606 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line
, r
? "in" : "not in");
610 int running_in_chroot(void) {
613 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
616 r
= files_same("/proc/1/root", "/", 0);
623 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
624 [VIRTUALIZATION_NONE
] = "none",
625 [VIRTUALIZATION_KVM
] = "kvm",
626 [VIRTUALIZATION_QEMU
] = "qemu",
627 [VIRTUALIZATION_BOCHS
] = "bochs",
628 [VIRTUALIZATION_XEN
] = "xen",
629 [VIRTUALIZATION_UML
] = "uml",
630 [VIRTUALIZATION_VMWARE
] = "vmware",
631 [VIRTUALIZATION_ORACLE
] = "oracle",
632 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
633 [VIRTUALIZATION_ZVM
] = "zvm",
634 [VIRTUALIZATION_PARALLELS
] = "parallels",
635 [VIRTUALIZATION_BHYVE
] = "bhyve",
636 [VIRTUALIZATION_QNX
] = "qnx",
637 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
639 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
640 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
641 [VIRTUALIZATION_LXC
] = "lxc",
642 [VIRTUALIZATION_OPENVZ
] = "openvz",
643 [VIRTUALIZATION_DOCKER
] = "docker",
644 [VIRTUALIZATION_RKT
] = "rkt",
645 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
648 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);