]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
util: unify how we see srand()
[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) {
296 r = 0;
297 goto finish;
298 }
299 if (r < 0)
300 return r;
301
302 e = m;
303 }
b52aae1d 304
ab94af92
LP
305 /* We only recognize a selected few here, since we want to
306 * enforce a redacted namespace */
0fb533a5
LP
307 if (streq(e, "lxc"))
308 _id ="lxc";
309 else if (streq(e, "lxc-libvirt"))
310 _id = "lxc-libvirt";
311 else if (streq(e, "systemd-nspawn"))
312 _id = "systemd-nspawn";
893e72da
MS
313 else if (streq(e, "docker"))
314 _id = "docker";
0fb533a5
LP
315 else
316 _id = "other";
317
fdd25311
LP
318 r = 1;
319
0fb533a5
LP
320finish:
321 cached_found = r;
322
323 cached_id = _id;
324 if (id)
325 *id = _id;
b52aae1d 326
ab94af92 327 return r;
b52aae1d
LP
328}
329
330/* Returns a short identifier for the various VM/container implementations */
248fab74 331int detect_virtualization(const char **id) {
b52aae1d 332 int r;
b52aae1d 333
0fb533a5
LP
334 r = detect_container(id);
335 if (r < 0)
336 return r;
337 if (r > 0)
338 return VIRTUALIZATION_CONTAINER;
b52aae1d 339
0fb533a5
LP
340 r = detect_vm(id);
341 if (r < 0)
342 return r;
343 if (r > 0)
344 return VIRTUALIZATION_VM;
b52aae1d 345
0fb533a5 346 return VIRTUALIZATION_NONE;
b52aae1d 347}