]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
virt: when detecting containers and /run/systemd/container cannot be read, check...
[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
104static int detect_vm_dmi(const char **_id) {
105
106 /* Both CPUID and DMI are x86 specific interfaces... */
107#if defined(__i386__) || defined(__x86_64__)
108
109 static const char *const dmi_vendors[] = {
110 "/sys/class/dmi/id/sys_vendor",
111 "/sys/class/dmi/id/board_vendor",
112 "/sys/class/dmi/id/bios_vendor"
113 };
114
115 static const char dmi_vendor_table[] =
116 "QEMU\0" "qemu\0"
117 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
118 "VMware\0" "vmware\0"
119 "VMW\0" "vmware\0"
bdb628ee
ZJS
120 "innotek GmbH\0" "oracle\0"
121 "Xen\0" "xen\0"
122 "Bochs\0" "bochs\0";
123 unsigned i;
b52aae1d
LP
124
125 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 126 _cleanup_free_ char *s = NULL;
bdb628ee
ZJS
127 const char *j, *k;
128 int r;
b52aae1d 129
b1b8e816
LP
130 r = read_one_line_file(dmi_vendors[i], &s);
131 if (r < 0) {
b52aae1d
LP
132 if (r != -ENOENT)
133 return r;
134
135 continue;
136 }
137
138 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
0fb533a5 139 if (startswith(s, j)) {
bdb628ee
ZJS
140 *_id = k;
141 return 1;
0fb533a5 142 }
b52aae1d 143 }
bdb628ee
ZJS
144#endif
145
146 return 0;
147}
148
149/* Returns a short identifier for the various VM implementations */
150int detect_vm(const char **id) {
37287585 151 _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
ec202eae
SL
152 static thread_local int cached_found = -1;
153 static thread_local const char *cached_id = NULL;
bdb628ee
ZJS
154 const char *_id = NULL;
155 int r;
b52aae1d 156
bdb628ee
ZJS
157 if (_likely_(cached_found >= 0)) {
158
159 if (id)
160 *id = cached_id;
161
162 return cached_found;
163 }
164
37287585 165 /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
bdb628ee 166 *
37287585
TB
167 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
168 r = read_one_line_file("/proc/xen/capabilities", &domcap);
bdb628ee 169 if (r >= 0) {
37287585
TB
170 char *cap, *i = domcap;
171
172 while ((cap = strsep(&i, ",")))
173 if (streq(cap, "control_d"))
174 break;
175
a71516df 176 if (!cap) {
bdb628ee
ZJS
177 _id = "xen";
178 r = 1;
bdb628ee 179 }
37287585
TB
180
181 goto finish;
182
183 } else if (r == -ENOENT) {
184 _cleanup_free_ char *hvtype = NULL;
185
186 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
187 if (r >= 0) {
188 if (streq(hvtype, "xen")) {
189 _id = "xen";
190 r = 1;
191 goto finish;
192 }
193 } else if (r != -ENOENT)
194 return r;
195 } else
bdb628ee
ZJS
196 return r;
197
198 /* this will set _id to "other" and return 0 for unknown hypervisors */
199 r = detect_vm_cpuid(&_id);
200 if (r != 0)
201 goto finish;
202
203 r = detect_vm_dmi(&_id);
204 if (r != 0)
205 goto finish;
206
207 if (_id) {
208 /* "other" */
0fb533a5
LP
209 r = 1;
210 goto finish;
b52aae1d
LP
211 }
212
7080ea16
RR
213 /* Detect User-Mode Linux by reading /proc/cpuinfo */
214 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
215 if (r < 0)
216 return r;
217 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
0fb533a5
LP
218 _id = "uml";
219 r = 1;
220 goto finish;
7080ea16
RR
221 }
222
f41925b4
TB
223#if defined(__s390__)
224 {
225 _cleanup_free_ char *t = NULL;
226
227 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
228 if (r >= 0) {
229 if (streq(t, "z/VM"))
230 _id = "zvm";
231 else
232 _id = "kvm";
233 r = 1;
234
235 goto finish;
236 }
237 }
238#endif
239
0fb533a5
LP
240 r = 0;
241
242finish:
243 cached_found = r;
244
245 cached_id = _id;
246 if (id)
247 *id = _id;
248
249 return r;
b52aae1d
LP
250}
251
252int detect_container(const char **id) {
0fb533a5 253
ec202eae
SL
254 static thread_local int cached_found = -1;
255 static thread_local const char *cached_id = NULL;
0fb533a5 256
fdd25311
LP
257 _cleanup_free_ char *m = NULL;
258 const char *_id = NULL, *e = NULL;
ab94af92 259 int r;
b52aae1d 260
0fb533a5
LP
261 if (_likely_(cached_found >= 0)) {
262
263 if (id)
264 *id = cached_id;
265
266 return cached_found;
267 }
268
b52aae1d
LP
269 /* /proc/vz exists in container and outside of the container,
270 * /proc/bc only outside of the container. */
271 if (access("/proc/vz", F_OK) >= 0 &&
272 access("/proc/bc", F_OK) < 0) {
0fb533a5
LP
273 _id = "openvz";
274 r = 1;
275 goto finish;
b52aae1d
LP
276 }
277
fdd25311
LP
278 if (getpid() == 1) {
279 /* If we are PID 1 we can just check our own
280 * environment variable */
281
282 e = getenv("container");
283 if (isempty(e)) {
284 r = 0;
285 goto finish;
286 }
287 } else {
288
289 /* Otherwise, PID 1 dropped this information into a
290 * file in /run. This is better than accessing
291 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
292 * for that. */
293
294 r = read_one_line_file("/run/systemd/container", &m);
295 if (r == -ENOENT) {
536bfdab
LP
296
297 /* Fallback for cases where PID 1 was not
298 * systemd (for example, cases where
299 * init=/bin/sh is used. */
300
301 r = getenv_for_pid(1, "container", &m);
302 if (r <= 0) {
303
304 /* If that didn't work, give up,
305 * assume no container manager.
306 *
307 * Note: This means we still cannot
308 * detect containers if init=/bin/sh
309 * is passed but privileges dropped,
310 * as /proc/1/environ is only readable
311 * with privileges. */
312
313 r = 0;
314 goto finish;
315 }
fdd25311
LP
316 }
317 if (r < 0)
318 return r;
319
320 e = m;
321 }
b52aae1d 322
ab94af92
LP
323 /* We only recognize a selected few here, since we want to
324 * enforce a redacted namespace */
0fb533a5
LP
325 if (streq(e, "lxc"))
326 _id ="lxc";
327 else if (streq(e, "lxc-libvirt"))
328 _id = "lxc-libvirt";
329 else if (streq(e, "systemd-nspawn"))
330 _id = "systemd-nspawn";
893e72da
MS
331 else if (streq(e, "docker"))
332 _id = "docker";
0fb533a5
LP
333 else
334 _id = "other";
335
fdd25311
LP
336 r = 1;
337
0fb533a5
LP
338finish:
339 cached_found = r;
340
341 cached_id = _id;
342 if (id)
343 *id = _id;
b52aae1d 344
ab94af92 345 return r;
b52aae1d
LP
346}
347
348/* Returns a short identifier for the various VM/container implementations */
248fab74 349int detect_virtualization(const char **id) {
b52aae1d 350 int r;
b52aae1d 351
0fb533a5
LP
352 r = detect_container(id);
353 if (r < 0)
354 return r;
355 if (r > 0)
356 return VIRTUALIZATION_CONTAINER;
b52aae1d 357
0fb533a5
LP
358 r = detect_vm(id);
359 if (r < 0)
360 return r;
361 if (r > 0)
362 return VIRTUALIZATION_VM;
b52aae1d 363
0fb533a5 364 return VIRTUALIZATION_NONE;
b52aae1d 365}