chiark / gitweb /
timesysnc: reword network watching messages, and move resolver errors to debug
[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 "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 *hvtype = 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 high-level hypervisor sysfs file first:
166          *
167          * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
168         r = read_one_line_file("/sys/hypervisor/type", &hvtype);
169         if (r >= 0) {
170                 if (streq(hvtype, "xen")) {
171                         _id = "xen";
172                         r = 1;
173                         goto finish;
174                 }
175         } else if (r != -ENOENT)
176                 return r;
177
178         /* this will set _id to "other" and return 0 for unknown hypervisors */
179         r = detect_vm_cpuid(&_id);
180         if (r != 0)
181                 goto finish;
182
183         r = detect_vm_dmi(&_id);
184         if (r != 0)
185                 goto finish;
186
187         if (_id) {
188                 /* "other" */
189                 r = 1;
190                 goto finish;
191         }
192
193         /* Detect User-Mode Linux by reading /proc/cpuinfo */
194         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
195         if (r < 0)
196                 return r;
197         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
198                 _id = "uml";
199                 r = 1;
200                 goto finish;
201         }
202
203         r = 0;
204
205 finish:
206         cached_found = r;
207
208         cached_id = _id;
209         if (id)
210                 *id = _id;
211
212         return r;
213 }
214
215 int detect_container(const char **id) {
216
217         static thread_local int cached_found = -1;
218         static thread_local const char *cached_id = NULL;
219
220         _cleanup_free_ char *e = NULL;
221         const char *_id = NULL;
222         int r;
223
224         if (_likely_(cached_found >= 0)) {
225
226                 if (id)
227                         *id = cached_id;
228
229                 return cached_found;
230         }
231
232         /* Unfortunately many of these operations require root access
233          * in one way or another */
234
235         r = running_in_chroot();
236         if (r < 0)
237                 return r;
238         if (r > 0) {
239                 _id = "chroot";
240                 goto finish;
241         }
242
243         /* /proc/vz exists in container and outside of the container,
244          * /proc/bc only outside of the container. */
245         if (access("/proc/vz", F_OK) >= 0 &&
246             access("/proc/bc", F_OK) < 0) {
247                 _id = "openvz";
248                 r = 1;
249                 goto finish;
250         }
251
252         r = getenv_for_pid(1, "container", &e);
253         if (r < 0)
254                 return r;
255         if (r == 0)
256                 goto finish;
257
258         /* We only recognize a selected few here, since we want to
259          * enforce a redacted namespace */
260         if (streq(e, "lxc"))
261                 _id ="lxc";
262         else if (streq(e, "lxc-libvirt"))
263                 _id = "lxc-libvirt";
264         else if (streq(e, "systemd-nspawn"))
265                 _id = "systemd-nspawn";
266         else
267                 _id = "other";
268
269 finish:
270         cached_found = r;
271
272         cached_id = _id;
273         if (id)
274                 *id = _id;
275
276         return r;
277 }
278
279 /* Returns a short identifier for the various VM/container implementations */
280 int detect_virtualization(const char **id) {
281         int r;
282
283         r = detect_container(id);
284         if (r < 0)
285                 return r;
286         if (r > 0)
287                 return VIRTUALIZATION_CONTAINER;
288
289         r = detect_vm(id);
290         if (r < 0)
291                 return r;
292         if (r > 0)
293                 return VIRTUALIZATION_VM;
294
295         return VIRTUALIZATION_NONE;
296 }