]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/virt.c
shared: add process-util.[ch]
[thirdparty/systemd.git] / src / shared / 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;
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 if (r != 0)
238 goto finish;
239
240 r = detect_vm_dmi(&_id);
241 if (r != 0)
242 goto finish;
243
244 r = detect_vm_devicetree(&_id);
245 if (r != 0)
246 goto finish;
247
248 if (_id) {
249 /* "other" */
250 r = 1;
251 goto finish;
252 }
253
254 /* Detect User-Mode Linux by reading /proc/cpuinfo */
255 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
256 if (r < 0)
257 return r;
258 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
259 _id = "uml";
260 r = 1;
261 goto finish;
262 }
263
264 #if defined(__s390__)
265 {
266 _cleanup_free_ char *t = NULL;
267
268 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
269 if (r >= 0) {
270 if (streq(t, "z/VM"))
271 _id = "zvm";
272 else
273 _id = "kvm";
274 r = 1;
275
276 goto finish;
277 }
278 }
279 #endif
280
281 r = 0;
282
283 finish:
284 cached_found = r;
285
286 cached_id = _id;
287 if (id)
288 *id = _id;
289
290 return r;
291 }
292
293 int detect_container(const char **id) {
294
295 static thread_local int cached_found = -1;
296 static thread_local const char *cached_id = NULL;
297
298 _cleanup_free_ char *m = NULL;
299 const char *_id = NULL, *e = NULL;
300 int r;
301
302 if (_likely_(cached_found >= 0)) {
303
304 if (id)
305 *id = cached_id;
306
307 return cached_found;
308 }
309
310 /* /proc/vz exists in container and outside of the container,
311 * /proc/bc only outside of the container. */
312 if (access("/proc/vz", F_OK) >= 0 &&
313 access("/proc/bc", F_OK) < 0) {
314 _id = "openvz";
315 r = 1;
316 goto finish;
317 }
318
319 if (getpid() == 1) {
320 /* If we are PID 1 we can just check our own
321 * environment variable */
322
323 e = getenv("container");
324 if (isempty(e)) {
325 r = 0;
326 goto finish;
327 }
328 } else {
329
330 /* Otherwise, PID 1 dropped this information into a
331 * file in /run. This is better than accessing
332 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
333 * for that. */
334
335 r = read_one_line_file("/run/systemd/container", &m);
336 if (r == -ENOENT) {
337
338 /* Fallback for cases where PID 1 was not
339 * systemd (for example, cases where
340 * init=/bin/sh is used. */
341
342 r = getenv_for_pid(1, "container", &m);
343 if (r <= 0) {
344
345 /* If that didn't work, give up,
346 * assume no container manager.
347 *
348 * Note: This means we still cannot
349 * detect containers if init=/bin/sh
350 * is passed but privileges dropped,
351 * as /proc/1/environ is only readable
352 * with privileges. */
353
354 r = 0;
355 goto finish;
356 }
357 }
358 if (r < 0)
359 return r;
360
361 e = m;
362 }
363
364 /* We only recognize a selected few here, since we want to
365 * enforce a redacted namespace */
366 if (streq(e, "lxc"))
367 _id ="lxc";
368 else if (streq(e, "lxc-libvirt"))
369 _id = "lxc-libvirt";
370 else if (streq(e, "systemd-nspawn"))
371 _id = "systemd-nspawn";
372 else if (streq(e, "docker"))
373 _id = "docker";
374 else
375 _id = "other";
376
377 r = 1;
378
379 finish:
380 cached_found = r;
381
382 cached_id = _id;
383 if (id)
384 *id = _id;
385
386 return r;
387 }
388
389 /* Returns a short identifier for the various VM/container implementations */
390 int detect_virtualization(const char **id) {
391 int r;
392
393 r = detect_container(id);
394 if (r < 0)
395 return r;
396 if (r > 0)
397 return VIRTUALIZATION_CONTAINER;
398
399 r = detect_vm(id);
400 if (r < 0)
401 return r;
402 if (r > 0)
403 return VIRTUALIZATION_VM;
404
405 return VIRTUALIZATION_NONE;
406 }