]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
update TODO
[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
0fb533a5
LP
223 r = 0;
224
225finish:
226 cached_found = r;
227
228 cached_id = _id;
229 if (id)
230 *id = _id;
231
232 return r;
b52aae1d
LP
233}
234
235int detect_container(const char **id) {
0fb533a5 236
ec202eae
SL
237 static thread_local int cached_found = -1;
238 static thread_local const char *cached_id = NULL;
0fb533a5 239
fdd25311
LP
240 _cleanup_free_ char *m = NULL;
241 const char *_id = NULL, *e = NULL;
ab94af92 242 int r;
b52aae1d 243
0fb533a5
LP
244 if (_likely_(cached_found >= 0)) {
245
246 if (id)
247 *id = cached_id;
248
249 return cached_found;
250 }
251
b52aae1d
LP
252 /* /proc/vz exists in container and outside of the container,
253 * /proc/bc only outside of the container. */
254 if (access("/proc/vz", F_OK) >= 0 &&
255 access("/proc/bc", F_OK) < 0) {
0fb533a5
LP
256 _id = "openvz";
257 r = 1;
258 goto finish;
b52aae1d
LP
259 }
260
fdd25311
LP
261 if (getpid() == 1) {
262 /* If we are PID 1 we can just check our own
263 * environment variable */
264
265 e = getenv("container");
266 if (isempty(e)) {
267 r = 0;
268 goto finish;
269 }
270 } else {
271
272 /* Otherwise, PID 1 dropped this information into a
273 * file in /run. This is better than accessing
274 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
275 * for that. */
276
277 r = read_one_line_file("/run/systemd/container", &m);
278 if (r == -ENOENT) {
279 r = 0;
280 goto finish;
281 }
282 if (r < 0)
283 return r;
284
285 e = m;
286 }
b52aae1d 287
ab94af92
LP
288 /* We only recognize a selected few here, since we want to
289 * enforce a redacted namespace */
0fb533a5
LP
290 if (streq(e, "lxc"))
291 _id ="lxc";
292 else if (streq(e, "lxc-libvirt"))
293 _id = "lxc-libvirt";
294 else if (streq(e, "systemd-nspawn"))
295 _id = "systemd-nspawn";
296 else
297 _id = "other";
298
fdd25311
LP
299 r = 1;
300
0fb533a5
LP
301finish:
302 cached_found = r;
303
304 cached_id = _id;
305 if (id)
306 *id = _id;
b52aae1d 307
ab94af92 308 return r;
b52aae1d
LP
309}
310
311/* Returns a short identifier for the various VM/container implementations */
248fab74 312int detect_virtualization(const char **id) {
b52aae1d 313 int r;
b52aae1d 314
0fb533a5
LP
315 r = detect_container(id);
316 if (r < 0)
317 return r;
318 if (r > 0)
319 return VIRTUALIZATION_CONTAINER;
b52aae1d 320
0fb533a5
LP
321 r = detect_vm(id);
322 if (r < 0)
323 return r;
324 if (r > 0)
325 return VIRTUALIZATION_VM;
b52aae1d 326
0fb533a5 327 return VIRTUALIZATION_NONE;
b52aae1d 328}