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"
28 #include "string-table.h"
29 #include "string-util.h"
33 static int detect_vm_cpuid(void) {
35 /* Both CPUID and DMI are x86 specific interfaces... */
36 #if defined(__i386__) || defined(__x86_64__)
41 } cpuid_vendor_table
[] = {
42 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
43 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
44 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
45 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
46 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
47 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
53 /* http://lwn.net/Articles/301888/ */
55 #if defined (__i386__)
58 #elif defined (__amd64__)
63 /* First detect whether there is a hypervisor */
65 __asm__
__volatile__ (
66 /* ebx/rbx is being used for PIC! */
67 " push %%"REG_b
" \n\t"
71 : "=a" (eax
), "=c" (ecx
)
75 hypervisor
= !!(ecx
& 0x80000000U
);
84 /* There is a hypervisor, see what it is */
86 __asm__
__volatile__ (
87 /* ebx/rbx is being used for PIC! */
88 " push %%"REG_b
" \n\t"
93 : "=a" (eax
), "=r" (sig
.sig32
[0]), "=c" (sig
.sig32
[1]), "=d" (sig
.sig32
[2])
97 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
98 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
99 return cpuid_vendor_table
[j
].id
;
101 return VIRTUALIZATION_VM_OTHER
;
105 return VIRTUALIZATION_NONE
;
108 static int detect_vm_device_tree(void) {
109 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
110 _cleanup_free_
char *hvtype
= NULL
;
113 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
115 _cleanup_closedir_
DIR *dir
= NULL
;
118 dir
= opendir("/proc/device-tree");
121 return VIRTUALIZATION_NONE
;
125 FOREACH_DIRENT(dent
, dir
, return -errno
)
126 if (strstr(dent
->d_name
, "fw-cfg"))
127 return VIRTUALIZATION_QEMU
;
129 return VIRTUALIZATION_NONE
;
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 return VIRTUALIZATION_NONE
;
144 static int detect_vm_dmi(void) {
146 /* Both CPUID and DMI are x86 specific interfaces... */
147 #if defined(__i386__) || defined(__x86_64__)
149 static const char *const dmi_vendors
[] = {
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 { "QEMU", VIRTUALIZATION_QEMU
},
160 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
161 { "VMware", VIRTUALIZATION_VMWARE
},
162 { "VMW", VIRTUALIZATION_VMWARE
},
163 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
164 { "Xen", VIRTUALIZATION_XEN
},
165 { "Bochs", VIRTUALIZATION_BOCHS
},
166 { "Parallels", VIRTUALIZATION_PARALLELS
},
171 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
172 _cleanup_free_
char *s
= NULL
;
175 r
= read_one_line_file(dmi_vendors
[i
], &s
);
183 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
184 if (startswith(s
, dmi_vendor_table
[j
].vendor
))
185 return dmi_vendor_table
[j
].id
;
189 return VIRTUALIZATION_NONE
;
192 static int detect_vm_xen(void) {
193 _cleanup_free_
char *domcap
= NULL
;
197 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
199 return VIRTUALIZATION_NONE
;
202 while ((cap
= strsep(&i
, ",")))
203 if (streq(cap
, "control_d"))
206 return cap
? VIRTUALIZATION_NONE
: VIRTUALIZATION_XEN
;
209 static int detect_vm_hypervisor(void) {
210 _cleanup_free_
char *hvtype
= NULL
;
213 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
215 return VIRTUALIZATION_NONE
;
219 if (streq(hvtype
, "xen"))
220 return VIRTUALIZATION_XEN
;
222 return VIRTUALIZATION_VM_OTHER
;
225 static int detect_vm_uml(void) {
226 _cleanup_free_
char *cpuinfo_contents
= NULL
;
229 /* Detect User-Mode Linux by reading /proc/cpuinfo */
230 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
233 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n"))
234 return VIRTUALIZATION_UML
;
236 return VIRTUALIZATION_NONE
;
239 static int detect_vm_zvm(void) {
241 #if defined(__s390__)
242 _cleanup_free_
char *t
= NULL
;
245 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
247 return VIRTUALIZATION_NONE
;
251 if (streq(t
, "z/VM"))
252 return VIRTUALIZATION_ZVM
;
254 return VIRTUALIZATION_KVM
;
256 return VIRTUALIZATION_NONE
;
260 /* Returns a short identifier for the various VM implementations */
261 int detect_vm(void) {
262 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
265 if (cached_found
>= 0)
268 /* Try xen capabilities file first, if not found try
269 * high-level hypervisor sysfs file:
271 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
276 if (r
!= VIRTUALIZATION_NONE
)
282 if (r
!= VIRTUALIZATION_NONE
)
285 r
= detect_vm_cpuid();
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_NONE
;
405 int detect_virtualization(void) {
408 r
= detect_container();
415 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
416 [VIRTUALIZATION_NONE
] = "none",
417 [VIRTUALIZATION_KVM
] = "kvm",
418 [VIRTUALIZATION_QEMU
] = "qemu",
419 [VIRTUALIZATION_BOCHS
] = "bochs",
420 [VIRTUALIZATION_XEN
] = "xen",
421 [VIRTUALIZATION_UML
] = "uml",
422 [VIRTUALIZATION_VMWARE
] = "vmware",
423 [VIRTUALIZATION_ORACLE
] = "oracle",
424 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
425 [VIRTUALIZATION_ZVM
] = "zvm",
426 [VIRTUALIZATION_PARALLELS
] = "parallels",
427 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
429 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
430 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
431 [VIRTUALIZATION_LXC
] = "lxc",
432 [VIRTUALIZATION_OPENVZ
] = "openvz",
433 [VIRTUALIZATION_DOCKER
] = "docker",
434 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
437 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);