]> git.ipfire.org Git - oddments/fireinfo.git/blob - src/_fireinfo/fireinfo.c
_fireinfo: Make harddisk serial numbers 21 characters again
[oddments/fireinfo.git] / src / _fireinfo / 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 <errno.h>
22 #include <fcntl.h>
23 #include <linux/hdreg.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include <sys/ioctl.h>
27
28 /* hypervisor vendors */
29 enum hypervisors {
30 HYPER_NONE = 0,
31 HYPER_XEN,
32 HYPER_KVM,
33 HYPER_MSHV,
34 HYPER_VMWARE,
35 HYPER_OTHER,
36 HYPER_LAST /* for loop - must be last*/
37 };
38
39 const 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
48 };
49
50 const 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 };
58
59 #define NEWLINE "\n\r"
60
61 static void truncate_nl(char *s) {
62 assert(s);
63
64 s[strcspn(s, NEWLINE)] = '\0';
65 }
66
67 static int read_one_line_file(const char *filename, char **line) {
68 char t[2048];
69
70 if (!filename || !line)
71 return -EINVAL;
72
73 FILE* f = fopen(filename, "re");
74 if (!f)
75 return -errno;
76
77 if (!fgets(t, sizeof(t), f)) {
78 if (ferror(f))
79 return errno ? -errno : -EIO;
80
81 t[0] = 0;
82 }
83
84 char *c = strdup(t);
85 if (!c)
86 return -ENOMEM;
87
88 truncate_nl(c);
89
90 *line = c;
91 return 0;
92 }
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
101 int 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;
113
114 /* http://lwn.net/Articles/301888/ */
115
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"
122 #endif
123
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 );
140
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
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 }
165 }
166 }
167
168 return 1;
169 }
170 #endif
171 return 0;
172 }
173
174 static PyObject *
175 do_detect_hypervisor() {
176 /*
177 Get hypervisor from the cpuid command.
178 */
179 int hypervisor = HYPER_NONE;
180
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;
186
187 return PyUnicode_FromString(hypervisor_vendor);
188 }
189
190 Py_RETURN_NONE;
191 }
192
193 static PyObject *
194 do_get_harddisk_serial(PyObject *o, PyObject *args) {
195 /*
196 Python wrapper around read_harddisk_serial.
197 */
198 static struct hd_driveid hd;
199 const char *device = NULL;
200 char serial[22];
201
202 if (!PyArg_ParseTuple(args, "s", &device))
203 return NULL;
204
205 int fd = open(device, O_RDONLY | O_NONBLOCK);
206 if (fd < 0) {
207 PyErr_Format(PyExc_OSError, "Could not open block device: %s", device);
208 return NULL;
209 }
210
211 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
212 snprintf(serial, sizeof(serial) - 1, "%s", (const char *)hd.serial_no);
213
214 if (*serial) {
215 close(fd);
216 return PyUnicode_FromString(serial);
217 }
218 }
219
220 close(fd);
221
222 Py_RETURN_NONE;
223 }
224
225 static PyMethodDef fireinfo_methods[] = {
226 { "detect_hypervisor", (PyCFunction) do_detect_hypervisor, METH_NOARGS, NULL },
227 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
228 { NULL, NULL, 0, NULL }
229 };
230
231 static 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
239 PyMODINIT_FUNC PyInit__fireinfo(void) {
240 PyObject* m = PyModule_Create(&fireinfo_module);
241 if (!m)
242 return NULL;
243
244 return m;
245 }