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