]> git.ipfire.org Git - oddments/fireinfo.git/blame - src/fireinfo.c
Fix build error on ix86.
[oddments/fireinfo.git] / src / fireinfo.c
CommitLineData
5f4159ab
MT
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
3f70e7fd
MT
22#include <fcntl.h>
23#include <linux/hdreg.h>
4cd8b976 24#include <stdbool.h>
3f70e7fd 25#include <sys/ioctl.h>
5f4159ab
MT
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 */
5f4159ab
MT
33#define _PATH_PROC_XEN "/proc/xen"
34#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
5f4159ab
MT
35#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
36
37/* virtualization types */
38enum {
39 VIRT_NONE = 0,
40 VIRT_PARA,
41 VIRT_FULL
42};
43const char *virt_types[] = {
44 [VIRT_NONE] = "none",
45 [VIRT_PARA] = "para",
46 [VIRT_FULL] = "full"
47};
48
49/* hypervisor vendors */
50enum {
51 HYPER_NONE = 0,
52 HYPER_XEN,
53 HYPER_KVM,
284eedb7
MT
54 HYPER_MSHV,
55 HYPER_VMWARE
5f4159ab
MT
56};
57const char *hv_vendors[] = {
58 [HYPER_NONE] = NULL,
59 [HYPER_XEN] = "Xen",
60 [HYPER_KVM] = "KVM",
284eedb7
MT
61 [HYPER_MSHV] = "Microsoft",
62 [HYPER_VMWARE] = "VMWare"
5f4159ab
MT
63};
64
715ba5ac
MT
65struct hypervisor_desc {
66 int hyper; /* hypervisor vendor ID */
67 int virtype; /* VIRT_PARA|FULL|NONE ? */
68};
69
5f4159ab
MT
70static size_t sysrootlen;
71static char pathbuf[PATH_MAX];
5f4159ab 72
4c1e1034
MT
73static FILE *path_fopen(const char *mode, const char *path, ...)
74 __attribute__ ((__format__ (__printf__, 2, 3)));
5f4159ab
MT
75static int path_exist(const char *path, ...)
76 __attribute__ ((__format__ (__printf__, 1, 2)));
77
78static const char *
79path_vcreate(const char *path, va_list ap)
80{
81 if (sysrootlen)
82 vsnprintf(pathbuf + sysrootlen,
83 sizeof(pathbuf) - sysrootlen, path, ap);
84 else
85 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
86 return pathbuf;
87}
88
89static FILE *
4c1e1034 90path_vfopen(const char *mode, const char *path, va_list ap)
5f4159ab 91{
5f4159ab
MT
92 const char *p = path_vcreate(path, ap);
93
4c1e1034 94 return fopen(p, mode);
5f4159ab
MT
95}
96
97static FILE *
4c1e1034 98path_fopen(const char *mode, const char *path, ...)
5f4159ab
MT
99{
100 FILE *fd;
101 va_list ap;
102
103 va_start(ap, path);
4c1e1034 104 fd = path_vfopen(mode, path, ap);
5f4159ab
MT
105 va_end(ap);
106
107 return fd;
108}
109
5f4159ab
MT
110static int
111path_exist(const char *path, ...)
112{
113 va_list ap;
114 const char *p;
115
116 va_start(ap, path);
117 p = path_vcreate(path, ap);
118 va_end(ap);
119
120 return access(p, F_OK) == 0;
121}
122
5f4159ab
MT
123static int
124has_pci_device(int vendor, int device)
125{
126 FILE *f;
127 int num, fn, ven, dev;
128 int res = 1;
129
4c1e1034 130 f = path_fopen("r", _PATH_PROC_PCIDEVS);
5f4159ab
MT
131 if (!f)
132 return 0;
133
134 /* for more details about bus/pci/devices format see
135 * drivers/pci/proc.c in linux kernel
136 */
137 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
138 &num, &fn, &ven, &dev) == 4) {
139
140 if (ven == vendor && dev == device)
141 goto found;
142 }
143
144 res = 0;
145found:
146 fclose(f);
147 return res;
148}
149
150#if defined(__x86_64__) || defined(__i386__)
151
152/*
153 * This CPUID leaf returns the information about the hypervisor.
154 * EAX : maximum input value for CPUID supported by the hypervisor.
155 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
156 */
157#define HYPERVISOR_INFO_LEAF 0x40000000
158
159static inline void
160cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
161 unsigned int *ecx, unsigned int *edx)
162{
163 __asm__(
164#if defined(__PIC__) && defined(__i386__)
165 /* x86 PIC cannot clobber ebx -- gcc bitches */
166 "pushl %%ebx;"
167 "cpuid;"
168 "movl %%ebx, %%esi;"
169 "popl %%ebx;"
170 : "=S" (*ebx),
171#else
172 "cpuid;"
173 : "=b" (*ebx),
174#endif
175 "=a" (*eax),
176 "=c" (*ecx),
177 "=d" (*edx)
178 : "1" (op), "c"(0));
179}
180
4c1e1034 181static void
715ba5ac 182read_hypervisor_cpuid(struct hypervisor_desc *desc)
5f4159ab
MT
183{
184 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
185 char hyper_vendor_id[13];
186
187 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
188
189 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
190 memcpy(hyper_vendor_id + 0, &ebx, 4);
191 memcpy(hyper_vendor_id + 4, &ecx, 4);
192 memcpy(hyper_vendor_id + 8, &edx, 4);
193 hyper_vendor_id[12] = '\0';
194
195 if (!hyper_vendor_id[0])
196 return;
197
198 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
199 desc->hyper = HYPER_XEN;
200 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
201 desc->hyper = HYPER_KVM;
202 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
203 desc->hyper = HYPER_MSHV;
284eedb7
MT
204 else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
205 desc->hyper = HYPER_VMWARE;
5f4159ab
MT
206}
207
208#else /* ! __x86_64__ */
209static void
4c1e1034 210read_hypervisor_cpuid(struct hypervisor_desc *desc)
5f4159ab
MT
211{
212}
213#endif
214
215static void
715ba5ac 216read_hypervisor(struct hypervisor_desc *desc)
5f4159ab
MT
217{
218 read_hypervisor_cpuid(desc);
219
220 if (desc->hyper)
221 /* hvm */
222 desc->virtype = VIRT_FULL;
223
224 else if (path_exist(_PATH_PROC_XEN)) {
225 /* Xen para-virt or dom0 */
4c1e1034 226 FILE *fd = path_fopen("r", _PATH_PROC_XENCAP);
5f4159ab
MT
227 int dom0 = 0;
228
229 if (fd) {
230 char buf[256];
231
232 if (fscanf(fd, "%s", buf) == 1 &&
233 !strcmp(buf, "control_d"))
234 dom0 = 1;
235 fclose(fd);
236 }
237 desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
238 desc->hyper = HYPER_XEN;
239
240 } else if (has_pci_device(0x5853, 0x0001)) {
241 /* Xen full-virt on non-x86_64 */
242 desc->hyper = HYPER_XEN;
243 desc->virtype = VIRT_FULL;
244 }
245}
246
3f70e7fd
MT
247static void
248read_harddisk_serial(char *device, char *serial) {
249 static struct hd_driveid hd;
250 int fd;
251
252 if ((fd = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
253 return;
254 }
255
256 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
5196c0c2 257 strncpy(serial, (const char *)hd.serial_no, 20);
3f70e7fd
MT
258 }
259}
260
4cd8b976
FM
261static bool
262is_virtualized() {
263 unsigned int eax, ebx, ecx, edx;
264
265 cpuid(0x1, &eax, &ebx, &ecx, &edx);
266
267 /*
268 Bitwise detection of the 31st bit.
269 This indicates if a host runs in a virtual environment.
270 */
271 if (ecx & (1<<31))
272 return true;
273
274 return false;
275}
276
715ba5ac
MT
277static PyObject *
278do_get_hypervisor() {
279 /*
280 Get hypervisor from the cpuid command.
281 */
282 struct hypervisor_desc _desc, *desc = &_desc;
283 memset(desc, 0, sizeof(*desc));
284
285 read_hypervisor(desc);
286
287 PyObject *d = PyDict_New();
288 PyObject *o;
289
290 /* Hypervisor */
291 if (desc->hyper == HYPER_NONE) {
292 o = Py_None;
293 } else {
294 o = PyString_FromString((const char *)hv_vendors[desc->hyper]);
295 }
296 PyDict_SetItemString(d, "hypervisor", o);
297
298 /* Virtualization type */
299 if (desc->virtype == VIRT_NONE) {
300 o = Py_None;
301 } else {
302 o = PyString_FromString((const char *)virt_types[desc->virtype]);
303 }
304 PyDict_SetItemString(d, "virtype", o);
305
306 return d;
307}
308
7cd41cc1
MT
309static PyObject *
310do_is_virtualized() {
311 /*
312 Python wrapper around is_virtualized().
313 */
314
315 if (is_virtualized())
316 return Py_True;
317
318 return Py_False;
319}
320
3f70e7fd
MT
321static PyObject *
322do_get_harddisk_serial(PyObject *o, PyObject *args) {
323 /*
324 Python wrapper around read_harddisk_serial.
325 */
326
327 char serial[21];
328 memset(serial, 0, sizeof(serial));
329
330 char *device;
331 if (!PyArg_ParseTuple(args, "s", &device))
332 return NULL;
333
334 read_harddisk_serial(device, serial);
335
336 if (serial[0])
337 return PyString_FromString(serial);
338
339 return Py_None;
340}
341
5f4159ab 342static PyMethodDef fireinfoModuleMethods[] = {
715ba5ac 343 { "get_hypervisor", (PyCFunction) do_get_hypervisor, METH_NOARGS, NULL },
7cd41cc1 344 { "is_virtualized", (PyCFunction) do_is_virtualized, METH_NOARGS, NULL },
3f70e7fd 345 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
5f4159ab
MT
346 { NULL, NULL, 0, NULL }
347};
348
349void init_fireinfo(void) {
350 PyObject *m;
351
352 m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
353}