]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
relicense to LGPLv2.1 (with exceptions)
[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"
28
29/* Returns a short identifier for the various VM implementations */
30int detect_vm(const char **id) {
31
32#if defined(__i386__) || defined(__x86_64__)
33
34 /* Both CPUID and DMI are x86 specific interfaces... */
35
36 static const char *const dmi_vendors[] = {
37 "/sys/class/dmi/id/sys_vendor",
38 "/sys/class/dmi/id/board_vendor",
39 "/sys/class/dmi/id/bios_vendor"
40 };
41
42 static const char dmi_vendor_table[] =
43 "QEMU\0" "qemu\0"
44 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
45 "VMware\0" "vmware\0"
46 "VMW\0" "vmware\0"
47 "Microsoft Corporation\0" "microsoft\0"
48 "innotek GmbH\0" "oracle\0"
49 "Xen\0" "xen\0"
50 "Bochs\0" "bochs\0";
51
52 static const char cpuid_vendor_table[] =
53 "XenVMMXenVMM\0" "xen\0"
54 "KVMKVMKVM\0" "kvm\0"
55 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
56 "VMwareVMware\0" "vmware\0"
57 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
58 "Microsoft Hv\0" "microsoft\0";
59
60 uint32_t eax, ecx;
61 union {
62 uint32_t sig32[3];
63 char text[13];
64 } sig;
65 unsigned i;
66 const char *j, *k;
67 bool hypervisor;
68
69 /* http://lwn.net/Articles/301888/ */
70 zero(sig);
71
72#if defined (__i386__)
73#define REG_a "eax"
74#define REG_b "ebx"
75#elif defined (__amd64__)
76#define REG_a "rax"
77#define REG_b "rbx"
78#endif
79
80 /* First detect whether there is a hypervisor */
81 eax = 1;
82 __asm__ __volatile__ (
83 /* ebx/rbx is being used for PIC! */
84 " push %%"REG_b" \n\t"
85 " cpuid \n\t"
86 " pop %%"REG_b" \n\t"
87
88 : "=a" (eax), "=c" (ecx)
89 : "0" (eax)
90 );
91
92 hypervisor = !!(ecx & 0x80000000U);
93
94 if (hypervisor) {
95
96 /* There is a hypervisor, see what it is */
97 eax = 0x40000000U;
98 __asm__ __volatile__ (
99 /* ebx/rbx is being used for PIC! */
100 " push %%"REG_b" \n\t"
101 " cpuid \n\t"
102 " mov %%ebx, %1 \n\t"
103 " pop %%"REG_b" \n\t"
104
105 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
106 : "0" (eax)
107 );
108
109 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
110 if (streq(sig.text, j)) {
111
112 if (id)
113 *id = k;
114
115 return 1;
116 }
117 }
118
119 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
120 char *s;
121 int r;
122 const char *found = NULL;
123
124 if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
125 if (r != -ENOENT)
126 return r;
127
128 continue;
129 }
130
131 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
132 if (startswith(s, j))
133 found = k;
134 free(s);
135
136 if (found) {
137 if (id)
138 *id = found;
139
140 return 1;
141 }
142 }
143
144 if (hypervisor) {
145 if (id)
146 *id = "other";
147
148 return 1;
149 }
150
151#endif
152 return 0;
153}
154
155int detect_container(const char **id) {
156 FILE *f;
157
158 /* Unfortunately many of these operations require root access
159 * in one way or another */
160
161 if (geteuid() != 0)
162 return -EPERM;
163
164 if (running_in_chroot() > 0) {
165
166 if (id)
167 *id = "chroot";
168
169 return 1;
170 }
171
172 /* /proc/vz exists in container and outside of the container,
173 * /proc/bc only outside of the container. */
174 if (access("/proc/vz", F_OK) >= 0 &&
175 access("/proc/bc", F_OK) < 0) {
176
177 if (id)
178 *id = "openvz";
179
180 return 1;
181 }
182
183 f = fopen("/proc/1/environ", "re");
184 if (f) {
185 bool done = false;
186
187 do {
188 char line[LINE_MAX];
189 unsigned i;
190
191 for (i = 0; i < sizeof(line)-1; i++) {
192 int c;
193
194 c = getc(f);
195 if (_unlikely_(c == EOF)) {
196 done = true;
197 break;
198 } else if (c == 0)
199 break;
200
201 line[i] = c;
202 }
203 line[i] = 0;
204
205 if (streq(line, "container=lxc")) {
206 fclose(f);
207
208 if (id)
209 *id = "lxc";
210 return 1;
211
7d39db92
LP
212 } else if (streq(line, "container=lxc-libvirt")) {
213 fclose(f);
214
215 if (id)
216 *id = "lxc-libvirt";
217 return 1;
218
b52aae1d
LP
219 } else if (streq(line, "container=systemd-nspawn")) {
220 fclose(f);
221
222 if (id)
223 *id = "systemd-nspawn";
224 return 1;
225
226 } else if (startswith(line, "container=")) {
227 fclose(f);
228
229 if (id)
230 *id = "other";
231 return 1;
232 }
233
234 } while (!done);
235
236 fclose(f);
237 }
238
b52aae1d
LP
239 return 0;
240}
241
242/* Returns a short identifier for the various VM/container implementations */
243Virtualization detect_virtualization(const char **id) {
244
245 static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
246 static __thread const char *cached_id = NULL;
247
248 const char *_id;
249 int r;
250 Virtualization v;
251
252 if (_likely_(cached_virt >= 0)) {
253
254 if (id && cached_virt > 0)
255 *id = cached_id;
256
257 return cached_virt;
258 }
259
260 r = detect_container(&_id);
261 if (r < 0) {
262 v = r;
263 goto finish;
264 } else if (r > 0) {
265 v = VIRTUALIZATION_CONTAINER;
266 goto finish;
267 }
268
269 r = detect_vm(&_id);
270 if (r < 0) {
271 v = r;
272 goto finish;
273 } else if (r > 0) {
274 v = VIRTUALIZATION_VM;
275 goto finish;
276 }
277
278 v = VIRTUALIZATION_NONE;
279
280finish:
281 if (v > 0) {
282 cached_id = _id;
283
284 if (id)
285 *id = _id;
286 }
287
288 if (v >= 0)
289 cached_virt = v;
290
291 return v;
292}