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
},
62 uint32_t eax
, ebx
, ecx
, edx
;
65 /* http://lwn.net/Articles/301888/ */
67 /* First detect whether there is a hypervisor */
68 if (__get_cpuid(1, &eax
, &ebx
, &ecx
, &edx
) == 0)
69 return VIRTUALIZATION_NONE
;
71 hypervisor
= !!(ecx
& 0x80000000U
);
80 /* There is a hypervisor, see what it is */
81 __cpuid(0x40000000U
, eax
, ebx
, ecx
, edx
);
87 log_debug("Virtualization found, CPUID=%s", sig
.text
);
89 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
90 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
91 return cpuid_vendor_table
[j
].id
;
93 return VIRTUALIZATION_VM_OTHER
;
96 log_debug("No virtualization found in CPUID");
98 return VIRTUALIZATION_NONE
;
101 static int detect_vm_device_tree(void) {
102 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
103 _cleanup_free_
char *hvtype
= NULL
;
106 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
108 _cleanup_closedir_
DIR *dir
= NULL
;
111 dir
= opendir("/proc/device-tree");
113 if (errno
== ENOENT
) {
114 log_debug_errno(errno
, "/proc/device-tree: %m");
115 return VIRTUALIZATION_NONE
;
120 FOREACH_DIRENT(dent
, dir
, return -errno
)
121 if (strstr(dent
->d_name
, "fw-cfg")) {
122 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent
->d_name
);
123 return VIRTUALIZATION_QEMU
;
126 log_debug("No virtualization found in /proc/device-tree/*");
127 return VIRTUALIZATION_NONE
;
131 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype
);
132 if (streq(hvtype
, "linux,kvm"))
133 return VIRTUALIZATION_KVM
;
134 else if (strstr(hvtype
, "xen"))
135 return VIRTUALIZATION_XEN
;
137 return VIRTUALIZATION_VM_OTHER
;
139 log_debug("This platform does not support /proc/device-tree");
140 return VIRTUALIZATION_NONE
;
144 static int detect_vm_dmi(void) {
145 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
147 static const char *const dmi_vendors
[] = {
148 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
149 "/sys/class/dmi/id/sys_vendor",
150 "/sys/class/dmi/id/board_vendor",
151 "/sys/class/dmi/id/bios_vendor"
154 static const struct {
157 } dmi_vendor_table
[] = {
158 { "KVM", VIRTUALIZATION_KVM
},
159 { "QEMU", VIRTUALIZATION_QEMU
},
160 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
161 { "VMware", VIRTUALIZATION_VMWARE
},
162 { "VMW", VIRTUALIZATION_VMWARE
},
163 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
164 { "Xen", VIRTUALIZATION_XEN
},
165 { "Bochs", VIRTUALIZATION_BOCHS
},
166 { "Parallels", VIRTUALIZATION_PARALLELS
},
167 /* https://wiki.freebsd.org/bhyve */
168 { "BHYVE", VIRTUALIZATION_BHYVE
},
173 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
174 _cleanup_free_
char *s
= NULL
;
177 r
= read_one_line_file(dmi_vendors
[i
], &s
);
185 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
186 if (startswith(s
, dmi_vendor_table
[j
].vendor
)) {
187 log_debug("Virtualization %s found in DMI (%s)", s
, dmi_vendors
[i
]);
188 return dmi_vendor_table
[j
].id
;
193 log_debug("No virtualization found in DMI");
195 return VIRTUALIZATION_NONE
;
198 static int detect_vm_xen(void) {
200 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
201 The presence of /proc/xen indicates some form of a Xen domain */
202 if (access("/proc/xen", F_OK
) < 0) {
203 log_debug("Virtualization XEN not found, /proc/xen does not exist");
204 return VIRTUALIZATION_NONE
;
207 log_debug("Virtualization XEN found (/proc/xen exists)");
208 return VIRTUALIZATION_XEN
;
211 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
212 #define PATH_FEATURES "/sys/hypervisor/properties/features"
213 /* Returns -errno, or 0 for domU, or 1 for dom0 */
214 static int detect_vm_xen_dom0(void) {
215 _cleanup_free_
char *domcap
= NULL
;
219 r
= read_one_line_file(PATH_FEATURES
, &domcap
);
220 if (r
< 0 && r
!= -ENOENT
)
223 unsigned long features
;
225 /* Here, we need to use sscanf() instead of safe_atoul()
226 * as the string lacks the leading "0x". */
227 r
= sscanf(domcap
, "%lx", &features
);
229 r
= !!(features
& (1U << XENFEAT_dom0
));
230 log_debug("Virtualization XEN, found %s with value %08lx, "
231 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
232 PATH_FEATURES
, features
, r
? "" : " not");
235 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
236 PATH_FEATURES
, domcap
);
239 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
241 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
248 while ((cap
= strsep(&i
, ",")))
249 if (streq(cap
, "control_d"))
252 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
256 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
260 static int detect_vm_hypervisor(void) {
261 _cleanup_free_
char *hvtype
= NULL
;
264 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
266 return VIRTUALIZATION_NONE
;
270 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype
);
272 if (streq(hvtype
, "xen"))
273 return VIRTUALIZATION_XEN
;
275 return VIRTUALIZATION_VM_OTHER
;
278 static int detect_vm_uml(void) {
279 _cleanup_free_
char *cpuinfo_contents
= NULL
;
282 /* Detect User-Mode Linux by reading /proc/cpuinfo */
283 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
287 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n")) {
288 log_debug("UML virtualization found in /proc/cpuinfo");
289 return VIRTUALIZATION_UML
;
292 log_debug("No virtualization found in /proc/cpuinfo.");
293 return VIRTUALIZATION_NONE
;
296 static int detect_vm_zvm(void) {
298 #if defined(__s390__)
299 _cleanup_free_
char *t
= NULL
;
302 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
304 return VIRTUALIZATION_NONE
;
308 log_debug("Virtualization %s found in /proc/sysinfo", t
);
309 if (streq(t
, "z/VM"))
310 return VIRTUALIZATION_ZVM
;
312 return VIRTUALIZATION_KVM
;
314 log_debug("This platform does not support /proc/sysinfo");
315 return VIRTUALIZATION_NONE
;
319 /* Returns a short identifier for the various VM implementations */
320 int detect_vm(void) {
321 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
325 if (cached_found
>= 0)
328 /* We have to use the correct order here:
330 * -> First try to detect Oracle Virtualbox, even if it uses KVM.
331 * -> Second try to detect from cpuid, this will report KVM for
332 * whatever software is used even if info in dmi is overwritten.
333 * -> Third try to detect from dmi. */
335 dmi
= detect_vm_dmi();
336 if (dmi
== VIRTUALIZATION_ORACLE
) {
341 r
= detect_vm_cpuid();
344 if (r
!= VIRTUALIZATION_NONE
) {
345 if (r
== VIRTUALIZATION_VM_OTHER
)
354 if (r
!= VIRTUALIZATION_NONE
) {
355 if (r
== VIRTUALIZATION_VM_OTHER
)
361 /* x86 xen will most likely be detected by cpuid. If not (most likely
362 * because we're not an x86 guest), then we should try the /proc/xen
363 * directory next. If that's not found, then we check for the high-level
364 * hypervisor sysfs file.
370 if (r
!= VIRTUALIZATION_NONE
) {
371 if (r
== VIRTUALIZATION_VM_OTHER
)
377 r
= detect_vm_hypervisor();
380 if (r
!= VIRTUALIZATION_NONE
) {
381 if (r
== VIRTUALIZATION_VM_OTHER
)
387 r
= detect_vm_device_tree();
390 if (r
!= VIRTUALIZATION_NONE
) {
391 if (r
== VIRTUALIZATION_VM_OTHER
)
400 if (r
!= VIRTUALIZATION_NONE
) {
401 if (r
== VIRTUALIZATION_VM_OTHER
)
412 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
413 * In order to detect the Dom0 as not virtualization we need to
415 if (r
== VIRTUALIZATION_XEN
) {
416 int ret
= detect_vm_xen_dom0();
420 r
= VIRTUALIZATION_NONE
;
421 } else if (r
== VIRTUALIZATION_NONE
&& other
)
422 r
= VIRTUALIZATION_VM_OTHER
;
425 log_debug("Found VM virtualization %s", virtualization_to_string(r
));
429 int detect_container(void) {
431 static const struct {
435 { "lxc", VIRTUALIZATION_LXC
},
436 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
437 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
438 { "docker", VIRTUALIZATION_DOCKER
},
439 { "rkt", VIRTUALIZATION_RKT
},
442 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
443 _cleanup_free_
char *m
= NULL
;
444 const char *e
= NULL
;
448 if (cached_found
>= 0)
451 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
452 if (access("/proc/vz", F_OK
) >= 0 &&
453 access("/proc/bc", F_OK
) < 0) {
454 r
= VIRTUALIZATION_OPENVZ
;
458 if (getpid_cached() == 1) {
459 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
461 e
= getenv("container");
463 r
= VIRTUALIZATION_NONE
;
470 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
471 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
472 r
= read_one_line_file("/run/systemd/container", &m
);
478 return log_debug_errno(r
, "Failed to read /run/systemd/container: %m");
480 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
481 r
= getenv_for_pid(1, "container", &m
);
486 if (r
< 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
487 log_debug_errno(r
, "Failed to read $container of PID 1, ignoring: %m");
489 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
490 * there is not 1, we know we are in a PID namespace. and hence a container. */
491 r
= read_one_line_file("/proc/1/sched", &m
);
499 if (!startswith(t
, "(1,")) {
500 r
= VIRTUALIZATION_CONTAINER_OTHER
;
503 } else if (r
!= -ENOENT
)
506 /* If that didn't work, give up, assume no container manager. */
507 r
= VIRTUALIZATION_NONE
;
511 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
512 if (streq(e
, value_table
[j
].value
)) {
513 r
= value_table
[j
].id
;
517 r
= VIRTUALIZATION_CONTAINER_OTHER
;
520 log_debug("Found container virtualization %s.", virtualization_to_string(r
));
525 int detect_virtualization(void) {
528 r
= detect_container();
535 static int userns_has_mapping(const char *name
) {
536 _cleanup_fclose_
FILE *f
= NULL
;
537 _cleanup_free_
char *buf
= NULL
;
538 size_t n_allocated
= 0;
543 f
= fopen(name
, "re");
545 log_debug_errno(errno
, "Failed to open %s: %m", name
);
546 return errno
== ENOENT
? false : -errno
;
549 n
= getline(&buf
, &n_allocated
, f
);
552 log_debug("%s is empty, we're in an uninitialized user namespace", name
);
556 return log_debug_errno(errno
, "Failed to read %s: %m", name
);
559 r
= sscanf(buf
, "%"PRIu32
" %"PRIu32
" %"PRIu32
, &a
, &b
, &c
);
561 return log_debug_errno(errno
, "Failed to parse %s: %m", name
);
563 if (a
== 0 && b
== 0 && c
== UINT32_MAX
) {
564 /* The kernel calls mappings_overlap() and does not allow overlaps */
565 log_debug("%s has a full 1:1 mapping", name
);
569 /* Anything else implies that we are in a user namespace */
570 log_debug("Mapping found in %s, we're in a user namespace", name
);
574 int running_in_userns(void) {
575 _cleanup_free_
char *line
= NULL
;
578 r
= userns_has_mapping("/proc/self/uid_map");
582 r
= userns_has_mapping("/proc/self/gid_map");
586 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
587 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
588 * also does not exist. We cannot distinguish those two cases, so assume that
589 * we're running on a stripped-down recent kernel, rather than on an old one,
590 * and if the file is not found, return false.
592 r
= read_one_line_file("/proc/self/setgroups", &line
);
594 log_debug_errno(r
, "/proc/self/setgroups: %m");
595 return r
== -ENOENT
? false : r
;
599 r
= streq(line
, "deny");
600 /* See user_namespaces(7) for a description of this "setgroups" contents. */
601 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line
, r
? "in" : "not in");
605 int running_in_chroot(void) {
608 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
611 ret
= files_same("/proc/1/root", "/", 0);
618 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
619 [VIRTUALIZATION_NONE
] = "none",
620 [VIRTUALIZATION_KVM
] = "kvm",
621 [VIRTUALIZATION_QEMU
] = "qemu",
622 [VIRTUALIZATION_BOCHS
] = "bochs",
623 [VIRTUALIZATION_XEN
] = "xen",
624 [VIRTUALIZATION_UML
] = "uml",
625 [VIRTUALIZATION_VMWARE
] = "vmware",
626 [VIRTUALIZATION_ORACLE
] = "oracle",
627 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
628 [VIRTUALIZATION_ZVM
] = "zvm",
629 [VIRTUALIZATION_PARALLELS
] = "parallels",
630 [VIRTUALIZATION_BHYVE
] = "bhyve",
631 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
633 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
634 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
635 [VIRTUALIZATION_LXC
] = "lxc",
636 [VIRTUALIZATION_OPENVZ
] = "openvz",
637 [VIRTUALIZATION_DOCKER
] = "docker",
638 [VIRTUALIZATION_RKT
] = "rkt",
639 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
642 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);