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