4 # THIS file used to be named 'blockdevicescan.py', but has been renamed
5 # systeminfo.py and made more generic (now includes info about memory,
6 # and other hardware info on the machine)
9 # Copyright (c) 2003 Intel Corporation
10 # All rights reserved.
12 # Redistribution and use in source and binary forms, with or without
13 # modification, are permitted provided that the following conditions are
16 # * Redistributions of source code must retain the above copyright
17 # notice, this list of conditions and the following disclaimer.
19 # * Redistributions in binary form must reproduce the above
20 # copyright notice, this list of conditions and the following
21 # disclaimer in the documentation and/or other materials provided
22 # with the distribution.
24 # * Neither the name of the Intel Corporation nor the names of its
25 # contributors may be used to endorse or promote products derived
26 # from this software without specific prior written permission.
28 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
32 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 # EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF
41 # YOUR JURISDICTION. It is licensee's responsibility to comply with any
42 # export regulations applicable in licensee's jurisdiction. Under
43 # CURRENT (May 2000) U.S. export regulations this software is eligible
44 # for export from the U.S. and can be downloaded by or otherwise
45 # exported or reexported worldwide EXCEPT to U.S. embargoed destinations
46 # which include Cuba, Iraq, Libya, North Korea, Iran, Syria, Sudan,
47 # Afghanistan and any other country to which the U.S. has embargoed
51 # expected /proc/partitions format
53 #----------------------------------------------------
54 #major minor #blocks name
61 #----------------------------------------------------
67 import merge_hw_tables
71 hwdatapath = "usr/share/hwdata"
74 a utility class for finding and returning information about
75 block devices, memory, and other hardware on the system
78 PROC_MEMINFO_PATH= "/proc/meminfo"
81 PROC_PARTITIONS_PATH= "/proc/partitions"
83 # set when the sfdisk -l <dev> trick has been done to make
85 DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
87 # a /proc/partitions block is 1024 bytes
88 # a GB to a HDD manufacturer is 10^9 bytes
89 BLOCKS_PER_GB = pow(10, 9) / 1024.0;
92 # -n is numeric ids (no lookup), -m is machine readable
93 LSPCI_CMD= "/sbin/lspci -nm"
95 MODULE_CLASS_NETWORK= "network"
96 MODULE_CLASS_SCSI= "scsi"
98 PCI_CLASS_NETWORK_ETHERNET=0x0200L
99 PCI_CLASS_STORAGE_SCSI=0x0100L
100 PCI_CLASS_STORAGE_IDE=0x0101L
101 PCI_CLASS_STORAGE_FLOPPY=0x0102L
102 PCI_CLASS_STORAGE_IPI=0x0103L
103 PCI_CLASS_STORAGE_RAID=0x0104L
104 PCI_CLASS_STORAGE_OTHER=0x0180L
108 def get_total_phsyical_mem(self):
110 return the total physical memory of the machine, in kilobytes.
112 Return None if /proc/meminfo not readable.
116 meminfo_file= file(self.PROC_MEMINFO_PATH,"r")
122 for line in meminfo_file:
125 (fieldname,value)= string.split(line,":")
126 except ValueError, e:
127 # this will happen for lines that don't have two values
128 # (like the first line on 2.4 kernels)
131 fieldname= string.strip(fieldname)
132 value= string.strip(value)
134 if fieldname == "MemTotal":
136 (total_memory,units)= string.split(value)
137 except ValueError, e:
140 if total_memory == "" or total_memory == None or \
141 units == "" or units == None:
144 if string.lower(units) != "kb":
148 total_memory= int(total_memory)
149 except ValueError, e:
160 def get_block_device_list(self):
162 get a list of block devices from this system.
163 return an associative array, where the device name
164 (full /dev/device path) is the key, and the value
165 is a tuple of (major,minor,numblocks,gb_size,readonly)
168 # make sure we can access to the files/directories in /proc
169 if not os.access(self.PROC_PARTITIONS_PATH, os.F_OK):
172 # table with valid scsi/sata/ide/raid block device names
174 # add in valid sd and hd block device names
175 for blk_prefix in ('sd','hd'):
176 for blk_num in map ( \
177 lambda x: chr(x), range(ord('a'),ord('z')+1)):
178 devicename="%s%c" % (blk_prefix, blk_num)
179 valid_blk_names[devicename]=None
181 # add in valid scsi raid block device names
182 for M in range(0,1+1):
183 for N in range(0,7+1):
184 devicename = "cciss/c%dd%d" % (M,N)
185 valid_blk_names[devicename]=None
187 # only do this once every system boot
188 if not os.access(self.DEVICES_SCANNED_FLAG, os.R_OK):
190 # this is ugly. under devfs, device
191 # entries in /dev/scsi/.. and /dev/ide/...
192 # don't show up until you attempt to read
193 # from the associated device at /dev (/dev/sda).
194 # so, lets run sfdisk -l (list partitions) against
195 # most possible block devices, that way they show
196 # up when it comes time to do the install.
197 devicenames = valid_blk_names.keys()
199 for devicename in devicenames:
200 os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
203 fb = open(self.DEVICES_SCANNED_FLAG,"w")
208 partitions_file= file(self.PROC_PARTITIONS_PATH,"r")
210 for line in partitions_file:
211 line_count= line_count + 1
213 # skip the first two lines always
217 parts= string.split(line)
224 # skip and ignore any partitions
225 if not valid_blk_names.has_key(device):
231 blocks= int(parts[2])
232 except ValueError, err:
235 gb_size= blocks/self.BLOCKS_PER_GB
237 # check to see if the blk device is readonly
239 # can we write to it?
240 dev_name= "/dev/%s" % device
241 fb = open(dev_name,"w")
245 # check if EROFS errno
246 if errno.errorcode.get(e.errno,None) == 'EROFS':
249 # got some other errno, pretend device is readonly
252 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
258 def get_system_modules( self, install_root, kernel_version= None ):
260 Return a list of kernel modules that this system requires.
261 This requires access to the installed system's root
262 directory, as the following files must exist and are used:
263 <install_root>/usr/share/hwdata/pcitable
264 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
265 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
267 If there are more than one kernels installed, and the kernel
268 version is not specified, then only the first one in
269 /lib/modules is used.
271 Returns a dictionary, keys being the type of module:
272 - scsi MODULE_CLASS_SCSI
273 - network MODULE_CLASS_NETWORK
274 The value being the kernel module name to load.
276 Some sata devices show up under an IDE device class,
277 hence the reason for checking for ide devices as well.
278 If there actually is a match in the pci -> module lookup
279 table, and its an ide device, its most likely sata,
280 as ide modules are built in to the kernel.
283 # get the kernel version we are assuming
284 if kernel_version is None:
286 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
290 if len(kernel_version) == 0:
293 if len(kernel_version) > 1:
294 print( "WARNING: We may be returning modules for the wrong kernel." )
296 kernel_version= kernel_version[0]
298 print( "Using kernel version %s" % kernel_version )
300 # test to make sure the three files we need are present
301 pcitable_path = "%s/%s/pcitable" % (install_root,hwdatapath)
302 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
303 (install_root,kernel_version)
304 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
305 (install_root,kernel_version)
307 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
308 if not os.access(path,os.R_OK):
309 print( "Unable to read %s" % path )
312 # now, with those three files, merge them all into one easy to
314 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
317 if all_modules is None:
318 print( "Unable to merge pci id tables." )
321 # this is the actual data structure we return
324 # these are the lists that will be in system_mods
329 # get all the system devices from lspci
330 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
331 if lspci_prog is None:
332 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
335 returncode= lspci_prog.wait()
337 print( "Running %s failed" % self.LSPCI_CMD )
340 print( "Successfully ran %s" % self.LSPCI_CMD )
342 # for every lspci line, parse in the four tuple PCI id and the
343 # search for the corresponding driver from the dictionary
344 # generated by merge_hw_tables
345 for line in lspci_prog.fromchild:
348 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
350 # Remove '"', 'Class ', and anything beginning with '-'
351 # (usually revisions and prog-if flags) so that we can
352 # split on whitespace:
354 # 00:1f.1 0101 8086 2411 8086 2411
357 line = line.replace('"', '')
358 line = line.replace('Class ', '')
359 line = re.sub('-[^ ]*', '', line)
365 classid = long(parts[1], 16)
366 vendorid = long(parts[2], 16)
367 deviceid = long(parts[3], 16)
369 print "Invalid line:", line
372 if classid not in (self.PCI_CLASS_NETWORK_ETHERNET,
373 self.PCI_CLASS_STORAGE_SCSI,
374 self.PCI_CLASS_STORAGE_RAID,
375 self.PCI_CLASS_STORAGE_OTHER,
376 self.PCI_CLASS_STORAGE_IDE):
379 # Device may have a subvendorid and subdeviceid
381 subvendorid = long(parts[4], 16)
382 subdeviceid = long(parts[5], 16)
384 subvendorid = self.PCI_ANY
385 subdeviceid = self.PCI_ANY
387 # search for driver that most closely matches the full_id
388 # to drivers that can handle any subvendor/subdevice
389 # version of the hardware.
390 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
391 (vendorid,deviceid,subvendorid,self.PCI_ANY),
392 (vendorid,deviceid,self.PCI_ANY,self.PCI_ANY))
394 for full_id in full_ids:
395 module = all_pci_ids.get(full_id, None)
396 if module is not None:
397 if classid == self.PCI_CLASS_NETWORK_ETHERNET:
398 network_mods.append(module[0])
399 elif classid in (self.PCI_CLASS_STORAGE_SCSI,
400 self.PCI_CLASS_STORAGE_RAID,
401 self.PCI_CLASS_STORAGE_OTHER,
402 self.PCI_CLASS_STORAGE_IDE):
403 scsi_mods.append(module[0])
405 print "not network or scsi: 0x%x" % classid
408 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
409 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
414 def remove_quotes( self, str ):
416 remove double quotes off of a string if they exist
418 if str == "" or str == None:
422 if str[len(str)-1] == '"':
423 str= str[:len(str)-1]
428 if __name__ == "__main__":
431 devices= info.get_block_device_list()
432 print "block devices detected:"
434 print "no devices found!"
436 for dev in devices.keys():
437 print "%s %s" % (dev, repr(devices[dev]))
441 memory= info.get_total_phsyical_mem()
443 print "unable to read /proc/meminfo for memory"
445 print "total physical memory: %d kb" % memory
451 kernel_version = None
452 if len(sys.argv) > 2:
453 kernel_version = sys.argv[1]
455 modules= info.get_system_modules("/",kernel_version)
457 print "unable to list system modules"
460 if type == info.MODULE_CLASS_SCSI:
461 print( "all scsi modules:" )
462 for a_mod in modules[type]:
464 elif type == info.MODULE_CLASS_NETWORK:
465 print( "all network modules:" )
466 for a_mod in modules[type]: