]>
Commit | Line | Data |
---|---|---|
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 |
29 | enum 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 |
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 | |
715ba5ac MT |
48 | }; |
49 | ||
e1ca6671 MT |
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 | }; | |
5f4159ab | 58 | |
e1ca6671 | 59 | #define NEWLINE "\n\r" |
5f4159ab | 60 | |
e1ca6671 MT |
61 | char *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 |
68 | int 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 |
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; | |
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 | ||
159 | int id; | |
160 | for (id = HYPER_NONE + 1; id < HYPER_LAST; id++) { | |
acd2c4c1 | 161 | if (strcmp(hypervisor_ids[id], sig.text) == 0) { |
e1ca6671 MT |
162 | *hypervisor = id; |
163 | break; | |
164 | } | |
5f4159ab | 165 | } |
5f4159ab | 166 | |
e1ca6671 | 167 | return 1; |
5f4159ab | 168 | } |
89ee06f9 | 169 | #endif |
e1ca6671 | 170 | return 0; |
3f4e98ab | 171 | } |
89ee06f9 | 172 | |
2dc5f0d0 | 173 | |
715ba5ac | 174 | static PyObject * |
e1ca6671 | 175 | do_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 | |
e1ca6671 | 187 | return PyString_FromString(hypervisor_vendor); |
715ba5ac | 188 | } |
715ba5ac | 189 | |
e1ca6671 | 190 | Py_RETURN_NONE; |
7cd41cc1 MT |
191 | } |
192 | ||
3f70e7fd MT |
193 | static PyObject * |
194 | do_get_harddisk_serial(PyObject *o, PyObject *args) { | |
195 | /* | |
196 | Python wrapper around read_harddisk_serial. | |
197 | */ | |
e1ca6671 MT |
198 | static struct hd_driveid hd; |
199 | int fd; | |
7890c775 | 200 | const char *device = NULL; |
e1ca6671 | 201 | |
3f70e7fd MT |
202 | if (!PyArg_ParseTuple(args, "s", &device)) |
203 | return NULL; | |
204 | ||
7890c775 MT |
205 | if ((fd = open(device, O_RDONLY | O_NONBLOCK)) < 0) { |
206 | PyErr_Format(PyExc_OSError, "Could not open block device: %s", device); | |
e1ca6671 | 207 | return NULL; |
7890c775 | 208 | } |
3f70e7fd | 209 | |
e1ca6671 MT |
210 | if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) { |
211 | char serial[21]; | |
212 | strncpy(serial, (const char *)hd.serial_no, sizeof(serial)); | |
2dc5f0d0 | 213 | |
e1ca6671 MT |
214 | if (serial[0]) |
215 | return PyString_FromString(serial); | |
216 | } | |
2dc5f0d0 | 217 | |
e1ca6671 | 218 | Py_RETURN_NONE; |
2dc5f0d0 MT |
219 | } |
220 | ||
5f4159ab | 221 | static PyMethodDef fireinfoModuleMethods[] = { |
e1ca6671 | 222 | { "detect_hypervisor", (PyCFunction) do_detect_hypervisor, METH_NOARGS, NULL }, |
3f70e7fd | 223 | { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL }, |
5f4159ab MT |
224 | { NULL, NULL, 0, NULL } |
225 | }; | |
226 | ||
227 | void init_fireinfo(void) { | |
228 | PyObject *m; | |
229 | ||
230 | m = Py_InitModule("_fireinfo", fireinfoModuleMethods); | |
e1ca6671 MT |
231 | if (m == NULL) |
232 | return; | |
5f4159ab | 233 | } |