]> git.ipfire.org Git - oddments/fireinfo.git/blame - src/_fireinfo/fireinfo.c
Skip search for hypervisor name when the CPU string is empty
[oddments/fireinfo.git] / src / _fireinfo / fireinfo.c
CommitLineData
5f4159ab 1/*
3b5ed4e1
MT
2 * Fireinfo
3 * Copyright (C) 2010, 2011 IPFire Team (www.ipfire.org)
5f4159ab
MT
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
3b5ed4e1 7 * the Free Software Foundation; either version 3 of the License, or
5f4159ab
MT
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
e1ca6671 21#include <errno.h>
3f70e7fd
MT
22#include <fcntl.h>
23#include <linux/hdreg.h>
4cd8b976 24#include <stdbool.h>
e1ca6671 25#include <string.h>
3f70e7fd 26#include <sys/ioctl.h>
5f4159ab 27
5f4159ab 28/* hypervisor vendors */
e1ca6671
MT
29enum hypervisors {
30 HYPER_NONE = 0,
5f4159ab
MT
31 HYPER_XEN,
32 HYPER_KVM,
284eedb7 33 HYPER_MSHV,
e1ca6671
MT
34 HYPER_VMWARE,
35 HYPER_OTHER,
36 HYPER_LAST /* for loop - must be last*/
5f4159ab
MT
37};
38
e1ca6671
MT
39const char *hypervisor_ids[] = {
40 [HYPER_NONE] = NULL,
41 [HYPER_XEN] = "XenVMMXenVMM",
42 [HYPER_KVM] = "KVMKVMKVM",
43 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
44 [HYPER_MSHV] = "Microsoft Hv",
45 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
46 [HYPER_VMWARE] = "VMwareVMware",
47 [HYPER_OTHER] = NULL
715ba5ac
MT
48};
49
e1ca6671
MT
50const char *hypervisor_vendors[] = {
51 [HYPER_NONE] = NULL,
52 [HYPER_XEN] = "Xen",
53 [HYPER_KVM] = "KVM",
54 [HYPER_MSHV] = "Microsoft",
55 [HYPER_VMWARE] = "VMWare",
56 [HYPER_OTHER] = "other"
57};
5f4159ab 58
e1ca6671 59#define NEWLINE "\n\r"
5f4159ab 60
e1ca6671
MT
61char *truncate_nl(char *s) {
62 assert(s);
5f4159ab 63
e1ca6671
MT
64 s[strcspn(s, NEWLINE)] = 0;
65 return s;
5f4159ab
MT
66}
67
e1ca6671
MT
68int read_one_line_file(const char *filename, char **line) {
69 assert(filename);
70 assert(line);
5f4159ab 71
e1ca6671
MT
72 FILE *f = NULL;
73 f = fopen(filename, "re");
5f4159ab 74 if (!f)
e1ca6671 75 return -errno;
5f4159ab 76
e1ca6671
MT
77 char t[2048];
78 if (!fgets(t, sizeof(t), f)) {
79 if (ferror(f))
80 return errno ? -errno : -EIO;
5f4159ab 81
e1ca6671 82 t[0] = 0;
5f4159ab
MT
83 }
84
e1ca6671
MT
85 char *c = strdup(t);
86 if (!c)
87 return -ENOMEM;
88 truncate_nl(c);
5f4159ab 89
e1ca6671
MT
90 *line = c;
91 return 0;
92}
5f4159ab
MT
93
94/*
95 * This CPUID leaf returns the information about the hypervisor.
96 * EAX : maximum input value for CPUID supported by the hypervisor.
97 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
98 */
99#define HYPERVISOR_INFO_LEAF 0x40000000
100
e1ca6671
MT
101int detect_hypervisor(int *hypervisor) {
102#if defined(__x86_64__) || defined(__i386__)
103 /* Try high-level hypervisor sysfs file first: */
104 char *hvtype = NULL;
105 int r = read_one_line_file("/sys/hypervisor/type", &hvtype);
106 if (r >= 0) {
107 if (strcmp(hvtype, "xen") == 0) {
108 *hypervisor = HYPER_XEN;
109 return 1;
110 }
111 } else if (r != -ENOENT)
112 return r;
5f4159ab 113
e1ca6671 114 /* http://lwn.net/Articles/301888/ */
5f4159ab 115
e1ca6671
MT
116#if defined(__amd64__)
117#define REG_a "rax"
118#define REG_b "rbx"
119#elif defined(__i386__)
120#define REG_a "eax"
121#define REG_b "ebx"
5f4159ab
MT
122#endif
123
e1ca6671
MT
124 uint32_t eax = 1;
125 uint32_t ecx;
126 union {
127 uint32_t sig32[3];
128 char text[13];
129 } sig = {};
130
131 __asm__ __volatile__ (
132 /* ebx/rbx is being used for PIC! */
133 " push %%"REG_b" \n\t"
134 " cpuid \n\t"
135 " pop %%"REG_b" \n\t"
136
137 : "=a" (eax), "=c" (ecx)
138 : "0" (eax)
139 );
5f4159ab 140
e1ca6671
MT
141 bool has_hypervisor = !!(ecx & 0x80000000U);
142
143 if (has_hypervisor) {
144 /* There is a hypervisor, see what it is... */
145 eax = 0x40000000U;
146 __asm__ __volatile__ (
147 " push %%"REG_b" \n\t"
148 " cpuid \n\t"
149 " mov %%ebx, %1 \n\t"
150 " pop %%"REG_b" \n\t"
151
152 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
153 : "0" (eax)
154 );
155 sig.text[12] = '\0';
156
157 *hypervisor = HYPER_OTHER;
158
c6675894
MT
159 if (*sig.text) {
160 for (int id = HYPER_NONE + 1; id < HYPER_LAST; id++) {
161 if (strcmp(hypervisor_ids[id], sig.text) == 0) {
162 *hypervisor = id;
163 break;
164 }
e1ca6671 165 }
5f4159ab 166 }
5f4159ab 167
e1ca6671 168 return 1;
5f4159ab 169 }
89ee06f9 170#endif
e1ca6671 171 return 0;
3f4e98ab 172}
89ee06f9 173
2dc5f0d0 174
715ba5ac 175static PyObject *
e1ca6671 176do_detect_hypervisor() {
715ba5ac
MT
177 /*
178 Get hypervisor from the cpuid command.
179 */
e1ca6671 180 int hypervisor = HYPER_NONE;
715ba5ac 181
e1ca6671
MT
182 int r = detect_hypervisor(&hypervisor);
183 if (r >= 1) {
184 const char* hypervisor_vendor = hypervisor_vendors[hypervisor];
185 if (!hypervisor_vendor)
186 Py_RETURN_NONE;
715ba5ac 187
e1ca6671 188 return PyString_FromString(hypervisor_vendor);
715ba5ac 189 }
715ba5ac 190
e1ca6671 191 Py_RETURN_NONE;
7cd41cc1
MT
192}
193
3f70e7fd
MT
194static PyObject *
195do_get_harddisk_serial(PyObject *o, PyObject *args) {
196 /*
197 Python wrapper around read_harddisk_serial.
198 */
e1ca6671
MT
199 static struct hd_driveid hd;
200 int fd;
7890c775 201 const char *device = NULL;
e1ca6671 202
3f70e7fd
MT
203 if (!PyArg_ParseTuple(args, "s", &device))
204 return NULL;
205
7890c775
MT
206 if ((fd = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
207 PyErr_Format(PyExc_OSError, "Could not open block device: %s", device);
e1ca6671 208 return NULL;
7890c775 209 }
3f70e7fd 210
e1ca6671
MT
211 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
212 char serial[21];
f3515637
MT
213 memset(serial, 0, sizeof(serial));
214
215 strncpy(serial, (const char *)hd.serial_no, sizeof(serial) - 1);
2dc5f0d0 216
e1ca6671
MT
217 if (serial[0])
218 return PyString_FromString(serial);
219 }
2dc5f0d0 220
e1ca6671 221 Py_RETURN_NONE;
2dc5f0d0
MT
222}
223
5f4159ab 224static PyMethodDef fireinfoModuleMethods[] = {
e1ca6671 225 { "detect_hypervisor", (PyCFunction) do_detect_hypervisor, METH_NOARGS, NULL },
3f70e7fd 226 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
5f4159ab
MT
227 { NULL, NULL, 0, NULL }
228};
229
230void init_fireinfo(void) {
231 PyObject *m;
232
233 m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
e1ca6671
MT
234 if (m == NULL)
235 return;
5f4159ab 236}