]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/virt.c
Merge pull request #2085 from fbuihuu/more-use-of-check-load-state
[thirdparty/systemd.git] / src / basic / virt.c
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "alloc-util.h"
29 #include "dirent-util.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "macro.h"
33 #include "process-util.h"
34 #include "stat-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
37 #include "virt.h"
38
39 static int detect_vm_cpuid(void) {
40
41 /* CPUID is an x86 specific interface. */
42 #if defined(__i386__) || defined(__x86_64__)
43
44 static const struct {
45 const char *cpuid;
46 int id;
47 } cpuid_vendor_table[] = {
48 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
49 { "KVMKVMKVM", VIRTUALIZATION_KVM },
50 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
51 { "VMwareVMware", VIRTUALIZATION_VMWARE },
52 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
53 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
54 };
55
56 uint32_t eax, ecx;
57 bool hypervisor;
58
59 /* http://lwn.net/Articles/301888/ */
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) {
84 union {
85 uint32_t sig32[3];
86 char text[13];
87 } sig = {};
88 unsigned j;
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
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;
106
107 return VIRTUALIZATION_VM_OTHER;
108 }
109 #endif
110
111 return VIRTUALIZATION_NONE;
112 }
113
114 static int detect_vm_device_tree(void) {
115 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
116 _cleanup_free_ char *hvtype = NULL;
117 int r;
118
119 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
120 if (r == -ENOENT) {
121 _cleanup_closedir_ DIR *dir = NULL;
122 struct dirent *dent;
123
124 dir = opendir("/proc/device-tree");
125 if (!dir) {
126 if (errno == ENOENT)
127 return VIRTUALIZATION_NONE;
128 return -errno;
129 }
130
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;
147 #endif
148 }
149
150 static int detect_vm_dmi(void) {
151 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
152
153 static const char *const dmi_vendors[] = {
154 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
155 "/sys/class/dmi/id/sys_vendor",
156 "/sys/class/dmi/id/board_vendor",
157 "/sys/class/dmi/id/bios_vendor"
158 };
159
160 static const struct {
161 const char *vendor;
162 int id;
163 } dmi_vendor_table[] = {
164 { "KVM", VIRTUALIZATION_KVM },
165 { "QEMU", VIRTUALIZATION_QEMU },
166 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
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 };
174 unsigned i;
175 int r;
176
177 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
178 _cleanup_free_ char *s = NULL;
179 unsigned j;
180
181 r = read_one_line_file(dmi_vendors[i], &s);
182 if (r < 0) {
183 if (r == -ENOENT)
184 continue;
185
186 return r;
187 }
188
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;
192 }
193 #endif
194
195 return VIRTUALIZATION_NONE;
196 }
197
198 static int detect_vm_xen(void) {
199 _cleanup_free_ char *domcap = NULL;
200 char *cap, *i;
201 int r;
202
203 r = read_one_line_file("/proc/xen/capabilities", &domcap);
204 if (r == -ENOENT)
205 return VIRTUALIZATION_NONE;
206
207 i = domcap;
208 while ((cap = strsep(&i, ",")))
209 if (streq(cap, "control_d"))
210 break;
211
212 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
213 }
214
215 static int detect_vm_hypervisor(void) {
216 _cleanup_free_ char *hvtype = NULL;
217 int r;
218
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;
224
225 if (streq(hvtype, "xen"))
226 return VIRTUALIZATION_XEN;
227 else
228 return VIRTUALIZATION_VM_OTHER;
229 }
230
231 static int detect_vm_uml(void) {
232 _cleanup_free_ char *cpuinfo_contents = NULL;
233 int r;
234
235 /* Detect User-Mode Linux by reading /proc/cpuinfo */
236 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
237 if (r < 0)
238 return r;
239 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
240 return VIRTUALIZATION_UML;
241
242 return VIRTUALIZATION_NONE;
243 }
244
245 static int detect_vm_zvm(void) {
246
247 #if defined(__s390__)
248 _cleanup_free_ char *t = NULL;
249 int r;
250
251 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
252 if (r == -ENOENT)
253 return VIRTUALIZATION_NONE;
254 if (r < 0)
255 return r;
256
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 }
265
266 /* Returns a short identifier for the various VM implementations */
267 int detect_vm(void) {
268 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
269 int r;
270
271 if (cached_found >= 0)
272 return cached_found;
273
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. */
281 r = detect_vm_dmi();
282 if (r < 0)
283 return r;
284 if (r != VIRTUALIZATION_NONE)
285 goto finish;
286
287 r = detect_vm_cpuid();
288 if (r < 0)
289 return r;
290 if (r != VIRTUALIZATION_NONE)
291 goto finish;
292
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();
301 if (r < 0)
302 return r;
303 if (r != VIRTUALIZATION_NONE)
304 goto finish;
305
306 r = detect_vm_hypervisor();
307 if (r < 0)
308 return r;
309 if (r != VIRTUALIZATION_NONE)
310 goto finish;
311
312 r = detect_vm_device_tree();
313 if (r < 0)
314 return r;
315 if (r != VIRTUALIZATION_NONE)
316 goto finish;
317
318 r = detect_vm_uml();
319 if (r < 0)
320 return r;
321 if (r != VIRTUALIZATION_NONE)
322 goto finish;
323
324 r = detect_vm_zvm();
325 if (r < 0)
326 return r;
327
328 finish:
329 cached_found = r;
330 return r;
331 }
332
333 int detect_container(void) {
334
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 },
343 { "rkt", VIRTUALIZATION_RKT },
344 };
345
346 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
347 _cleanup_free_ char *m = NULL;
348 const char *e = NULL;
349 unsigned j;
350 int r;
351
352 if (cached_found >= 0)
353 return cached_found;
354
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;
360 goto finish;
361 }
362
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)) {
369 r = VIRTUALIZATION_NONE;
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) {
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
398 r = VIRTUALIZATION_NONE;
399 goto finish;
400 }
401 }
402 if (r < 0)
403 return r;
404
405 e = m;
406 }
407
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 }
413
414 r = VIRTUALIZATION_CONTAINER_OTHER;
415
416 finish:
417 cached_found = r;
418 return r;
419 }
420
421 int detect_virtualization(void) {
422 int r;
423
424 r = detect_container();
425 if (r != 0)
426 return r;
427
428 return detect_vm();
429 }
430
431 int 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
441 static 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",
460 [VIRTUALIZATION_RKT] = "rkt",
461 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
462 };
463
464 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);