]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/virt.c
core: still make progress when throttling the manager loop
[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 <string.h>
24 #include <unistd.h>
25
26 #include "alloc-util.h"
27 #include "fileio.h"
28 #include "process-util.h"
29 #include "stat-util.h"
30 #include "string-table.h"
31 #include "string-util.h"
32 #include "util.h"
33 #include "virt.h"
34
35 static int detect_vm_cpuid(void) {
36
37 /* CPUID is an x86 specific interface. */
38 #if defined(__i386__) || defined(__x86_64__)
39
40 static const struct {
41 const char *cpuid;
42 int id;
43 } cpuid_vendor_table[] = {
44 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
45 { "KVMKVMKVM", VIRTUALIZATION_KVM },
46 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
47 { "VMwareVMware", VIRTUALIZATION_VMWARE },
48 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
49 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
50 };
51
52 uint32_t eax, ecx;
53 bool hypervisor;
54
55 /* http://lwn.net/Articles/301888/ */
56
57 #if defined (__i386__)
58 #define REG_a "eax"
59 #define REG_b "ebx"
60 #elif defined (__amd64__)
61 #define REG_a "rax"
62 #define REG_b "rbx"
63 #endif
64
65 /* First detect whether there is a hypervisor */
66 eax = 1;
67 __asm__ __volatile__ (
68 /* ebx/rbx is being used for PIC! */
69 " push %%"REG_b" \n\t"
70 " cpuid \n\t"
71 " pop %%"REG_b" \n\t"
72
73 : "=a" (eax), "=c" (ecx)
74 : "0" (eax)
75 );
76
77 hypervisor = !!(ecx & 0x80000000U);
78
79 if (hypervisor) {
80 union {
81 uint32_t sig32[3];
82 char text[13];
83 } sig = {};
84 unsigned j;
85
86 /* There is a hypervisor, see what it is */
87 eax = 0x40000000U;
88 __asm__ __volatile__ (
89 /* ebx/rbx is being used for PIC! */
90 " push %%"REG_b" \n\t"
91 " cpuid \n\t"
92 " mov %%ebx, %1 \n\t"
93 " pop %%"REG_b" \n\t"
94
95 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
96 : "0" (eax)
97 );
98
99 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
100 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
101 return cpuid_vendor_table[j].id;
102
103 return VIRTUALIZATION_VM_OTHER;
104 }
105 #endif
106
107 return VIRTUALIZATION_NONE;
108 }
109
110 static int detect_vm_device_tree(void) {
111 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
112 _cleanup_free_ char *hvtype = NULL;
113 int r;
114
115 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
116 if (r == -ENOENT) {
117 _cleanup_closedir_ DIR *dir = NULL;
118 struct dirent *dent;
119
120 dir = opendir("/proc/device-tree");
121 if (!dir) {
122 if (errno == ENOENT)
123 return VIRTUALIZATION_NONE;
124 return -errno;
125 }
126
127 FOREACH_DIRENT(dent, dir, return -errno)
128 if (strstr(dent->d_name, "fw-cfg"))
129 return VIRTUALIZATION_QEMU;
130
131 return VIRTUALIZATION_NONE;
132 } else if (r < 0)
133 return r;
134
135 if (streq(hvtype, "linux,kvm"))
136 return VIRTUALIZATION_KVM;
137 else if (strstr(hvtype, "xen"))
138 return VIRTUALIZATION_XEN;
139 else
140 return VIRTUALIZATION_VM_OTHER;
141 #else
142 return VIRTUALIZATION_NONE;
143 #endif
144 }
145
146 static int detect_vm_dmi(void) {
147 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
148
149 static const char *const dmi_vendors[] = {
150 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
151 "/sys/class/dmi/id/sys_vendor",
152 "/sys/class/dmi/id/board_vendor",
153 "/sys/class/dmi/id/bios_vendor"
154 };
155
156 static const struct {
157 const char *vendor;
158 int id;
159 } dmi_vendor_table[] = {
160 { "KVM", VIRTUALIZATION_KVM },
161 { "QEMU", VIRTUALIZATION_QEMU },
162 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
163 { "VMware", VIRTUALIZATION_VMWARE },
164 { "VMW", VIRTUALIZATION_VMWARE },
165 { "innotek GmbH", VIRTUALIZATION_ORACLE },
166 { "Xen", VIRTUALIZATION_XEN },
167 { "Bochs", VIRTUALIZATION_BOCHS },
168 { "Parallels", VIRTUALIZATION_PARALLELS },
169 };
170 unsigned i;
171 int r;
172
173 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
174 _cleanup_free_ char *s = NULL;
175 unsigned j;
176
177 r = read_one_line_file(dmi_vendors[i], &s);
178 if (r < 0) {
179 if (r == -ENOENT)
180 continue;
181
182 return r;
183 }
184
185 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
186 if (startswith(s, dmi_vendor_table[j].vendor))
187 return dmi_vendor_table[j].id;
188 }
189 #endif
190
191 return VIRTUALIZATION_NONE;
192 }
193
194 static int detect_vm_xen(void) {
195 _cleanup_free_ char *domcap = NULL;
196 char *cap, *i;
197 int r;
198
199 r = read_one_line_file("/proc/xen/capabilities", &domcap);
200 if (r == -ENOENT)
201 return VIRTUALIZATION_NONE;
202
203 i = domcap;
204 while ((cap = strsep(&i, ",")))
205 if (streq(cap, "control_d"))
206 break;
207
208 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
209 }
210
211 static int detect_vm_hypervisor(void) {
212 _cleanup_free_ char *hvtype = NULL;
213 int r;
214
215 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
216 if (r == -ENOENT)
217 return VIRTUALIZATION_NONE;
218 if (r < 0)
219 return r;
220
221 if (streq(hvtype, "xen"))
222 return VIRTUALIZATION_XEN;
223 else
224 return VIRTUALIZATION_VM_OTHER;
225 }
226
227 static int detect_vm_uml(void) {
228 _cleanup_free_ char *cpuinfo_contents = NULL;
229 int r;
230
231 /* Detect User-Mode Linux by reading /proc/cpuinfo */
232 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
233 if (r < 0)
234 return r;
235 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
236 return VIRTUALIZATION_UML;
237
238 return VIRTUALIZATION_NONE;
239 }
240
241 static int detect_vm_zvm(void) {
242
243 #if defined(__s390__)
244 _cleanup_free_ char *t = NULL;
245 int r;
246
247 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
248 if (r == -ENOENT)
249 return VIRTUALIZATION_NONE;
250 if (r < 0)
251 return r;
252
253 if (streq(t, "z/VM"))
254 return VIRTUALIZATION_ZVM;
255 else
256 return VIRTUALIZATION_KVM;
257 #else
258 return VIRTUALIZATION_NONE;
259 #endif
260 }
261
262 /* Returns a short identifier for the various VM implementations */
263 int detect_vm(void) {
264 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
265 int r;
266
267 if (cached_found >= 0)
268 return cached_found;
269
270 r = detect_vm_cpuid();
271 if (r < 0)
272 return r;
273 if (r != VIRTUALIZATION_NONE)
274 goto finish;
275
276 r = detect_vm_dmi();
277 if (r < 0)
278 return r;
279 if (r != VIRTUALIZATION_NONE)
280 goto finish;
281
282 /* x86 xen will most likely be detected by cpuid. If not (most likely
283 * because we're not an x86 guest), then we should try the xen capabilities
284 * file next. If that's not found, then we check for the high-level
285 * hypervisor sysfs file:
286 *
287 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
288
289 r = detect_vm_xen();
290 if (r < 0)
291 return r;
292 if (r != VIRTUALIZATION_NONE)
293 goto finish;
294
295 r = detect_vm_hypervisor();
296 if (r < 0)
297 return r;
298 if (r != VIRTUALIZATION_NONE)
299 goto finish;
300
301 r = detect_vm_device_tree();
302 if (r < 0)
303 return r;
304 if (r != VIRTUALIZATION_NONE)
305 goto finish;
306
307 r = detect_vm_uml();
308 if (r < 0)
309 return r;
310 if (r != VIRTUALIZATION_NONE)
311 goto finish;
312
313 r = detect_vm_zvm();
314 if (r < 0)
315 return r;
316
317 finish:
318 cached_found = r;
319 return r;
320 }
321
322 int detect_container(void) {
323
324 static const struct {
325 const char *value;
326 int id;
327 } value_table[] = {
328 { "lxc", VIRTUALIZATION_LXC },
329 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
330 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
331 { "docker", VIRTUALIZATION_DOCKER },
332 };
333
334 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
335 _cleanup_free_ char *m = NULL;
336 const char *e = NULL;
337 unsigned j;
338 int r;
339
340 if (cached_found >= 0)
341 return cached_found;
342
343 /* /proc/vz exists in container and outside of the container,
344 * /proc/bc only outside of the container. */
345 if (access("/proc/vz", F_OK) >= 0 &&
346 access("/proc/bc", F_OK) < 0) {
347 r = VIRTUALIZATION_OPENVZ;
348 goto finish;
349 }
350
351 if (getpid() == 1) {
352 /* If we are PID 1 we can just check our own
353 * environment variable */
354
355 e = getenv("container");
356 if (isempty(e)) {
357 r = VIRTUALIZATION_NONE;
358 goto finish;
359 }
360 } else {
361
362 /* Otherwise, PID 1 dropped this information into a
363 * file in /run. This is better than accessing
364 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
365 * for that. */
366
367 r = read_one_line_file("/run/systemd/container", &m);
368 if (r == -ENOENT) {
369
370 /* Fallback for cases where PID 1 was not
371 * systemd (for example, cases where
372 * init=/bin/sh is used. */
373
374 r = getenv_for_pid(1, "container", &m);
375 if (r <= 0) {
376
377 /* If that didn't work, give up,
378 * assume no container manager.
379 *
380 * Note: This means we still cannot
381 * detect containers if init=/bin/sh
382 * is passed but privileges dropped,
383 * as /proc/1/environ is only readable
384 * with privileges. */
385
386 r = VIRTUALIZATION_NONE;
387 goto finish;
388 }
389 }
390 if (r < 0)
391 return r;
392
393 e = m;
394 }
395
396 for (j = 0; j < ELEMENTSOF(value_table); j++)
397 if (streq(e, value_table[j].value)) {
398 r = value_table[j].id;
399 goto finish;
400 }
401
402 r = VIRTUALIZATION_NONE;
403
404 finish:
405 cached_found = r;
406 return r;
407 }
408
409 int detect_virtualization(void) {
410 int r;
411
412 r = detect_container();
413 if (r != 0)
414 return r;
415
416 return detect_vm();
417 }
418
419 int running_in_chroot(void) {
420 int ret;
421
422 ret = files_same("/proc/1/root", "/");
423 if (ret < 0)
424 return ret;
425
426 return ret == 0;
427 }
428
429 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
430 [VIRTUALIZATION_NONE] = "none",
431 [VIRTUALIZATION_KVM] = "kvm",
432 [VIRTUALIZATION_QEMU] = "qemu",
433 [VIRTUALIZATION_BOCHS] = "bochs",
434 [VIRTUALIZATION_XEN] = "xen",
435 [VIRTUALIZATION_UML] = "uml",
436 [VIRTUALIZATION_VMWARE] = "vmware",
437 [VIRTUALIZATION_ORACLE] = "oracle",
438 [VIRTUALIZATION_MICROSOFT] = "microsoft",
439 [VIRTUALIZATION_ZVM] = "zvm",
440 [VIRTUALIZATION_PARALLELS] = "parallels",
441 [VIRTUALIZATION_VM_OTHER] = "vm-other",
442
443 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
444 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
445 [VIRTUALIZATION_LXC] = "lxc",
446 [VIRTUALIZATION_OPENVZ] = "openvz",
447 [VIRTUALIZATION_DOCKER] = "docker",
448 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
449 };
450
451 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);