]> git.ipfire.org Git - oddments/fireinfo.git/blame - src/_fireinfo/fireinfo.c
_fireinfo: Make harddisk serial numbers 21 characters again
[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
74911c0b 61static void truncate_nl(char *s) {
e1ca6671 62 assert(s);
5f4159ab 63
74911c0b 64 s[strcspn(s, NEWLINE)] = '\0';
5f4159ab
MT
65}
66
74911c0b
MT
67static int read_one_line_file(const char *filename, char **line) {
68 char t[2048];
69
70 if (!filename || !line)
71 return -EINVAL;
5f4159ab 72
74911c0b 73 FILE* f = fopen(filename, "re");
5f4159ab 74 if (!f)
e1ca6671 75 return -errno;
5f4159ab 76
e1ca6671
MT
77 if (!fgets(t, sizeof(t), f)) {
78 if (ferror(f))
79 return errno ? -errno : -EIO;
5f4159ab 80
e1ca6671 81 t[0] = 0;
5f4159ab
MT
82 }
83
e1ca6671
MT
84 char *c = strdup(t);
85 if (!c)
86 return -ENOMEM;
74911c0b 87
e1ca6671 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
715ba5ac 174static PyObject *
e1ca6671 175do_detect_hypervisor() {
715ba5ac
MT
176 /*
177 Get hypervisor from the cpuid command.
178 */
e1ca6671 179 int hypervisor = HYPER_NONE;
715ba5ac 180
e1ca6671
MT
181 int r = detect_hypervisor(&hypervisor);
182 if (r >= 1) {
183 const char* hypervisor_vendor = hypervisor_vendors[hypervisor];
184 if (!hypervisor_vendor)
185 Py_RETURN_NONE;
715ba5ac 186
ba11fa33 187 return PyUnicode_FromString(hypervisor_vendor);
715ba5ac 188 }
715ba5ac 189
e1ca6671 190 Py_RETURN_NONE;
7cd41cc1
MT
191}
192
3f70e7fd
MT
193static PyObject *
194do_get_harddisk_serial(PyObject *o, PyObject *args) {
195 /*
196 Python wrapper around read_harddisk_serial.
197 */
e1ca6671 198 static struct hd_driveid hd;
7890c775 199 const char *device = NULL;
91c66e18 200 char serial[22];
e1ca6671 201
3f70e7fd
MT
202 if (!PyArg_ParseTuple(args, "s", &device))
203 return NULL;
204
74911c0b
MT
205 int fd = open(device, O_RDONLY | O_NONBLOCK);
206 if (fd < 0) {
7890c775 207 PyErr_Format(PyExc_OSError, "Could not open block device: %s", device);
e1ca6671 208 return NULL;
7890c775 209 }
3f70e7fd 210
e1ca6671 211 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
74911c0b 212 snprintf(serial, sizeof(serial) - 1, "%s", (const char *)hd.serial_no);
2dc5f0d0 213
74911c0b
MT
214 if (*serial) {
215 close(fd);
ba11fa33 216 return PyUnicode_FromString(serial);
74911c0b 217 }
e1ca6671 218 }
2dc5f0d0 219
74911c0b
MT
220 close(fd);
221
e1ca6671 222 Py_RETURN_NONE;
2dc5f0d0
MT
223}
224
ba11fa33 225static PyMethodDef fireinfo_methods[] = {
e1ca6671 226 { "detect_hypervisor", (PyCFunction) do_detect_hypervisor, METH_NOARGS, NULL },
3f70e7fd 227 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
5f4159ab
MT
228 { NULL, NULL, 0, NULL }
229};
230
ba11fa33
MT
231static struct PyModuleDef fireinfo_module = {
232 .m_base = PyModuleDef_HEAD_INIT,
233 .m_name = "_fireinfo",
234 .m_size = -1,
235 .m_doc = "Python module for fireinfo",
236 .m_methods = fireinfo_methods,
237};
238
239PyMODINIT_FUNC PyInit__fireinfo(void) {
240 PyObject* m = PyModule_Create(&fireinfo_module);
241 if (!m)
242 return NULL;
5f4159ab 243
ba11fa33 244 return m;
5f4159ab 245}