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