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/>.
28 #include "alloc-util.h"
30 #include "process-util.h"
32 #include "stat-util.h"
33 #include "string-table.h"
34 #include "string-util.h"
37 static int detect_vm_cpuid(void) {
39 /* CPUID is an x86 specific interface. */
40 #if defined(__i386__) || defined(__x86_64__)
45 } cpuid_vendor_table
[] = {
46 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
47 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
48 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
49 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
50 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
51 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
57 /* http://lwn.net/Articles/301888/ */
59 #if defined (__i386__)
62 #elif defined (__amd64__)
67 /* First detect whether there is a hypervisor */
69 __asm__
__volatile__ (
70 /* ebx/rbx is being used for PIC! */
71 " push %%"REG_b
" \n\t"
75 : "=a" (eax
), "=c" (ecx
)
79 hypervisor
= !!(ecx
& 0x80000000U
);
88 /* There is a hypervisor, see what it is */
90 __asm__
__volatile__ (
91 /* ebx/rbx is being used for PIC! */
92 " push %%"REG_b
" \n\t"
97 : "=a" (eax
), "=r" (sig
.sig32
[0]), "=c" (sig
.sig32
[1]), "=d" (sig
.sig32
[2])
101 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
102 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
103 return cpuid_vendor_table
[j
].id
;
105 return VIRTUALIZATION_VM_OTHER
;
109 return VIRTUALIZATION_NONE
;
112 static int detect_vm_device_tree(void) {
113 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
114 _cleanup_free_
char *hvtype
= NULL
;
117 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
119 _cleanup_closedir_
DIR *dir
= NULL
;
122 dir
= opendir("/proc/device-tree");
125 return VIRTUALIZATION_NONE
;
129 FOREACH_DIRENT(dent
, dir
, return -errno
)
130 if (strstr(dent
->d_name
, "fw-cfg"))
131 return VIRTUALIZATION_QEMU
;
133 return VIRTUALIZATION_NONE
;
137 if (streq(hvtype
, "linux,kvm"))
138 return VIRTUALIZATION_KVM
;
139 else if (strstr(hvtype
, "xen"))
140 return VIRTUALIZATION_XEN
;
142 return VIRTUALIZATION_VM_OTHER
;
144 return VIRTUALIZATION_NONE
;
148 static int detect_vm_dmi(void) {
149 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
151 static const char *const dmi_vendors
[] = {
152 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
153 "/sys/class/dmi/id/sys_vendor",
154 "/sys/class/dmi/id/board_vendor",
155 "/sys/class/dmi/id/bios_vendor"
158 static const struct {
161 } dmi_vendor_table
[] = {
162 { "KVM", VIRTUALIZATION_KVM
},
163 { "QEMU", VIRTUALIZATION_QEMU
},
164 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
165 { "VMware", VIRTUALIZATION_VMWARE
},
166 { "VMW", VIRTUALIZATION_VMWARE
},
167 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
168 { "Xen", VIRTUALIZATION_XEN
},
169 { "Bochs", VIRTUALIZATION_BOCHS
},
170 { "Parallels", VIRTUALIZATION_PARALLELS
},
175 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
176 _cleanup_free_
char *s
= NULL
;
179 r
= read_one_line_file(dmi_vendors
[i
], &s
);
187 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
188 if (startswith(s
, dmi_vendor_table
[j
].vendor
))
189 return dmi_vendor_table
[j
].id
;
193 return VIRTUALIZATION_NONE
;
196 static int detect_vm_xen(void) {
197 _cleanup_free_
char *domcap
= NULL
;
201 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
203 return VIRTUALIZATION_NONE
;
206 while ((cap
= strsep(&i
, ",")))
207 if (streq(cap
, "control_d"))
210 return cap
? VIRTUALIZATION_NONE
: VIRTUALIZATION_XEN
;
213 static int detect_vm_hypervisor(void) {
214 _cleanup_free_
char *hvtype
= NULL
;
217 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
219 return VIRTUALIZATION_NONE
;
223 if (streq(hvtype
, "xen"))
224 return VIRTUALIZATION_XEN
;
226 return VIRTUALIZATION_VM_OTHER
;
229 static int detect_vm_uml(void) {
230 _cleanup_free_
char *cpuinfo_contents
= NULL
;
233 /* Detect User-Mode Linux by reading /proc/cpuinfo */
234 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
237 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n"))
238 return VIRTUALIZATION_UML
;
240 return VIRTUALIZATION_NONE
;
243 static int detect_vm_zvm(void) {
245 #if defined(__s390__)
246 _cleanup_free_
char *t
= NULL
;
249 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
251 return VIRTUALIZATION_NONE
;
255 if (streq(t
, "z/VM"))
256 return VIRTUALIZATION_ZVM
;
258 return VIRTUALIZATION_KVM
;
260 return VIRTUALIZATION_NONE
;
264 /* Returns a short identifier for the various VM implementations */
265 int detect_vm(void) {
266 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
269 if (cached_found
>= 0)
272 /* We have to use the correct order here:
273 * Some virtualization technologies do use KVM hypervisor but are
274 * expected to be detected as something else. So detect DMI first.
276 * An example is Virtualbox since version 5.0, which uses KVM backend.
277 * Detection via DMI works corretly, the CPU ID would find KVM
282 if (r
!= VIRTUALIZATION_NONE
)
285 r
= detect_vm_cpuid();
288 if (r
!= VIRTUALIZATION_NONE
)
291 /* x86 xen will most likely be detected by cpuid. If not (most likely
292 * because we're not an x86 guest), then we should try the xen capabilities
293 * file next. If that's not found, then we check for the high-level
294 * hypervisor sysfs file:
296 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
301 if (r
!= VIRTUALIZATION_NONE
)
304 r
= detect_vm_hypervisor();
307 if (r
!= VIRTUALIZATION_NONE
)
310 r
= detect_vm_device_tree();
313 if (r
!= VIRTUALIZATION_NONE
)
319 if (r
!= VIRTUALIZATION_NONE
)
331 int detect_container(void) {
333 static const struct {
337 { "lxc", VIRTUALIZATION_LXC
},
338 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
339 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
340 { "docker", VIRTUALIZATION_DOCKER
},
341 { "rkt", VIRTUALIZATION_RKT
},
344 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
345 _cleanup_free_
char *m
= NULL
;
346 const char *e
= NULL
;
350 if (cached_found
>= 0)
353 /* /proc/vz exists in container and outside of the container,
354 * /proc/bc only outside of the container. */
355 if (access("/proc/vz", F_OK
) >= 0 &&
356 access("/proc/bc", F_OK
) < 0) {
357 r
= VIRTUALIZATION_OPENVZ
;
362 /* If we are PID 1 we can just check our own
363 * environment variable */
365 e
= getenv("container");
367 r
= VIRTUALIZATION_NONE
;
372 /* Otherwise, PID 1 dropped this information into a
373 * file in /run. This is better than accessing
374 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
377 r
= read_one_line_file("/run/systemd/container", &m
);
380 /* Fallback for cases where PID 1 was not
381 * systemd (for example, cases where
382 * init=/bin/sh is used. */
384 r
= getenv_for_pid(1, "container", &m
);
387 /* If that didn't work, give up,
388 * assume no container manager.
390 * Note: This means we still cannot
391 * detect containers if init=/bin/sh
392 * is passed but privileges dropped,
393 * as /proc/1/environ is only readable
394 * with privileges. */
396 r
= VIRTUALIZATION_NONE
;
406 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
407 if (streq(e
, value_table
[j
].value
)) {
408 r
= value_table
[j
].id
;
412 r
= VIRTUALIZATION_CONTAINER_OTHER
;
419 int detect_virtualization(void) {
422 r
= detect_container();
429 int running_in_chroot(void) {
432 ret
= files_same("/proc/1/root", "/");
439 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
440 [VIRTUALIZATION_NONE
] = "none",
441 [VIRTUALIZATION_KVM
] = "kvm",
442 [VIRTUALIZATION_QEMU
] = "qemu",
443 [VIRTUALIZATION_BOCHS
] = "bochs",
444 [VIRTUALIZATION_XEN
] = "xen",
445 [VIRTUALIZATION_UML
] = "uml",
446 [VIRTUALIZATION_VMWARE
] = "vmware",
447 [VIRTUALIZATION_ORACLE
] = "oracle",
448 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
449 [VIRTUALIZATION_ZVM
] = "zvm",
450 [VIRTUALIZATION_PARALLELS
] = "parallels",
451 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
453 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
454 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
455 [VIRTUALIZATION_LXC
] = "lxc",
456 [VIRTUALIZATION_OPENVZ
] = "openvz",
457 [VIRTUALIZATION_DOCKER
] = "docker",
458 [VIRTUALIZATION_RKT
] = "rkt",
459 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
462 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);