]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
systemd-bootchart: Repair Entropy Graph
[thirdparty/systemd.git] / src / shared / 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"
27#include "virt.h"
a5c32cff 28#include "fileio.h"
b52aae1d 29
bdb628ee 30static int detect_vm_cpuid(const char **_id) {
b52aae1d
LP
31
32 /* Both CPUID and DMI are x86 specific interfaces... */
bdb628ee 33#if defined(__i386__) || defined(__x86_64__)
b52aae1d
LP
34
35 static const char cpuid_vendor_table[] =
36 "XenVMMXenVMM\0" "xen\0"
37 "KVMKVMKVM\0" "kvm\0"
38 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
39 "VMwareVMware\0" "vmware\0"
40 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
41 "Microsoft Hv\0" "microsoft\0";
42
43 uint32_t eax, ecx;
44 union {
45 uint32_t sig32[3];
46 char text[13];
b92bea5d 47 } sig = {};
b52aae1d
LP
48 const char *j, *k;
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) {
76
77 /* There is a hypervisor, see what it is */
78 eax = 0x40000000U;
79 __asm__ __volatile__ (
80 /* ebx/rbx is being used for PIC! */
81 " push %%"REG_b" \n\t"
82 " cpuid \n\t"
83 " mov %%ebx, %1 \n\t"
84 " pop %%"REG_b" \n\t"
85
86 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
87 : "0" (eax)
88 );
89
90 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
91 if (streq(sig.text, j)) {
bdb628ee
ZJS
92 *_id = k;
93 return 1;
b52aae1d 94 }
bdb628ee
ZJS
95
96 *_id = "other";
97 return 0;
b52aae1d 98 }
bdb628ee
ZJS
99#endif
100
101 return 0;
102}
103
d831deb5
CA
104static int detect_vm_devicetree(const char **_id) {
105#if defined(__powerpc__) || defined(__powerpc64__)
106 _cleanup_free_ char *hvtype = NULL;
107 int r;
108
109 r = read_one_line_file("/sys/firmware/devicetree/base/hypervisor/compatible", &hvtype);
110 if (r >= 0) {
111 if (streq(hvtype, "linux,kvm")) {
112 *_id = "kvm";
113 return 1;
114 }
115 }
116#endif
117 return 0;
118}
119
bdb628ee
ZJS
120static int detect_vm_dmi(const char **_id) {
121
122 /* Both CPUID and DMI are x86 specific interfaces... */
123#if defined(__i386__) || defined(__x86_64__)
124
125 static const char *const dmi_vendors[] = {
126 "/sys/class/dmi/id/sys_vendor",
127 "/sys/class/dmi/id/board_vendor",
128 "/sys/class/dmi/id/bios_vendor"
129 };
130
131 static const char dmi_vendor_table[] =
132 "QEMU\0" "qemu\0"
133 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
134 "VMware\0" "vmware\0"
135 "VMW\0" "vmware\0"
bdb628ee
ZJS
136 "innotek GmbH\0" "oracle\0"
137 "Xen\0" "xen\0"
138 "Bochs\0" "bochs\0";
139 unsigned i;
b52aae1d
LP
140
141 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 142 _cleanup_free_ char *s = NULL;
bdb628ee
ZJS
143 const char *j, *k;
144 int r;
b52aae1d 145
b1b8e816
LP
146 r = read_one_line_file(dmi_vendors[i], &s);
147 if (r < 0) {
b52aae1d
LP
148 if (r != -ENOENT)
149 return r;
150
151 continue;
152 }
153
154 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
0fb533a5 155 if (startswith(s, j)) {
bdb628ee
ZJS
156 *_id = k;
157 return 1;
0fb533a5 158 }
b52aae1d 159 }
bdb628ee
ZJS
160#endif
161
162 return 0;
163}
164
165/* Returns a short identifier for the various VM implementations */
166int detect_vm(const char **id) {
37287585 167 _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
ec202eae
SL
168 static thread_local int cached_found = -1;
169 static thread_local const char *cached_id = NULL;
bdb628ee
ZJS
170 const char *_id = NULL;
171 int r;
b52aae1d 172
bdb628ee
ZJS
173 if (_likely_(cached_found >= 0)) {
174
175 if (id)
176 *id = cached_id;
177
178 return cached_found;
179 }
180
37287585 181 /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
bdb628ee 182 *
37287585
TB
183 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
184 r = read_one_line_file("/proc/xen/capabilities", &domcap);
bdb628ee 185 if (r >= 0) {
37287585
TB
186 char *cap, *i = domcap;
187
188 while ((cap = strsep(&i, ",")))
189 if (streq(cap, "control_d"))
190 break;
191
a71516df 192 if (!cap) {
bdb628ee
ZJS
193 _id = "xen";
194 r = 1;
bdb628ee 195 }
37287585
TB
196
197 goto finish;
198
199 } else if (r == -ENOENT) {
200 _cleanup_free_ char *hvtype = NULL;
201
202 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
203 if (r >= 0) {
204 if (streq(hvtype, "xen")) {
205 _id = "xen";
206 r = 1;
207 goto finish;
208 }
209 } else if (r != -ENOENT)
210 return r;
211 } else
bdb628ee
ZJS
212 return r;
213
214 /* this will set _id to "other" and return 0 for unknown hypervisors */
215 r = detect_vm_cpuid(&_id);
216 if (r != 0)
217 goto finish;
218
219 r = detect_vm_dmi(&_id);
220 if (r != 0)
221 goto finish;
222
d831deb5
CA
223 r = detect_vm_devicetree(&_id);
224 if (r != 0)
225 goto finish;
226
bdb628ee
ZJS
227 if (_id) {
228 /* "other" */
0fb533a5
LP
229 r = 1;
230 goto finish;
b52aae1d
LP
231 }
232
7080ea16
RR
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")) {
0fb533a5
LP
238 _id = "uml";
239 r = 1;
240 goto finish;
7080ea16
RR
241 }
242
f41925b4
TB
243#if defined(__s390__)
244 {
245 _cleanup_free_ char *t = NULL;
246
247 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
248 if (r >= 0) {
249 if (streq(t, "z/VM"))
250 _id = "zvm";
251 else
252 _id = "kvm";
253 r = 1;
254
255 goto finish;
256 }
257 }
258#endif
259
0fb533a5
LP
260 r = 0;
261
262finish:
263 cached_found = r;
264
265 cached_id = _id;
266 if (id)
267 *id = _id;
268
269 return r;
b52aae1d
LP
270}
271
272int detect_container(const char **id) {
0fb533a5 273
ec202eae
SL
274 static thread_local int cached_found = -1;
275 static thread_local const char *cached_id = NULL;
0fb533a5 276
fdd25311
LP
277 _cleanup_free_ char *m = NULL;
278 const char *_id = NULL, *e = NULL;
ab94af92 279 int r;
b52aae1d 280
0fb533a5
LP
281 if (_likely_(cached_found >= 0)) {
282
283 if (id)
284 *id = cached_id;
285
286 return cached_found;
287 }
288
b52aae1d
LP
289 /* /proc/vz exists in container and outside of the container,
290 * /proc/bc only outside of the container. */
291 if (access("/proc/vz", F_OK) >= 0 &&
292 access("/proc/bc", F_OK) < 0) {
0fb533a5
LP
293 _id = "openvz";
294 r = 1;
295 goto finish;
b52aae1d
LP
296 }
297
fdd25311
LP
298 if (getpid() == 1) {
299 /* If we are PID 1 we can just check our own
300 * environment variable */
301
302 e = getenv("container");
303 if (isempty(e)) {
304 r = 0;
305 goto finish;
306 }
307 } else {
308
309 /* Otherwise, PID 1 dropped this information into a
310 * file in /run. This is better than accessing
311 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
312 * for that. */
313
314 r = read_one_line_file("/run/systemd/container", &m);
315 if (r == -ENOENT) {
536bfdab
LP
316
317 /* Fallback for cases where PID 1 was not
318 * systemd (for example, cases where
319 * init=/bin/sh is used. */
320
321 r = getenv_for_pid(1, "container", &m);
322 if (r <= 0) {
323
324 /* If that didn't work, give up,
325 * assume no container manager.
326 *
327 * Note: This means we still cannot
328 * detect containers if init=/bin/sh
329 * is passed but privileges dropped,
330 * as /proc/1/environ is only readable
331 * with privileges. */
332
333 r = 0;
334 goto finish;
335 }
fdd25311
LP
336 }
337 if (r < 0)
338 return r;
339
340 e = m;
341 }
b52aae1d 342
ab94af92
LP
343 /* We only recognize a selected few here, since we want to
344 * enforce a redacted namespace */
0fb533a5
LP
345 if (streq(e, "lxc"))
346 _id ="lxc";
347 else if (streq(e, "lxc-libvirt"))
348 _id = "lxc-libvirt";
349 else if (streq(e, "systemd-nspawn"))
350 _id = "systemd-nspawn";
893e72da
MS
351 else if (streq(e, "docker"))
352 _id = "docker";
0fb533a5
LP
353 else
354 _id = "other";
355
fdd25311
LP
356 r = 1;
357
0fb533a5
LP
358finish:
359 cached_found = r;
360
361 cached_id = _id;
362 if (id)
363 *id = _id;
b52aae1d 364
ab94af92 365 return r;
b52aae1d
LP
366}
367
368/* Returns a short identifier for the various VM/container implementations */
248fab74 369int detect_virtualization(const char **id) {
b52aae1d 370 int r;
b52aae1d 371
0fb533a5
LP
372 r = detect_container(id);
373 if (r < 0)
374 return r;
375 if (r > 0)
376 return VIRTUALIZATION_CONTAINER;
b52aae1d 377
0fb533a5
LP
378 r = detect_vm(id);
379 if (r < 0)
380 return r;
381 if (r > 0)
382 return VIRTUALIZATION_VM;
b52aae1d 383
0fb533a5 384 return VIRTUALIZATION_NONE;
b52aae1d 385}