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 if (__get_cpuid(0x40000000U
, &eax
, &ebx
, &ecx
, &edx
) == 0)
82 return VIRTUALIZATION_NONE
;
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 r
= safe_atolu(domcap
, &features
);
228 r
= !!(features
& (1U << XENFEAT_dom0
));
229 log_debug("Virtualization XEN, found %s with value %08lx, "
230 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
231 PATH_FEATURES
, features
, r
? "" : " not");
234 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
235 PATH_FEATURES
, domcap
);
238 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
240 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
247 while ((cap
= strsep(&i
, ",")))
248 if (streq(cap
, "control_d"))
251 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
255 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
259 static int detect_vm_hypervisor(void) {
260 _cleanup_free_
char *hvtype
= NULL
;
263 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
265 return VIRTUALIZATION_NONE
;
269 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype
);
271 if (streq(hvtype
, "xen"))
272 return VIRTUALIZATION_XEN
;
274 return VIRTUALIZATION_VM_OTHER
;
277 static int detect_vm_uml(void) {
278 _cleanup_free_
char *cpuinfo_contents
= NULL
;
281 /* Detect User-Mode Linux by reading /proc/cpuinfo */
282 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
286 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n")) {
287 log_debug("UML virtualization found in /proc/cpuinfo");
288 return VIRTUALIZATION_UML
;
291 log_debug("No virtualization found in /proc/cpuinfo.");
292 return VIRTUALIZATION_NONE
;
295 static int detect_vm_zvm(void) {
297 #if defined(__s390__)
298 _cleanup_free_
char *t
= NULL
;
301 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
303 return VIRTUALIZATION_NONE
;
307 log_debug("Virtualization %s found in /proc/sysinfo", t
);
308 if (streq(t
, "z/VM"))
309 return VIRTUALIZATION_ZVM
;
311 return VIRTUALIZATION_KVM
;
313 log_debug("This platform does not support /proc/sysinfo");
314 return VIRTUALIZATION_NONE
;
318 /* Returns a short identifier for the various VM implementations */
319 int detect_vm(void) {
320 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
324 if (cached_found
>= 0)
327 /* We have to use the correct order here:
329 * -> First try to detect Oracle Virtualbox, even if it uses KVM.
330 * -> Second try to detect from cpuid, this will report KVM for
331 * whatever software is used even if info in dmi is overwritten.
332 * -> Third try to detect from dmi. */
334 dmi
= detect_vm_dmi();
335 if (dmi
== VIRTUALIZATION_ORACLE
) {
340 r
= detect_vm_cpuid();
343 if (r
!= VIRTUALIZATION_NONE
) {
344 if (r
== VIRTUALIZATION_VM_OTHER
)
353 if (r
!= VIRTUALIZATION_NONE
) {
354 if (r
== VIRTUALIZATION_VM_OTHER
)
360 /* x86 xen will most likely be detected by cpuid. If not (most likely
361 * because we're not an x86 guest), then we should try the /proc/xen
362 * directory next. If that's not found, then we check for the high-level
363 * hypervisor sysfs file.
369 if (r
!= VIRTUALIZATION_NONE
) {
370 if (r
== VIRTUALIZATION_VM_OTHER
)
376 r
= detect_vm_hypervisor();
379 if (r
!= VIRTUALIZATION_NONE
) {
380 if (r
== VIRTUALIZATION_VM_OTHER
)
386 r
= detect_vm_device_tree();
389 if (r
!= VIRTUALIZATION_NONE
) {
390 if (r
== VIRTUALIZATION_VM_OTHER
)
399 if (r
!= VIRTUALIZATION_NONE
) {
400 if (r
== VIRTUALIZATION_VM_OTHER
)
411 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
412 * In order to detect the Dom0 as not virtualization we need to
414 if (r
== VIRTUALIZATION_XEN
) {
415 int ret
= detect_vm_xen_dom0();
419 r
= VIRTUALIZATION_NONE
;
420 } else if (r
== VIRTUALIZATION_NONE
&& other
)
421 r
= VIRTUALIZATION_VM_OTHER
;
424 log_debug("Found VM virtualization %s", virtualization_to_string(r
));
428 int detect_container(void) {
430 static const struct {
434 { "lxc", VIRTUALIZATION_LXC
},
435 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
436 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
437 { "docker", VIRTUALIZATION_DOCKER
},
438 { "rkt", VIRTUALIZATION_RKT
},
441 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
442 _cleanup_free_
char *m
= NULL
;
443 const char *e
= NULL
;
447 if (cached_found
>= 0)
450 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
451 if (access("/proc/vz", F_OK
) >= 0 &&
452 access("/proc/bc", F_OK
) < 0) {
453 r
= VIRTUALIZATION_OPENVZ
;
457 if (getpid_cached() == 1) {
458 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
460 e
= getenv("container");
462 r
= VIRTUALIZATION_NONE
;
469 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
470 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
471 r
= read_one_line_file("/run/systemd/container", &m
);
477 return log_debug_errno(r
, "Failed to read /run/systemd/container: %m");
479 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
480 r
= getenv_for_pid(1, "container", &m
);
485 if (r
< 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
486 log_debug_errno(r
, "Failed to read $container of PID 1, ignoring: %m");
488 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
489 * there is not 1, we know we are in a PID namespace. and hence a container. */
490 r
= read_one_line_file("/proc/1/sched", &m
);
498 if (!startswith(t
, "(1,")) {
499 r
= VIRTUALIZATION_CONTAINER_OTHER
;
502 } else if (r
!= -ENOENT
)
505 /* If that didn't work, give up, assume no container manager. */
506 r
= VIRTUALIZATION_NONE
;
510 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
511 if (streq(e
, value_table
[j
].value
)) {
512 r
= value_table
[j
].id
;
516 r
= VIRTUALIZATION_CONTAINER_OTHER
;
519 log_debug("Found container virtualization %s.", virtualization_to_string(r
));
524 int detect_virtualization(void) {
527 r
= detect_container();
534 static int userns_has_mapping(const char *name
) {
535 _cleanup_fclose_
FILE *f
= NULL
;
536 _cleanup_free_
char *buf
= NULL
;
537 size_t n_allocated
= 0;
542 f
= fopen(name
, "re");
544 log_debug_errno(errno
, "Failed to open %s: %m", name
);
545 return errno
== ENOENT
? false : -errno
;
548 n
= getline(&buf
, &n_allocated
, f
);
551 log_debug("%s is empty, we're in an uninitialized user namespace", name
);
555 return log_debug_errno(errno
, "Failed to read %s: %m", name
);
558 r
= sscanf(buf
, "%"PRIu32
" %"PRIu32
" %"PRIu32
, &a
, &b
, &c
);
560 return log_debug_errno(errno
, "Failed to parse %s: %m", name
);
562 if (a
== 0 && b
== 0 && c
== UINT32_MAX
) {
563 /* The kernel calls mappings_overlap() and does not allow overlaps */
564 log_debug("%s has a full 1:1 mapping", name
);
568 /* Anything else implies that we are in a user namespace */
569 log_debug("Mapping found in %s, we're in a user namespace", name
);
573 int running_in_userns(void) {
574 _cleanup_free_
char *line
= NULL
;
577 r
= userns_has_mapping("/proc/self/uid_map");
581 r
= userns_has_mapping("/proc/self/gid_map");
585 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
586 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
587 * also does not exist. We cannot distinguish those two cases, so assume that
588 * we're running on a stripped-down recent kernel, rather than on an old one,
589 * and if the file is not found, return false.
591 r
= read_one_line_file("/proc/self/setgroups", &line
);
593 log_debug_errno(r
, "/proc/self/setgroups: %m");
594 return r
== -ENOENT
? false : r
;
598 r
= streq(line
, "deny");
599 /* See user_namespaces(7) for a description of this "setgroups" contents. */
600 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line
, r
? "in" : "not in");
604 int running_in_chroot(void) {
607 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
610 ret
= files_same("/proc/1/root", "/", 0);
617 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
618 [VIRTUALIZATION_NONE
] = "none",
619 [VIRTUALIZATION_KVM
] = "kvm",
620 [VIRTUALIZATION_QEMU
] = "qemu",
621 [VIRTUALIZATION_BOCHS
] = "bochs",
622 [VIRTUALIZATION_XEN
] = "xen",
623 [VIRTUALIZATION_UML
] = "uml",
624 [VIRTUALIZATION_VMWARE
] = "vmware",
625 [VIRTUALIZATION_ORACLE
] = "oracle",
626 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
627 [VIRTUALIZATION_ZVM
] = "zvm",
628 [VIRTUALIZATION_PARALLELS
] = "parallels",
629 [VIRTUALIZATION_BHYVE
] = "bhyve",
630 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
632 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
633 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
634 [VIRTUALIZATION_LXC
] = "lxc",
635 [VIRTUALIZATION_OPENVZ
] = "openvz",
636 [VIRTUALIZATION_DOCKER
] = "docker",
637 [VIRTUALIZATION_RKT
] = "rkt",
638 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
641 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);