chiark / gitweb /
Prep v220: Apply "Fixes to user and session saving"
[elogind.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 "process-util.h"
28 #include "virt.h"
29 #include "fileio.h"
30
31 static int detect_vm_cpuid(const char **_id) {
32
33         /* Both CPUID and DMI are x86 specific interfaces... */
34 #if defined(__i386__) || defined(__x86_64__)
35
36         static const char cpuid_vendor_table[] =
37                 "XenVMMXenVMM\0"          "xen\0"
38                 "KVMKVMKVM\0"             "kvm\0"
39                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
40                 "VMwareVMware\0"          "vmware\0"
41                 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
42                 "Microsoft Hv\0"          "microsoft\0";
43
44         uint32_t eax, ecx;
45         union {
46                 uint32_t sig32[3];
47                 char text[13];
48         } sig = {};
49         const char *j, *k;
50         bool hypervisor;
51
52         /* http://lwn.net/Articles/301888/ */
53
54 #if defined (__i386__)
55 #define REG_a "eax"
56 #define REG_b "ebx"
57 #elif defined (__amd64__)
58 #define REG_a "rax"
59 #define REG_b "rbx"
60 #endif
61
62         /* First detect whether there is a hypervisor */
63         eax = 1;
64         __asm__ __volatile__ (
65                 /* ebx/rbx is being used for PIC! */
66                 "  push %%"REG_b"         \n\t"
67                 "  cpuid                  \n\t"
68                 "  pop %%"REG_b"          \n\t"
69
70                 : "=a" (eax), "=c" (ecx)
71                 : "0" (eax)
72         );
73
74         hypervisor = !!(ecx & 0x80000000U);
75
76         if (hypervisor) {
77
78                 /* There is a hypervisor, see what it is */
79                 eax = 0x40000000U;
80                 __asm__ __volatile__ (
81                         /* ebx/rbx is being used for PIC! */
82                         "  push %%"REG_b"         \n\t"
83                         "  cpuid                  \n\t"
84                         "  mov %%ebx, %1          \n\t"
85                         "  pop %%"REG_b"          \n\t"
86
87                         : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
88                         : "0" (eax)
89                 );
90
91                 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
92                         if (streq(sig.text, j)) {
93                                 *_id = k;
94                                 return 1;
95                         }
96
97                 *_id = "other";
98                 return 0;
99         }
100 #endif
101
102         return 0;
103 }
104
105 static int detect_vm_devicetree(const char **_id) {
106 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
107         _cleanup_free_ char *hvtype = NULL;
108         int r;
109
110         r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
111         if (r >= 0) {
112                 if (streq(hvtype, "linux,kvm")) {
113                         *_id = "kvm";
114                         return 1;
115                 } else if (strstr(hvtype, "xen")) {
116                         *_id = "xen";
117                         return 1;
118                 }
119         } else if (r == -ENOENT) {
120                 _cleanup_closedir_ DIR *dir = NULL;
121                 struct dirent *dent;
122
123                 dir = opendir("/proc/device-tree");
124                 if (!dir) {
125                         if (errno == ENOENT)
126                                 return 0;
127                         return -errno;
128                 }
129
130                 FOREACH_DIRENT(dent, dir, return -errno) {
131                         if (strstr(dent->d_name, "fw-cfg")) {
132                                 *_id = "qemu";
133                                 return 1;
134                         }
135                 }
136         }
137 #endif
138         return 0;
139 }
140
141 static int detect_vm_dmi(const char **_id) {
142
143         /* Both CPUID and DMI are x86 specific interfaces... */
144 #if defined(__i386__) || defined(__x86_64__)
145
146         static const char *const dmi_vendors[] = {
147                 "/sys/class/dmi/id/sys_vendor",
148                 "/sys/class/dmi/id/board_vendor",
149                 "/sys/class/dmi/id/bios_vendor"
150         };
151
152         static const char dmi_vendor_table[] =
153                 "QEMU\0"                  "qemu\0"
154                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
155                 "VMware\0"                "vmware\0"
156                 "VMW\0"                   "vmware\0"
157                 "innotek GmbH\0"          "oracle\0"
158                 "Xen\0"                   "xen\0"
159                 "Bochs\0"                 "bochs\0";
160         unsigned i;
161
162         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
163                 _cleanup_free_ char *s = NULL;
164                 const char *j, *k;
165                 int r;
166
167                 r = read_one_line_file(dmi_vendors[i], &s);
168                 if (r < 0) {
169                         if (r != -ENOENT)
170                                 return r;
171
172                         continue;
173                 }
174
175                 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
176                         if (startswith(s, j)) {
177                                 *_id = k;
178                                 return 1;
179                         }
180         }
181 #endif
182
183         return 0;
184 }
185
186 /* Returns a short identifier for the various VM implementations */
187 int detect_vm(const char **id) {
188         _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
189         static thread_local int cached_found = -1;
190         static thread_local const char *cached_id = NULL;
191         const char *_id = NULL;
192         int r;
193
194         if (_likely_(cached_found >= 0)) {
195
196                 if (id)
197                         *id = cached_id;
198
199                 return cached_found;
200         }
201
202         /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
203          *
204          * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
205         r = read_one_line_file("/proc/xen/capabilities", &domcap);
206         if (r >= 0) {
207                 char *cap, *i = domcap;
208
209                 while ((cap = strsep(&i, ",")))
210                         if (streq(cap, "control_d"))
211                                 break;
212
213                 if (!cap)  {
214                         _id = "xen";
215                         r = 1;
216                 }
217
218                 goto finish;
219
220         } else if (r == -ENOENT) {
221                 _cleanup_free_ char *hvtype = NULL;
222
223                 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
224                 if (r >= 0) {
225                         if (streq(hvtype, "xen")) {
226                                 _id = "xen";
227                                 r = 1;
228                                 goto finish;
229                         }
230                 } else if (r != -ENOENT)
231                         return r;
232         } else
233                 return r;
234
235         /* this will set _id to "other" and return 0 for unknown hypervisors */
236         r = detect_vm_cpuid(&_id);
237         if (r != 0)
238                 goto finish;
239
240         r = detect_vm_dmi(&_id);
241         if (r != 0)
242                 goto finish;
243
244         r = detect_vm_devicetree(&_id);
245         if (r != 0)
246                 goto finish;
247
248         if (_id) {
249                 /* "other" */
250                 r = 1;
251                 goto finish;
252         }
253
254         /* Detect User-Mode Linux by reading /proc/cpuinfo */
255         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
256         if (r < 0)
257                 return r;
258         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
259                 _id = "uml";
260                 r = 1;
261                 goto finish;
262         }
263
264 #if defined(__s390__)
265         {
266                 _cleanup_free_ char *t = NULL;
267
268                 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
269                 if (r >= 0) {
270                         if (streq(t, "z/VM"))
271                                 _id = "zvm";
272                         else
273                                 _id = "kvm";
274                         r = 1;
275
276                         goto finish;
277                 }
278         }
279 #endif
280
281         r = 0;
282
283 finish:
284         cached_found = r;
285
286         cached_id = _id;
287         if (id)
288                 *id = _id;
289
290         return r;
291 }
292
293 int detect_container(const char **id) {
294
295         static thread_local int cached_found = -1;
296         static thread_local const char *cached_id = NULL;
297
298         _cleanup_free_ char *m = NULL;
299         const char *_id = NULL, *e = NULL;
300         int r;
301
302         if (_likely_(cached_found >= 0)) {
303
304                 if (id)
305                         *id = cached_id;
306
307                 return cached_found;
308         }
309
310         /* /proc/vz exists in container and outside of the container,
311          * /proc/bc only outside of the container. */
312         if (access("/proc/vz", F_OK) >= 0 &&
313             access("/proc/bc", F_OK) < 0) {
314                 _id = "openvz";
315                 r = 1;
316                 goto finish;
317         }
318
319         if (getpid() == 1) {
320                 /* If we are PID 1 we can just check our own
321                  * environment variable */
322
323                 e = getenv("container");
324                 if (isempty(e)) {
325                         r = 0;
326                         goto finish;
327                 }
328         } else {
329
330                 /* Otherwise, PID 1 dropped this information into a
331                  * file in /run. This is better than accessing
332                  * /proc/1/environ, since we don't need CAP_SYS_PTRACE
333                  * for that. */
334
335                 r = read_one_line_file("/run/systemd/container", &m);
336                 if (r == -ENOENT) {
337
338                         /* Fallback for cases where PID 1 was not
339                          * systemd (for example, cases where
340                          * init=/bin/sh is used. */
341
342                         r = getenv_for_pid(1, "container", &m);
343                         if (r <= 0) {
344
345                                 /* If that didn't work, give up,
346                                  * assume no container manager.
347                                  *
348                                  * Note: This means we still cannot
349                                  * detect containers if init=/bin/sh
350                                  * is passed but privileges dropped,
351                                  * as /proc/1/environ is only readable
352                                  * with privileges. */
353
354                                 r = 0;
355                                 goto finish;
356                         }
357                 }
358                 if (r < 0)
359                         return r;
360
361                 e = m;
362         }
363
364         /* We only recognize a selected few here, since we want to
365          * enforce a redacted namespace */
366         if (streq(e, "lxc"))
367                 _id ="lxc";
368         else if (streq(e, "lxc-libvirt"))
369                 _id = "lxc-libvirt";
370         else if (streq(e, "systemd-nspawn"))
371                 _id = "systemd-nspawn";
372         else if (streq(e, "docker"))
373                 _id = "docker";
374         else
375                 _id = "other";
376
377         r = 1;
378
379 finish:
380         cached_found = r;
381
382         cached_id = _id;
383         if (id)
384                 *id = _id;
385
386         return r;
387 }
388
389 /* Returns a short identifier for the various VM/container implementations */
390 int detect_virtualization(const char **id) {
391         int r;
392
393         r = detect_container(id);
394         if (r < 0)
395                 return r;
396         if (r > 0)
397                 return VIRTUALIZATION_CONTAINER;
398
399         r = detect_vm(id);
400         if (r < 0)
401                 return r;
402         if (r > 0)
403                 return VIRTUALIZATION_VM;
404
405         return VIRTUALIZATION_NONE;
406 }