]> git.ipfire.org Git - oddments/fireinfo.git/blame - fireinfo/system.py
Add --secret-id switch and --hardware-string.
[oddments/fireinfo.git] / fireinfo / system.py
CommitLineData
b45f0e98
MT
1#!/usr/bin/python
2
31a30328 3import hashlib
b45f0e98
MT
4import json
5import os
6import string
7
3f70e7fd
MT
8import _fireinfo
9
b45f0e98
MT
10import cpu
11import device
715ba5ac 12import hypervisor
b45f0e98 13
0c5ef738
MT
14PROFILE_VERSION = 0
15
45b74ad5 16SYS_CLASS_DMI = "/sys/class/dmi/id"
73014efe 17SECRET_ID_FILE = "/etc/fireinfo-id"
45b74ad5 18
7eadbfba
MT
19INVALID_ID_STRINGS = (
20 "OEM", "O.E.M.", "o.e.m.",
21 "N/A", "n/a",
22)
23
32aeec73
MT
24class Singleton(type):
25 def __init__(cls, name, bases, dict):
26 super(Singleton, cls).__init__(name, bases, dict)
27 cls.instance = None
28
29 def __call__(cls, *args, **kw):
30 if cls.instance is None:
31 cls.instance = super(Singleton, cls).__call__(*args, **kw)
32
33 return cls.instance
34
35
45b74ad5
MT
36def read_from_file(filename):
37 """
38 Read all data from filename.
39 """
40 if not os.path.exists(filename):
41 return
42
43 try:
44 with open(filename) as f:
45 return f.read().strip()
46 except IOError:
47 pass
48
b45f0e98 49class System(object):
32aeec73 50 __metaclass__ = Singleton
b45f0e98
MT
51
52 def __init__(self):
53 # find all devices
54 self.devices = []
55 self.scan()
56 self.cpu = cpu.CPU()
715ba5ac
MT
57 self.hypervisor = hypervisor.Hypervisor()
58
b45f0e98 59 def profile(self):
0c5ef738
MT
60 p = {}
61 p["system"] = {
45b74ad5
MT
62 # System information
63 "model" : self.model,
64 "vendor" : self.vendor,
65
0c5ef738
MT
66 # Indicator if the system is running in a
67 # virtual environment.
d8614fc3 68 "virtual" : self.virtual,
b45f0e98 69
0c5ef738
MT
70 # System language
71 "language" : self.language,
72
73 # Release information
74 "release" : self.release,
75 "kernel_release" : self.kernel_release,
76
77 "memory" : self.memory,
78 "root_size" : self.root_size,
79 }
80
81 p["devices"] = []
b45f0e98 82 for device in self.devices:
97679775 83 d = {
b45f0e98
MT
84 "subsystem" : device.subsystem.lower(),
85 "vendor" : device.vendor.lower(),
86 "model" : device.model.lower(),
7923840b
MT
87 "deviceclass" : device.deviceclass,
88 "driver" : device.driver,
97679775
MT
89 }
90
91 # PCI devices provide subsystem information, USB don't.
92 if d["subsystem"] == "pci":
93 d["sub_model"] = device.sub_model
94 d["sub_vendor"] = device.sub_vendor
95
96 p["devices"].append(d)
97
b45f0e98 98 p["cpu"] = {
0c5ef738 99 "arch" : self.arch,
b45f0e98
MT
100 "vendor" : self.cpu.vendor,
101 "model" : self.cpu.model,
eecf2eea 102 "model_string" : self.cpu.model_string,
b45f0e98
MT
103 "stepping" : self.cpu.stepping,
104 "flags" : self.cpu.flags,
105 "bogomips" : self.cpu.bogomips,
106 "speed" : self.cpu.speed,
b45f0e98
MT
107 "family" : self.cpu.family,
108 "count" : self.cpu.count
109 }
715ba5ac
MT
110
111 # Only append hypervisor information if we are virtualized.
112 if self.virtual:
113 p["hypervisor"] = {
114 "type" : self.hypervisor.type,
115 "vendor" : self.hypervisor.vendor,
116 }
117
0c5ef738
MT
118 return {
119 # Profile version
120 "profile_version" : PROFILE_VERSION,
121
122 # Identification and authorization codes
123 "public_id" : self.public_id,
124 "private_id" : self.private_id,
125
126 # Actual profile data
127 "profile" : p,
128 }
b45f0e98
MT
129
130
131 @property
132 def arch(self):
133 return os.uname()[4]
134
135 @property
136 def public_id(self):
31a30328
MT
137 """
138 This returns a globally (hopefully) ID to identify the host
139 later (by request) in the database.
140 """
73014efe 141 public_id = self.secret_id
3d10b6d9
MT
142 if not public_id:
143 return "0" * 40
144
145 return hashlib.sha1(public_id).hexdigest()
146
b45f0e98
MT
147 @property
148 def private_id(self):
31a30328
MT
149 """
150 The private ID is built out of the _unique_id and used to
151 permit a host to do changes on the database.
152
153 No one could ever guess this without access to the host.
154 """
5e2ba24e 155 private_id = ""
73014efe 156 for i in reversed(self.secret_id):
5e2ba24e
MT
157 private_id += i
158
3d10b6d9
MT
159 if not private_id:
160 return "0" * 40
161
5e2ba24e 162 return hashlib.sha1(private_id).hexdigest()
31a30328 163
73014efe
MT
164 @property
165 def secret_id(self):
166 """
167 Read a "secret" ID from a file if available
168 or calculate it from the hardware.
169 """
170 if os.path.exists(SECRET_ID_FILE):
171 return read_from_file(SECRET_ID_FILE)
172
173 return hashlib.sha1(self._unique_id).hexdigest()
174
31a30328
MT
175 @property
176 def _unique_id(self):
177 """
178 This is a helper ID which is generated out of some hardware information
179 that is considered to be constant over a PC's lifetime.
180
181 None of the data here is ever sent to the server.
182 """
ec65e266 183 ids = []
3d10b6d9
MT
184
185 # Virtual machines (for example) and some boards have a UUID
186 # which is globally unique.
187 for file in ("product_uuid", "product_serial", "chassis_serial"):
7eadbfba 188 id = read_from_file(os.path.join(SYS_CLASS_DMI, file))
c0ef2eb7 189 ids.append(id)
3d10b6d9 190
3f70e7fd
MT
191 # Use serial number from root disk (if available)
192 root_disk_serial = self.root_disk_serial
193 if root_disk_serial:
194 ids.append(root_disk_serial)
195
7eadbfba
MT
196 # Sort out all bogous or invalid strings from the list.
197 _ids = []
198 for id in ids:
199 for i in INVALID_ID_STRINGS:
c0ef2eb7
MT
200 if i in id:
201 id = None
7eadbfba
MT
202 break
203
204 if id:
205 _ids.append(id)
206
207 ids = _ids
208
f05eac0d
MT
209 # As last resort, we use the UUID from pakfire.
210 if not ids:
211 id = read_from_file("/opt/pakfire/db/uuid")
212 ids.append(id)
213
ec65e266 214 return "#".join(ids)
31a30328 215
b45f0e98
MT
216 @property
217 def language(self):
b3ea53a7
MT
218 # Return "unknown" if settings file does not exist.
219 filename = "/var/ipfire/main/settings"
220 if not os.path.exists(filename):
221 return "unknown"
222
223 with open(filename, "r") as f:
b45f0e98
MT
224 for line in f.readlines():
225 key, val = line.split("=", 1)
226 if key=="LANGUAGE":
227 return val.strip()
228
229 @property
230 def release(self):
770c8841 231 return read_from_file("/etc/system-release")
b56bde73
SP
232
233 @property
234 def bios_vendor(self):
770c8841 235 return read_from_file("/sys/class/dmi/id/bios_vendor")
b56bde73 236
45b74ad5
MT
237 @property
238 def vendor(self):
239 ret = None
240 for file in ("chassis_vendor", "board_vendor", "sys_vendor",):
241 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
242 if ret:
243 break
244
245 return ret
246
247 @property
248 def model(self):
249 ret = None
250 for file in ("chassis_model", "board_model", "product_name",):
251 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
252 if ret:
253 break
254
255 return ret
256
b45f0e98
MT
257 @property
258 def memory(self):
259 with open("/proc/meminfo", "r") as f:
260 firstline = f.readline().strip()
2ced5413 261 return int(firstline.split()[1])
b45f0e98
MT
262
263 @property
0c5ef738 264 def kernel_release(self):
b45f0e98
MT
265 return os.uname()[2]
266
267 @property
268 def root_disk(self):
269 with open("/etc/mtab", "r") as f:
270 dev, mountpoint, rest = f.readline().split(" ",2)
271 if mountpoint == "/":
272 dev = dev[5:]
273 # cut off all digits at end of string
274 while dev[-1] in string.digits:
275 dev = dev[:-1]
276 return dev
277
278 @property
279 def root_size(self):
280 path="/sys/block/%s/size" %self.root_disk
281 if not os.path.exists(path):
282 return
283 with open(path, "r") as f:
284 return int(f.readline())*512/1024
3f70e7fd
MT
285
286 @property
287 def root_disk_serial(self):
e658b1a0
MT
288 serial = _fireinfo.get_harddisk_serial("/dev/%s" % self.root_disk)
289
290 if serial:
291 # Strip all spaces
292 return serial.strip()
293
b45f0e98
MT
294 def scan(self):
295 toscan = (("/sys/bus/pci/devices", device.PCIDevice),
296 ("/sys/bus/usb/devices", device.USBDevice))
297 for path, cls in toscan:
298 dirlist = os.listdir(path)
299 for dir in dirlist:
300 self.devices.append(cls(os.path.join(path, dir)))
d8614fc3
MT
301
302 @property
303 def virtual(self):
304 """
305 Say if the host is running in a virtual environment.
306 """
715ba5ac 307 return self.hypervisor.virtual
b45f0e98
MT
308
309
310
311if __name__ == "__main__":
312 s=System()
313 print s.arch
314 print s.language
315 print s.release
b56bde73 316 print s.bios_vendor
b45f0e98
MT
317 print s.memory
318 print s.kernel
319 print s.root_disk
320 print s.root_size
321 print "------------\n", s.devices, "\n------------\n"
65891720 322 print json.dumps(s.profile(), sort_keys=True, indent=4)