]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
virt: rework container detection logic
[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) {
151 _cleanup_free_ char *hvtype = 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
165 /* Try high-level hypervisor sysfs file first:
166 *
167 * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
168 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
169 if (r >= 0) {
170 if (streq(hvtype, "xen")) {
171 _id = "xen";
172 r = 1;
173 goto finish;
174 }
175 } else if (r != -ENOENT)
176 return r;
177
178 /* this will set _id to "other" and return 0 for unknown hypervisors */
179 r = detect_vm_cpuid(&_id);
180 if (r != 0)
181 goto finish;
182
183 r = detect_vm_dmi(&_id);
184 if (r != 0)
185 goto finish;
186
187 if (_id) {
188 /* "other" */
0fb533a5
LP
189 r = 1;
190 goto finish;
b52aae1d
LP
191 }
192
7080ea16
RR
193 /* Detect User-Mode Linux by reading /proc/cpuinfo */
194 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
195 if (r < 0)
196 return r;
197 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
0fb533a5
LP
198 _id = "uml";
199 r = 1;
200 goto finish;
7080ea16
RR
201 }
202
0fb533a5
LP
203 r = 0;
204
205finish:
206 cached_found = r;
207
208 cached_id = _id;
209 if (id)
210 *id = _id;
211
212 return r;
b52aae1d
LP
213}
214
215int detect_container(const char **id) {
0fb533a5 216
ec202eae
SL
217 static thread_local int cached_found = -1;
218 static thread_local const char *cached_id = NULL;
0fb533a5 219
fdd25311
LP
220 _cleanup_free_ char *m = NULL;
221 const char *_id = NULL, *e = NULL;
ab94af92 222 int r;
b52aae1d 223
0fb533a5
LP
224 if (_likely_(cached_found >= 0)) {
225
226 if (id)
227 *id = cached_id;
228
229 return cached_found;
230 }
231
b52aae1d
LP
232 /* /proc/vz exists in container and outside of the container,
233 * /proc/bc only outside of the container. */
234 if (access("/proc/vz", F_OK) >= 0 &&
235 access("/proc/bc", F_OK) < 0) {
0fb533a5
LP
236 _id = "openvz";
237 r = 1;
238 goto finish;
b52aae1d
LP
239 }
240
fdd25311
LP
241 if (getpid() == 1) {
242 /* If we are PID 1 we can just check our own
243 * environment variable */
244
245 e = getenv("container");
246 if (isempty(e)) {
247 r = 0;
248 goto finish;
249 }
250 } else {
251
252 /* Otherwise, PID 1 dropped this information into a
253 * file in /run. This is better than accessing
254 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
255 * for that. */
256
257 r = read_one_line_file("/run/systemd/container", &m);
258 if (r == -ENOENT) {
259 r = 0;
260 goto finish;
261 }
262 if (r < 0)
263 return r;
264
265 e = m;
266 }
b52aae1d 267
ab94af92
LP
268 /* We only recognize a selected few here, since we want to
269 * enforce a redacted namespace */
0fb533a5
LP
270 if (streq(e, "lxc"))
271 _id ="lxc";
272 else if (streq(e, "lxc-libvirt"))
273 _id = "lxc-libvirt";
274 else if (streq(e, "systemd-nspawn"))
275 _id = "systemd-nspawn";
276 else
277 _id = "other";
278
fdd25311
LP
279 r = 1;
280
0fb533a5
LP
281finish:
282 cached_found = r;
283
284 cached_id = _id;
285 if (id)
286 *id = _id;
b52aae1d 287
ab94af92 288 return r;
b52aae1d
LP
289}
290
291/* Returns a short identifier for the various VM/container implementations */
248fab74 292int detect_virtualization(const char **id) {
b52aae1d 293 int r;
b52aae1d 294
0fb533a5
LP
295 r = detect_container(id);
296 if (r < 0)
297 return r;
298 if (r > 0)
299 return VIRTUALIZATION_CONTAINER;
b52aae1d 300
0fb533a5
LP
301 r = detect_vm(id);
302 if (r < 0)
303 return r;
304 if (r > 0)
305 return VIRTUALIZATION_VM;
b52aae1d 306
0fb533a5 307 return VIRTUALIZATION_NONE;
b52aae1d 308}