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/>.
27 #include "process-util.h"
31 static int detect_vm_cpuid(void) {
33 /* CPUID is an x86 specific interface. */
34 #if defined(__i386__) || defined(__x86_64__)
39 } cpuid_vendor_table[] = {
40 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
41 { "KVMKVMKVM", VIRTUALIZATION_KVM },
42 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
43 { "VMwareVMware", VIRTUALIZATION_VMWARE },
44 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
45 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
51 /* http://lwn.net/Articles/301888/ */
53 #if defined (__i386__)
56 #elif defined (__amd64__)
61 /* First detect whether there is a hypervisor */
63 __asm__ __volatile__ (
64 /* ebx/rbx is being used for PIC! */
65 " push %%"REG_b" \n\t"
69 : "=a" (eax), "=c" (ecx)
73 hypervisor = !!(ecx & 0x80000000U);
82 /* There is a hypervisor, see what it is */
84 __asm__ __volatile__ (
85 /* ebx/rbx is being used for PIC! */
86 " push %%"REG_b" \n\t"
91 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
95 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
96 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
97 return cpuid_vendor_table[j].id;
99 return VIRTUALIZATION_VM_OTHER;
103 return VIRTUALIZATION_NONE;
106 static int detect_vm_device_tree(void) {
107 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
108 _cleanup_free_ char *hvtype = NULL;
111 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
113 _cleanup_closedir_ DIR *dir = NULL;
116 dir = opendir("/proc/device-tree");
119 return VIRTUALIZATION_NONE;
123 FOREACH_DIRENT(dent, dir, return -errno)
124 if (strstr(dent->d_name, "fw-cfg"))
125 return VIRTUALIZATION_QEMU;
127 return VIRTUALIZATION_NONE;
131 if (streq(hvtype, "linux,kvm"))
132 return VIRTUALIZATION_KVM;
133 else if (strstr(hvtype, "xen"))
134 return VIRTUALIZATION_XEN;
136 return VIRTUALIZATION_VM_OTHER;
138 return VIRTUALIZATION_NONE;
142 static int detect_vm_dmi(void) {
143 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
145 static const char *const dmi_vendors[] = {
146 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
147 "/sys/class/dmi/id/sys_vendor",
148 "/sys/class/dmi/id/board_vendor",
149 "/sys/class/dmi/id/bios_vendor"
152 static const struct {
155 } dmi_vendor_table[] = {
156 { "KVM", VIRTUALIZATION_KVM },
157 { "QEMU", VIRTUALIZATION_QEMU },
158 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
159 { "VMware", VIRTUALIZATION_VMWARE },
160 { "VMW", VIRTUALIZATION_VMWARE },
161 { "innotek GmbH", VIRTUALIZATION_ORACLE },
162 { "Xen", VIRTUALIZATION_XEN },
163 { "Bochs", VIRTUALIZATION_BOCHS },
164 { "Parallels", VIRTUALIZATION_PARALLELS },
169 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
170 _cleanup_free_ char *s = NULL;
173 r = read_one_line_file(dmi_vendors[i], &s);
181 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
182 if (startswith(s, dmi_vendor_table[j].vendor))
183 return dmi_vendor_table[j].id;
187 return VIRTUALIZATION_NONE;
190 static int detect_vm_xen(void) {
191 _cleanup_free_ char *domcap = NULL;
195 r = read_one_line_file("/proc/xen/capabilities", &domcap);
197 return VIRTUALIZATION_NONE;
200 while ((cap = strsep(&i, ",")))
201 if (streq(cap, "control_d"))
204 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
207 static int detect_vm_hypervisor(void) {
208 _cleanup_free_ char *hvtype = NULL;
211 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
213 return VIRTUALIZATION_NONE;
217 if (streq(hvtype, "xen"))
218 return VIRTUALIZATION_XEN;
220 return VIRTUALIZATION_VM_OTHER;
223 static int detect_vm_uml(void) {
224 _cleanup_free_ char *cpuinfo_contents = NULL;
227 /* Detect User-Mode Linux by reading /proc/cpuinfo */
228 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
231 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
232 return VIRTUALIZATION_UML;
234 return VIRTUALIZATION_NONE;
237 static int detect_vm_zvm(void) {
239 #if defined(__s390__)
240 _cleanup_free_ char *t = NULL;
243 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
245 return VIRTUALIZATION_NONE;
249 if (streq(t, "z/VM"))
250 return VIRTUALIZATION_ZVM;
252 return VIRTUALIZATION_KVM;
254 return VIRTUALIZATION_NONE;
258 /* Returns a short identifier for the various VM implementations */
259 int detect_vm(void) {
260 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
263 if (cached_found >= 0)
266 r = detect_vm_cpuid();
269 if (r != VIRTUALIZATION_NONE)
275 if (r != VIRTUALIZATION_NONE)
278 /* x86 xen will most likely be detected by cpuid. If not (most likely
279 * because we're not an x86 guest), then we should try the xen capabilities
280 * file next. If that's not found, then we check for the high-level
281 * hypervisor sysfs file:
283 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
288 if (r != VIRTUALIZATION_NONE)
291 r = detect_vm_hypervisor();
294 if (r != VIRTUALIZATION_NONE)
297 r = detect_vm_device_tree();
300 if (r != VIRTUALIZATION_NONE)
306 if (r != VIRTUALIZATION_NONE)
318 int detect_container(void) {
320 static const struct {
324 { "lxc", VIRTUALIZATION_LXC },
325 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
326 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
327 { "docker", VIRTUALIZATION_DOCKER },
330 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
331 _cleanup_free_ char *m = NULL;
332 const char *e = NULL;
336 if (cached_found >= 0)
339 /* /proc/vz exists in container and outside of the container,
340 * /proc/bc only outside of the container. */
341 if (access("/proc/vz", F_OK) >= 0 &&
342 access("/proc/bc", F_OK) < 0) {
343 r = VIRTUALIZATION_OPENVZ;
348 /* If we are PID 1 we can just check our own
349 * environment variable */
351 e = getenv("container");
353 r = VIRTUALIZATION_NONE;
358 /* Otherwise, PID 1 dropped this information into a
359 * file in /run. This is better than accessing
360 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
363 r = read_one_line_file("/run/systemd/container", &m);
366 /* Fallback for cases where PID 1 was not
367 * systemd (for example, cases where
368 * init=/bin/sh is used. */
370 r = getenv_for_pid(1, "container", &m);
373 /* If that didn't work, give up,
374 * assume no container manager.
376 * Note: This means we still cannot
377 * detect containers if init=/bin/sh
378 * is passed but privileges dropped,
379 * as /proc/1/environ is only readable
380 * with privileges. */
382 r = VIRTUALIZATION_NONE;
392 for (j = 0; j < ELEMENTSOF(value_table); j++)
393 if (streq(e, value_table[j].value)) {
394 r = value_table[j].id;
398 r = VIRTUALIZATION_CONTAINER_OTHER;
405 /// UNNEEDED by elogind
407 int detect_virtualization(void) {
410 r = detect_container();
418 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
419 [VIRTUALIZATION_NONE] = "none",
420 [VIRTUALIZATION_KVM] = "kvm",
421 [VIRTUALIZATION_QEMU] = "qemu",
422 [VIRTUALIZATION_BOCHS] = "bochs",
423 [VIRTUALIZATION_XEN] = "xen",
424 [VIRTUALIZATION_UML] = "uml",
425 [VIRTUALIZATION_VMWARE] = "vmware",
426 [VIRTUALIZATION_ORACLE] = "oracle",
427 [VIRTUALIZATION_MICROSOFT] = "microsoft",
428 [VIRTUALIZATION_ZVM] = "zvm",
429 [VIRTUALIZATION_PARALLELS] = "parallels",
430 [VIRTUALIZATION_VM_OTHER] = "vm-other",
432 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
433 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
434 [VIRTUALIZATION_LXC] = "lxc",
435 [VIRTUALIZATION_OPENVZ] = "openvz",
436 [VIRTUALIZATION_DOCKER] = "docker",
437 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
440 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);