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