]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/virt.c
util-lib: split out allocation calls into alloc-util.[ch]
[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 <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "alloc-util.h"
27 #include "fileio.h"
28 #include "process-util.h"
29 #include "stat-util.h"
30 #include "string-table.h"
31 #include "string-util.h"
32 #include "util.h"
33 #include "virt.h"
34
35 static int detect_vm_cpuid(void) {
36
37 /* Both CPUID and DMI are x86 specific interfaces... */
38 #if defined(__i386__) || defined(__x86_64__)
39
40 static const struct {
41 const char *cpuid;
42 int id;
43 } cpuid_vendor_table[] = {
44 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
45 { "KVMKVMKVM", VIRTUALIZATION_KVM },
46 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
47 { "VMwareVMware", VIRTUALIZATION_VMWARE },
48 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
49 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
50 };
51
52 uint32_t eax, ecx;
53 bool hypervisor;
54
55 /* http://lwn.net/Articles/301888/ */
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) {
80 union {
81 uint32_t sig32[3];
82 char text[13];
83 } sig = {};
84 unsigned j;
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
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;
102
103 return VIRTUALIZATION_VM_OTHER;
104 }
105 #endif
106
107 return VIRTUALIZATION_NONE;
108 }
109
110 static int detect_vm_device_tree(void) {
111 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
112 _cleanup_free_ char *hvtype = NULL;
113 int r;
114
115 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
116 if (r == -ENOENT) {
117 _cleanup_closedir_ DIR *dir = NULL;
118 struct dirent *dent;
119
120 dir = opendir("/proc/device-tree");
121 if (!dir) {
122 if (errno == ENOENT)
123 return VIRTUALIZATION_NONE;
124 return -errno;
125 }
126
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;
143 #endif
144 }
145
146 static int detect_vm_dmi(void) {
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
157 static const struct {
158 const char *vendor;
159 int id;
160 } dmi_vendor_table[] = {
161 { "QEMU", VIRTUALIZATION_QEMU },
162 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
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 };
170 unsigned i;
171 int r;
172
173 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
174 _cleanup_free_ char *s = NULL;
175 unsigned j;
176
177 r = read_one_line_file(dmi_vendors[i], &s);
178 if (r < 0) {
179 if (r == -ENOENT)
180 continue;
181
182 return r;
183 }
184
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;
188 }
189 #endif
190
191 return VIRTUALIZATION_NONE;
192 }
193
194 static int detect_vm_xen(void) {
195 _cleanup_free_ char *domcap = NULL;
196 char *cap, *i;
197 int r;
198
199 r = read_one_line_file("/proc/xen/capabilities", &domcap);
200 if (r == -ENOENT)
201 return VIRTUALIZATION_NONE;
202
203 i = domcap;
204 while ((cap = strsep(&i, ",")))
205 if (streq(cap, "control_d"))
206 break;
207
208 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
209 }
210
211 static int detect_vm_hypervisor(void) {
212 _cleanup_free_ char *hvtype = NULL;
213 int r;
214
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;
220
221 if (streq(hvtype, "xen"))
222 return VIRTUALIZATION_XEN;
223 else
224 return VIRTUALIZATION_VM_OTHER;
225 }
226
227 static int detect_vm_uml(void) {
228 _cleanup_free_ char *cpuinfo_contents = NULL;
229 int r;
230
231 /* Detect User-Mode Linux by reading /proc/cpuinfo */
232 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
233 if (r < 0)
234 return r;
235 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
236 return VIRTUALIZATION_UML;
237
238 return VIRTUALIZATION_NONE;
239 }
240
241 static int detect_vm_zvm(void) {
242
243 #if defined(__s390__)
244 _cleanup_free_ char *t = NULL;
245 int r;
246
247 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
248 if (r == -ENOENT)
249 return VIRTUALIZATION_NONE;
250 if (r < 0)
251 return r;
252
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 }
261
262 /* Returns a short identifier for the various VM implementations */
263 int detect_vm(void) {
264 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
265 int r;
266
267 if (cached_found >= 0)
268 return cached_found;
269
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)
279 goto finish;
280
281 r = detect_vm_dmi();
282 if (r < 0)
283 return r;
284 if (r != VIRTUALIZATION_NONE)
285 goto finish;
286
287 r = detect_vm_cpuid();
288 if (r < 0)
289 return r;
290 if (r != VIRTUALIZATION_NONE)
291 goto finish;
292
293 r = detect_vm_hypervisor();
294 if (r < 0)
295 return r;
296 if (r != VIRTUALIZATION_NONE)
297 goto finish;
298
299 r = detect_vm_device_tree();
300 if (r < 0)
301 return r;
302 if (r != VIRTUALIZATION_NONE)
303 goto finish;
304
305 r = detect_vm_uml();
306 if (r < 0)
307 return r;
308 if (r != VIRTUALIZATION_NONE)
309 goto finish;
310
311 r = detect_vm_zvm();
312 if (r < 0)
313 return r;
314
315 finish:
316 cached_found = r;
317 return r;
318 }
319
320 int detect_container(void) {
321
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 };
331
332 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
333 _cleanup_free_ char *m = NULL;
334 const char *e = NULL;
335 unsigned j;
336 int r;
337
338 if (cached_found >= 0)
339 return cached_found;
340
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) {
345 r = VIRTUALIZATION_OPENVZ;
346 goto finish;
347 }
348
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)) {
355 r = VIRTUALIZATION_NONE;
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) {
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
384 r = VIRTUALIZATION_NONE;
385 goto finish;
386 }
387 }
388 if (r < 0)
389 return r;
390
391 e = m;
392 }
393
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 }
399
400 r = VIRTUALIZATION_NONE;
401
402 finish:
403 cached_found = r;
404 return r;
405 }
406
407 int detect_virtualization(void) {
408 int r;
409
410 r = detect_container();
411 if (r != 0)
412 return r;
413
414 return detect_vm();
415 }
416
417 int 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
427 static 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
449 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);