]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/virt.c
Merge pull request #2085 from fbuihuu/more-use-of-check-load-state
[thirdparty/systemd.git] / src / basic / virt.c
CommitLineData
b52aae1d
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
b52aae1d
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
b52aae1d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
b52aae1d
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b52aae1d 22#include <errno.h>
11c3a366
TA
23#include <stdint.h>
24#include <stdlib.h>
07630cea 25#include <string.h>
b52aae1d
LP
26#include <unistd.h>
27
b5efdb8a 28#include "alloc-util.h"
ade61d3b
ZJS
29#include "dirent-util.h"
30#include "fd-util.h"
07630cea 31#include "fileio.h"
11c3a366 32#include "macro.h"
93cc7779 33#include "process-util.h"
b5efdb8a 34#include "stat-util.h"
8b43440b 35#include "string-table.h"
07630cea 36#include "string-util.h"
b52aae1d
LP
37#include "virt.h"
38
75f86906 39static int detect_vm_cpuid(void) {
b52aae1d 40
2ef8a4c4 41 /* CPUID is an x86 specific interface. */
bdb628ee 42#if defined(__i386__) || defined(__x86_64__)
b52aae1d 43
75f86906
LP
44 static const struct {
45 const char *cpuid;
46 int id;
47 } cpuid_vendor_table[] = {
48 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
49 { "KVMKVMKVM", VIRTUALIZATION_KVM },
b52aae1d 50 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906 51 { "VMwareVMware", VIRTUALIZATION_VMWARE },
b52aae1d 52 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
75f86906
LP
53 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
54 };
b52aae1d
LP
55
56 uint32_t eax, ecx;
b52aae1d
LP
57 bool hypervisor;
58
59 /* http://lwn.net/Articles/301888/ */
b52aae1d
LP
60
61#if defined (__i386__)
62#define REG_a "eax"
63#define REG_b "ebx"
64#elif defined (__amd64__)
65#define REG_a "rax"
66#define REG_b "rbx"
67#endif
68
69 /* First detect whether there is a hypervisor */
70 eax = 1;
71 __asm__ __volatile__ (
72 /* ebx/rbx is being used for PIC! */
73 " push %%"REG_b" \n\t"
74 " cpuid \n\t"
75 " pop %%"REG_b" \n\t"
76
77 : "=a" (eax), "=c" (ecx)
78 : "0" (eax)
79 );
80
81 hypervisor = !!(ecx & 0x80000000U);
82
83 if (hypervisor) {
75f86906
LP
84 union {
85 uint32_t sig32[3];
86 char text[13];
87 } sig = {};
88 unsigned j;
b52aae1d
LP
89
90 /* There is a hypervisor, see what it is */
91 eax = 0x40000000U;
92 __asm__ __volatile__ (
93 /* ebx/rbx is being used for PIC! */
94 " push %%"REG_b" \n\t"
95 " cpuid \n\t"
96 " mov %%ebx, %1 \n\t"
97 " pop %%"REG_b" \n\t"
98
99 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
100 : "0" (eax)
101 );
102
75f86906
LP
103 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
104 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
105 return cpuid_vendor_table[j].id;
bdb628ee 106
75f86906 107 return VIRTUALIZATION_VM_OTHER;
b52aae1d 108 }
bdb628ee
ZJS
109#endif
110
75f86906 111 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
112}
113
75f86906 114static int detect_vm_device_tree(void) {
db6a8689 115#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
d831deb5
CA
116 _cleanup_free_ char *hvtype = NULL;
117 int r;
118
b8f1df82 119 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
75f86906 120 if (r == -ENOENT) {
ce09c71d
AJ
121 _cleanup_closedir_ DIR *dir = NULL;
122 struct dirent *dent;
123
124 dir = opendir("/proc/device-tree");
125 if (!dir) {
126 if (errno == ENOENT)
75f86906 127 return VIRTUALIZATION_NONE;
ce09c71d
AJ
128 return -errno;
129 }
130
75f86906
LP
131 FOREACH_DIRENT(dent, dir, return -errno)
132 if (strstr(dent->d_name, "fw-cfg"))
133 return VIRTUALIZATION_QEMU;
134
135 return VIRTUALIZATION_NONE;
136 } else if (r < 0)
137 return r;
138
139 if (streq(hvtype, "linux,kvm"))
140 return VIRTUALIZATION_KVM;
141 else if (strstr(hvtype, "xen"))
142 return VIRTUALIZATION_XEN;
143 else
144 return VIRTUALIZATION_VM_OTHER;
145#else
146 return VIRTUALIZATION_NONE;
d831deb5 147#endif
d831deb5
CA
148}
149
75f86906 150static int detect_vm_dmi(void) {
2ef8a4c4 151#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
bdb628ee
ZJS
152
153 static const char *const dmi_vendors[] = {
3728dcde 154 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
bdb628ee
ZJS
155 "/sys/class/dmi/id/sys_vendor",
156 "/sys/class/dmi/id/board_vendor",
157 "/sys/class/dmi/id/bios_vendor"
158 };
159
75f86906
LP
160 static const struct {
161 const char *vendor;
162 int id;
163 } dmi_vendor_table[] = {
3728dcde 164 { "KVM", VIRTUALIZATION_KVM },
75f86906 165 { "QEMU", VIRTUALIZATION_QEMU },
bdb628ee 166 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906
LP
167 { "VMware", VIRTUALIZATION_VMWARE },
168 { "VMW", VIRTUALIZATION_VMWARE },
169 { "innotek GmbH", VIRTUALIZATION_ORACLE },
170 { "Xen", VIRTUALIZATION_XEN },
171 { "Bochs", VIRTUALIZATION_BOCHS },
172 { "Parallels", VIRTUALIZATION_PARALLELS },
173 };
bdb628ee 174 unsigned i;
75f86906 175 int r;
b52aae1d
LP
176
177 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 178 _cleanup_free_ char *s = NULL;
75f86906 179 unsigned j;
b52aae1d 180
b1b8e816
LP
181 r = read_one_line_file(dmi_vendors[i], &s);
182 if (r < 0) {
75f86906
LP
183 if (r == -ENOENT)
184 continue;
b52aae1d 185
75f86906 186 return r;
b52aae1d
LP
187 }
188
75f86906
LP
189 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
190 if (startswith(s, dmi_vendor_table[j].vendor))
191 return dmi_vendor_table[j].id;
b52aae1d 192 }
bdb628ee
ZJS
193#endif
194
75f86906 195 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
196}
197
75f86906
LP
198static int detect_vm_xen(void) {
199 _cleanup_free_ char *domcap = NULL;
200 char *cap, *i;
bdb628ee 201 int r;
b52aae1d 202
75f86906
LP
203 r = read_one_line_file("/proc/xen/capabilities", &domcap);
204 if (r == -ENOENT)
205 return VIRTUALIZATION_NONE;
bdb628ee 206
75f86906
LP
207 i = domcap;
208 while ((cap = strsep(&i, ",")))
209 if (streq(cap, "control_d"))
210 break;
bdb628ee 211
75f86906
LP
212 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
213}
37287585 214
75f86906
LP
215static int detect_vm_hypervisor(void) {
216 _cleanup_free_ char *hvtype = NULL;
217 int r;
37287585 218
75f86906
LP
219 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
220 if (r == -ENOENT)
221 return VIRTUALIZATION_NONE;
222 if (r < 0)
223 return r;
37287585 224
75f86906
LP
225 if (streq(hvtype, "xen"))
226 return VIRTUALIZATION_XEN;
227 else
228 return VIRTUALIZATION_VM_OTHER;
229}
37287585 230
75f86906
LP
231static int detect_vm_uml(void) {
232 _cleanup_free_ char *cpuinfo_contents = NULL;
233 int r;
37287585 234
75f86906
LP
235 /* Detect User-Mode Linux by reading /proc/cpuinfo */
236 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
237 if (r < 0)
bdb628ee 238 return r;
75f86906
LP
239 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
240 return VIRTUALIZATION_UML;
bdb628ee 241
75f86906
LP
242 return VIRTUALIZATION_NONE;
243}
e32886e0 244
75f86906 245static int detect_vm_zvm(void) {
bdb628ee 246
75f86906
LP
247#if defined(__s390__)
248 _cleanup_free_ char *t = NULL;
249 int r;
e32886e0 250
c4cd1d4d 251 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
75f86906
LP
252 if (r == -ENOENT)
253 return VIRTUALIZATION_NONE;
254 if (r < 0)
255 return r;
e32886e0 256
75f86906
LP
257 if (streq(t, "z/VM"))
258 return VIRTUALIZATION_ZVM;
259 else
260 return VIRTUALIZATION_KVM;
261#else
262 return VIRTUALIZATION_NONE;
263#endif
264}
e32886e0 265
75f86906
LP
266/* Returns a short identifier for the various VM implementations */
267int detect_vm(void) {
268 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
269 int r;
e32886e0 270
75f86906
LP
271 if (cached_found >= 0)
272 return cached_found;
bdb628ee 273
f6875b0a
CH
274 /* We have to use the correct order here:
275 * Some virtualization technologies do use KVM hypervisor but are
276 * expected to be detected as something else. So detect DMI first.
277 *
278 * An example is Virtualbox since version 5.0, which uses KVM backend.
279 * Detection via DMI works corretly, the CPU ID would find KVM
280 * only. */
050e65ad 281 r = detect_vm_dmi();
75f86906
LP
282 if (r < 0)
283 return r;
284 if (r != VIRTUALIZATION_NONE)
d831deb5
CA
285 goto finish;
286
050e65ad 287 r = detect_vm_cpuid();
75f86906
LP
288 if (r < 0)
289 return r;
290 if (r != VIRTUALIZATION_NONE)
0fb533a5 291 goto finish;
b52aae1d 292
42685451
AJ
293 /* x86 xen will most likely be detected by cpuid. If not (most likely
294 * because we're not an x86 guest), then we should try the xen capabilities
295 * file next. If that's not found, then we check for the high-level
296 * hypervisor sysfs file:
297 *
298 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
299
300 r = detect_vm_xen();
7080ea16
RR
301 if (r < 0)
302 return r;
75f86906 303 if (r != VIRTUALIZATION_NONE)
0fb533a5 304 goto finish;
7080ea16 305
75f86906
LP
306 r = detect_vm_hypervisor();
307 if (r < 0)
308 return r;
309 if (r != VIRTUALIZATION_NONE)
310 goto finish;
f41925b4 311
75f86906
LP
312 r = detect_vm_device_tree();
313 if (r < 0)
314 return r;
315 if (r != VIRTUALIZATION_NONE)
316 goto finish;
f41925b4 317
75f86906
LP
318 r = detect_vm_uml();
319 if (r < 0)
320 return r;
321 if (r != VIRTUALIZATION_NONE)
322 goto finish;
f41925b4 323
75f86906
LP
324 r = detect_vm_zvm();
325 if (r < 0)
326 return r;
0fb533a5
LP
327
328finish:
329 cached_found = r;
0fb533a5 330 return r;
b52aae1d
LP
331}
332
75f86906 333int detect_container(void) {
0fb533a5 334
75f86906
LP
335 static const struct {
336 const char *value;
337 int id;
338 } value_table[] = {
339 { "lxc", VIRTUALIZATION_LXC },
340 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
341 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
342 { "docker", VIRTUALIZATION_DOCKER },
9fb16425 343 { "rkt", VIRTUALIZATION_RKT },
75f86906 344 };
0fb533a5 345
75f86906 346 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
fdd25311 347 _cleanup_free_ char *m = NULL;
75f86906
LP
348 const char *e = NULL;
349 unsigned j;
ab94af92 350 int r;
b52aae1d 351
75f86906 352 if (cached_found >= 0)
0fb533a5 353 return cached_found;
0fb533a5 354
b52aae1d
LP
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) {
75f86906 359 r = VIRTUALIZATION_OPENVZ;
0fb533a5 360 goto finish;
b52aae1d
LP
361 }
362
fdd25311
LP
363 if (getpid() == 1) {
364 /* If we are PID 1 we can just check our own
365 * environment variable */
366
367 e = getenv("container");
368 if (isempty(e)) {
75f86906 369 r = VIRTUALIZATION_NONE;
fdd25311
LP
370 goto finish;
371 }
372 } else {
373
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
377 * for that. */
378
379 r = read_one_line_file("/run/systemd/container", &m);
380 if (r == -ENOENT) {
536bfdab
LP
381
382 /* Fallback for cases where PID 1 was not
383 * systemd (for example, cases where
384 * init=/bin/sh is used. */
385
386 r = getenv_for_pid(1, "container", &m);
387 if (r <= 0) {
388
389 /* If that didn't work, give up,
390 * assume no container manager.
391 *
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. */
397
75f86906 398 r = VIRTUALIZATION_NONE;
536bfdab
LP
399 goto finish;
400 }
fdd25311
LP
401 }
402 if (r < 0)
403 return r;
404
405 e = m;
406 }
b52aae1d 407
75f86906
LP
408 for (j = 0; j < ELEMENTSOF(value_table); j++)
409 if (streq(e, value_table[j].value)) {
410 r = value_table[j].id;
411 goto finish;
412 }
0fb533a5 413
f499daf4 414 r = VIRTUALIZATION_CONTAINER_OTHER;
fdd25311 415
0fb533a5
LP
416finish:
417 cached_found = r;
ab94af92 418 return r;
b52aae1d
LP
419}
420
75f86906 421int detect_virtualization(void) {
b52aae1d 422 int r;
b52aae1d 423
75f86906
LP
424 r = detect_container();
425 if (r != 0)
0fb533a5 426 return r;
b52aae1d 427
75f86906 428 return detect_vm();
b52aae1d 429}
75f86906 430
7f4b3c5e
LP
431int running_in_chroot(void) {
432 int ret;
433
434 ret = files_same("/proc/1/root", "/");
435 if (ret < 0)
436 return ret;
437
438 return ret == 0;
439}
440
75f86906
LP
441static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
442 [VIRTUALIZATION_NONE] = "none",
443 [VIRTUALIZATION_KVM] = "kvm",
444 [VIRTUALIZATION_QEMU] = "qemu",
445 [VIRTUALIZATION_BOCHS] = "bochs",
446 [VIRTUALIZATION_XEN] = "xen",
447 [VIRTUALIZATION_UML] = "uml",
448 [VIRTUALIZATION_VMWARE] = "vmware",
449 [VIRTUALIZATION_ORACLE] = "oracle",
450 [VIRTUALIZATION_MICROSOFT] = "microsoft",
451 [VIRTUALIZATION_ZVM] = "zvm",
452 [VIRTUALIZATION_PARALLELS] = "parallels",
453 [VIRTUALIZATION_VM_OTHER] = "vm-other",
454
455 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
456 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
457 [VIRTUALIZATION_LXC] = "lxc",
458 [VIRTUALIZATION_OPENVZ] = "openvz",
459 [VIRTUALIZATION_DOCKER] = "docker",
9fb16425 460 [VIRTUALIZATION_RKT] = "rkt",
75f86906
LP
461 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
462};
463
464DEFINE_STRING_TABLE_LOOKUP(virtualization, int);