]> git.ipfire.org Git - oddments/fireinfo.git/blob - src/fireinfo.c
Implement the VMWare hypervisor port check.
[oddments/fireinfo.git] / src / fireinfo.c
1 /*
2 * fireinfo.c
3 *
4 * Copyright (C) 2010 IPFire. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <Python.h>
21
22 #include <fcntl.h>
23 #include <linux/hdreg.h>
24 #include <stdbool.h>
25 #include <sys/ioctl.h>
26
27 /*
28 Big parts of this were taken from
29 http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=blob;f=sys-utils/lscpu.c
30 */
31
32 /* /sys paths */
33 #define _PATH_PROC_XEN "/proc/xen"
34 #define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
35 #define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
36
37 /* Used for the vmware hypervisor port detection */
38 #define VMWARE_HYPERVISOR_MAGIC 0x564D5868
39 #define VMWARE_HYPERVISOR_PORT 0x5658
40
41 #define VMWARE_PORT_CMD_GETVERSION 10
42
43 #define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
44 __asm__("inl (%%dx)" : \
45 "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
46 "0"(VMWARE_HYPERVISOR_MAGIC), \
47 "1"(VMWARE_PORT_CMD_##cmd), \
48 "2"(VMWARE_HYPERVISOR_PORT), "3"(UINT_MAX) : \
49 "memory");
50
51 /* virtualization types */
52 enum {
53 VIRT_NONE = 0,
54 VIRT_PARA,
55 VIRT_FULL
56 };
57 const char *virt_types[] = {
58 [VIRT_NONE] = "none",
59 [VIRT_PARA] = "para",
60 [VIRT_FULL] = "full"
61 };
62
63 /* hypervisor vendors */
64 enum {
65 HYPER_NONE = 0,
66 HYPER_XEN,
67 HYPER_KVM,
68 HYPER_MSHV,
69 HYPER_VMWARE
70 };
71 const char *hv_vendors[] = {
72 [HYPER_NONE] = NULL,
73 [HYPER_XEN] = "Xen",
74 [HYPER_KVM] = "KVM",
75 [HYPER_MSHV] = "Microsoft",
76 [HYPER_VMWARE] = "VMWare"
77 };
78
79 struct hypervisor_desc {
80 int hyper; /* hypervisor vendor ID */
81 int virtype; /* VIRT_PARA|FULL|NONE ? */
82 };
83
84 static size_t sysrootlen;
85 static char pathbuf[PATH_MAX];
86
87 static FILE *path_fopen(const char *mode, const char *path, ...)
88 __attribute__ ((__format__ (__printf__, 2, 3)));
89 static int path_exist(const char *path, ...)
90 __attribute__ ((__format__ (__printf__, 1, 2)));
91
92 static const char *
93 path_vcreate(const char *path, va_list ap)
94 {
95 if (sysrootlen)
96 vsnprintf(pathbuf + sysrootlen,
97 sizeof(pathbuf) - sysrootlen, path, ap);
98 else
99 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
100 return pathbuf;
101 }
102
103 static FILE *
104 path_vfopen(const char *mode, const char *path, va_list ap)
105 {
106 const char *p = path_vcreate(path, ap);
107
108 return fopen(p, mode);
109 }
110
111 static FILE *
112 path_fopen(const char *mode, const char *path, ...)
113 {
114 FILE *fd;
115 va_list ap;
116
117 va_start(ap, path);
118 fd = path_vfopen(mode, path, ap);
119 va_end(ap);
120
121 return fd;
122 }
123
124 static int
125 path_exist(const char *path, ...)
126 {
127 va_list ap;
128 const char *p;
129
130 va_start(ap, path);
131 p = path_vcreate(path, ap);
132 va_end(ap);
133
134 return access(p, F_OK) == 0;
135 }
136
137 static int
138 has_pci_device(int vendor, int device)
139 {
140 FILE *f;
141 int num, fn, ven, dev;
142 int res = 1;
143
144 f = path_fopen("r", _PATH_PROC_PCIDEVS);
145 if (!f)
146 return 0;
147
148 /* for more details about bus/pci/devices format see
149 * drivers/pci/proc.c in linux kernel
150 */
151 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
152 &num, &fn, &ven, &dev) == 4) {
153
154 if (ven == vendor && dev == device)
155 goto found;
156 }
157
158 res = 0;
159 found:
160 fclose(f);
161 return res;
162 }
163
164 #if defined(__x86_64__) || defined(__i386__)
165
166 /*
167 * This CPUID leaf returns the information about the hypervisor.
168 * EAX : maximum input value for CPUID supported by the hypervisor.
169 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
170 */
171 #define HYPERVISOR_INFO_LEAF 0x40000000
172
173 static inline void
174 cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
175 unsigned int *ecx, unsigned int *edx)
176 {
177 __asm__(
178 #if defined(__PIC__) && defined(__i386__)
179 /* x86 PIC cannot clobber ebx -- gcc bitches */
180 "pushl %%ebx;"
181 "cpuid;"
182 "movl %%ebx, %%esi;"
183 "popl %%ebx;"
184 : "=S" (*ebx),
185 #else
186 "cpuid;"
187 : "=b" (*ebx),
188 #endif
189 "=a" (*eax),
190 "=c" (*ecx),
191 "=d" (*edx)
192 : "1" (op), "c"(0));
193 }
194
195 static void
196 read_hypervisor_cpuid(struct hypervisor_desc *desc)
197 {
198 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
199 char hyper_vendor_id[13];
200
201 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
202
203 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
204 memcpy(hyper_vendor_id + 0, &ebx, 4);
205 memcpy(hyper_vendor_id + 4, &ecx, 4);
206 memcpy(hyper_vendor_id + 8, &edx, 4);
207 hyper_vendor_id[12] = '\0';
208
209 if (!hyper_vendor_id[0])
210 return;
211
212 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
213 desc->hyper = HYPER_XEN;
214 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
215 desc->hyper = HYPER_KVM;
216 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
217 desc->hyper = HYPER_MSHV;
218 else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
219 desc->hyper = HYPER_VMWARE;
220 }
221
222 #else /* ! __x86_64__ */
223 static void
224 read_hypervisor_cpuid(struct hypervisor_desc *desc)
225 {
226 }
227 #endif
228
229 static void
230 read_hypervisor(struct hypervisor_desc *desc)
231 {
232 read_hypervisor_cpuid(desc);
233
234 if (desc->hyper)
235 /* hvm */
236 desc->virtype = VIRT_FULL;
237
238 else if (path_exist(_PATH_PROC_XEN)) {
239 /* Xen para-virt or dom0 */
240 FILE *fd = path_fopen("r", _PATH_PROC_XENCAP);
241 int dom0 = 0;
242
243 if (fd) {
244 char buf[256];
245
246 if (fscanf(fd, "%s", buf) == 1 &&
247 !strcmp(buf, "control_d"))
248 dom0 = 1;
249 fclose(fd);
250 }
251 desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
252 desc->hyper = HYPER_XEN;
253
254 } else if (has_pci_device(0x5853, 0x0001)) {
255 /* Xen full-virt on non-x86_64 */
256 desc->hyper = HYPER_XEN;
257 desc->virtype = VIRT_FULL;
258 }
259 }
260
261 static void
262 read_harddisk_serial(char *device, char *serial) {
263 static struct hd_driveid hd;
264 int fd;
265
266 if ((fd = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
267 return;
268 }
269
270 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
271 strncpy(serial, (const char *)hd.serial_no, 20);
272 }
273 }
274
275 static bool
276 is_virtualized() {
277 unsigned int eax, ebx, ecx, edx;
278
279 cpuid(0x1, &eax, &ebx, &ecx, &edx);
280
281 /*
282 Bitwise detection of the 31st bit.
283 This indicates if a host runs in a virtual environment.
284 */
285 if (ecx & (1<<31))
286 return true;
287
288 return false;
289 }
290
291 int
292 hypervisor_port_check(void) {
293 uint32_t eax, ebx, ecx, edx;
294 VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
295 if (ebx == VMWARE_HYPERVISOR_MAGIC)
296 return 1; // Success - running under VMware
297 else
298 return 0;
299 }
300
301 static PyObject *
302 do_get_hypervisor() {
303 /*
304 Get hypervisor from the cpuid command.
305 */
306 struct hypervisor_desc _desc, *desc = &_desc;
307 memset(desc, 0, sizeof(*desc));
308
309 read_hypervisor(desc);
310
311 PyObject *d = PyDict_New();
312 PyObject *o;
313
314 /* Hypervisor */
315 if (desc->hyper == HYPER_NONE) {
316 o = Py_None;
317 } else {
318 o = PyString_FromString((const char *)hv_vendors[desc->hyper]);
319 }
320 PyDict_SetItemString(d, "hypervisor", o);
321
322 /* Virtualization type */
323 if (desc->virtype == VIRT_NONE) {
324 o = Py_None;
325 } else {
326 o = PyString_FromString((const char *)virt_types[desc->virtype]);
327 }
328 PyDict_SetItemString(d, "virtype", o);
329
330 return d;
331 }
332
333 static PyObject *
334 do_is_virtualized() {
335 /*
336 Python wrapper around is_virtualized().
337 */
338
339 if (is_virtualized())
340 return Py_True;
341
342 return Py_False;
343 }
344
345 static PyObject *
346 do_get_harddisk_serial(PyObject *o, PyObject *args) {
347 /*
348 Python wrapper around read_harddisk_serial.
349 */
350
351 char serial[21];
352 memset(serial, 0, sizeof(serial));
353
354 char *device;
355 if (!PyArg_ParseTuple(args, "s", &device))
356 return NULL;
357
358 read_harddisk_serial(device, serial);
359
360 if (serial[0])
361 return PyString_FromString(serial);
362
363 return Py_None;
364 }
365
366 static PyObject *
367 do_hypervisor_port_check() {
368 /*
369 Python wrapper around hypervisor_port_check().
370 */
371
372 if (hypervisor_port_check())
373 return Py_True;
374
375 return Py_False;
376 }
377
378 static PyMethodDef fireinfoModuleMethods[] = {
379 { "get_hypervisor", (PyCFunction) do_get_hypervisor, METH_NOARGS, NULL },
380 { "is_virtualized", (PyCFunction) do_is_virtualized, METH_NOARGS, NULL },
381 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
382 { "vmware_hypervisor_port_check", (PyCFunction) do_hypervisor_port_check, METH_NOARGS, NULL },
383 { NULL, NULL, 0, NULL }
384 };
385
386 void init_fireinfo(void) {
387 PyObject *m;
388
389 m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
390 }