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 static int detect_vm_cpuid(void) {
41 /* CPUID is an x86 specific interface. */
42 #if defined(__i386__) || defined(__x86_64__)
47 } cpuid_vendor_table
[] = {
48 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
49 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
50 { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU
},
51 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
52 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
53 /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
54 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
55 /* https://wiki.freebsd.org/bhyve */
56 { "bhyve bhyve ", VIRTUALIZATION_BHYVE
},
62 /* http://lwn.net/Articles/301888/ */
64 #if defined (__i386__)
67 #elif defined (__amd64__)
72 /* First detect whether there is a hypervisor */
74 __asm__
__volatile__ (
75 /* ebx/rbx is being used for PIC! */
76 " push %%"REG_b
" \n\t"
80 : "=a" (eax
), "=c" (ecx
)
84 hypervisor
= !!(ecx
& 0x80000000U
);
93 /* There is a hypervisor, see what it is */
95 __asm__
__volatile__ (
96 /* ebx/rbx is being used for PIC! */
97 " push %%"REG_b
" \n\t"
100 " pop %%"REG_b
" \n\t"
102 : "=a" (eax
), "=r" (sig
.sig32
[0]), "=c" (sig
.sig32
[1]), "=d" (sig
.sig32
[2])
106 log_debug("Virtualization found, CPUID=%s", sig
.text
);
108 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
109 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
110 return cpuid_vendor_table
[j
].id
;
112 return VIRTUALIZATION_VM_OTHER
;
115 log_debug("No virtualization found in CPUID");
117 return VIRTUALIZATION_NONE
;
120 static int detect_vm_device_tree(void) {
121 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
122 _cleanup_free_
char *hvtype
= NULL
;
125 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
127 _cleanup_closedir_
DIR *dir
= NULL
;
130 dir
= opendir("/proc/device-tree");
132 if (errno
== ENOENT
) {
133 log_debug_errno(errno
, "/proc/device-tree: %m");
134 return VIRTUALIZATION_NONE
;
139 FOREACH_DIRENT(dent
, dir
, return -errno
)
140 if (strstr(dent
->d_name
, "fw-cfg")) {
141 log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent
->d_name
);
142 return VIRTUALIZATION_QEMU
;
145 log_debug("No virtualization found in /proc/device-tree/*");
146 return VIRTUALIZATION_NONE
;
150 log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype
);
151 if (streq(hvtype
, "linux,kvm"))
152 return VIRTUALIZATION_KVM
;
153 else if (strstr(hvtype
, "xen"))
154 return VIRTUALIZATION_XEN
;
156 return VIRTUALIZATION_VM_OTHER
;
158 log_debug("This platform does not support /proc/device-tree");
159 return VIRTUALIZATION_NONE
;
163 static int detect_vm_dmi(void) {
164 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
166 static const char *const dmi_vendors
[] = {
167 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
168 "/sys/class/dmi/id/sys_vendor",
169 "/sys/class/dmi/id/board_vendor",
170 "/sys/class/dmi/id/bios_vendor"
173 static const struct {
176 } dmi_vendor_table
[] = {
177 { "KVM", VIRTUALIZATION_KVM
},
178 { "QEMU", VIRTUALIZATION_QEMU
},
179 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
180 { "VMware", VIRTUALIZATION_VMWARE
},
181 { "VMW", VIRTUALIZATION_VMWARE
},
182 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
183 { "Xen", VIRTUALIZATION_XEN
},
184 { "Bochs", VIRTUALIZATION_BOCHS
},
185 { "Parallels", VIRTUALIZATION_PARALLELS
},
186 /* https://wiki.freebsd.org/bhyve */
187 { "BHYVE", VIRTUALIZATION_BHYVE
},
192 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
193 _cleanup_free_
char *s
= NULL
;
196 r
= read_one_line_file(dmi_vendors
[i
], &s
);
204 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
205 if (startswith(s
, dmi_vendor_table
[j
].vendor
)) {
206 log_debug("Virtualization %s found in DMI (%s)", s
, dmi_vendors
[i
]);
207 return dmi_vendor_table
[j
].id
;
212 log_debug("No virtualization found in DMI");
214 return VIRTUALIZATION_NONE
;
217 static int detect_vm_xen(void) {
219 /* Check for Dom0 will be executed later in detect_vm_xen_dom0
220 The presence of /proc/xen indicates some form of a Xen domain */
221 if (access("/proc/xen", F_OK
) < 0) {
222 log_debug("Virtualization XEN not found, /proc/xen does not exist");
223 return VIRTUALIZATION_NONE
;
226 log_debug("Virtualization XEN found (/proc/xen exists)");
227 return VIRTUALIZATION_XEN
;
230 #define XENFEAT_dom0 11 /* xen/include/public/features.h */
231 #define PATH_FEATURES "/sys/hypervisor/properties/features"
232 /* Returns -errno, or 0 for domU, or 1 for dom0 */
233 static int detect_vm_xen_dom0(void) {
234 _cleanup_free_
char *domcap
= NULL
;
238 r
= read_one_line_file(PATH_FEATURES
, &domcap
);
239 if (r
< 0 && r
!= -ENOENT
)
242 unsigned long features
;
244 r
= safe_atolu(domcap
, &features
);
246 r
= !!(features
& (1U << XENFEAT_dom0
));
247 log_debug("Virtualization XEN, found %s with value %08lx, "
248 "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
249 PATH_FEATURES
, features
, r
? "" : " not");
252 log_debug("Virtualization XEN, found %s, unhandled content '%s'",
253 PATH_FEATURES
, domcap
);
256 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
258 log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
265 while ((cap
= strsep(&i
, ",")))
266 if (streq(cap
, "control_d"))
269 log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)");
273 log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
277 static int detect_vm_hypervisor(void) {
278 _cleanup_free_
char *hvtype
= NULL
;
281 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
283 return VIRTUALIZATION_NONE
;
287 log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype
);
289 if (streq(hvtype
, "xen"))
290 return VIRTUALIZATION_XEN
;
292 return VIRTUALIZATION_VM_OTHER
;
295 static int detect_vm_uml(void) {
296 _cleanup_free_
char *cpuinfo_contents
= NULL
;
299 /* Detect User-Mode Linux by reading /proc/cpuinfo */
300 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
304 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n")) {
305 log_debug("UML virtualization found in /proc/cpuinfo");
306 return VIRTUALIZATION_UML
;
309 log_debug("No virtualization found in /proc/cpuinfo.");
310 return VIRTUALIZATION_NONE
;
313 static int detect_vm_zvm(void) {
315 #if defined(__s390__)
316 _cleanup_free_
char *t
= NULL
;
319 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
321 return VIRTUALIZATION_NONE
;
325 log_debug("Virtualization %s found in /proc/sysinfo", t
);
326 if (streq(t
, "z/VM"))
327 return VIRTUALIZATION_ZVM
;
329 return VIRTUALIZATION_KVM
;
331 log_debug("This platform does not support /proc/sysinfo");
332 return VIRTUALIZATION_NONE
;
336 /* Returns a short identifier for the various VM implementations */
337 int detect_vm(void) {
338 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
342 if (cached_found
>= 0)
345 /* We have to use the correct order here:
347 * -> First try to detect Oracle Virtualbox, even if it uses KVM.
348 * -> Second try to detect from cpuid, this will report KVM for
349 * whatever software is used even if info in dmi is overwritten.
350 * -> Third try to detect from dmi. */
352 dmi
= detect_vm_dmi();
353 if (dmi
== VIRTUALIZATION_ORACLE
) {
358 r
= detect_vm_cpuid();
361 if (r
!= VIRTUALIZATION_NONE
) {
362 if (r
== VIRTUALIZATION_VM_OTHER
)
371 if (r
!= VIRTUALIZATION_NONE
) {
372 if (r
== VIRTUALIZATION_VM_OTHER
)
378 /* x86 xen will most likely be detected by cpuid. If not (most likely
379 * because we're not an x86 guest), then we should try the /proc/xen
380 * directory next. If that's not found, then we check for the high-level
381 * hypervisor sysfs file.
387 if (r
!= VIRTUALIZATION_NONE
) {
388 if (r
== VIRTUALIZATION_VM_OTHER
)
394 r
= detect_vm_hypervisor();
397 if (r
!= VIRTUALIZATION_NONE
) {
398 if (r
== VIRTUALIZATION_VM_OTHER
)
404 r
= detect_vm_device_tree();
407 if (r
!= VIRTUALIZATION_NONE
) {
408 if (r
== VIRTUALIZATION_VM_OTHER
)
417 if (r
!= VIRTUALIZATION_NONE
) {
418 if (r
== VIRTUALIZATION_VM_OTHER
)
429 /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
430 * In order to detect the Dom0 as not virtualization we need to
432 if (r
== VIRTUALIZATION_XEN
) {
433 int ret
= detect_vm_xen_dom0();
437 r
= VIRTUALIZATION_NONE
;
438 } else if (r
== VIRTUALIZATION_NONE
&& other
)
439 r
= VIRTUALIZATION_VM_OTHER
;
442 log_debug("Found VM virtualization %s", virtualization_to_string(r
));
446 int detect_container(void) {
448 static const struct {
452 { "lxc", VIRTUALIZATION_LXC
},
453 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
454 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
455 { "docker", VIRTUALIZATION_DOCKER
},
456 { "rkt", VIRTUALIZATION_RKT
},
459 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
460 _cleanup_free_
char *m
= NULL
;
461 const char *e
= NULL
;
465 if (cached_found
>= 0)
468 /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
469 if (access("/proc/vz", F_OK
) >= 0 &&
470 access("/proc/bc", F_OK
) < 0) {
471 r
= VIRTUALIZATION_OPENVZ
;
475 if (getpid_cached() == 1) {
476 /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
478 e
= getenv("container");
480 r
= VIRTUALIZATION_NONE
;
487 /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
488 * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
489 r
= read_one_line_file("/run/systemd/container", &m
);
495 return log_debug_errno(r
, "Failed to read /run/systemd/container: %m");
497 /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
498 r
= getenv_for_pid(1, "container", &m
);
503 if (r
< 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
504 log_debug_errno(r
, "Failed to read $container of PID 1, ignoring: %m");
506 /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
507 * there is not 1, we know we are in a PID namespace. and hence a container. */
508 r
= read_one_line_file("/proc/1/sched", &m
);
516 if (!startswith(t
, "(1,")) {
517 r
= VIRTUALIZATION_CONTAINER_OTHER
;
520 } else if (r
!= -ENOENT
)
523 /* If that didn't work, give up, assume no container manager. */
524 r
= VIRTUALIZATION_NONE
;
528 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
529 if (streq(e
, value_table
[j
].value
)) {
530 r
= value_table
[j
].id
;
534 r
= VIRTUALIZATION_CONTAINER_OTHER
;
537 log_debug("Found container virtualization %s.", virtualization_to_string(r
));
542 int detect_virtualization(void) {
545 r
= detect_container();
552 static int userns_has_mapping(const char *name
) {
553 _cleanup_fclose_
FILE *f
= NULL
;
554 _cleanup_free_
char *buf
= NULL
;
555 size_t n_allocated
= 0;
560 f
= fopen(name
, "re");
562 log_debug_errno(errno
, "Failed to open %s: %m", name
);
563 return errno
== ENOENT
? false : -errno
;
566 n
= getline(&buf
, &n_allocated
, f
);
569 log_debug("%s is empty, we're in an uninitialized user namespace", name
);
573 return log_debug_errno(errno
, "Failed to read %s: %m", name
);
576 r
= sscanf(buf
, "%"PRIu32
" %"PRIu32
" %"PRIu32
, &a
, &b
, &c
);
578 return log_debug_errno(errno
, "Failed to parse %s: %m", name
);
580 if (a
== 0 && b
== 0 && c
== UINT32_MAX
) {
581 /* The kernel calls mappings_overlap() and does not allow overlaps */
582 log_debug("%s has a full 1:1 mapping", name
);
586 /* Anything else implies that we are in a user namespace */
587 log_debug("Mapping found in %s, we're in a user namespace", name
);
591 int running_in_userns(void) {
592 _cleanup_free_
char *line
= NULL
;
595 r
= userns_has_mapping("/proc/self/uid_map");
599 r
= userns_has_mapping("/proc/self/gid_map");
603 /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
604 * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
605 * also does not exist. We cannot distinguish those two cases, so assume that
606 * we're running on a stripped-down recent kernel, rather than on an old one,
607 * and if the file is not found, return false.
609 r
= read_one_line_file("/proc/self/setgroups", &line
);
611 log_debug_errno(r
, "/proc/self/setgroups: %m");
612 return r
== -ENOENT
? false : r
;
616 r
= streq(line
, "deny");
617 /* See user_namespaces(7) for a description of this "setgroups" contents. */
618 log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line
, r
? "in" : "not in");
622 int running_in_chroot(void) {
625 if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
628 ret
= files_same("/proc/1/root", "/", 0);
635 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
636 [VIRTUALIZATION_NONE
] = "none",
637 [VIRTUALIZATION_KVM
] = "kvm",
638 [VIRTUALIZATION_QEMU
] = "qemu",
639 [VIRTUALIZATION_BOCHS
] = "bochs",
640 [VIRTUALIZATION_XEN
] = "xen",
641 [VIRTUALIZATION_UML
] = "uml",
642 [VIRTUALIZATION_VMWARE
] = "vmware",
643 [VIRTUALIZATION_ORACLE
] = "oracle",
644 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
645 [VIRTUALIZATION_ZVM
] = "zvm",
646 [VIRTUALIZATION_PARALLELS
] = "parallels",
647 [VIRTUALIZATION_BHYVE
] = "bhyve",
648 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
650 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
651 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
652 [VIRTUALIZATION_LXC
] = "lxc",
653 [VIRTUALIZATION_OPENVZ
] = "openvz",
654 [VIRTUALIZATION_DOCKER
] = "docker",
655 [VIRTUALIZATION_RKT
] = "rkt",
656 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
659 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);