]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/virt.c
update TODO
[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 "virt.h"
28 #include "fileio.h"
29
30 static int detect_vm_cpuid(const char **_id) {
31
32 /* Both CPUID and DMI are x86 specific interfaces... */
33 #if defined(__i386__) || defined(__x86_64__)
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];
47 } sig = {};
48 const char *j, *k;
49 bool hypervisor;
50
51 /* http://lwn.net/Articles/301888/ */
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)) {
92 *_id = k;
93 return 1;
94 }
95
96 *_id = "other";
97 return 0;
98 }
99 #endif
100
101 return 0;
102 }
103
104 static 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"
120 "innotek GmbH\0" "oracle\0"
121 "Xen\0" "xen\0"
122 "Bochs\0" "bochs\0";
123 unsigned i;
124
125 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
126 _cleanup_free_ char *s = NULL;
127 const char *j, *k;
128 int r;
129
130 r = read_one_line_file(dmi_vendors[i], &s);
131 if (r < 0) {
132 if (r != -ENOENT)
133 return r;
134
135 continue;
136 }
137
138 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
139 if (startswith(s, j)) {
140 *_id = k;
141 return 1;
142 }
143 }
144 #endif
145
146 return 0;
147 }
148
149 /* Returns a short identifier for the various VM implementations */
150 int detect_vm(const char **id) {
151 _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
152 static thread_local int cached_found = -1;
153 static thread_local const char *cached_id = NULL;
154 const char *_id = NULL;
155 int r;
156
157 if (_likely_(cached_found >= 0)) {
158
159 if (id)
160 *id = cached_id;
161
162 return cached_found;
163 }
164
165 /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
166 *
167 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
168 r = read_one_line_file("/proc/xen/capabilities", &domcap);
169 if (r >= 0) {
170 char *cap, *i = domcap;
171
172 while ((cap = strsep(&i, ",")))
173 if (streq(cap, "control_d"))
174 break;
175
176 if (!cap) {
177 _id = "xen";
178 r = 1;
179 }
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
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" */
209 r = 1;
210 goto finish;
211 }
212
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")) {
218 _id = "uml";
219 r = 1;
220 goto finish;
221 }
222
223 r = 0;
224
225 finish:
226 cached_found = r;
227
228 cached_id = _id;
229 if (id)
230 *id = _id;
231
232 return r;
233 }
234
235 int detect_container(const char **id) {
236
237 static thread_local int cached_found = -1;
238 static thread_local const char *cached_id = NULL;
239
240 _cleanup_free_ char *m = NULL;
241 const char *_id = NULL, *e = NULL;
242 int r;
243
244 if (_likely_(cached_found >= 0)) {
245
246 if (id)
247 *id = cached_id;
248
249 return cached_found;
250 }
251
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) {
256 _id = "openvz";
257 r = 1;
258 goto finish;
259 }
260
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 }
287
288 /* We only recognize a selected few here, since we want to
289 * enforce a redacted namespace */
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
299 r = 1;
300
301 finish:
302 cached_found = r;
303
304 cached_id = _id;
305 if (id)
306 *id = _id;
307
308 return r;
309 }
310
311 /* Returns a short identifier for the various VM/container implementations */
312 int detect_virtualization(const char **id) {
313 int r;
314
315 r = detect_container(id);
316 if (r < 0)
317 return r;
318 if (r > 0)
319 return VIRTUALIZATION_CONTAINER;
320
321 r = detect_vm(id);
322 if (r < 0)
323 return r;
324 if (r > 0)
325 return VIRTUALIZATION_VM;
326
327 return VIRTUALIZATION_NONE;
328 }