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