1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "alloc-util.h"
27 #include "dirent-util.h"
30 #include "process-util.h"
31 #include "stat-util.h"
32 #include "string-table.h"
33 #include "string-util.h"
37 #if 0 /// UNNEEDED by elogind
38 static int detect_vm_cpuid(void) {
40 /* CPUID is an x86 specific interface. */
41 #if defined(__i386__) || defined(__x86_64__)
46 } cpuid_vendor_table[] = {
47 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
48 { "KVMKVMKVM", VIRTUALIZATION_KVM },
49 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
50 { "VMwareVMware", VIRTUALIZATION_VMWARE },
51 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
52 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
58 /* http://lwn.net/Articles/301888/ */
60 #if defined (__i386__)
63 #elif defined (__amd64__)
68 /* First detect whether there is a hypervisor */
70 __asm__ __volatile__ (
71 /* ebx/rbx is being used for PIC! */
72 " push %%"REG_b" \n\t"
76 : "=a" (eax), "=c" (ecx)
80 hypervisor = !!(ecx & 0x80000000U);
89 /* There is a hypervisor, see what it is */
91 __asm__ __volatile__ (
92 /* ebx/rbx is being used for PIC! */
93 " push %%"REG_b" \n\t"
98 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
102 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
103 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
104 return cpuid_vendor_table[j].id;
106 return VIRTUALIZATION_VM_OTHER;
110 return VIRTUALIZATION_NONE;
113 static int detect_vm_device_tree(void) {
114 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
115 _cleanup_free_ char *hvtype = NULL;
118 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
120 _cleanup_closedir_ DIR *dir = NULL;
123 dir = opendir("/proc/device-tree");
126 return VIRTUALIZATION_NONE;
130 FOREACH_DIRENT(dent, dir, return -errno)
131 if (strstr(dent->d_name, "fw-cfg"))
132 return VIRTUALIZATION_QEMU;
134 return VIRTUALIZATION_NONE;
138 if (streq(hvtype, "linux,kvm"))
139 return VIRTUALIZATION_KVM;
140 else if (strstr(hvtype, "xen"))
141 return VIRTUALIZATION_XEN;
143 return VIRTUALIZATION_VM_OTHER;
145 return VIRTUALIZATION_NONE;
149 static int detect_vm_dmi(void) {
150 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
152 static const char *const dmi_vendors[] = {
153 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
154 "/sys/class/dmi/id/sys_vendor",
155 "/sys/class/dmi/id/board_vendor",
156 "/sys/class/dmi/id/bios_vendor"
159 static const struct {
162 } dmi_vendor_table[] = {
163 { "KVM", VIRTUALIZATION_KVM },
164 { "QEMU", VIRTUALIZATION_QEMU },
165 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
166 { "VMware", VIRTUALIZATION_VMWARE },
167 { "VMW", VIRTUALIZATION_VMWARE },
168 { "innotek GmbH", VIRTUALIZATION_ORACLE },
169 { "Xen", VIRTUALIZATION_XEN },
170 { "Bochs", VIRTUALIZATION_BOCHS },
171 { "Parallels", VIRTUALIZATION_PARALLELS },
176 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
177 _cleanup_free_ char *s = NULL;
180 r = read_one_line_file(dmi_vendors[i], &s);
188 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
189 if (startswith(s, dmi_vendor_table[j].vendor))
190 return dmi_vendor_table[j].id;
194 return VIRTUALIZATION_NONE;
197 static int detect_vm_xen(void) {
198 _cleanup_free_ char *domcap = NULL;
202 r = read_one_line_file("/proc/xen/capabilities", &domcap);
204 return VIRTUALIZATION_NONE;
207 while ((cap = strsep(&i, ",")))
208 if (streq(cap, "control_d"))
211 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
214 static int detect_vm_hypervisor(void) {
215 _cleanup_free_ char *hvtype = NULL;
218 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
220 return VIRTUALIZATION_NONE;
224 if (streq(hvtype, "xen"))
225 return VIRTUALIZATION_XEN;
227 return VIRTUALIZATION_VM_OTHER;
230 static int detect_vm_uml(void) {
231 _cleanup_free_ char *cpuinfo_contents = NULL;
234 /* Detect User-Mode Linux by reading /proc/cpuinfo */
235 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
238 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
239 return VIRTUALIZATION_UML;
241 return VIRTUALIZATION_NONE;
244 static int detect_vm_zvm(void) {
246 #if defined(__s390__)
247 _cleanup_free_ char *t = NULL;
250 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
252 return VIRTUALIZATION_NONE;
256 if (streq(t, "z/VM"))
257 return VIRTUALIZATION_ZVM;
259 return VIRTUALIZATION_KVM;
261 return VIRTUALIZATION_NONE;
265 /* Returns a short identifier for the various VM implementations */
266 int detect_vm(void) {
267 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
270 if (cached_found >= 0)
273 /* We have to use the correct order here:
274 * Some virtualization technologies do use KVM hypervisor but are
275 * expected to be detected as something else. So detect DMI first.
277 * An example is Virtualbox since version 5.0, which uses KVM backend.
278 * Detection via DMI works corretly, the CPU ID would find KVM
283 if (r != VIRTUALIZATION_NONE)
286 r = detect_vm_cpuid();
289 if (r != VIRTUALIZATION_NONE)
292 /* x86 xen will most likely be detected by cpuid. If not (most likely
293 * because we're not an x86 guest), then we should try the xen capabilities
294 * file next. If that's not found, then we check for the high-level
295 * hypervisor sysfs file:
297 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
302 if (r != VIRTUALIZATION_NONE)
305 r = detect_vm_hypervisor();
308 if (r != VIRTUALIZATION_NONE)
311 r = detect_vm_device_tree();
314 if (r != VIRTUALIZATION_NONE)
320 if (r != VIRTUALIZATION_NONE)
333 int detect_container(void) {
335 static const struct {
339 { "lxc", VIRTUALIZATION_LXC },
340 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
341 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
342 { "docker", VIRTUALIZATION_DOCKER },
343 { "rkt", VIRTUALIZATION_RKT },
346 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
347 _cleanup_free_ char *m = NULL;
348 const char *e = NULL;
352 if (cached_found >= 0)
355 /* /proc/vz exists in container and outside of the container,
356 * /proc/bc only outside of the container. */
357 if (access("/proc/vz", F_OK) >= 0 &&
358 access("/proc/bc", F_OK) < 0) {
359 r = VIRTUALIZATION_OPENVZ;
364 /* If we are PID 1 we can just check our own
365 * environment variable */
367 e = getenv("container");
369 r = VIRTUALIZATION_NONE;
374 /* Otherwise, PID 1 dropped this information into a
375 * file in /run. This is better than accessing
376 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
379 r = read_one_line_file("/run/systemd/container", &m);
382 /* Fallback for cases where PID 1 was not
383 * systemd (for example, cases where
384 * init=/bin/sh is used. */
386 r = getenv_for_pid(1, "container", &m);
389 /* If that didn't work, give up,
390 * assume no container manager.
392 * Note: This means we still cannot
393 * detect containers if init=/bin/sh
394 * is passed but privileges dropped,
395 * as /proc/1/environ is only readable
396 * with privileges. */
398 r = VIRTUALIZATION_NONE;
408 for (j = 0; j < ELEMENTSOF(value_table); j++)
409 if (streq(e, value_table[j].value)) {
410 r = value_table[j].id;
414 r = VIRTUALIZATION_CONTAINER_OTHER;
421 #if 0 /// UNNEEDED by elogind
422 int detect_virtualization(void) {
425 r = detect_container();
433 int running_in_chroot(void) {
436 ret = files_same("/proc/1/root", "/");
443 #if 0 /// UNNEEDED by elogind
444 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
445 [VIRTUALIZATION_NONE] = "none",
446 [VIRTUALIZATION_KVM] = "kvm",
447 [VIRTUALIZATION_QEMU] = "qemu",
448 [VIRTUALIZATION_BOCHS] = "bochs",
449 [VIRTUALIZATION_XEN] = "xen",
450 [VIRTUALIZATION_UML] = "uml",
451 [VIRTUALIZATION_VMWARE] = "vmware",
452 [VIRTUALIZATION_ORACLE] = "oracle",
453 [VIRTUALIZATION_MICROSOFT] = "microsoft",
454 [VIRTUALIZATION_ZVM] = "zvm",
455 [VIRTUALIZATION_PARALLELS] = "parallels",
456 [VIRTUALIZATION_VM_OTHER] = "vm-other",
458 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
459 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
460 [VIRTUALIZATION_LXC] = "lxc",
461 [VIRTUALIZATION_OPENVZ] = "openvz",
462 [VIRTUALIZATION_DOCKER] = "docker",
463 [VIRTUALIZATION_RKT] = "rkt",
464 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
467 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);