]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/virt.c
Merge pull request #1775 from fbuihuu/set-property-completion
[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>
07630cea 23#include <string.h>
b52aae1d
LP
24#include <unistd.h>
25
b5efdb8a 26#include "alloc-util.h"
07630cea 27#include "fileio.h"
0b452006 28#include "process-util.h"
b5efdb8a 29#include "stat-util.h"
8b43440b 30#include "string-table.h"
07630cea
LP
31#include "string-util.h"
32#include "util.h"
b52aae1d
LP
33#include "virt.h"
34
75f86906 35static int detect_vm_cpuid(void) {
b52aae1d
LP
36
37 /* Both CPUID and DMI are x86 specific interfaces... */
bdb628ee 38#if defined(__i386__) || defined(__x86_64__)
b52aae1d 39
75f86906
LP
40 static const struct {
41 const char *cpuid;
42 int id;
43 } cpuid_vendor_table[] = {
44 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
45 { "KVMKVMKVM", VIRTUALIZATION_KVM },
b52aae1d 46 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906 47 { "VMwareVMware", VIRTUALIZATION_VMWARE },
b52aae1d 48 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
75f86906
LP
49 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
50 };
b52aae1d
LP
51
52 uint32_t eax, ecx;
b52aae1d
LP
53 bool hypervisor;
54
55 /* http://lwn.net/Articles/301888/ */
b52aae1d
LP
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) {
75f86906
LP
80 union {
81 uint32_t sig32[3];
82 char text[13];
83 } sig = {};
84 unsigned j;
b52aae1d
LP
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
75f86906
LP
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;
bdb628ee 102
75f86906 103 return VIRTUALIZATION_VM_OTHER;
b52aae1d 104 }
bdb628ee
ZJS
105#endif
106
75f86906 107 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
108}
109
75f86906 110static int detect_vm_device_tree(void) {
db6a8689 111#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
d831deb5
CA
112 _cleanup_free_ char *hvtype = NULL;
113 int r;
114
b8f1df82 115 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
75f86906 116 if (r == -ENOENT) {
ce09c71d
AJ
117 _cleanup_closedir_ DIR *dir = NULL;
118 struct dirent *dent;
119
120 dir = opendir("/proc/device-tree");
121 if (!dir) {
122 if (errno == ENOENT)
75f86906 123 return VIRTUALIZATION_NONE;
ce09c71d
AJ
124 return -errno;
125 }
126
75f86906
LP
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;
d831deb5 143#endif
d831deb5
CA
144}
145
75f86906 146static int detect_vm_dmi(void) {
bdb628ee
ZJS
147
148 /* Both CPUID and DMI are x86 specific interfaces... */
149#if defined(__i386__) || defined(__x86_64__)
150
151 static const char *const dmi_vendors[] = {
152 "/sys/class/dmi/id/sys_vendor",
153 "/sys/class/dmi/id/board_vendor",
154 "/sys/class/dmi/id/bios_vendor"
155 };
156
75f86906
LP
157 static const struct {
158 const char *vendor;
159 int id;
160 } dmi_vendor_table[] = {
161 { "QEMU", VIRTUALIZATION_QEMU },
bdb628ee 162 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906
LP
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 };
bdb628ee 170 unsigned i;
75f86906 171 int r;
b52aae1d
LP
172
173 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 174 _cleanup_free_ char *s = NULL;
75f86906 175 unsigned j;
b52aae1d 176
b1b8e816
LP
177 r = read_one_line_file(dmi_vendors[i], &s);
178 if (r < 0) {
75f86906
LP
179 if (r == -ENOENT)
180 continue;
b52aae1d 181
75f86906 182 return r;
b52aae1d
LP
183 }
184
75f86906
LP
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;
b52aae1d 188 }
bdb628ee
ZJS
189#endif
190
75f86906 191 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
192}
193
75f86906
LP
194static int detect_vm_xen(void) {
195 _cleanup_free_ char *domcap = NULL;
196 char *cap, *i;
bdb628ee 197 int r;
b52aae1d 198
75f86906
LP
199 r = read_one_line_file("/proc/xen/capabilities", &domcap);
200 if (r == -ENOENT)
201 return VIRTUALIZATION_NONE;
bdb628ee 202
75f86906
LP
203 i = domcap;
204 while ((cap = strsep(&i, ",")))
205 if (streq(cap, "control_d"))
206 break;
bdb628ee 207
75f86906
LP
208 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
209}
37287585 210
75f86906
LP
211static int detect_vm_hypervisor(void) {
212 _cleanup_free_ char *hvtype = NULL;
213 int r;
37287585 214
75f86906
LP
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;
37287585 220
75f86906
LP
221 if (streq(hvtype, "xen"))
222 return VIRTUALIZATION_XEN;
223 else
224 return VIRTUALIZATION_VM_OTHER;
225}
37287585 226
75f86906
LP
227static int detect_vm_uml(void) {
228 _cleanup_free_ char *cpuinfo_contents = NULL;
229 int r;
37287585 230
75f86906
LP
231 /* Detect User-Mode Linux by reading /proc/cpuinfo */
232 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
233 if (r < 0)
bdb628ee 234 return r;
75f86906
LP
235 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
236 return VIRTUALIZATION_UML;
bdb628ee 237
75f86906
LP
238 return VIRTUALIZATION_NONE;
239}
e32886e0 240
75f86906 241static int detect_vm_zvm(void) {
bdb628ee 242
75f86906
LP
243#if defined(__s390__)
244 _cleanup_free_ char *t = NULL;
245 int r;
e32886e0 246
c4cd1d4d 247 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
75f86906
LP
248 if (r == -ENOENT)
249 return VIRTUALIZATION_NONE;
250 if (r < 0)
251 return r;
e32886e0 252
75f86906
LP
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}
e32886e0 261
75f86906
LP
262/* Returns a short identifier for the various VM implementations */
263int detect_vm(void) {
264 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
265 int r;
e32886e0 266
75f86906
LP
267 if (cached_found >= 0)
268 return cached_found;
bdb628ee 269
75f86906
LP
270 /* Try xen capabilities file first, if not found try
271 * high-level hypervisor sysfs file:
272 *
273 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
274
275 r = detect_vm_xen();
276 if (r < 0)
277 return r;
278 if (r != VIRTUALIZATION_NONE)
d831deb5
CA
279 goto finish;
280
75f86906
LP
281 r = detect_vm_dmi();
282 if (r < 0)
283 return r;
284 if (r != VIRTUALIZATION_NONE)
0fb533a5 285 goto finish;
b52aae1d 286
75f86906 287 r = detect_vm_cpuid();
7080ea16
RR
288 if (r < 0)
289 return r;
75f86906 290 if (r != VIRTUALIZATION_NONE)
0fb533a5 291 goto finish;
7080ea16 292
75f86906
LP
293 r = detect_vm_hypervisor();
294 if (r < 0)
295 return r;
296 if (r != VIRTUALIZATION_NONE)
297 goto finish;
f41925b4 298
75f86906
LP
299 r = detect_vm_device_tree();
300 if (r < 0)
301 return r;
302 if (r != VIRTUALIZATION_NONE)
303 goto finish;
f41925b4 304
75f86906
LP
305 r = detect_vm_uml();
306 if (r < 0)
307 return r;
308 if (r != VIRTUALIZATION_NONE)
309 goto finish;
f41925b4 310
75f86906
LP
311 r = detect_vm_zvm();
312 if (r < 0)
313 return r;
0fb533a5
LP
314
315finish:
316 cached_found = r;
0fb533a5 317 return r;
b52aae1d
LP
318}
319
75f86906 320int detect_container(void) {
0fb533a5 321
75f86906
LP
322 static const struct {
323 const char *value;
324 int id;
325 } value_table[] = {
326 { "lxc", VIRTUALIZATION_LXC },
327 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
328 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
329 { "docker", VIRTUALIZATION_DOCKER },
330 };
0fb533a5 331
75f86906 332 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
fdd25311 333 _cleanup_free_ char *m = NULL;
75f86906
LP
334 const char *e = NULL;
335 unsigned j;
ab94af92 336 int r;
b52aae1d 337
75f86906 338 if (cached_found >= 0)
0fb533a5 339 return cached_found;
0fb533a5 340
b52aae1d
LP
341 /* /proc/vz exists in container and outside of the container,
342 * /proc/bc only outside of the container. */
343 if (access("/proc/vz", F_OK) >= 0 &&
344 access("/proc/bc", F_OK) < 0) {
75f86906 345 r = VIRTUALIZATION_OPENVZ;
0fb533a5 346 goto finish;
b52aae1d
LP
347 }
348
fdd25311
LP
349 if (getpid() == 1) {
350 /* If we are PID 1 we can just check our own
351 * environment variable */
352
353 e = getenv("container");
354 if (isempty(e)) {
75f86906 355 r = VIRTUALIZATION_NONE;
fdd25311
LP
356 goto finish;
357 }
358 } else {
359
360 /* Otherwise, PID 1 dropped this information into a
361 * file in /run. This is better than accessing
362 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
363 * for that. */
364
365 r = read_one_line_file("/run/systemd/container", &m);
366 if (r == -ENOENT) {
536bfdab
LP
367
368 /* Fallback for cases where PID 1 was not
369 * systemd (for example, cases where
370 * init=/bin/sh is used. */
371
372 r = getenv_for_pid(1, "container", &m);
373 if (r <= 0) {
374
375 /* If that didn't work, give up,
376 * assume no container manager.
377 *
378 * Note: This means we still cannot
379 * detect containers if init=/bin/sh
380 * is passed but privileges dropped,
381 * as /proc/1/environ is only readable
382 * with privileges. */
383
75f86906 384 r = VIRTUALIZATION_NONE;
536bfdab
LP
385 goto finish;
386 }
fdd25311
LP
387 }
388 if (r < 0)
389 return r;
390
391 e = m;
392 }
b52aae1d 393
75f86906
LP
394 for (j = 0; j < ELEMENTSOF(value_table); j++)
395 if (streq(e, value_table[j].value)) {
396 r = value_table[j].id;
397 goto finish;
398 }
0fb533a5 399
75f86906 400 r = VIRTUALIZATION_NONE;
fdd25311 401
0fb533a5
LP
402finish:
403 cached_found = r;
ab94af92 404 return r;
b52aae1d
LP
405}
406
75f86906 407int detect_virtualization(void) {
b52aae1d 408 int r;
b52aae1d 409
75f86906
LP
410 r = detect_container();
411 if (r != 0)
0fb533a5 412 return r;
b52aae1d 413
75f86906 414 return detect_vm();
b52aae1d 415}
75f86906 416
7f4b3c5e
LP
417int running_in_chroot(void) {
418 int ret;
419
420 ret = files_same("/proc/1/root", "/");
421 if (ret < 0)
422 return ret;
423
424 return ret == 0;
425}
426
75f86906
LP
427static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
428 [VIRTUALIZATION_NONE] = "none",
429 [VIRTUALIZATION_KVM] = "kvm",
430 [VIRTUALIZATION_QEMU] = "qemu",
431 [VIRTUALIZATION_BOCHS] = "bochs",
432 [VIRTUALIZATION_XEN] = "xen",
433 [VIRTUALIZATION_UML] = "uml",
434 [VIRTUALIZATION_VMWARE] = "vmware",
435 [VIRTUALIZATION_ORACLE] = "oracle",
436 [VIRTUALIZATION_MICROSOFT] = "microsoft",
437 [VIRTUALIZATION_ZVM] = "zvm",
438 [VIRTUALIZATION_PARALLELS] = "parallels",
439 [VIRTUALIZATION_VM_OTHER] = "vm-other",
440
441 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
442 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
443 [VIRTUALIZATION_LXC] = "lxc",
444 [VIRTUALIZATION_OPENVZ] = "openvz",
445 [VIRTUALIZATION_DOCKER] = "docker",
446 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
447};
448
449DEFINE_STRING_TABLE_LOOKUP(virtualization, int);