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