Centos初始化脚本

#!/bin/bash
# SCRIPT: system.sh work on Centos5
# AUTHOR: Gaizaoren
# DATE: 2012/03/29  210.72.145.44
# REV: 1.0 http://www.gaizaoren.org
# PURPOSE: This shell script use at the beginning of system work
##########################################################
#               BEGINNING OF MAIN
##########################################################
#Disable ipv6
echo "options ipv6 disable=1" > /etc/modprobe.d/disable-ipv6.conf 

#Disable selinux
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

#Disable sshd dns
sed -i 's/#UseDNS.*/UseDNS no/g' /etc/ssh/sshd_config
sed -i 's/^GSSAPIAuthentication.*/GSSAPIAuthentication no/g' /etc/ssh/sshd_config
sed -i 's/^#GSSAPIAuthentication.*/GSSAPIAuthentication no/' /etc/ssh/sshd_config

#Disable Keyboard reset
sed -i 's/ca::/#ca::/' /etc/inittab

#OFF and ON
/sbin/chkconfig --list |grep 3:on |egrep -v "network|sysstat|sshd|syslog|crond|irqbalance|lvm2-monitor|iptables|microcode_ctl|ntpd|snmpd" |awk '{print $1}' |while read OFF
do 
	/sbin/chkconfig --level 345 $OFF off >/dev/null 2>&1
done 
for ON in network sysstat sshd syslog crond irqbalance lvm2-monitor iptables microcode_ctl ntpd snmpd
do	
	/sbin/chkconfig --level 2345 $ON on >/dev/null 2>&1
done

#System time
sed -i 's/ZONE=.*/ZONE="Asia\/Shanghai"/g'  /etc/sysconfig/clock
/bin/cp -pf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

#Record history log time
sed -i '/HISTTIMEFORMAT=/d' /etc/profile
echo 'export HISTTIMEFORMAT="%F %T	"' >> /etc/profile

#Alias
echo 'alias vi="vim"' >> /etc/profile
echo "alias grep='grep --colour=auto'" >> /etc/profile

#Delete sendmail queue
if [ -d /var/spool/clientmqueue ];then
	/bin/rm -rf /var/spool/clientmqueue/
	ln -s /dev/null /var/spool/clientmqueue
fi

#Sudo log
echo "%admin ALL=(ALL)  NOPASSWD: !/sbin/reboot,!/sbin/shutdown,!/sbin/poweroff,!/sbin/halt,!/sbin/init,!/sbin/init,!/sbin/telinit" >> /etc/sudoers
echo "Defaults        logfile = \"/var/log/sudo\""  >> /etc/sudoers
echo "local2.debug	/var/log/sudo" >> /etc/syslog.conf

#Turn off snmpd log
echo "OPTIONS=\"-LS3d -Lf /dev/null -p /var/run/snmpd.pid\"" >> /etc/sysconfig/snmpd.options

#Record sftp log
sed -i '/Subsystem/d' /etc/ssh/sshd_config
echo "Subsystem       sftp    /usr/libexec/openssh/sftp-server -l INFO -f AUTH" >> /etc/ssh/sshd_config
echo "auth,authpriv.*                                          /var/log/sftp" >> /etc/syslog.conf

#Reboot system
echo "You must reboot your system first!"

##########################################################
# End of script
##########################################################

服务器硬件信息脚本

#!/usr/bin/perl -w

# $Header: /CVSROOT/yahoo/ops/software/hwconfig/hwconfig,v 1.51.2.1 2008/11/06 01:01:10 filo Exp $

use strict 'vars';
use Getopt::Std;
use POSIX;

# constants
$ENV{PATH} = "/etc/bin:/sbin:/usr/sbin:/bin:/usr/bin:/home/y/bin:/usr/local/bin:/usr/local/sbin:/home/opt:/opt/MegaRAID/MegaCli:/usr/StorMan";

my($version) = '1.8.6';
my($timeout) = 120;
my($email) = 'hwconfig@yahoo-inc.com';

my($smbinfo) = 'smbinfo';
my($smbiosinfo) = 'smbiosinfo';
my($mptable) = 'mptable';
my($pciconf) = 'pciconf';
my($megarc) = 'megarc';
my($mfiutil) = 'mfiutil';
my($mptutil) = 'mptutil';
my($cissutil) = 'cissutil';
my($camcontrol) = 'camcontrol';
my($atacontrol) = 'atacontrol';
my($grub) = '/etc/grub.conf';
my($dmesg_file) = '/var/run/dmesg.boot';
my($dmesg_prog) = 'dmesg';
my($varrun) = '/home/y/var/run';
my($feature_htt_bit) = 28;
# linux constants
my($cpuinfo) = '/proc/cpuinfo';
my($meminfo) = '/proc/meminfo';
my($proc_ide) = '/proc/ide';
my($proc_scsi) = '/proc/scsi/scsi';
my($proc_cciss) = '/proc/driver/cciss';
my($sys_cciss) = '/sys/bus/pci/drivers/cciss';
my($sys_pci) = '/sys/bus/pci/devices';
my($proc_partitions) = '/proc/partitions';
my($proc_mdstat) = '/proc/mdstat';
my($proc_interrupts) = '/proc/interrupts';
my($proc_ioports) = '/proc/ioports';
my($proc_iomem) = '/proc/iomem';
my($lspci) = 'lspci';
my($lshal) = 'lshal';
my($ethtool) = 'ethtool';
my($dmidecode) = 'dmidecode';
my($hpacucli) = 'hpacucli';
my($twcli) = 'tw_cli';
my($arcconf) = 'arcconf';
my($freebsd_net_devs) = 'bge|em|fxp|xl|rl|nve|dc|bce|myk|msk|lnc|le';
my($perc_names) = 'PERC|ServeRAID-MR10M|SROMBSASFC';
my($lock) = '/var/tmp/hwconfig.lock';

my($need_megarc, $want_megarc, $have_megarc, $got_megarc) = (0, 0, 0, 0);
my($need_megacli, $want_megacli, $have_megacli, $got_megacli) = (0, 0, 0, 0);
my($need_mfiutil, $want_mfiutil, $have_mfiutil, $got_mfiutil) = (0, 0, 0, 0);
my($need_mptutil, $want_mptutil, $have_mptutil, $got_mptutil) = (0, 0, 0, 0);
my($need_arcconf, $want_arcconf, $have_arcconf, $got_arcconf) = (0, 0, 0, 0);
my($need_cissutil, $want_cissutil, $have_cissutil, $got_cissutil) = (0, 0, 0, 0);
my($need_hpacucli, $want_hpacucli, $have_hpacucli, $got_hpacucli) = (0, 0, 0, 0);
my($need_twcli, $want_twcli, $have_twcli, $got_twcli) = (0, 0, 0, 0);
my($need_omreport, $want_omreport, $have_omreport, $got_omreport) = (0, 0, 0, 0);

my($need_mdstat) = 0;
my($need_lvm) = 0;
my($need_ccdconfig) = 0;
my($need_rd) = 0;

my($dmesg_buf) = '';
my($output) = '';
my($stderr) = '';
my($perl_warn) = '';
my($debug) = '';
my($debug_pciconf) = '';
my($debug_lspci) = '';
my($debug_lshal) = '';
my($debug_sysctl_dev) = '';
my($debug_dmidecode) = '';
my($debug_storage) = '';
my($debug_network) = '';
my($debug_cpuinfo) = '';
my($debug_meminfo) = '';
my($sig_pci) = '';
my($os, $os_type, $os_date, $os_version, $os_up, $os_distro, $parent_os);
my($jailed, $jailer) = (0, '');
my($yroot) = '';
my($in) = 'IN';
my($line);
my($zero);
my($hostname) = '';
my(%opts);
my(%success);
my(%other_devices);
my(%smbios);
my(%dmi);
my(%sysctl_dev);

my($clean_smbios_regex) = '(\(null\)|N\/A|_?To Be Filled( By O\.E\.M\.)?_?|Not Specified|empty|<OUT OF SPEC>|System Name|System Manufacturer|NO DIMM|No Module Installed|MemUndefined|None|Unknown|<BAD INDEX>|ModulePartNumber\d+|Manufacturer\d+|SerNum\d+|AssetTagNum\d+|PartNum\d+|0000000000000000*|\.\.\.\.\.\.*)';

my($ignore_disk_models) = 'partitions|LSILogic-MegaRAID|3ware-Logical|COMPAQ-RAID|NETAPP-LUN|MegaRAID-LD|MFI-Logical-Disk|LSILOGIC-1030-IM|1030-IM|Adaptec-Mirror|ADAPTEC-RAID|LD-?\d+-RAID|Dell-VIRTUAL-DISK|DELL-PERC-|PERC-|Logical-Volume|DGC-RAID|VMware-Virtual|SRCSAS18E|RAID-0-Stripe|RAID-1-Mirror|U12U-G4020|AMCC-|LSILOGIC-Logical|Compaq-Logical|RAID-0-1|2120S-RAID|RAID-5|Dell-Virtual-Floppy|VIRTUALFLOPPY|Logical-Disk-\d+';

my(%chipsets) = (
	# http://balusc.xs4all.nl/srv/har-chi-int.php
	# http://en.wikipedia.org/wiki/List_of_Intel_chipsets

	# Intel South - use LPC bridge
	# http://en.wikipedia.org/wiki/I/O_Controller_Hub
	"0x70008086" => "SB: Intel 82371SB (PIIX3)",
	"0x71108086" => "SB: Intel 82371 (PIIX4)",
	"0x24108086" => "SB: Intel 82801AA (ICH)",
	"0x24208086" => "SB: Intel 82801AB (ICH0)",
	"0x24408086" => "SB: Intel 82801BA (ICH2)",
	"0x24808086" => "SB: Intel 82801CA (ICH3)",

	"0x24c08086" => "SB: Intel 82801DB (ICH4)",
	"0x24c08086,0x01" => "SB: Intel 82801DB A1 (ICH4)",
	"0x24c08086,0x02" => "SB: Intel 82801DB B0 (ICH4)",

	"0x24d08086" => "SB: Intel 82801E (ICH5)",		# two steppings A2/A3, both with same rev 0x02

	"0x25a18086" => "SB: Intel 6300ESB (ESB)",
	"0x25a18086,0x00" => "SB: Intel 6300ESB A0 (ESB)",
	"0x25a18086,0x01" => "SB: Intel 6300ESB A1/A2 (ESB)",
	"0x25a18086,0x02" => "SB: Intel 6300ESB A3 (ESB)",

	"0x26408086" => "SB: Intel 82801FB/FR (ICH6/R)",
	"0x26408086,0x03" => "SB: Intel 82801FB/FR B1 (ICH6/R)",
	"0x26408086,0x04" => "SB: Intel 82801FB/FR B2 (ICH6/R)",
	"0x26408086,0x05" => "SB: Intel 82801FB/FR C0 (ICH6/R)",
	"0x26418086" => "SB: Intel 82801FBM (ICH6M)",
	"0x26418086,0x03" => "SB: Intel 82801FBM B1 (ICH6M)",		# HP NC6220
	"0x26418086,0x04" => "SB: Intel 82801FBM B2 (ICH6M)",
	"0x26428086" => "SB: Intel 82801FW (ICH6W)",

	"0x26708086" => "SB: Intel 631xESB/632xESB (ESB2)",
	"0x26708086,0x00" => "IB: Intel 3100 A0 (Whitmore Lake)",
	"0x26708086,0x01" => "IB: Intel 3100 A1 (Whitmore Lake)",
	"0x26708086,0x08" => "SB: Intel 631xESB/632xESB A0 (ESB2)",
	"0x26708086,0x09" => "SB: Intel 631xESB/632xESB A1 (ESB2)",

	"0x27b08086" => "SB: Intel 82801GDH (ICH7DH)",
	"0x27b88086" => "SB: Intel 82801GB/GR (ICH7/R)",
	"0x27b98086" => "SB: Intel 82801GBM (ICH7M)",
	"0x27b98086,0x01" => "SB: Intel 82801GBM A1 (ICH7M)",
	"0x27b98086,0x02" => "SB: Intel 82801GBM B0 (ICH7M)",
	"0x27bd8086" => "SB: Intel 82801GHM (ICH7MDH)",
	"0x27bd8086,0x01" => "SB: Intel 82801GHM A1 (ICH7MDH)",
	"0x27bd8086,0x02" => "SB: Intel 82801GHM B0 (ICH7MDH)",

	"0x28108086" => "SB: Intel 82801HB/HR (ICH8/R)",
	"0x28118086" => "SB: Intel 82801HEM (ICH8M-E)",
	"0x28118086,0x02" => "SB: Intel 82801HEM B0 (ICH8M-E)",
	"0x28118086,0x03" => "SB: Intel 82801HEM B1 (ICH8M-E)",	# HP 6910p
	"0x28118086,0x04" => "SB: Intel 82801HEM B2 (ICH8M-E)",
	"0x28128086" => "SB: Intel 82801HH (ICH8DH)",
	"0x28148086" => "SB: Intel 82801HO (ICH8DO)",
	"0x28158086" => "SB: Intel 82801HBM (ICH8M)",
	"0x28158086,0x02" => "SB: Intel 82801HBM B0 (ICH8M)",
	"0x28158086,0x03" => "SB: Intel 82801HBM B1 (ICH8M)",	# MSI GM965
	"0x28158086,0x04" => "SB: Intel 82801HBM B2 (ICH8M)",

	# search for site:intel.com 2912h
	"0x29128086" => "SB: Intel 82801IH (ICH9H)",		# Home:		+AHCI -RAID
	"0x29148086" => "SB: Intel 82801IO (ICH9O)",		# Office:	+AHCI +RAID
	"0x29168086" => "SB: Intel 82801IR (ICH9R)",		# Raid:		+AHCI +RAID
	"0x29178086" => "SB: Intel 82801IBM (ICH9M)",		# Base Moble:
	"0x29178086,0x02" => "SB: Intel 82801IBM A2 (ICH9M)",
	"0x29178086,0x03" => "SB: Intel 82801IBM A3 (ICH9M)",
	"0x29188086" => "SB: Intel 82801IB (ICH9)",		# Base:		-AHCI -RAID
	"0x29198086" => "SB: Intel 82801IEM (ICH9M-E)",		# Enhanced Moble:
	"0x29198086,0x02" => "SB: Intel 82801IEM A2 (ICH9M-E)",
	"0x29198086,0x03" => "SB: Intel 82801IEM A3 (ICH9M-E)",

	"0x3a168086" => "SB: Intel 82801JIR (ICH10R)",		# Raid:
	"0x3a188086" => "SB: Intel 82801JIB (ICH10)",		# Base:

	# Intel North
	"0x12378086" => "NB: Intel 440FX (Natoma)",
	"0x71908086" => "NB: Intel 440BX",
	"0x71928086" => "NB: Intel 440BX",
	"0x71a08086" => "NB: Intel 440GX",
	"0x84ca8086" => "NB: Intel 450NX",				# Compaq 6400R
	"0x71248086" => "NB: Intel 810E (Whitney)",
	"0x11308086" => "NB: Intel 815E (Solano)",			# SM P3TSSR
	"0x25018086" => "NB: Intel 820 (Camino)",
	"0x1a308086" => "NB: Intel 845 (Brookdale)",			# HP D500
	"0x25608086" => "NB: Intel 845G (Brookdale)",			# HP D510
	"0x25708086" => "NB: Intel 865 (Springdale)",			# HP D530, HP DC5000, Dell GX270
	"0x25788086" => "NB: Intel 875P (Canterwood)",			# Intel S875WP1, PE750
	"0x25808086" => "NB: Intel 915 (Grantsdale)",			# HP DC5100, HP DX6120, HP DC7100
	"0x27708086" => "NB: Intel 945 (Lakeport)",			#
	"0x27708086,0x02" => "NB: Intel 945G (Lakeport)",		# Dell GX620, HP DC7600
	"0x27708086,0x81" => "NB: Intel 945P (Lakeport)",		#
	"0x27708086,0x82" => "NB: Intel 945PL (Lakeport)",		#
	"0x29708086" => "NB: Intel 946GZ (Lakeport G)",			# SM PDSBM
	"0x29908086" => "NB: Intel Q963/Q965 (Broadwater)",		# HP DC5700
	"0x29f08086" => "NB: Intel 3200 (Bigby)",			# Dell R200, DL120 G5
	"0x25888086" => "NB: Intel E7221 (Copper River)",		# DL320 G3
	"0x27788086" => "NB: Intel E7230 (Mukilteo)",			#
	"0x27788086,0x00" => "NB: Intel E7230 (Mukilteo)",		# PE850, DL320 G4, Intel S3000PT, DL320 G5, IR1250
	"0x27788086,0xc0" => "NB: Intel 3000 A0 (Mukilteo 2)",		# SM PDSML
	"0x25408086" => "NB: Intel E7500 (Plumas)",
	"0x25408086,0x02" => "NB: Intel E7500 A2 (Plumas)",		# SM P4DP6
	"0x25408086,0x03" => "NB: Intel E7500 A3 (Plumas)",		# SM P4DP6, Tyan i7500 (S2722)
	"0x254c8086" => "NB: Intel E7501 (Plumas)",			# Intel SE7501CW2
	"0x35908086" => "NB: Intel E7520 (Lindenhurst)",		# PE2850, DL360 G4, DL380 G4
	"0x35908086,0x09" => "NB: Intel E7520 C1 (Lindenhurst)",
	"0x35908086,0x0a" => "NB: Intel E7520 C2 (Lindenhurst)",
	"0x35908086,0x0c" => "NB: Intel E7520 C4 (Lindenhurst)",
	"0x35928086" => "NB: Intel E7320 (Lindenhurst VS)",
	"0x35928086,0x09" => "NB: Intel E7320 C1 (Lindenhurst VS)",
	"0x35928086,0x0a" => "NB: Intel E7320 C2 (Lindenhurst VS)",
	"0x35928086,0x0c" => "NB: Intel E7320 C4 (Lindenhurst VS)",	# Intel SE7320VP2
	"0x26008086" => "NB: Intel E8500 (Twin Castle)",
	"0x26008086,0x10" => "NB: Intel E8500 B0 (Twin Castle)",	# PE6850, DL580 G4
	"0x26008086,0x11" => "NB: Intel E8500 B1 (Twin Castle)",
	"0x25c08086" => "NB: Intel 5000X (Greencreek)",			# PE2950, PE1950, DL140 G3, SM X7DAL
	"0x25c08086,0x12" => "NB: Intel 5000X B2 (Greencreek)",
	"0x25c08086,0x13" => "NB: Intel 5000X B3 (Greencreek)",
	"0x25c08086,0x30" => "NB: Intel 5000X G0 (Greencreek)",
	"0x25c08086,0x31" => "NB: Intel 5000X G1 (Greencreek)",
	"0x25d08086" => "NB: Intel 5000Z (Blackford)",
	"0x25d08086,0x93" => "NB: Intel 5000Z B3 (Blackford)",
	"0x25d48086" => "NB: Intel 5000V (Blackford VS)",		# SM X7DVL-E, X7DVL-L, DL180
	"0x25d48086,0x92" => "NB: Intel 5000V B2 (Blackford VS)",
	"0x25d48086,0x93" => "NB: Intel 5000V B3 (Blackford VS)",
	"0x25d48086,0xb1" => "NB: Intel 5000V G1 (Blackford VS)",
	"0x25d88086" => "NB: Intel 5000P (Blackford)",			# Intel S5000PSL, PE1955, SM X7DBU, SM X7DBi+
	"0x25d88086,0x92" => "NB: Intel 5000P B2 (Blackford)",
	"0x25d88086,0x93" => "NB: Intel 5000P B3 (Blackford)",
	"0x25d88086,0xb1" => "NB: Intel 5000P G1 (Blackford)",
	"0x29c08086" => "NB: Intel G31/G33/P31/P35 (Bearlake)",		# Gigabyte P35-S3G, Gigabyte G33M-S2L
	"0x29d08086" => "NB: Intel Q33 (Bearlake)",			# HP DC5800
	"0x29e08086" => "NB: Intel X38/X48 (Bearlake)",			# Intel X38ML
	"0x2e208086" => "NB: Intel P45 (Eaglelake)",			# Gigabyte EP45-DS3R
	"0x65c08086" => "NB: Intel 5100 (San Clemente)",
	"0x65c08086,0x80" => "NB: Intel 5100 A0 (San Clemente)",	# DL180 G5
	"0x65c08086,0x90" => "NB: Intel 5100 B0 (San Clemente)",	# Dell R300, SM X7DCL, SM X7DCA-L, HP BL260c G5
	"0x40018086" => "NB: Intel 5400A (Seaburg)",
	"0x40018086,0x20" => "NB: Intel 5400A C0 (Seaburg)",
	"0x40038086" => "NB: Intel 5400B (Seaburg)",
	"0x40038086,0x20" => "NB: Intel 5400B C0 (Seaburg)",		# Intel S5400SF, DL160 G5
	"0x36008086" => "NB: Intel 7300 (Clarksboro)",
	"0x36008086,0x01" => "NB: Intel 7300 A1 (Clarksboro)",		# DL580 G5, Intel S7000FC4UR

	# ServerWorks Intel South
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x02001166" => "SB: ServerWorks OSB4",
	"0x02011166" => "SB: ServerWorks CSB5",
	"0x02031166" => "SB: ServerWorks CSB6",

	# ServerWorks Intel North
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x00081166" => "NB: ServerWorks HE-SL",			# DL360 G2, DL380 G2, PE2550
	"0x00091166" => "NB: ServerWorks LE",				# DL320, DL360
	"0x00111166" => "NB: ServerWorks GC-HE",			# DL580 G2, PE6650
	"0x00121166" => "NB: ServerWorks GC-LE",			# DL360 G3, DL380 G3 (rev 13), DL560 G1
	"0x00141166" => "NB: ServerWorks GC-LE",			# DL360 G3, DL380 G3 (rev 33)
	"0x00171166" => "NB: ServerWorks GC-SL",			# DL140, DL320 G2

	# AMD
	"0x59501002" => "SB: ATI Radeon Xpress 200",		# HP DX5150
	"0x74501022" => "NB: AMD 8131 PCI-X",			# DL145 G1, DL385 G1, DL585
	"0x74581022" => "NB: AMD 8132 PCI-X 2.0",		# DL585 G2
	"0x74681022" => "SB: AMD 8111 I/O Hub",			# Tyan S2875, DL585
	"0x005e10de" => "SB: nVIDIA nForce Professional 2200",	# DL145 G2, DL585 G2
	"0x036910de" => "SB: nVIDIA nForce Professional 3600",	# IR2400
	"0x075410de" => "SB: nVIDIA GeForce 8200",		# Asus M3N78-VM
	"0x06501039" => "SB: SiS 650GX",			# ASUS P4SGX-MX
	"0x03361106" => "NB: VIA K8M890",			# north - HP DX2255
	"0x31881106" => "NB: VIA K8T800",			# north
	"0x32271106" => "SB: VIA VT8237",			# south
	"0x33371106" => "SB: VIA VT8237A",			# south - HP DX2255

	# ServerWorks AMD
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x02341166" => "ServerWorks HT-1000",			# PE6950, PE2970, DL145 G3, DL365 G1, DL385 G2

	# Intel Mobile
	"0x25908086" => "NB: Intel 915GM (Alviso)",		# NC6220
	"0x27a08086" => "NB: Intel 945GM (Calistoga)",		# NC6400
	"0x2a008086" => "NB: Intel PM965/GM965 (Crestline)",	# HP 6910p, MSI GM965, AOpen i965GMt-LA
);

my(%pci_classes) = (
	# pciutils-2.2.4/lib/header.h

	"0x0100" => "Disk-Control",	# SCSI
	"0x0101" => "Disk-Control",	# IDE
	"0x0104" => "Disk-Control",	# RAID
	"0x0105" => "Disk-Control",	# ATA
	"0x0106" => "Disk-Control",	# SATA
	"0x0107" => "Disk-Control",	# SAS
	"0x0180" => "Disk-Control",	# Other
	"0x0200" => "Network",		# Ethernet
	"0x0280" => "Network",		# Other (e.g. wireless)
	"0x0c04" => "Disk-Control",	# FC
#	"0x0c05" => "SMBus",		# SMBus
	"0x0c06" => "Network",		# InfiniBand
	"0x1000" => "Crypto",		# Crypt Network
);

my(%pci_devices) = (
	# http://www.pcidatabase.com/
	# http://www.pcidatabase.com/reports.php?type=tab-delimeted
	# http://pciids.sourceforge.net/pci.ids

	# 0e11 - Compaq
	# 1000 - Symbios/LSI - http://en.wikipedia.org/wiki/Symbios_Logic
	# 1002 - ATI
	# 100b - National Semiconductor
	# 1014 - IBM
	# 1022 - AMD
	# 1028 - Dell
	# 1033 - NEC
	# 103c - HP
	# 1044 - DPT/Adaptec
	# 1069 - Mylex
	# 1077 - QLogic
	# 107b - Gateway
	# 108e - Sun
	# 10b7 - 3com
	# 10df - Emulex
	# 10f1 - Tyan
	# 1095 - Silicon Image
	# 1148 - SysKonnect
	# 1166 - Serverworks
	# 1170 - Inventec
	# 11ab - Marvell
	# 11ad - Netgear
	# 13c1 - 3ware
	# 1458 - Gigabyte
	# 1462 - MSI
	# 14c1 - Myricom
	# 14e4 - Broadcom
	# 152d - Quanta
	# 15ad - VMware
	# 161f - Arima (Rioworks)
	# 17c2 - Newisys
	# 1749 - RLX
	# 8086 - Intel
	# 9004 - Adaptec
	# 9005 - Adaptec
	# a0a0 - AOpen

	'network' => {
		"0xf0041385,0x000211ad,0x20" =>	"Netgear FA310TX 10/100",			# Adapter

		"0x20001022,0x20001022,0x10" => "AMD 79C971 Lance/PCnet 10/100",		# VMware
		"0x20001022,0x20001022,0x40" => "AMD 79C971 Lance/PCnet 10/100",		# VirtualBox

		"0x000914c1,0x000814c1,0x00" => "Myricom Myri-10G",				# Adapter

		"0x00000000,0x0035100b,0x30" => "NatSemi DP83065 Gigabit",

		"0x21001148,0x9e001148,0x12" => "SysKonnect 9E21 Gigabit",			# SysKonnect 9E21 Server Adapter

		"0x811a1043,0x432011ab,0x14" => "Asus/Marvell Yukon 88E8001 Gigabit",		# Asus P5Q-Deluxe

		"0x502111ab,0x436111ab,0x16" => "Marvell Yukon 88E8050 Gigabit",		# Intel SE7320VP2
		"0x50218086,0x436111ab,0x17" => "Intel/Marvell Yukon 88E8050 Gigabit",		# Intel SE7320VP2, SE7520BD2
		"0x34398086,0x436111ab,0x18" => "Intel/Marvell Yukon 88E8050 Gigabit",		# Intel SE7520BB2
		"0x826e1043,0x436411ab,0x12" => "Asus/Marvell Yukon 88E8056 Gigabit",		# Asus P5BV-C
		"0x81f81043,0x436411ab,0x12" => "Asus/Marvell Yukon 88E8056 Gigabit",		# Asus P5Q-Deluxe

		"0xe0001458,0x813610ec,0x01" => "Gigabyte/RealTek RTL8101E 10/100",		# Gigabyte 945GCM-S2C
		"0x00018086,0x813610ec,0x02" => "Intel/RealTek RTL8102EL 10/100",		# Intel D945GCLF

		"0x????????,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x03231154,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x813910ec,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x81091043,0x813910ec,0x10" => "Asus/RealTek 8100C 10/100",			# Asus P4GE-MX
		"0x301e103c,0x813910ec,0x10" => "HP/RealTek 8100C 10/100",			# HP DX2180
		"0x3024103c,0x813910ec,0x10" => "HP/RealTek 8100C 10/100",			# HP DX2255

		"0x3022103c,0x816710ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP DX2280
		"0xe0001458,0x816810ec,0x01" => "Gigabyte/RealTek 8111C Gigabit",		# Gigabyte P35-S3G, Gigabyte G33M-S2L
		"0xe0001458,0x816810ec,0x02" => "Gigabyte/RealTek 8111C Gigabit",		# Gigabyte EP45-DS3R
		"0x063ea0a0,0x816810ec,0x02" => "AOpen/RealTek 8111C Gigabit",			# AOpen i965GMt-LA
		"0x3022103c,0x816910ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP DX2280
		"0x301f103c,0x816910ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP D290

		"0x100010b7,0x920010b7,0x74" => "3Com 3c905C-TX 10/100",
		"0x00fe1028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			# Dell OptiPlex GX240
		"0x00b41028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			#
		"0x00d81028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			# Dell 530

		"0x00261170,0x037310de,0xa3" => "Inventec/nVIDIA MCP55 Gigabit",		# IR2400
		"0x286510f1,0x005710de,0xa3" => "Tyan/nVIDIA nForce CK8-04 Gigabit",		# Tyan S2865
		"0x5348108e,0x005710de,0xa3" => "Sun/nVIDIA nForce CK8-04 Gigabit",		# Sun X2100

		# Broadcom

		"0x3011103c,0x160014e4,0x01" => "HP/Broadcom BCM5752 Gigabit",			# HP DC7600

		"0x037c1014,0x163914e4,0x01" => "IBM/Broadcom BCM5709 Gigabit",			# IBM x3850 M2

		"0x00d11028,0x164414e4,0x10" => "Dell/Broadcom BCM5700 Gigabit",		# PE2550
		"0x00d11028,0x164414e4,0x12" => "Dell/Broadcom BCM5700 Gigabit",		# PE2550
		"0x01091028,0x164414e4,0x14" => "Dell/Broadcom BCM5700 Gigabit",		# PE6650

		"0x????????,0x164514e4,0x15" => "Broadcom BCM5701 Gigabit",
		"0x01211028,0x164514e4,0x15" => "Dell/Broadcom BCM5701 Gigabit",		# PE2650
		"0x007c0e11,0x164514e4,0x15" => "Compaq/Broadcom BCM5701 Gigabit",		# DL580 G2
		"0x00c10e11,0x164514e4,0x15" => "HP NC6770/Broadcom BCM5701 Gigabit",		# HP NC6770 Adapter
		"0x00850e11,0x164514e4,0x15" => "Compaq/Broadcom BCM5701 Gigabit",		# DL360 G2
		"0x100410b7,0x164514e4,0x15" => "3Com/Broadcom BCM5701 Gigabit-SX",		# 3Com 3C996-SX
		"0x100610b7,0x164514e4,0x15" => "3Com/Broadcom BCM5701 Gigabit",		# 3Com 3C996B-T

		"0x????????,0x164814e4,0x??" => "Broadcom BCM5704C Gigabit",
		"0x164814e4,0x164814e4,0x00" => "Broadcom BCM5704C Gigabit",			# DL380 G4
		"0x014a1028,0x164814e4,0x02" => "Dell/Broadcom BCM5704C Gigabit",		# PE1750
		"0x164814e4,0x164814e4,0x02" => "Broadcom BCM5704C Gigabit",			# DL140
		"0x36c01022,0x164814e4,0x02" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x001017c2,0x164814e4,0x03" => "Newisys/Broadcom BCM5704C Gigabit",		# V20z
		"0x164814e4,0x164814e4,0x03" => "Broadcom BCM5704C Gigabit",			# Tyan S4882, Tyan S4881
		"0x164414e4,0x164814e4,0x03" => "Broadcom BCM5704C Gigabit",			# Tyan S2882, Tyan S2881, Tyan S2880, Tyan S4882
		"0x36c01022,0x164814e4,0x03" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x16481022,0x164814e4,0x03" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x16101462,0x164814e4,0x03" => "MSI/Broadcom BCM5704C Gigabit",		# MSI ???
		"0x24501462,0x164814e4,0x03" => "MSI/Broadcom BCM5704C Gigabit",		# MSI 8480000
		"0x00d00e11,0x164814e4,0x10" => "HP/Broadcom BCM5704C Gigabit",			# DL360 G4, DL380 G4, DL 385, DL 585
		"0x310f103c,0x164814e4,0x10" => "HP/Broadcom BCM5704C Gigabit",			# DL185 G5
		"0x01701028,0x164814e4,0x10" => "Dell/Broadcom BCM5704C Gigabit",		# PE6850
		"0x164414e4,0x164814e4,0x10" => "Broadcom BCM5704C Gigabit",			# Tyan S2882, Tyan S2880
		"0x164814e4,0x164814e4,0x10" => "Broadcom BCM5704C Gigabit",			# Arima HDAMA-I

		"0x1709103c,0x164a14e4,0x02" => "HP/Broadcom BCM5706 Gigabit",			# DL585 G2, DL580 G4

		"0x????????,0x164c14e4,0x??" => "Broadcom BCM5708 Gigabit",			#
		"0x164c14e4,0x164c14e4,0x10" => "Broadcom BCM5708 Gigabit",			# Tyan S2865
		"0x7038103c,0x164c14e4,0x11" => "HP/Broadcom BCM5708 Gigabit",			# DL360 G5, DL380 G5, DL365 G1
		"0x01b31028,0x164c14e4,0x11" => "Dell/Broadcom BCM5708 Gigabit",		# PE1950
		"0x01b21028,0x164c14e4,0x11" => "Dell/Broadcom BCM5708 Gigabit",		# PE2950
		"0x03421014,0x164c14e4,0x11" => "IBM/Broadcom BCM5708 Gigabit",			# IBM x3550
		"0x164c14e4,0x164c14e4,0x12" => "Broadcom BCM5708 Gigabit",			# Adapter?
		"0x01b31028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE1950
		"0x01b21028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE2950
		"0x7037103c,0x164c14e4,0x12" => "HP NC373T/Broadcom BCM5708 Gigabit",		# HP NC373T Adapter
		"0x7038103c,0x164c14e4,0x12" => "HP NC373i/Broadcom BCM5708 Gigabit",		# DL360 G5, DL380 G5, DL365 G1, DL385 G2, DL580 G5
		"0x01ea1028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE6950
		"0x01f01028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# Dell R900
		"0x02051028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE2970

		"0x86531028,0x165314e4,0x01" => "Dell/Broadcom BCM5705 Gigabit",

		"0x3100103c,0x165414e4,0x03" => "HP NC1020/Broadcom BCM5705 Gigabit",		# HP NC1020 Adapter

		"0x????????,0x165914e4,0x??" => "Broadcom BCM5721 Gigabit",
		"0x165914e4,0x165914e4,0x00" => "Broadcom BCM5721 Gigabit",
		"0x165914e4,0x165914e4,0x01" => "Broadcom BCM5721 Gigabit",			# Gigabyte 9ILDR	!!! FreeBSD 6 says 5750
		"0x01b61028,0x165914e4,0x11" => "Dell/Broadcom BCM5721 Gigabit",		# PE850
		"0x01e61028,0x165914e4,0x11" => "Dell/Broadcom BCM5721 Gigabit",		# PE860
		"0x165914e4,0x165914e4,0x11" => "Broadcom BCM5721 Gigabit",			# Inventec Tuna, Tyan S2865, Inventec IR2100
		"0x00221170,0x165914e4,0x11" => "Inventec/Broadcom BCM5721 Gigabit",		# IR1250
		"0x1659103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL140 G2
		"0x3209103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL145 G2
		"0x3260103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL140 G3
		"0x81491043,0x165914e4,0x11" => "Asus/Broadcom BCM5721 Gigabit",		# Asus K8N-DRE
		"0x5348108e,0x165914e4,0x11" => "Sun/Broadcom BCM5721 Gigabit",			# Sun X2100
		"0x02c61014,0x165914e4,0x11" => "IBM/Broadcom BCM5721 Gigabit",			# IBM 306m
		"0x81491043,0x165914e4,0x21" => "Asus/Broadcom BCM5721 Gigabit",		# Asus K8N-DRE
		"0x01eb1028,0x165914e4,0x21" => "Dell/Broadcom BCM5721 Gigabit",		# SC1435		!!! FreeBSD 6 says 5750
		"0x023c1028,0x165914e4,0x21" => "Dell/Broadcom BCM5721 Gigabit",		# Dell R200

		"0x020f1028,0x165a14e4,0x00" => "Dell/Broadcom BCM5722 Gigabit",		# Dell R300
		"0x7051103c,0x165a14e4,0x00" => "HP/Broadcom BCM5722 Gigabit",			# DL180 G5, DL120 G5, DL160 G5

		"0x7039103c,0x166814e4,0xa3" => "HP/Broadcom BCM5714 Gigabit",			# DL320 G5

		"0x????????,0x167714e4,0x??" => "Broadcom BCM5751 Gigabit",
		"0x3005103c,0x167714e4,0x01" => "HP/Broadcom BCM5751 Gigabit",			# HP DC5100, HP DC7100
		"0x3006103c,0x167714e4,0x01" => "HP/Broadcom BCM5751 Gigabit",			# HP DC5100 (PT005AW)
		"0x00221170,0x167714e4,0x11" => "Inventec/Broadcom BCM5751 Gigabit",		# IR1250
		"0x3009103c,0x167714e4,0x20" => "HP/Broadcom BCM5751 Gigabit",			# HP DX5150
		"0x01791028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell GX280
		"0x01ad1028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell GX620
		"0x01821028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell Latitude D610

		"0x????????,0x167814e4,0xa3" => "Broadcom BCM5715 Gigabit",
		"0x167814e4,0x167814e4,0xa3" => "Broadcom BCM5715 Gigabit",			# Huawei Tecal
		"0x703e103c,0x167814e4,0xa3" => "HP/Broadcom BCM5715 Gigabit",			# DL145 G3
		"0x8959152d,0x167814e4,0xa3" => "Quanta/Broadcom BCM5715 Gigabit",		# Quanta S47

		"0x703c103c,0x167914e4,0xa3" => "HP/Broadcom BCM5715S Gigabit",			# HP BL480c G1, HP BL260c G5, HP NC326i Adapter

		"0x2808103c,0x167b14e4,0x02" => "HP/Broadcom BCM5755 Gigabit",			# HP DC5700

		"0x000c103c,0x169614e4,0x03" => "HP/Broadcom BCM5782 Gigabit",			#
		"0x12bc103c,0x169614e4,0x03" => "HP/Broadcom BCM5782 Gigabit",			# HP DC5000, HP D530

		"0x0944103c,0x167d14e4,0x11" => "HP/Broadcom BCM5751M Gigabit",			# HP NC6220

		"0x000c14e4,0x16a614e4,0x02" => "Broadcom BCM5702 Gigabit",			# Arima HDAMA
		"0x00bb0e11,0x16a614e4,0x02" => "HP/Broadcom BCM5702 Gigabit",			# DL320 G2
		"0x800914e4,0x16a614e4,0x02" => "Broadcom BCM5702 Gigabit",			# AMD Melody

		"0x01211028,0x16a714e4,0x02" => "Dell/Broadcom BCM5703 Gigabit",		# PE2650
		"0x00cb0e11,0x16a714e4,0x02" => "HP/Broadcom BCM5703 Gigabit",			# DL360 G3, DL380 G3, DL180, DL560 G1
		"0x001017c2,0x16a714e4,0x02" => "Newisys/Broadcom BCM5703 Gigabit",		# V20z
		"0x002017c2,0x16a714e4,0x02" => "Newisys/Broadcom BCM5703 Gigabit",		# V40z
		"0x00ca0e11,0x16c714e4,0x10" => "HP NC7771/Broadcom BCM5703 Gigabit",		# HP NC7771 adapter

		"0x03011014,0x16a814e4,0x10" => "IBM/Broadcom BCM5704S Gigabit",		# IBM eServer BladeCenter LS20

		"0x01bb1028,0x16ac14e4,0x12" => "Dell/Broadcom BCM5708S Gigabit",		# PE1955
		"0x703b103c,0x16ac14e4,0x12" => "HP/Broadcom BCM5708S Gigabit",			# HP BL460c G1, HP BL480c G1, HP SB600c

		"0x30ad103c,0x16fd14e4,0x21" => "HP/Broadcom BCM5753M Gigabit",			# HP NC6400

		# Intel
		# http://support.intel.com/support/network/sb/cs-008441.htm

		"0x004a0e11,0x10018086,0x02" => "HP NC6136/Intel 82543GC Gigabit",		# HP NC6136 adapter

		"0x10048086,0x10048086,0x02" => "Intel 82543GC Gigabit",			# Intel PRO/1000 T Server Adapter

		"0x011b1028,0x10088086,0x02" => "Dell/Intel 82544EI Gigabit",			# PE1650
		"0x21201749,0x10088086,0x02" => "RLX/Intel 82544EI Gigabit",			# RLX ServerBlade
		"0x11078086,0x10088086,0x02" => "Intel 82544EI Gigabit",			# Intel PRO/1000 XT Server Adapter

		"0x34128086,0x100d8086,0x02" => "Intel 82544GC Gigabit",			# Intel SRSH4

		"0x????????,0x100e8086,0x02" => "Intel 82540EM Gigabit",
		"0x004e8086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# SM P4SGR
		"0x34188086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel SE7501BR2
		"0x34258086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel SE7501CW2
		"0x002e8086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel adapter
		"0x892d152d,0x100e8086,0x02" => "Quanta/Intel 82540EM Gigabit",			# SU2-4200
		"0x01341028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# SU2-4200
		"0x01351028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell 1600SC
		"0x01511028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell GX270
		"0x01381028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell SX260

		"0x10018086,0x100f8086,0x01" => "Intel 82545EM Gigabit",			# Tyan S2722
		"0x075015ad,0x100f8086,0x01" => "VMware/Intel 82545EM Gigabit",			#

		"0x????????,0x10108086,0x01" => "Intel 82546EB Gigabit",
		"0x00db0e11,0x10108086,0x01" => "HP NC7170/Intel 82546EB Gigabit",		# HP NC7170 adapter
		"0x10128086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x34158086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7500WV2
		"0x34168086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7500WV2A
		"0x341a8086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7501WV2S
		"0x341b8086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7501WV2A
		"0x10118086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# SM X5DP8

		"0x10138086,0x10138086,0x00" => "Intel 82541EI Gigabit",			# Tyan S2875

		"0x34288086,0x10198086,0x00" => "Intel 82547EI Gigabit",			# Intel S875WP1

		"0x10018086,0x10268086,0x04" => "Intel 82545GM Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x10028086,0x10268086,0x04" => "Intel 82545GM Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x30001458,0x10268086,0x04" => "Gigabyte/Intel 82545GM Gigabit",		# Gigabyte GA-7A8DRH

		"0x00120e11,0x103b8086,0x81" => "Compaq/Intel 82801DB ICH4 10/100 VM",		# HP D510

		"0x30be103c,0x10498086,0x03" => "Intel 82566MM Gigabit",			# HP 6910p
		"0x30c1103c,0x10498086,0x03" => "Intel 82566MM Gigabit",			# HP 6910p

		"0x00008086,0x104b8086,0x03" => "Intel 82566DC Gigabit",			# MSI GM965

		"0x34288086,0x10508086,0x01" => "Intel 82562ET 10/100",				# Intel S875WP1

		"0x????????,0x105e8086,0x06" => "Intel 82571EB Gigabit",
		"0x115e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x125e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x135e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x7044103c,0x105e8086,0x06" => "HP NC360T/Intel 82571EB Gigabit",		# HP NC360T adapter

		"0x30548086,0x10648086,0x01" => "Intel 82562EZ 10/100",				# Intel D915GAG
		"0x305d8086,0x10648086,0x01" => "Intel 82562EZ 10/100",				# Intel D915GUX

		"0x01651028,0x10758086,0x00" => "Dell/Intel 82547GI Gigabit",			# PE750

		"0x10761458,0x10768086,0x00" => "Gigabyte/Intel 82541GI Gigabit",		# Gigabyte GA-7A8DRH

		"0x????????,0x10768086,0x05" => "Intel 82541PI Gigabit",			#
		"0x347e8086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7520BB2
		"0x016d1028,0x10768086,0x05" => "Dell/Intel 82541PI Gigabit",			# PE1850, PE2850
		"0x019a1028,0x10768086,0x05" => "Dell/Intel 82541PI Gigabit",			# SC1425
		"0x30a18086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7230NH1LX
		"0x34398086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7520BD2
		"0x34448086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7320VP2
		"0x34508086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7320VP2
		"0x348d8086,0x10768086,0x05" => "Intel 82541GI Gigabit",			# Intel AspenHill
		"0x01651028,0x10768086,0x00" => "Dell/Intel 82541GI Gigabit",			# PE750

		"0x????????,0x10798086,0x03" => "Intel 82546GB Gigabit",
		"0x10798086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel SE7520JR2
		"0x11798086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x118a8086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel Server Adapter

		"0x018a1028,0x107b8086,0x03" => "Dell/Intel 82546GB Gigabit",			# PE1855

		"0x10828086,0x107d8086,0x06" => "Intel 82572EI Gigabit",			# Intel PRO/1000 PT Server Adapter

		"0x????????,0x108b8086,0x03" => "Intel 82573V Gigabit",
		"0x349b8086,0x108b8086,0x03" => "Intel 82573V Gigabit",				# Intel Coretta, S3000PT
		"0x00008086,0x108b8086,0x03" => "Intel 82573V Gigabit",				# Quanta QSSC-295MB, DCS S45
		"0x00001458,0x108b8086,0x03" => "Gigabyte/Intel 82573V Gigabit",		# Gigabyte R116
		"0x521110f1,0x108b8086,0x03" => "Tyan/Intel 82573V Gigabit",			# Tyan S5376

		"0x????????,0x108c8086,0x03" => "Intel 82573E Gigabit",
		"0x30a28086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel SE7230NH1LX
		"0x348d8086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel AspenHill
		"0x349b8086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel Coretta, S3000PT
		"0x108c15d9,0x108c8086,0x03" => "Supermicro/Intel 82573E Gigabit",		# SM X6DLP, SM X7DCL-3, SM X7DCT

		"0x????????,0x10968086,0x01" => "Intel 82563EB Gigabit",
		"0x00008086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Tyan S5397
		"0x346c8086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000PAL
		"0x34768086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000PSL
		"0x34848086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000VSA
		"0x34d48086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5400SF
		"0x000015d9,0x10968086,0x01" => "Supermicro/Intel 82563EB Gigabit",		# SM X7DB8, X7DVL-E, X7DVL-L
		"0x109615d9,0x10968086,0x01" => "Supermicro/Intel 82563EB Gigabit",		# SM X7DBR, X7DBi+
		"0x8949152d,0x10968086,0x01" => "Quanta/Intel 82563EB Gigabit",			# DCS S29, DCS S58
		"0x34cc8086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel Tigerton
		"0x00231170,0x10968086,0x01" => "Inventec/Intel 82563EB Gigabit",		# eslim, Inventec Seabream
		"0x003a1170,0x10968086,0x01" => "Inventec/Intel 82563EB Gigabit",		# Inventec 5220
		"0x83361033,0x10968086,0x01" => "NEC/Intel 82563EB Gigabit",			# NEC Express5800/120Ri-2
		"0x82171043,0x10968086,0x01" => "Asus/Intel 82563EB Gigabit",			# IBM dx340

		"0x00008086,0x10988086,0x01" => "Intel 82563EB Gigabit",			# SM B7DBE

		"0x????????,0x109a8086,0x00" => "Intel 82573L Gigabit",
		"0x00008086,0x109a8086,0x00" => "Intel 82573L Gigabit",				# Rackable S45
		"0x109a15d9,0x109a8086,0x00" => "Supermicro/Intel 82573L Gigabit",		# SM X6DLP, SM PDSML, SM X7DCA-L, SM X7DCT
		"0x80301462,0x109a8086,0x00" => "MSI/Intel 82573L Gigabit",			# MSI GM965
		"0x83661033,0x109a8086,0x00" => "NEC/Intel 82573L Gigabit",			# NEC Express5800/i120Ra-e1

		"0x10a48086,0x10a48086,0x06" => "Intel 82571EB Quad Gigabit",			# PRO/1000 PT Quad Port Server Adapter

		"0x????????,0x10a78086,0x02" => "Intel 82575EB Gigabit",
		"0x34ce8086,0x10a78086,0x02" => "Intel 82575EB Gigabit",			# Intel X38ML
		"0x10a88086,0x10a78086,0x02" => "Intel 82575EB Gigabit",			# Intel S7000FC4UR
		"0x10a715d9,0x10a78086,0x02" => "Supermicro/Intel 82575EB Gigabit",		# SM X8DTN

		"0x11998086,0x10b58086,0x03" => "Intel 82546GB Quad Gigabit",			# PRO/1000 GT Quad Port Server Adapter

		"0x704a103c,0x10b98086,0x06" => "HP NC110T/Intel 82572EI Gigabit",		# HP NC110T adapter

		"0x704b103c,0x10bc8086,0x06" => "HP NC364T/Intel 82571EB Quad Gigabit",		# HP NC364T adapter

		"0x????????,0x10bd8086,0x02" => "Intel 82566DM-2 Gigabit",
		"0x00008086,0x10bd8086,0x02" => "Intel 82566DM-2 Gigabit",			# DCS S45, Quanta QSSC-295MB
		"0x281e103c,0x10bd8086,0x02" => "HP/Intel 82566DM-2 Gigabit",			# HP DC5800
		"0x83661033,0x10bd8086,0x02" => "NEC/Intel 82566DM-2 Gigabit",			# NEC Express5800/i120Ra-e1

		"0x145a8086,0x10d68086,0x02" => "Intel 82575GB Quad Gigabit",			# VT Quad Port Server Adapter

		# http://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
		"0x????????,0x12298086,0x01" => "Intel 82557 A 10/100",
		"0x00000000,0x12298086,0x01" => "Intel 82557 A 10/100",

		"0x????????,0x12298086,0x02" => "Intel 82557 B 10/100",
		"0x00018086,0x12298086,0x02" => "Intel 82557 B 10/100",

		"0x????????,0x12298086,0x03" => "Intel 82557 C 10/100",

		"0x????????,0x12298086,0x04" => "Intel 82558 A 10/100",

		"0x????????,0x12298086,0x05" => "Intel 82558 B 10/100",
		"0x00088086,0x12298086,0x05" => "Intel 82558 B 10/100",
		"0x00098086,0x12298086,0x05" => "Intel 82558 B 10/100",				# adapter
		"0x10f08086,0x12298086,0x05" => "Intel 82558 B 10/100",				# PRO/100+ Dual Port Adapter
		"0xb0dd0e11,0x12298086,0x05" => "Compaq NC3131/Intel 82558 B 10/100",		# Compaq 6400R

		"0x????????,0x12298086,0x06" => "Intel 82559 A 10/100",

		"0x????????,0x12298086,0x07" => "Intel 82559 B 10/100",

		"0x????????,0x12298086,0x08" => "Intel 82559 C 10/100",
		"0x00000000,0x12298086,0x08" => "Intel 82559 C 10/100",				# Compaq ML370
		"0x000b8086,0x12298086,0x08" => "Intel 82559 C 10/100",
		"0x000c8086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Tyan S2510
		"0x100c8086,0x12298086,0x08" => "Intel 82559 C 10/100",				# SM P3TSSR, SM 370SSR
		"0x12298086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Intel STL2
		"0x30008086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Quanta SU2, Intel TR440BX
		"0x30108086,0x12298086,0x08" => "Intel 82559 C 10/100",				# SM P3TSSR
		"0x34148086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Intel SAI2
		"0x009b1028,0x12298086,0x08" => "Dell/Intel 82559 C 10/100",			# PE2450, PE2550
		"0x00da1028,0x12298086,0x08" => "Dell/Intel 82559 C 10/100",			# PE1550
		"0x10ca103c,0x12298086,0x08" => "HP/Intel 82559 C 10/100",			# HP NetServer
		"0x8916152d,0x12298086,0x08" => "Quanta/Intel 82559 C 10/100",			# Quanta SU6
		"0x8917152d,0x12298086,0x08" => "Quanta/Intel 82559 C 10/100",			# Gateway 7450R, Quanta SU2-3000
		"0xb1340e11,0x12298086,0x08" => "Compaq NC3163/Intel 82559 C 10/100",		# DL320, DL360, DL380 G2, ML530 G2
		"0xb1440e11,0x12298086,0x08" => "Compaq NC3123/Intel 82559 C 10/100",		# Compaq NC3123 adapter

		"0x????????,0x12298086,0x09" => "Intel 82559ER A 10/100",
		"0x10128086,0x12298086,0x09" => "Intel 82559ER A 10/100",			# PRO/100 S Server Adapter

		"0x????????,0x12298086,0x0c" => "Intel 82550 10/100",
		"0x00408086,0x12298086,0x0c" => "Intel 82550 10/100",				# PRO/100 S Desktop Adapter

		"0x????????,0x12298086,0x0d" => "Intel 82550 10/100",
		"0x10508086,0x12298086,0x0d" => "Intel 82550 10/100",				# SM P4DP6
		"0x301a8086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel S845WD1
		"0x340f8086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SCB20-S
		"0x34108086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SCB20-A
		"0x34128086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SRSH4
		"0x34188086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7501BR2
		"0x34198086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7500CW2
		"0x34258086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7501CW2

		"0x????????,0x12298086,0x0e" => "Intel 82550 10/100",

		"0x????????,0x12298086,0x0f" => "Intel 82551 10/100",				#

		"0x????????,0x12298086,0x10" => "Intel 82551 10/100",				#
		"0x10408086,0x12298086,0x10" => "Intel 82551 10/100",				# Tyan S2722, Tyan S2882
		"0x00708086,0x12298086,0x10" => "Intel 82551 10/100",				#

		"0x00120e11,0x24498086,0x01" => "Compaq/Intel 82801BA ICH2 10/100 VM",
		"0x00120e11,0x24498086,0x03" => "Compaq/Intel 82801BA ICH2 10/100 VM",		# HP D500
		"0x30108086,0x24498086,0x03" => "Intel 82801BA ICH2 10/100 VE",			# SM 370SSR
		"0x20101749,0x24498086,0x42" => "RLX/Intel 82801BA ICH2 10/100",		# RLX ServerBlade
		"0x308d8086,0x27dc8086,0x01" => "Intel 82562GX (82801G ICH7) 10/100",		# Intel D945GTP
		"0x30001458,0x294c8086,0x02" => "Gigabyte/Intel 82566DC-2 Gigabit",		# Gigabyte R116

		# wireless
		"0x27228086,0x42208086,0x05" => "Intel 2200BG Wireless",			# Dell Latitude D610
		"0x12f6103c,0x42208086,0x05" => "HP/Intel 2200BG Wireless",			# HP NC6220
		"0x135b103c,0x42228086,0x02" => "HP/Intel 3945ABG Wireless",			# HP NC6400
		"0x135c103c,0x42228086,0x02" => "HP/Intel 3945ABG Wireless",			# HP NC6400
		"0x10008086,0x42298086,0x61" => "Intel 4965AG Wireless",			# HP 6910p
		"0x10018086,0x42298086,0x61" => "Intel 4965AG Wireless",			# HP 6910p

		# infiniband
		"0x627415b3,0x627415b3,0x20" => "Mellanox MT25204 InfiniHost III",
		"0x627815b3,0x627815b3,0x20" => "Mellanox MT25208 InfiniHost III",
	},

	'storage' => {
		"0x0944103c,0x8033104c,0x00" => "HP/TI PCIxx21 SD Flash Reader",		# NC6220
		"0x30ad103c,0x803b104c" => "HP/TI 5-in-1 Multimedia Card Reader",		# NC6400

		"0x40500e11,0x00461011,0x01" => "Compaq Smart Array 4200",			# ML530 G2
		"0x40580e11,0x00461011,0x01" => "Compaq Smart Array 431",			# ML370

		"0x40800e11,0xb1780e11" => "Compaq Smart Array 5i",			# DL560 G1, DL585 G1
		"0x40830e11,0xb1780e11" => "Compaq Smart Array 5i",
		"0x40910e11,0x00460e11" => "HP Smart Array 6i",
		"0x40700e11,0xb0600e11" => "Compaq Smart Array 5300",
		"0x409b0e11,0x00460e11" => "HP Smart Array 642",
		"0x409c0e11,0x00460e11" => "HP Smart Array 6400",
		"0x409d0e11,0x00460e11" => "HP Smart Array 6400 EM",
		"0x001b0e11,0x00121000" => "Compaq/Symbios 895A Ultra2",
		"0x3225103c,0x3220103c" => "HP Smart Array P600",
		"0x3223103c,0x3230103c" => "HP Smart Array P800",			# DL360 G5
		"0x3234103c,0x3230103c" => "HP Smart Array P400",			# DL380 G5, DL385 G2
		"0x3235103c,0x3230103c" => "HP Smart Array P400i",			# DL360 G5, DL365 G1
		"0x3211103c,0x3238103c" => "HP Smart Array E200i",			# DL360 G5, HP SB600c
		"0x3212103c,0x3238103c" => "HP Smart Array E200i",			# DL360 G5
		"0x0475101e,0x1960101e" => "MegaRAID Express 500 Ultra160",
		"0x04711028,0x1960101e" => "Dell/MegaRAID PERC 3/QC",			# PE6650
		"0x04931028,0x1960101e" => "Dell/MegaRAID PERC 3/DC",
		"0x05111028,0x1960101e,0x02" => "Dell/MegaRAID CERC QC SATA/150",	# Dell 1600SC
		"0x05181028,0x19601000" => "Dell/MegaRAID PERC 4/DC",
		"0x05201028,0x19601000" => "Dell/MegaRAID PERC 4/SC",
		"0x05181000,0x19601000" => "LSILogic MegaRAID Ultra320-2",
		"0x05201000,0x19601000" => "LSILogic MegaRAID Ultra320-1",
		"0x00d11028,0x00021028,0x01" => "Dell PERC 3/Di",			# PE2550
		"0x00031028,0x00031028" => "Dell PERC 3/Si",
		"0x01211028,0x000a1028" => "Dell PERC 3/Di",				# PE2650
		"0x014a1028,0x000f1028" => "Dell PE1750/LSILogic PERC 4/Di",
	
		"0x????????,0x00131028" => "LSILogic PERC 4",
		"0x016c1028,0x00131028" => "Dell PE1850/LSILogic PERC 4e/Si",
		"0x016d1028,0x00131028" => "Dell PE2850/LSILogic PERC 4e/Di",
		"0x01701028,0x00131028" => "Dell PE6850/LSILogic PERC 4e/Di",		# PE6850
	
		"0x????????,0x00151028" => "LSILogic PERC 5",
		"0x1f011028,0x00151028" => "Dell/LSILogic PERC 5/E",
		"0x1f021028,0x00151028" => "Dell PE6950/LSILogic PERC 5/i",		# PE6950
		"0x1f031028,0x00151028" => "Dell/LSILogic PERC 5/i",			# PE2950, PE1950
	
		"0xb1430e11,0x000a1000" => "Compaq/LSILogic 1510 Ultra2",		# DL360, ML570
	
		"0x????????,0x000f1000" => "LSILogic 53C875 Ultra",
		"0x00000000,0x000f1000" => "LSILogic 53C875 Ultra",
		"0x10001000,0x000f1000" => "LSILogic 53C876 Ultra",
		"0x70040e11,0x000f1000,0x14" => "LSILogic 53C875 Ultra",		# Compaq 6400R
	
		"0x40400e11,0x00101000" => "Compaq/LSILogic 1510 Ultra2",		# DL360
		"0x60b0103c,0x00201000" => "HP/LSILogic 1010-33 Ultra160",		# HP NetServer
		"0x10001000,0x00201000" => "LSILogic 1010-33 Ultra160",			# Tyan S2510
		"0x10001000,0x00211000" => "LSILogic 1010-66 Ultra160",
	
		"0x????????,0x00301000" => "LSILogic 1030 Ultra320",
		"0x00000000,0x00301000" => "LSILogic 1030 Ultra320",			# VMware
		"0x10001000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S4882
		"0x10101000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S4881
		"0x10601000,0x00301000" => "LSILogic 1030 Ultra320",			# Inventec IR2300 (onboard?)
		"0x10005100,0x00301000" => "LSILogic 1030 Ultra320",			# Arima HDAMA
		"0x30e01000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S2880
		"0x51001000,0x00301000" => "LSILogic 1030 Ultra320",			# Arima HDAMA
		"0x71301000,0x00301000" => "LSILogic 1030 Ultra320",
		"0x24501462,0x00301000" => "MSI/LSILogic 1030 Ultra320",
		"0x014a1028,0x00301000" => "Dell PE1750/LSILogic 1030 Ultra320",
		"0x016c1028,0x00301000" => "Dell PE1850/LSILogic 1030 Ultra320",
		"0x016d1028,0x00301000" => "Dell PE2850/LSILogic 1030 Ultra320",
		"0x01701028,0x00301000" => "Dell PE6850/LSILogic 1030 Ultra320",
		"0x018a1028,0x00301000" => "Dell PE1855/LSILogic 1030 Ultra320",
		"0x10401028,0x00301000" => "Dell/LSILogic 1030 Ultra320",
		"0x00da0e11,0x00301000" => "HP/LSILogic 1030 Ultra320",			# DL145 G1
		"0x00f40e11,0x00301000" => "HP/LSILogic 1030 Ultra320",			# DL140 G2
		"0x3108103c,0x00301000" => "HP/LSILogic 1030 Ultra320",
		"0x34628086,0x00301000" => "Intel/LSILogic 1030 Ultra320",		# Intel SE7520BD2
		"0x34358086,0x00301000" => "Intel/LSILogic 1030 Ultra320",		# Intel SE7520JR2
		"0x03011014,0x00301000" => "IBM/LSILogic 1030 Ultra320",		# IBM eServer BladeCenter LS20
		"0x001017c2,0x00301000" => "Newisys/LSILogic 1030 Ultra320",		# V20z
		"0x002017c2,0x00301000" => "Newisys/LSILogic 1030 Ultra320",		# V40z
	
		"0x10051000,0x04131000" => "MegaRAID SAS 8300XLP",
	
		"0x????????,0x00501000" => "LSILogic SAS 1064",
		"0x10001000,0x00501000,0x02" => "LSILogic SAS 1064",
	
		"0x????????,0x00541000" => "LSILogic SAS 1068",
		"0x1f061028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE2950
		"0x1f081028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE1955
		"0x1f091028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE860, SC1435
		"0x3228103c,0x00541000" => "HP/LSILogic SAS 1068",			# DL140 G3
	
		"0x????????,0x00561000" => "LSILogic SAS 1064E",
		"0x10001000,0x00561000" => "LSILogic SAS 1064E",
		"0x34798086,0x00561000" => "Intel/LSILogic SAS 1064E",			# Intel S5000PSL
		"0x894b152d,0x00561000" => "Quanta/LSILogic SAS 1064E",			# DCS-SAS
		"0x322b103c,0x00561000" => "HP/LSILogic SAS 1064E",			# DL160 G5
	
		"0x????????,0x00581000" => "LSILogic SAS 1068E",
		"0x10001000,0x00581000,0x04" => "LSILogic SAS 1068E",			# Tyan S5397 (onboard)
		"0x31401000,0x00581000,0x08" => "LSILogic SAS 1068E",			# Dell S45
		"0x1f0e1028,0x00581000" => "Dell/LSILogic SAS 6/iR",			# Dell R200 (card)
		"0x1f101028,0x00581000" => "Dell/LSILogic SAS 6/iR",			# Dell PE1950 (integrated)
		"0xa38015d9,0x00581000,0x08" => "Supermicro/LSILogic SAS 1068E",	# SM X7DCT (add-on card?)
		"0xa48015d9,0x00581000,0x08" => "Supermicro/LSILogic SAS 1068E",	# SM X7DCL-3
	
		"0xa18015d9,0x00591000" => "Supermicro/LSILogic 1068E AOC-SAS-L4iR",	# SM UIO
		"0xa48015d9,0x00591000" => "Supermicro/LSILogic 1068E AOC-SAS-L4iR",	# SM X7DCL-3
	
		"0x002f1170,0x00601000,0x03" => "Inventec/LSILogic MegaRAID SAS 1078",	# Inventec 5220
		"0x10131000,0x00601000,0x04" => "LSILogic MegaRAID SAS 1078",
		"0x34cc8086,0x00601000" => "Intel/LSILogic MegaRAID SAS 1078",		# Intel Tigerton
		"0x1f0a1028,0x00601000,0x04" => "Dell/LSILogic PERC 6/E",		# DCS
		"0x1f0c1028,0x00601000" => "Dell/LSILogic PERC 6/i",			# PE2950 III, Dell R900
		"0x03791014,0x00601000,0x04" => "IBM/LSILogic ServeRAID-MR10M",

		"0x03661014,0x00621000" => "IBM/LSILogic SAS 1078",			# IBM x3859 M2
		"0x05321000,0x04071000" => "MegaRAID 320-2X Ultra320 RAID",
		"0x00021028,0x04081000" => "Dell/LSILogic PERC 4e/DC",
		"0x10018086,0x04111000" => "Intel/MegaRAID SRCSAS18E SAS",		# Intel S5000PAL
	
		"0x00000000,0x80789004" => "Adaptec AIC-7880 Ultra",
		"0x????????,0x81789004" => "Adaptec 2940 Ultra",
		"0x00000000,0x81789004" => "Adaptec 2940 Ultra",
		"0x78819004,0x81789004" => "Adaptec 2940 Ultra",
		"0x78879004,0x87789004" => "Adaptec 2940 Ultra",			# 2940UW Pro Ultra-Wide SCSI Controller
		"0x????????,0x00109005" => "Adaptec 2940 Ultra2",
		"0x21809005,0x00109005" => "Adaptec 2940 Ultra2",
		"0xa1809005,0x00109005" => "Adaptec 2940 Ultra2",
		"0x????????,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0x00539004,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0x080f9005,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0xe2a09005,0x00809005" => "Adaptec 29160 Ultra160",
		"0xe2a00e11,0x00809005" => "Compaq/Adaptec AIC-7892A Ultra160",
		"0x????????,0x008f9005" => "Adaptec AIC-7892P Ultra160",		#
		"0x010a1028,0x008f9005" => "Dell/Adaptec AIC-7892P Ultra160",		# PE6650
		"0x8916152d,0x008f9005" => "Quanta/Adaptec AIC-7892P Ultra160",		# Quanta SU6
		"0x8917152d,0x008f9005" => "Quanta/Adaptec AIC-7892P Ultra160",		# Quanta SU2-3000
		"0xf6209005,0x00c09005" => "Adaptec 3960D Ultra160",
		"0xf6200e11,0x00c09005,0x01" => "Compaq/Adaptec AIC-7899A Ultra160",	# ML530 G2
		"0x00c51028,0x00c59005" => "Dell/Adaptec RAID subsystem HBA",		# PE2450, PE2550, PE2650 (noop SCSI channels of a Dell PERC card, see /sys/dev/aac/aac_pci.c)

		"0x????????,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0xffffffff,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0xffff9005,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0x00cf8086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",
		"0x340f8086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",		# Intel SCB20-S
		"0x34158086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",		# Intel SE7500WV2
		"0x00a61028,0x00cf9005" => "Dell PE2450/Adaptec AIC-7899 Ultra160",
		"0x00d11028,0x00cf9005" => "Dell PE2550/Adaptec AIC-7899 Ultra160",
		"0x00df1028,0x00cf9005,0x01" => "Dell/Adaptec AIC-7899 Ultra160",	# PE1550
		"0x011b1028,0x00cf9005" => "Dell PE1650/Adaptec AIC-7899 Ultra160",
		"0x01211028,0x00cf9005" => "Dell PE2650/Adaptec AIC-7899 Ultra160",
		"0x900515d9,0x00cf9005" => "Supermicro/Adaptec AIC-7899 Ultra160",	# SM P4DP6
		"0x5539d915,0x00cf9005" => "Supermicro/Adaptec AIC-7899 Ultra160",	# SM P4DLR

		"0x00609005,0x80009005" => "Adaptec ASC-29320A Ultra320",
		"0x91661462,0x800f9005" => "MSI/Adaptec AIC-7901 Ultra320",		# MSI ???
		"0x005e9005,0x801d9005" => "Adaptec AIC-7902B Ultra320",
		"0x90059005,0x801d9005" => "Adaptec AIC-7902B Ultra320",
		"0xb0101458,0x801d9005" => "Gigabyte/Adaptec AIC-7902B Ultra320",	# Gigabyte GA-7A8DRH
		"0x????????,0x801f9005" => "Adaptec AIC-7902 Ultra320",
		"0x34128086,0x801f9005,0x03" => "Intel/Adaptec AIC-7902 Ultra320",	# Intel SRSH4
		"0x341a8086,0x801f9005" => "Intel/Adaptec AIC-7902 Ultra320",		# Intel SE7501WV2S
		"0x90059005,0x801f9005" => "Adaptec AIC-7902 Ultra320",
		"0xffff9005,0x801f9005" => "Adaptec AIC-7902 Ultra320",			# Tyan S2881
		"0x01681028,0x80959005" => "Dell/Adaptec ASC-39320 Ultra320 RAID",	# SC1425
		"0x005e9005,0x809d9005" => "Adaptec AIC-7902B Ultra320 RAID",
		"0xb0101458,0x809d9005" => "Gigabyte/Adaptec AIC-7902B Ultra320 RAID",	# Gigabyte GA-7A8DRH
		"0x90059005,0x809f9005" => "Adaptec AIC-7902 Ultra320 RAID",
		"0x02859005,0x02859005" => "Adaptec 2200S Ultra320 RAID",
		"0x02869005,0x02859005" => "Adaptec 2120S Ultra320 RAID",
		"0x02911028,0x02859005" => "Dell/Adaptec CERC SATA 2",			# PE850
		"0x02ca15d9,0x02859005" => "Supermicro/Adaptec AOC-USAS-S8iR",		# SM UIO
		"0x608015d9,0x041e9005" => "Supermicro/Adaptec 9410W SAS",		# SM X7DB8?
		"0x928015d9,0x041e9005" => "Supermicro/Adaptec 9410W SAS",		# SM X7DBR-3
	
		"0x100013c1,0x100013c1" => "3ware ATA-RAID",
		"0x100113c1,0x100113c1" => "3ware 7xxx/8xxx ATA/SATA RAID",
		"0x100213c1,0x100213c1" => "3ware 9xxx SATA RAID",
		"0x100313c1,0x100313c1" => "3ware 9550X SATA RAID",
	
		"0xc0331044,0xa5111044" => "DPT/Adaptec 2000S Ultra160 RAID",
		"0xc05a1044,0xa5011044" => "DPT/Adaptec 2400A ATA RAID",
	
		"0x00541069,0x00501069" => "Mylex AcceleRAID 160",
	
		"0x00091077,0x23001077" => "QLogic QLA2300 2G FC",
		"0x01061077,0x23001077,0x01" => "QLogic QLA2300 2G FC",

		"0x01001077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x01011077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x01021077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x010d1077,0x23121077,0x02" => "QLogic ISP2312 2G FC",

		"0x01331077,0x24221077" => "QLogic ISP2422 4G FC",
		"0x01341077,0x24221077" => "QLogic ISP2422 4G FC",

		"0x01371077,0x24321077,0x03" => "QLogic ISP2432 4G FC",
		"0x01381077,0x24321077" => "QLogic ISP2432 4G FC",
		"0x7040103c,0x24321077,0x02" => "HP/QLogic ISP2432 4G FC",
		"0x7041103c,0x24321077" => "HP/QLogic ISP2432 4G FC",
		"0x7041103c,0x24321077" => "HP/QLogic ISP2432 4G FC",

		"0x8471101e,0x12161077" => "QLogic ISP12160 Ultra160 RAID",		# PE6650
		"0x8493101e,0x12161077" => "QLogic ISP12160 Ultra160 RAID",		# PE2650
	
		"0xf0a510df,0xf0a510df" => "Emulex LP1050 2G FC",			# Emulex Thor LightPulse
		"0xf90010df,0xf90010df" => "Emulex LP9000 2G FC",
		"0xfa0010df,0xfa0010df" => "Emulex LP10000 2G FC",			# Emulex Thor-X LightPulse
	
		"0x72119005,0x564a9005" => "Adaptec iSCSI ASA-7211",
	
		"0xa48015d9,0x82131283" => "Supermicro/ITE IT8213F ATA/133",

		"0x82121043,0x612111ab,0xb1" => "Asus/Marvell 88SE6121 ATA/133 + SATA/300",	# Asus P5Q-Deluxe

		"0x2368197b,0x2368197b,0x00" => "JMicron JMB368 ATA/133",		# SM X8DTN
		"0xb0001458,0x2368197b,0x00" => "Gigabyte/JMicron JMB368 ATA/133",
	
		"0x3009103c,0x43761002" => "HP/ATI ATA",				# HP DX5150
		"0x3009103c,0x43791002" => "HP/ATI 4379 SATA/150",			# HP DX5150
	
		"0x807a1043,0x55131039" => "Asus/SiS 5513 ATA/100",			# Asus P4SGX-MX
	
		"0x3024103c,0x53371106,0x07" => "HP/VIA VT8237S SATA/300",		# HP DX2255

		"0x00000000,0x05711106" => "VIA VT82C686 ATA/66",			# Asus CUV4X
		"0x3024103c,0x05711106" => "HP/VIA VT82C686 ATA/66",			# HP DX2255
		"0x50021458,0x05711106" => "Gigabyte/VIA VT82C686 ATA/66",
	
		"0x3024103c,0x05911106" => "HP/VIA VT8237A SATA/150",			# HP DX2255
	
		"0xb0031458,0x31491106" => "Gigabyte/VIA VT6420 SATA/150 RAID",		# Gigabyte EP45-DS3R
	
		"0x????????,0x74691022" => "AMD-8111 ATA/133",				#
		"0x001017c2,0x74691022" => "Newisys/AMD-8111 ATA/133",			# V20z
		"0x002017c2,0x74691022" => "Newisys/AMD-8111 ATA/133",			# V40z
		"0x3016161f,0x74691022" => "Arima/AMD-8111 ATA/133",			# Arima HDAMA
		"0x2b801022,0x74691022" => "AMD-8111 ATA/133",				# Tyan S4882
		"0x36c01022,0x74691022" => "AMD-8111 ATA/133",				# DL145 G1
		"0x74691022,0x74691022" => "AMD-8111 ATA/133",				# Tyan S2882, Tyan S2881, Tyan S2880, MSI 8480000
		"0x74691462,0x74691022" => "MSI/AMD-8111 ATA/133",			# MSI 8480000
		"0x32040e11,0x74691022" => "Compaq/AMD-8111 ATA/133",			# DL385, DL585
	
		"0x4d33105a,0x0d30105a,0x02" => "Promise PDC20265 ATA/100",		# FastTrak100 Adapter
		"0x6619105a,0x3373105a" => "Promise PDC20378 SATA/150",			# Tyan S2880
		"0x34108086,0x4d30105a" => "Intel/Promise PDC20267 ATA/100",		# Intel SCB20-A
		"0x06801095,0x06801095" => "SiI 0680A ATA/133",				# Silicon Image
		"0x61141095,0x31141095" => "SiI 3114 SATA/150 RAID",			# Arima HDAMA
		"0x31141095,0x31141095" => "SiI 3114 SATA/150 RAID",			# Tyan S2882, Tyan S2881
		"0x31248086,0x31241095" => "SiI 3124 SATA/150 RAID",			# Intel SE7520BB2
		"0x71248086,0x31241095" => "SiI 3124 SATA/150 RAID",			# Intel SE7520BB2
		"0x00450e11,0x06491095" => "Compaq/CMD 649 ATA/100",			# DL320
		"0x007e0e11,0x06491095" => "Compaq/CMD 649 ATA/100",			# DL320 G2
		"0x00000000,0x02111166" => "ServerWorks OSB4 ATA/33",			# DL320, DL360, DL380 G2, ML570, Quanta SU2-3000, PE2550, Tyan S2510
	
		"0x????????,0x02121166" => "ServerWorks CSB5 ATA/100",			#
		"0x02121166,0x02121166" => "ServerWorks CSB5 ATA/100",			# DL360 G2, DL360 G3, DL380 G3, PE6650, DL580 G2, DL560 G1
		"0x014a1028,0x02121166" => "Dell/ServerWorks CSB5 ATA/100",		# PE1750
		"0x810b1028,0x02121166" => "Dell/ServerWorks CSB5 ATA/100",		# PE1650
		"0x41351028,0x02121166,0x93" => "Dell/ServerWorks CSB5 ATA/100",	# Dell 1600SC
		"0x340f8086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SCB20-S
		"0x34108086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SCB20-A
		"0x34148086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SAI2
	
		"0x????????,0x02131166" => "ServerWorks CSB6 ATA/100",			#
		"0x02201166,0x02131166" => "ServerWorks CSB6 ATA/100",			# SU2-4200
		"0x02121166,0x02131166" => "ServerWorks CSB6 ATA/100",			# DL320 G2, DL140
		"0x8920107b,0x02131166,0xa0" => "Gateway/ServerWorks CSB6 ATA/100",	# SU2-4200
	
		"0x????????,0x02141166" => "ServerWorks HT-1000 ATA/100",		#
		"0x01ea1028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# PE6950
		"0x01eb1028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# SC1435
		"0x02051028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# PE2970
		"0x320b103c,0x02141166" => "HP/ServerWorks HT-1000 ATA/100",		# DL145 G3, DL365 G1, DL385 G2
	
		"0x320b103c,0x024a1166" => "HP/ServerWorks HT-1000 SATA/150",		# DL145 G3 (native SATA mode), DL185 G5
	
		"0x????????,0x024b1166" => "ServerWorks HT-1000 SATA/150 (PATA mode)",		#
		"0x01eb1028,0x024b1166" => "Dell/ServerWorks HT-1000 SATA/150 (PATA mode)",	# SC1435
		"0x02051028,0x024b1166" => "Dell/ServerWorks HT-1000 SATA/150 (PATA mode)",	# PE2970
		"0x320b103c,0x024b1166" => "HP/ServerWorks HT-1000 SATA/150 (PATA mode)",	# DL145 G3 (PATA mode)
	
		"0x00000000,0x70108086" => "Intel 82371SB PIIX3 ATA/33",		#
		"0x00000000,0x71118086" => "Intel 82371AB PIIX4 ATA/33",		# L440GX, TR440BX
		"0x197615ad,0x71118086" => "VMware/Intel 82371AB PIIX4 ATA/33",		#
		"0x24118086,0x24118086" => "Intel ICH 82801AA ATA/66",			# 810
	
		"0x????????,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		#
		"0x57448086,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		# Intel S845WD1
		"0x24118086,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		#
		"0x24110e11,0x244b8086" => "Compaq/Intel 82801BA ICH2 ATA/100",		# HP D500
		"0x113015d9,0x244b8086" => "Supermicro/Intel 82801BA ICH2 ATA/100",	# SM P3TSSR
		"0x010e1028,0x244b8086" => "Dell/Intel 82801BA ICH2 ATA/100",		# Dell OptiPlex GX240
		"0x00d81028,0x244b8086" => "Dell/Intel 82801BA ICH2 ATA/100",		# Dell 530
	
		"0x????????,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		#
		"0x00000000,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Tyan S2722
		"0x34158086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500WV2
		"0x34168086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500WV2A
		"0x34188086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501BR2
		"0x34198086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500CW2
		"0x341a8086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501WV2S
		"0x341b8086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501WV2A
		"0x34258086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501CW2
		"0x00001749,0x248b8086" => "RLX/Intel 82801CA ICH3 ATA/100",		# RLX ServerBlade
		"0x348015d9,0x248b8086" => "Supermicro/Intel 82801CA ICH3 ATA/100",	# SM P4DP6, SM X5DP8
	
		"0x????????,0x24cb8086" => "Intel ICH4 82801DB ATA/100",		#
		"0x00b80e11,0x24cb8086" => "Compaq/Intel ICH4 82801DB ATA/100",		# HP D510
		"0x00b90e11,0x24cb8086" => "Compaq/Intel ICH4 82801DB ATA/100",		# HP D510
		"0x01381028,0x24cb8086,0x01" => "Dell/Intel ICH4 82801DB ATA/100",	# Dell SX260
		"0x368015d9,0x24cb8086" => "Supermicro/Intel ICH4 82801DB ATA/100",	# SM P4SGR
		"0x80891043,0x24cb8086,0x02" => "Asus/Intel ICH4 82801DB ATA/100",	# Asus P4GE-MX
	
		"0x????????,0x24d18086" => "Intel ICH5 82801EB SATA/150",		#
		"0x00000000,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# NEC Express5800/120Re-1
		"0x34288086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel S875WP1
		"0x34398086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BD2
		"0x343b8086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BD2S
		"0x347e8086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BB2
		"0x3208103c,0x24d18086" => "HP/Intel ICH5 82801EB SATA/150",		# DL140 G2
		"0x12bc103c,0x24d18086" => "HP/Intel ICH5 82801EB SATA/150",		# HP D530
		"0x12bd103c,0x24d18086,0x02" => "HP/Intel ICH5 82801EB SATA/150",	# HP ??
		"0x01511028,0x24d18086" => "Dell/Intel ICH5 82801EB SATA/150",		# Dell GX270
		"0x01561028,0x24d18086,0x02" => "Dell/Intel ICH5 82801EB SATA/150",	# Dell Precision 360
		"0x019a1028,0x24d18086" => "Dell/Intel ICH5 82801EB SATA/150",		# SC1425
		"0xb0021458,0x24d18086" => "Gigabyte/Intel ICH5 82801EB SATA/150",	# Gigabyte 9ILDR
	
		"0x????????,0x24db8086" => "Intel ICH5 82801EB ATA/100",		#
		"0x000c1170,0x24db8086" => "Inventec/Intel ICH5 82801EB ATA/100",	# Inventec IR2100
		"0x34288086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel S875WP1
		"0x347e8086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520BB2
		"0x34398086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520BD2
		"0x10798086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520JR2
		"0x12bc103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# HP D530, DC5000
		"0x12bd103c,0x24db8086,0x02" => "HP/Intel ICH5 82801EB ATA/100",	# HP ??
		"0x3201103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# DL580 G4
		"0x3208103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# DL140 G2
		"0x01511028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# Dell GX270
		"0x01561028,0x24db8086,0x02" => "Dell/Intel ICH5 82801EB ATA/100",	# Dell Precision 360
		"0x016c1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE1850
		"0x016d1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE2850
		"0x01701028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE6850
		"0x019a1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# SC1425
		"0x32010e11,0x24db8086" => "Compaq/Intel ICH5 82801EB ATA/100",		# DL380 G4
		"0x50061458,0x24db8086" => "Gigabyte/Intel ICH5 82801EB ATA/100",	# Gigabyte 9ILDR
	
		"0x019a1028,0x24df8086" => "Dell/Intel ICH5R 82801ER SATA/150",		# SC1425
	
		"0x????????,0x26518086" => "Intel ICH6 82801FB SATA/150",
		"0x3005103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC7100
		"0x300a103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DX6120
		"0x300c103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC5100
		"0x300d103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC5100 (PT005AW)
		"0x301e103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DX2180
		"0x301f103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP D290
		"0x41478086,0x26518086" => "Intel ICH6 82801FB SATA/150",		# Intel D915GAG
		"0x4c588086,0x26518086" => "Intel ICH6 82801FB SATA/150",		# Intel D915GUX
		"0x01791028,0x26518086" => "Dell/Intel ICH6 82801FB SATA/150",		# Dell GX280
	
		"0x32010e11,0x26528086" => "Compaq/Intel ICH6R 82801FR SATA/150",	# DL320 G3

		"0x01821028,0x26538086,0x03" => "Dell/Intel ICH6M 82801FBM SATA/150",	# Dell Latitude D610
	
		"0x????????,0x266f8086" => "Intel ICH6 82801FB ATA/100",
		"0x32010e11,0x266f8086" => "Compaq/Intel ICH6 82801FB ATA/100",		# DL320 G3
		"0x3005103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC7100
		"0x300a103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DX6120
		"0x300c103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC5100
		"0x300d103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC5100 (PT005AW)
		"0x301e103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DX2180
		"0x301f103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP D290
		"0x0944103c,0x266f8086,0x03" => "HP/Intel ICH6 82801FB ATA/100",	# HP NC6220
		"0x41478086,0x266f8086" => "Intel ICH6 82801FB ATA/100",		# Intel D915GAG
		"0x4c588086,0x266f8086" => "Intel ICH6 82801FB ATA/100",		# Intel D915GUX
		"0x01791028,0x266f8086" => "Dell/Intel ICH6 82801FB ATA/100",		# Dell GX280
	
		"0x????????,0x27c08086" => "Intel ICH7 82801GB SATA/300",		#
		"0x348f8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel Coretta
		"0x349a8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel S3000PT
		"0x349b8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel S3000PT
		"0x348d8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel AspenHill
		"0x464c8086,0x27c08086,0x01" => "Intel ICH7 82801GB SATA/300",		# Intel D945GCLF
		"0x544e8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel D945GTP
		"0x3011103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# HP DC7600
		"0x31fe103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# DL320 G5
		"0x3022103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# HP DX2280
		"0x00000000,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# DL320s
		"0x01ad1028,0x27c08086,0x01" => "Dell/Intel ICH7 82801GB SATA/300",	# Dell GX620
		"0x01b61028,0x27c08086" => "Dell/Intel ICH7 82801GB SATA/300",		# PE850
		"0x01e61028,0x27c08086" => "Dell/Intel ICH7 82801GB SATA/300",		# PE860
		"0x02fd1014,0x27c08086" => "IBM/Intel ICH7 82801GB SATA/300",		# IBM 306m
		"0x858015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSML
		"0x798015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSMi
		"0xb58015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSBM
		"0x00221170,0x27c08086" => "Inventec/Intel ICH7 82801GB SATA/300",	# IR1250
		"0x81901043,0x27c08086,0x01" => "Asus/Intel ICH7 82801GB SATA/300",	# Asus P5BV-C
		"0xb0021458,0x27c08086,0x01" => "Gigabyte/Intel ICH7 82801GB SATA/300",	# Gigabyte 945GCM-S2C
	
		"0x????????,0x27c18086,0x01" => "Intel ICH7 82801GR SATA/300 AHCI",
		"0x346a8086,0x27c18086,0x01" => "Intel ICH7 82801GR SATA/300 AHCI",		# Intel Allagash
		"0x858015d9,0x27c18086,0x01" => "Supermicro/Intel ICH7 82801GR SATA/300 AHCI",	# SM PDSML
		"0x81901043,0x27c18086,0x01" => "Asus/Intel ICH7 82801GR SATA/300 AHCI",	# Asus P5BV-C
	
		"0x????????,0x27c38086,0x01" => "Intel ICH7 82801GB SATA/300 RAID",
		"0x3206103c,0x27c38086,0x01" => "HP/Intel ICH7 82801GB SATA/300 RAID",	# DL320 G5

		"0x????????,0x27c58086,0x01" => "Intel ICH7 82801GBM SATA/300 AHCI",
		"0x30ad103c,0x27c58086,0x01" => "HP/Intel ICH7 82801GBM SATA/300 AHCI",	# HP NC6400

		"0x????????,0x27df8086" => "Intel ICH7 82801G ATA/100",			#
		"0x346a8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel Allagash
		"0x348d8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel AspenHill
		"0x348f8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel Coretta
		"0x349a8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel S3000PT
		"0x349b8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel S3000PT
		"0x464c8086,0x27df8086,0x01" => "Intel ICH7 82801G ATA/100",		# Intel D945GCLF
		"0x544e8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel D945GTP
		"0x3011103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP DC7600
		"0x30ad103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP NC6400
		"0x31fe103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# DL320 G5
		"0x3022103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP DX2280
		"0x01ad1028,0x27df8086,0x01" => "Dell/Intel ICH7 82801G ATA/100",	# Dell GX620
		"0x01b61028,0x27df8086" => "Dell/Intel ICH7 82801G ATA/100",		# PE850
		"0x01e61028,0x27df8086" => "Dell/Intel ICH7 82801G ATA/100",		# PE860
		"0x00221170,0x27df8086" => "Inventec/Intel ICH7 82801G ATA/100",	# IR1250
		"0x858015d9,0x27df8086" => "Supermicro/Intel ICH7 82801G ATA/100",	# SM PDSML
		"0x03811014,0x27df8086" => "IBM/Intel ICH7 82801G ATA/100",		# IBM x3850 M2
		"0x819e1043,0x27df8086,0x01" => "Asus/Intel ICH7 82801G ATA/100",	# Asus P5BV-C
		"0xb0011458,0x27df8086,0x01" => "Gigabyte/Intel ICH7 82801G ATA/100",	# Gigabyte 945GCM-S2C
	
		"0x2808103c,0x28208086" => "HP/Intel ICH8 82801H 4 Port SATA/300",	# HP DC5700

		"0x31f4103c,0x28228086,0x02" => "HP/Intel ICH8 82801H SATA/300 RAID",	# DL180 G5
		"0x28228086,0x28228086,0x02" => "Intel ICH8 82801H SATA/300 RAID",	# Dell S45, SM X8DTN

		"0x2808103c,0x28258086" => "HP/Intel ICH8 82801H 2 Port SATA/300",	# HP DC5700
	
		"0x????????,0x28288086,0x03" => "Intel ICH8M 82801HBM SATA/300",
		"0x80301462,0x28288086,0x03" => "MSI/Intel ICH8M 82801HBM SATA/300",	# MSI GM965
		"0x063ea0a0,0x28288086,0x03" => "AOpen/Intel ICH8M 82801HBM SATA/300",	# AOpen i965GMt-LA

		"0x????????,0x28298086,0x03" => "Intel ICH8M 82801HBM SATA/300 AHCI",
		"0x30c1103c,0x28298086,0x03" => "HP/Intel ICH8M 82801HBM SATA/300 AHCI",	# HP 6910p
		"0x80301462,0x28298086,0x03" => "MSI/Intel ICH8M 82801HBM SATA/300 AHCI",	# MSI GM965

		"0x????????,0x28508086,0x03" => "Intel ICH8M 82801HBM ATA/100",
		"0x30be103c,0x28508086" => "HP/Intel ICH8M 82801HBM ATA/100",		# HP 6910p
		"0x30c1103c,0x28508086,0x03" => "HP/Intel ICH8M 82801HBM ATA/100",	# HP 6910p
		"0x80301462,0x28508086,0x03" => "MSI/Intel ICH8M 82801HBM ATA/100",	# MSI GM965
		"0x063ea0a0,0x28508086,0x03" => "AOpen/Intel ICH8M 82801HBM ATA/100",	# AOpen i965GMt-LA
	
		"0x????????,0x29208086" => "Intel ICH9 82801I 4 Port SATA/300",
		"0x29208086,0x29208086" => "Intel ICH9 82801I 4 Port SATA/300",			# DCS S45
		"0x31f4103c,0x29208086" => "HP/Intel ICH9 82801I 4 Port SATA/300",		# DL180 G5, DL120 G5
		"0x31fe103c,0x29208086" => "HP/Intel ICH9 82801I 4 Port SATA/300",		# BL260c G5
		"0x023c1028,0x29208086" => "Dell/Intel ICH9 82801I 4 Port SATA/300",		# Dell R200
		"0x020f1028,0x29208086" => "Dell/Intel ICH9 82801I 4 Port SATA/300",		# Dell R300
		"0xdc8015d9,0x29208086,0x02" => "Supermicro/Intel ICH9 82801I 4 Port SATA/300",	# SM X7DCA-L
		"0xa48015d9,0x29208086" => "Supermicro/Intel ICH9 82801I 4 Port SATA/300",	# SM X7DCL
		"0x8954152d,0x29208086" => "Quanta/Intel ICH9 82801I 4 Port SATA/300",		# Quanta QSSC-295MB
		"0x8959152d,0x29208086,0x02" => "Quanta/Intel ICH9 82801I 4 Port SATA/300",	# Quanta S47
	
		"0x????????,0x29218086,0x02" => "Intel ICH9 82801IB 2 Port SATA/300",
		"0x281e103c,0x29218086,0x02" => "HP/Intel ICH9 82801IB 2 Port SATA/300",	# HP DC5800

		"0x????????,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",
		"0x10001458,0x29228086" => "Gigabyte/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Gigabyte R116
		"0xdc8015d9,0x29228086" => "Supermicro/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# SM X7DCA-L
		"0xd88015d9,0x29228086,0x02" => "Supermicro/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# SM X7SBL
		"0x29228086,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",		# DCS S45, SM X8DTN
		"0x34d08086,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Intel X38ML
		"0x537610f1,0x29228086,0x02" => "Tyan/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Tyan S5376
		"0x8954152d,0x29228086,0x02" => "Quanta/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# Quanta S45
		"0x8959152d,0x29228086,0x02" => "Quanta/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# Quanta S47
		"0x83661033,0x29228086,0x02" => "NEC/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# NEC Express5800/i120Ra-e1
	
		"0x????????,0x29238086,0x02" => "Intel ICH9 82801IB 4 Port SATA/300 AHCI",
		"0xb0051458,0x29238086,0x02" => "Gigabyte/Intel ICH9 82801IB 4 Port SATA/300 AHCI",	# Gigabyte P35-S3G, Gigabyte G33M-S2L

		"0x????????,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",
		"0x00000000,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",			# Dell R300
		"0x020f1028,0x29268086,0x02" => "Dell/Intel ICH9 82801I 2 Port SATA/300",	# Dell R300
		"0x29268086,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",			# DCS S45
		"0x281e103c,0x29268086,0x02" => "HP/Intel ICH9 82801I 2 Port SATA/300",		# HP DC5800
		"0x31f4103c,0x29268086" => "HP/Intel ICH9 82801I 2 Port SATA/300",		# DL180 G5, DL120 G5
		"0xa48015d9,0x29268086" => "Supermicro/Intel ICH9 82801I 2 Port SATA/300",	# SM X7DCL
		"0xdc8015d9,0x29268086,0x02" => "Supermicro/Intel ICH9 82801I 2 Port SATA/300",	# SM X7DCA-L
		"0x8954152d,0x29268086" => "Quanta/Intel ICH9 82801I 2 Port SATA/300",		# Quanta QSSC-295MB
		"0x8959152d,0x29268086,0x02" => "Quanta/Intel ICH9 82801I 2 Port SATA/300",	# Quanta S47
	
		"0x????????,0x25a28086" => "Intel 6300ESB ATA/100",			#
		"0x32010e11,0x25a28086" => "Compaq/Intel 6300ESB ATA/100",		# DL360 G4
		"0x25a18086,0x25a28086" => "Intel 6300ESB ATA/100",			# Intel
		"0x34508086,0x25a28086" => "Intel 6300ESB ATA/100",			# Intel SE7320VP2
	
		"0x????????,0x25a38086" => "Intel 6300ESB SATA/150",			#
		"0x01651028,0x25a38086" => "Dell/Intel 6300ESB SATA/150",		# PE750
		"0x698015d9,0x25a38086" => "Supermicro/Intel 6300ESB SATA/150",		# SM X6DLP
	
		# http://download.intel.com/design/chipsets/datashts/31308201.pdf
		"0x????????,0x26808086" => "Intel 631xESB SATA/300",			#
		"0x346c8086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PAL
		"0x34768086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PSL
		"0x34788086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PSL-SAS
		"0x347a8086,0x26808086,0x09" => "Intel 631xESB SATA/300",		# Intel S5000XSL
		"0x34848086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000VSA
		"0x34cc8086,0x26808086,0x09" => "Intel 631xESB SATA/300",		# Intel S7000FC4UR
		"0x34d48086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5400SF
		"0x808015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DB8
		"0x848015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBR
		"0x868015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DVL-E
		"0x928015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBR-3 (SATA)
		"0x978015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBU
		"0xb08015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM B7DBE
		"0xb28015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DVL-L
		"0x01b21028,0x26808086" => "Dell/Intel 631xESB SATA/300",		# PE2950
		"0x01b31028,0x26808086" => "Dell/Intel 631xESB SATA/300",		# PE1950
		"0x31f6103c,0x26808086" => "HP/Intel 631xESB SATA/300",			# DL180, DL160 G5
		"0x31fe103c,0x26808086" => "HP/Intel 631xESB SATA/300",			# DL140 G3
		"0x8949152d,0x26808086" => "Quanta/Intel 631xESB SATA/300",		# DCS S29
		"0x894b152d,0x26808086" => "Quanta/Intel 631xESB SATA/300",		# DCS S58
		"0x00231170,0x26808086" => "Inventec/Intel 631xESB SATA/300",		# Inventec Seabream
		"0x003a1170,0x26808086,0x09" => "Inventec/Intel 631xESB SATA/300",	# Inventec 5220
		"0x539710f1,0x26808086,0x09" => "Tyan/Intel 631xESB SATA/300",		# Tyan S5397
	
		"0x????????,0x26818086" => "Intel 631xESB SATA/300 AHCI",		#
		"0x347a8086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000XSL
		"0x34768086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000PSL
		"0x34788086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000PSL-SAS
		"0x34848086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000VSA
		"0x34d48086,0x26818086,0x09" => "Intel 631xESB SATA/300 AHCI",		# Intel S5400SF
		"0x808015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DB8
		"0x848015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBR
		"0x868015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DVL-E
		"0x928015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBR-3 (SATA)
		"0x948015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DAL
		"0x978015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBU
		"0xb28015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DVL-L
		"0x00231170,0x26818086" => "Inventec/Intel 631xESB SATA/300 AHCI",	# eslim
		"0x31f6103c,0x26818086,0x09" => "HP/Intel 631xESB SATA/300 AHCI",	# DL160 G5
		"0x31fe103c,0x26818086" => "HP/Intel 631xESB SATA/300 AHCI",		# DL140 G3
		"0x8949152d,0x26818086" => "Quanta/Intel 631xESB SATA/300 AHCI",	# DCS S29
		"0x894b152d,0x26818086" => "Quanta/Intel 631xESB SATA/300 AHCI",	# DCS S58
		"0x03251014,0x26818086,0x09" => "IBM/Intel 631xESB SATA/300 AHCI",	# IBM dx340
	
		"0x????????,0x26828086" => "Intel 631xESB SATA/300 RAID",			#
		"0x346d8086,0x26828086" => "Intel 631xESB SATA/300 RAID",			# Intel S5000PAL
		"0x34d48086,0x26828086,0x09" => "Intel 631xESB SATA/300 RAID",			# Intel S5400SF
		"0x02dd1014,0x26828086" => "IBM/Intel 631xESB SATA/300 RAID",			# IBM x3550
		"0x31f6103c,0x26828086,0x09" => "HP/Intel 631xESB SATA/300 RAID",		# DL160 G5
		"0x31fe103c,0x26828086" => "HP/Intel 631xESB SATA/300 RAID",			# DL140 G3
		"0x8949152d,0x26828086" => "Quanta/Intel 631xESB SATA/300 RAID",		# DCS S29
		"0x8950152d,0x26828086" => "Quanta/Intel 631xESB SATA/300 RAID",		# DCS S29, DCS S58
		"0x868015d9,0x26828086" => "Supermicro/Intel 631xESB SATA/300 RAID",		# SM X7DVL-E
		"0x928015d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBR-3 (SATA)
		"0x978015d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBU
		"0x978115d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBU
		"0xb28015d9,0x26828086" => "Supermicro/Intel 631xESB SATA/300 RAID",		# SM X7DVL-L
	
		"0x????????,0x269e8086" => "Intel 631xESB ATA/133",			#
		"0x00000000,0x269e8086" => "Intel 631xESB ATA/133",			# Dell R900
		"0x346c8086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PAL
		"0x346d8086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PAL
		"0x34768086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PSL
		"0x34788086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PSL-SAS
		"0x347a8086,0x269e8086,0x09" => "Intel 631xESB ATA/133",		# Intel S5000XSL
		"0x34848086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000VSA
		"0x34d48086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5400SF
		"0x01b21028,0x269e8086" => "Dell/Intel 631xESB ATA/133",		# PE2950
		"0x01b31028,0x269e8086" => "Dell/Intel 631xESB ATA/133",		# PE1950
		"0x01f01028,0x269e8086,0x09" => "Dell/Intel 631xESB ATA/133",		# Dell R900
		"0x31fe103c,0x269e8086" => "HP/Intel 631xESB ATA/133",			# DL360 G5, DL380 G5, DL140 G3, DL180, DL580 G5
		"0x02dd1014,0x269e8086" => "IBM/Intel 631xESB ATA/133",			# IBM x3550
		"0x808015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DB8
		"0x838015d9,0x269e8086,0x09" => "Supermicro/Intel 631xESB ATA/133",	# SM X7DBP
		"0x848015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBR
		"0x868015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DVL-E
		"0x928015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBR-3 (SAS)
		"0x948015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DAL
		"0xb18015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBi+
		"0xb28015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DVL-L
		"0x8949152d,0x269e8086" => "Quanta/Intel 631xESB ATA/133",		# DCS S29
		"0x894b152d,0x269e8086" => "Quanta/Intel 631xESB ATA/133",		# DCS-SAS
		"0x00231170,0x269e8086" => "Inventec/Intel 631xESB ATA/133",		# eslim, Inventec Seabream
		"0x003a1170,0x269e8086,0x09" => "Inventec/Intel 631xESB ATA/133",	# Inventec 5220
		"0x83361033,0x269e8086" => "NEC/Intel 631xESB ATA/133",			# NEC Express5800/120Ri-2
		"0x539710f1,0x269e8086,0x09" => "Tyan/Intel 631xESB ATA/133",		# Tyan S5397
	
		"0x????????,0x3a208086,0x00" => "Intel ICH10 82801J 4 Port SATA/300",
		"0xb0021458,0x3a208086,0x00" => "Gigabyte/Intel ICH10 82801J 4 Port SATA/300",	# Gigabyte EP45-DS3R

		"0x????????,0x3a228086,0x00" => "Intel ICH10 82801J 6 Port SATA/300 AHCI",
		"0x82d41043,0x3a228086,0x00" => "Asus/Intel ICH10 82801J 6 Port SATA/300 AHCI",		# Asus P5Q-Deluxe
		"0xb0051458,0x3a228086,0x00" => "Gigabyte/Intel ICH10 82801J 6 Port SATA/300 AHCI",	# Gigabyte EP45-DS3R

		"0x????????,0x3a268086,0x00" => "Intel ICH10 82801J 2 Port SATA/300",
		"0xb0021458,0x3a268086,0x00" => "Gigabyte/Intel ICH10 82801J 2 Port SATA/300",	# Gigabyte EP45-DS3R

		"0x00261170,0x036e10de" => "Inventec/nVIDIA MCP55 ATA/133",		# IR2400
		"0x00261170,0x037f10de" => "Inventec/nVIDIA MCP55 SATA/300",		# IR2400
	
		"0x????????,0x005310de" => "nVIDIA nForce4 ATA/133",			#
		"0x3207103c,0x005310de" => "HP/nVIDIA nForce4 ATA/133",			# DL145 G2
		"0x31f8103c,0x005310de" => "HP/nVIDIA nForce4 ATA/133",			# DL585 G2
		"0x286510f1,0x005310de" => "Tyan/nVIDIA nForce4 ATA/133",		# Tyan S2865
		"0x289110f1,0x005310de" => "Tyan/nVIDIA nForce4 ATA/133",		# Tyan S4881
		"0x00161170,0x005310de" => "Inventec/nVIDIA nForce4 ATA/133",		# Inventec IR2300
		"0x00171170,0x005310de" => "Inventec/nVIDIA nForce4 ATA/133",		# Inventec IR2350
		"0x81621043,0x005310de" => "Asus/nVIDIA nForce4 ATA/133",		# Asus K8N-DRE
		"0x5348108e,0x005310de" => "Sun/nVIDIA nForce4 ATA/133",		# Sun X2100
	
		"0x????????,0x005410de" => "nVIDIA nForce4 SATA/150",			#
		"0x286510f1,0x005410de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S2865
		"0x289110f1,0x005410de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S4881
		"0x00161170,0x005410de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2300
		"0x00171170,0x005410de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2350
		"0x81621043,0x005410de" => "Asus/nVIDIA nForce4 SATA/150",		# Asus K8N-DRE
		"0x5348108e,0x005410de" => "Sun/nVIDIA nForce4 SATA/150",		# Sun X2100
	
		"0x????????,0x005510de" => "nVIDIA nForce4 SATA/150",			#
		"0x3207103c,0x005510de" => "HP/nVIDIA nForce4 SATA/150",		# DL145 G2
		"0x286510f1,0x005510de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S2865
		"0x289110f1,0x005510de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S4881
		"0x00161170,0x005510de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2300
		"0x00171170,0x005510de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2350
		"0x81621043,0x005510de" => "Asus/nVIDIA nForce4 SATA/150",		# Asus K8N-DRE
		"0x5348108e,0x005510de" => "Sun/nVIDIA nForce4 SATA/150",		# Sun X2100
	},
	'other' => {
		"0x0001177d,0x0001177d,0x01" => { "type" => "Crypto", "model" => "Cavium Nitrox XL" },
# Nitrox PX Processor - CN1620(proc) NHB(half-height)
#		"0x0001177d,0x0010177d,0x01" => { "type" => "Crypto", "model" => "Cavium Nitrox XL" },
	},
);

my(%cpu_models) = (
	# http://www.cpu-world.com/index.html

	# for AMD, many procs will share same family/model/stepping - e.g. normal, HE, EE, etc.
	# for HE info you have to read cpu string
	# http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf

	"AMD-15,4,8" => ["Newcastle CG, 64-bit, 130nm, L2: 512KB",1,1,0],		# e.g. Athlon 64 3000+
	"AMD-15,5,0" => ["SledgeHammer, 64-bit, 130nm, L2: 1MB",1,1,0],			# e.g. 242
	"AMD-15,5,1" => ["SledgeHammer B3, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping SH7-B3, socket940 - e.g. 242, 240
	"AMD-15,5,8" => ["SledgeHammer C0, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping SH7-C0, socket940 - e.g. 848, 246, 244, 240
	"AMD-15,5,10" => ["SledgeHammer CG, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping CG, socket940 - e.g. 250, 244, 850, 240EE, 246HE
	"AMD-15,31,0" => ["64-bit, 90nm, L2: 512KB",1,1,0],				# e.g. Athlon 64 3200+
	"AMD-15,33,0" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 875 (dual-core)
	"AMD-15,33,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 275 (dual-core)
	"AMD-15,1,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# buggy linux kernel 2.4.22-y24 on bangles.ink?
	"AMD-15,35,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 180 (dual-core)
	"AMD-15,37,1" => ["64-bit, 90nm, L2: 1MB",1,1,0],				# stepping SH-E4, socket940 (not dual-core)
	"AMD-15,65,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 8220 SE
	"AMD-15,65,3" => ["64-bit, dual-core, 65nm, L2: 2MB",2,1,0],			# e.g. 2220, not sure about 65nm - YYYY
	"AMD-15,75,2" => ["Windsor F2, 64-bit, dual-core, 90nm, L2: 1MB",2,1,0],	# e.g. Athlon 64 X2 4200+

	"AMD-15,107,1" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. Athlon 64 X2 4000+

	"AMD-16,2,3" => [
		["Opteron", ["Barcelona B3, 64-bit, quad-core, 65nm, L2: 2MB, L3: 2MB",4,1,0]],	# e.g. Opteron 2352
		["Phenom", ["Agena B3, 64-bit, quad-core, 65nm, L2: 2MB, L3: 2MB",4,1,0]],	# e.g. Phenom 9850
	],

	# http://processorfinder.intel.com/
	# http://www.intel.com/design/xeon/applnots/24161830.pdf#search='intel%20cpuid'
	# http://en.wikipedia.org/wiki/Intel_Next_Generation_Microarchitecture
	# http://balusc.xs4all.nl/srv/har-cpu-int-p1.php
	# click "cpu search", select intel brand, and type f-6-5 in family, search
	# http://www.cpu-world.com/index.html

	"Intel-6,1,7" => ["PPro, 350nm, L2: 256KB/512KB",1,1,0],		# PPro
	"Intel-6,1,9" => ["PPro, 350nm, L2: 256KB/512KB/1MB",1,1,0],		# PPro

	"Intel-6,3,3" => ["Klamath C0, 350nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,3,4" => ["Klamath C1, 350nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,0" => ["Deschutes A0, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,1" => ["Deschutes A1, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,2" => ["Deschutes B0, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,3" => ["Deschutes B1, 250nm, L2: 512KB half-speed",1,1,0],	# P2

	"Intel-6,7,2" => ["Katmai, 250nm, L2: 512KB half-speed",1,1,0],		# P3
	"Intel-6,7,3" => ["Katmai, 250nm, L2: 512KB half-speed",1,1,0],		# P3
	"Intel-6,8,1" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,8,3" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,8,6" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,9,5" => ["Banias B1, 130nm, L2: 1MB",1,1,0],			# Pentium M
	"Intel-6,8,10" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,10,1" => ["Cascades A1, 180nm, L2: 2MB",1,1,0],		# Pentium III Xeon 700
	"Intel-6,11,1" => ["Tualatin, 130nm, L2: 256KB/512KB",1,1,0],		# P3
	"Intel-6,11,4" => ["Tualatin, 130nm, L2: 512KB",1,1,0],			# P3

	"Intel-6,13,8" => ["Dothan C0, 90nm, L2: 2MB",1,1,0],			# e.g. Pentium M 740

	"Intel-6,14,4" => ["Sossaman B0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,14,8" => ["Sossaman C0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,14,12" => ["Sossaman D0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,15,2" => [
		["Core", ["Allendale L2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Xeon", ["Conroe L2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# only 3040, 3050
	],
	"Intel-6,15,4" => ["Woodcrest, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2],
	"Intel-6,15,5" => [
		["Core", ["Conroe B1, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# Core 2 Duo
		["Xeon 3", ["Conroe B1, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# Core 2 Duo
		["Xeon", ["Woodcrest B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],	# Core 2 Duo DP
	],
	"Intel-6,15,6" => [
		["Core 2 [LTX]7", ["Merom B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Core 2 T5", ["Merom B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Core 2 6(300|400)", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Core", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 30(40|50)", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Xeon 3", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon", ["Woodcrest B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
	],
	"Intel-6,15,7" => [								# B3 stepping is only quad-core
		["Quad", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
		["Xeon X3", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
		["Xeon", ["Clovertown B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Xeon DP
		["Core", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
	],
	"Intel-6,15,9" => ["Tigerton, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2],
	"Intel-6,15,10" => [
		["Core 2 [LTX]7", ["Merom E1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
	],
	"Intel-6,15,11" => [
		["Xeon 30", ["Conroe G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 51", ["Woodcrest G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 72", ["Tigerton-DC G0, 64-bit, dual-core, 65nm, L2: 8MB",2,1,2]],
		["Xeon X32", ["Kentsfield G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon [LEX]53", ["Clovertown G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon [LEX]73", ["Tigerton G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon", ["Clovertown G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Solo", ["Merom G0, 64-bit, single-core, 65nm, L2: 1MB",1,1,2]],
		["Duo Mobile", ["Merom G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Core 2 [LTX]7", ["Merom G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Duo", ["Allendale G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Quad", ["Kentsfield G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
	],
	"Intel-6,22,1" => [
		["Celeron", ["Merom A1, 64-bit, 65nm, L2: 1M",1,1,2]],
	],
	"Intel-6,23,1" => [
		["Xeon", ["Harpertown, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,4" => [
		["Xeon", ["Harpertown, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,6" => [
		["Core 2 Duo [PT]8", ["Penryn M0, 64-bit, dual-core, 45nm, L2: 3MB",2,1,3]],
		["Core 2 Duo E7", ["Wolfdale M0, 64-bit, dual-core, 45nm, L2: 3MB",2,1,3]],
		["Core 2 Duo E8", ["Wolfdale C0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP C0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown C0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown C0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,7" => [
		["Quad Q93", ["Yorkfield M1, 64-bit, quad-core, 45nm, L2: 6MB",4,1,3]],
		["Quad Q9", ["Yorkfield C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon X33", ["Yorkfield C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP C1, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,10" => [
		["Quad Q9", ["Yorkfield E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon X3320", ["Yorkfield R0, 64-bit, quad-core, 45nm, L2: 6MB",4,1,3]],
		["Xeon X33", ["Yorkfield E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP E0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,26,2" => [
		["Xeon", ["Nehalem B0, 64-bit, 45nm, L3: 8MB",4,2,4]],
	],
	"Intel-6,28,2" => [
		["Atom", ["Silverthorne C0, 64-bit, 45nm, L2: 512KB",1,2,3]],
	],
	"Intel-15,1,2" => ["Willamette D0, 180nm, L2: 256KB",1,1,0],		# P4
	"Intel-15,1,3" => [
		["Celeron", ["Willamette E0, 180nm, L2: 128KB",1,1,0]],		# P4
		["Pentium", ["Willamette E0, 180nm, L2: 256KB",1,1,0]],		# P4
	],
	"Intel-15,2,2" => ["Gallatin A0, 130nm, L3: 1MB",1,2,0],		# P4 Xeon MP
	"Intel-15,2,4" => [
		["Pentium", ["Northwood B0, 130nm, L2: 512KB",1,1,0]],		# P4, no support for HT
		["Xeon", ["Prestonia B0, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,5" => [
		["Pentium", ["Northwood M0, 130nm, L2: 512KB",1,2,0]],		# P4, some low end GHz parts do not support HT
		["Xeon MP", ["Gallatin B1, 130nm, L3: 1MB",1,2,0]],		# P4 Xeon MP
		["Xeon", ["Prestonia M0, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,6" => ["Gallatin C0, 130nm, L3: 2MB",1,2,0],		# P4 Xeon MP
	"Intel-15,2,7" => [
		["Pentium 4 3060", ["Northwood C1, 130nm, L2: 512KB",1,2,0]],	# P4, accoring to Intel only 3.06GHz parts do support HT (e.g. fe2.rd.aue)
		["Pentium", ["Northwood C1, 130nm, L2: 512KB",1,1,0]],		# P4, accoring to Intel only 3.06GHz parts do support HT (e.g. fe2.rd.aue)
		["Xeon", ["Prestonia C1, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,9" => [
		["Celeron", ["Northwood D1, 130nm, L2: 128KB",1,1,0]],		# P4
		["Pentium", ["Northwood D1, 130nm, L2: 512KB",1,2,0]],		# P4, some low end GHz parts do not support HT
		["Xeon", ["Prestonia D1, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,3,3" => [
		["Celeron", ["Prescott C0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott C0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
	],
	"Intel-15,3,4" => [
		["Celeron", ["Prescott D0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott D0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
		["Xeon", ["Nocona D0, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,4,1" => [
		["Celeron", ["Prescott E0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott E0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
		["Xeon MP", ["Cranford A0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4 Xeon MP
		["Xeon", ["Nocona E0, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,4,3" => [
		["Pentium", ["Prescott N0, 64-bit, 90nm, L2: 2MB",1,2,0]],	# P4, full support for HT
		["Xeon", ["Irwindale N0, 64-bit, 90nm, L2: 2MB",1,2,0]],	# P4 Xeon DP
	],
	"Intel-15,4,4" => ["Smithfield A0, 64-bit, dual-core, 90nm, L2: 2MB",2,1,0],	# Pentium D
	"Intel-15,4,7" => ["Smithfield B0, 64-bit, dual-core, 90nm, L2: 2MB",2,1,0],	# Pentium D
	"Intel-15,4,8" => ["Paxville A0, 64-bit, dual-core, 90nm, L2: 2MB",2,2,0],	# PD Xeon MP
	"Intel-15,4,9" => [
		["Pentium", ["Prescott G1, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4
		["Xeon", ["Nocona G1, 64-bit, 90nm, L2: 1MB",1,2,0]],			# P4 Xeon DP
	],
	"Intel-15,4,10" => [
		["Pentium", ["Prescott R0, 64-bit, 90nm, L2: 2MB",1,2,0]],		# P4
		["Xeon", ["Irwindale R0, 64-bit, 90nm, L2: 2MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,6,2" => [
		["Pentium", ["Presler B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
		["Xeon", ["Dempsey B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# PD Xeon DP
	],
	"Intel-15,6,4" => [
		["Pentium", ["Presler C1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
		["Xeon", ["Dempsey C1, 64-bit, dual-core, 65nm, L2: 4MB",2,2,0]],	# PD Xeon DP
	],
	"Intel-15,6,5" => [
		["Pentium", ["Presler D0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
	],
	"Intel-15,6,8" => [
		["Xeon", ["Tulsa B0, 64-bit, dual-core, 65nm, L2: 4MB",2,2,0]],		# PD Xeon MP
	],
);

# http://www.stchas.edu/faculty/mcse-cisco/a-plus/Fuszner/SANDRA/program/Sandra.bio
my(%jedec) = (
	"2C" => "Micron",
	"AD" => "Hynix",
	"C1" => "Infineon",
	"CE" => "Samsung",
	"7F61" => "Wintec",
	"7F94" => "Smart Modular",
	"7F98" => "Kingston",
	"7FA8" => "Simple Tech",
	"7F7F9E" => "Corsair",
	"7F7FFE" => "Elpida",
	"7F7F7F0B" => "Nanya",
	"7F7F7F16" => "Netlist",
	"7F7F7F83" => "Buffalo",
	"7F7F7F7F46" => "Legacy",
	"7F7F7F7F43" => "Ramaxel",
);

my(%memory_models) = (

	# 2Rx4 with ECC ==> 36 chips (2R ==> 144 bits / x4 == 36)
	# 1Rx4 with ECC ==> 18 chips (1R ==> 72 bits / x4 == 18)
	# 4Rx8 with ECC ==> 36 chips (4R ==> 288 bits / x8 == 36)
	# 2Rx8 with ECC ==> 18 chips
	# 1Rx8 with ECC ==> 9 chips
	# 2Rx16 ==> 8 chips
	# 1Rx16 ==> 4 chips
	# 2Rx4 DDP ==> 36 chips / 2 (DDP) == 18 chips
	# DDP = dual die package (2 dies embedded on one chip)

	# http://www.digchips.com/datasheets/search.php
	# http://www.datasheetcatalog.com/

	# amb info for fbdimms
	# http://developer.intel.com/technology/memory/FBDIMM/667_FBDIMM_web_posting_results_ww30_5.pdf

	# Unknown
	"GM431-QAA-INTD1F" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56724QB667C" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56728MT667N" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56728EL667Q" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GC6CD404" => "512MB PC-2700 Legend DDR-333 Unbuffered CL2.5 2R",
	"FC7CD905" => "256MB PC-3200 Legend DDR-400 Unbuffered CL3",
	"GC7CD905" => "512MB PC-3200 Legend DDR-400 Unbuffered CL3 2R",
	"7D-22KB5K1FPP" => "1GB PC2-4200 DDR2-533 Unbuffered CL4 2R",
	"HPQ00-21183-723SCU" => "2GB PC-2700 HP/Simple DDR-333 ECC Registered CL2.5 2Rx4",
	"140134-031" => "256MB PC133 HP SDRAM Unbuffered CL3",
	"127006-041" => "512MB PC133 HP/Micron SDRAM ECC Registered CL3 1Rx4",
	"512E40K3R24" => "512MB PC2-3200 Itaucom DDR2-400 ECC Registered CL3",
	"M0840038" => "2GB PC2-4200 PQI DDR2-533 Unbuffered CL3",

	# Ramaxel
	"RML1040HB38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx4",
	"RML1040MD38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1040EG38D6W-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4",
	"RML1040EG38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1040E38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4",
	"RML1040S38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1320EG38D7W-667" => "1GB PC2-5300 Ramaxel DDR2-667 Unbuffered CL5",
	"RML1520EC48D7W-800" => "1GB PC2-6400 Ramaxel DDR2-800 Unbuffered CL6",

	# Transend
	"TS64MLD64V4F" => "512MB PC-3200 Transend DDR-400 Unbuffered CL2.5 2Rx8",
	"TS128MLD64V3J" => "1GB PC-2700 Transend DDR-333 Unbuffered CL2.5 2Rx8",
	"TS128MLD64V4J" => "1GB PC-3200 Transend DDR-400 Unbuffered CL3 2Rx8",

	"TS64MLQ64V4J" => "512MB PC2-3200 Transend DDR2-400 Unbuffered CL3 1Rx8",
	"TS64MLQ64V5J" => "512MB PC2-4200 Transend DDR2-533 Unbuffered CL4 1Rx8",
	"JM388Q643A-5" => "1GB PC2-4200 Transend DDR2-533 Unbuffered CL4 2Rx8",
	"JM667QLJ-1G" => "1GB PC2-5300 Transend DDR2-667 Unbuffered CL5 2Rx8",
	"JM667QLU-2G" => "2GB PC2-5300 Transend DDR2-667 Unbuffered CL5 2Rx8",

	# Aeneon
	"AET660UD00-30DB97X" => "512MB PC2-5300 Aeneon DDR2-667 Unbuffered CL5 1Rx8",

	# http://www.corsairmicro.com/main/prddr.html
	"CM766S512-133" => "512MB PC133 Corsair SDRAM ECC Registered CL3 1Rx4",
	"CM73SD512R-2100" => "512MB PC-2100 Corsair DDR-266 ECC Registered CL2.5 1Rx4",
	"CM74SD1024R-2100" => "1GB PC-2100 Corsair DDR-266 ECC Registered CL2.5 2Rx4",
	"CM66SD1024-2700" => "1GB PC-2700 Corsair DDR-333 Unbuffered CL2.5 2Rx4",
	"CM73DD1024R-400" => "1GB PC2-3200 Corsair DDR2-400 ECC Registered CL3 1Rx4",
	"CM2X512-5400C4" => "512MB PC2-5400 Corsair DDR2-675 Unbuffered CL4 2Rx8",	# specs are correct

	# Kingston
	"KT326667-041-INCE5" => "256MB PC-3200 Kingston DDR-400 Unbuffered CL3",
	"KT305957-041-INCE5" => "256MB PC-2700 Kingston DDR-333 Unbuffered CL2.5",
	"UW728-IFA-INTC0S" => "1GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4",
	"UW729-IFA-INTC0S" => "2GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"KG7132-IFA-INTC0S" => "2GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4",
	"9W657-NAA-INTC0F" => "2GB PC2-5300 Dell/Kingston DDR2-667 ECC Fully Buffered CL5 2R",
	"GM431-NAA-INTD1F" => "2GB PC2-5300 Dell/Kingston DDR2-667 ECC Fully Buffered CL5",
	"KD6502-ELG" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",
	"KD6502-ELJ" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",
	"KX1563-NAB" => "2GB PC2-3200 Kingston DDR2-400 ECC Registered CL3",
	"9905316-005.A04LF" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",	# KVR667D2N5/1G
	"9905316-131.A01LF" => "1GB PC2-6400 Kingston DDR2-800 Unbuffered CL6",
	"9905316-132.A01LF" => "2GB PC2-6400 Kingston DDR2-800 Unbuffered CL6",
	"DGM431NABINTD1F" => "2GB PC2-5300 Kingston DDR2-667 ECC Fully Buffered CL5 2R",

	# Viking
	# http://www.vikingcomponents.com/brochure/details.asp
	"VR5ER567214EBPD1" => "2GB PC2-3200 Viking DDR2-400 ECC Registered CL3 2Rx4",

	# Simple Tech
	"ST72P4T128M-A05A" => "1GB PC2-3200 Simple DDR2-400 ECC Registered CL3 1R",
	"S1024R3NN2QK-I" => "1GB PC2-5300 Simple DDR2-667 Unbuffered CL5 2R",

	# http://www.legacyelectronics.com/products.php
	"L512872M20A-50." => "1GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 1Rx4",
	"L527R5A2AHA-50." => "2GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 1Rx4",
	"L527R5A2AHA-37R" => "2GB PC2-4200 Legacy DDR2-533 ECC Registered CL4 1Rx4",
	"L547R5A2A4B-50Y" => "4GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 2Rx4",

	# http://www.qimonda.com/index.html
	"HYS64V32220GU-7.5." => "256MB PC133 Infineon SDRAM Unbuffered CL3",

	# http://www.qimonda.com/system/galleries/download/products/nomenclature_ddr_ddr2.pdf

	"64D16301[GHE]U6B" => "128MB PC-2700 Infineon DDR-333 Unbuffered CL2.5 1Rx8",
	"64D32000[GHE]U7B" => "256MB PC-2100 Infineon DDR-266 Unbuffered CL2 1Rx8",
	"64D32300[GHE]U6C" => "256MB PC-2700 Infineon DDR-333 Unbuffered CL2.5 1Rx4",
	"64D32300[GHE]U5C" => "256MB PC-3200 Infineon DDR-400 Unbuffered CL3 1Rx4",
	"64D64020[GHE]U7B" => "512MB PC-2100 Infineon DDR-266 Unbuffered CL2 2Rx8",
	"64D64320[GHE]U5C" => "512MB PC-3200 Infineon DDR-400 Unbuffered CL3 2Rx8",
	"72D128521[GHE]R7B" => "1GB PC-2100 Infineon DDR-266 ECC Registered CL2 2Rx4",
	"HYS72D128520[GHE]R-7" => "1GB PC-2100 Infineon DDR-266 ECC Registered CL2 2Rx4",

	# http://www.qimonda.com/computing-dram/ddr2/registered-dimms.html
	# http://www.qimonda.com/download.jsp?ref=/qis_docs/Downloads%20Computing/DDR2%20%26%20DDR_SDRAM_Mod.pdf
	# HYS prefix
	"72T32000[EH]R5." => "256MB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",
	"72T64001[EH]R5." => "512MB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",

	"72T128000[EH]R3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Registered CL4 1Rx4",
	"72T128000[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx4",
	"72T128001[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",
	"72T128020[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 2Rx8",
	"72T128000[EH]R3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 1Rx4",
	"72T128000[EH]P3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 1Rx4",

	"72T256220[EH]R5." => "2GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 2Rx4",
	"72T256000[EH]R5." => "2GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx4",		# R = reg
	"72T256220[EH]P3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 2Rx4",	# P = reg with addr parity
	"72T256920[EH]P3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 2Rx4",

	"72T512022[EH]R5." => "4GB PC2-3200 Qimonda DDR2-400 ECC Registered CL3 2Rx4 DDP",
	"72T512022[EH]R3.7." => "4GB PC2-4200 Qimonda DDR2-533 ECC Registered CL4 2Rx4 DDP",
	"72T512022[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4 DDP",
	"72T512220[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4",
	"72T512922[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4 DDP",
	"72T512040[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 4Rx8",
	#       ^     ^
	#       |     |-- R=registered, P=registerd/addr parity
	#       |-------- rank: 0=1R, 2=2R, 4=4R

	# http://www.qimonda.com/computing-dram/ddr2/unbuffered.html
	"64T32000[EH]U3.7." => "256MB PC2-4200 Infineon DDR2-533 Unbuffered CL4 1Rx16",
	"64T64000[EH]U5." => "512MB PC2-3200 Infineon DDR2-400 Unbuffered CL3 1Rx8",
	"64T64000[EH]U3.7." => "512MB PC2-4200 Infineon DDR2-533 Unbuffered CL4 1Rx8",
	"64T64000[EH]U3S..?" => "512MB PC2-5300 Infineon DDR2-667 Unbuffered CL5 1Rx8",
	"64T128020[EH]U3S..?" => "1GB PC2-5300 Infineon DDR2-667 Unbuffered CL5 2Rx8",
	"72T64000[EH]U3.7." => "512MB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 1Rx8",
	"72T64000[EH]U3S..?" => "512MB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL4 1Rx8",
	"72T128020[EH]U3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 2Rx8",
	"72T128020[EH]U3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL5 2Rx8",
	"64T128020[EH]U2.5.." => "1GB PC2-6400 Infineon DDR2-800 ECC Unbuffered CL6 2Rx8",
	"72T256020[EH]U3.7." => "2GB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 2Rx8",
	"72T256020[EH]U3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL5 2Rx8",

	# http://www.qimonda.com/download.jsp?ref=/qis_docs/Downloads%20Computing/Nomenclature_DDR_DDR2.pdf
	# http://www.qimonda.com/computing-dram/ddr2/fully-buffered-dimms.html
	"72T64.00[EH]F[AND]3S..?" => "512MB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 1Rx8",
	"72T64.00[EH]F[AND]3.7." => "512MB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 1Rx8",
	"72T128.20[EH]F[AND]3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 2Rx8",
	"72T128.20[EH]F[AND]3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx8",	# A=?, N=Intel C0 AMB, D=IDT 1.5 AMB
	"72T256.20[EH]F[AND]3.7." => "2GB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"72T256.20[EH]F[AND]3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx4",

	"72T512.20[EH]F[AND]3S..?" => "4GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx4",

	# http://www.qimonda.com/computing-dram/ddr/registered-dimms.html
	"72D128320GBR6C" => "1GB PC-2700 Infineon DDR-333 ECC Registered CL2.5 2Rx4",

	"72D128300GBR5B" => "1GB PC-3200 Infineon DDR-400 ECC Registered CL3 1Rx4",
	"72D256220HBR5B" => "2GB PC-3200 Infineon DDR-400 ECC Registered CL3 2Rx4",
	"72D256220GBR5B" => "2GB PC-3200 Infineon DDR-400 ECC Registered CL3 2Rx4",

	# Nanya
	# http://www.nanya.com/e-htm/B/2abc/2abc-01.htm
	# http://www.nanya.com/PageEdition1.aspx?Menu_ID=23&lan=en-us&def=210&isPrint=&KeyWords=
	# http://www.nanya.com/NanyaAdmin/GetFiles.ashx?ID=351
	"NT256D64S88B1G-6K" => "256MB PC-2700 Nanya DDR-333 Unbuffered CL2.5 1Rx8",
	"NT256D64S88C0G-5T" => "256MB PC-3200 Nanya DDR-400 Unbuffered CL3 1Rx8",
	"NT512D64S8HB0G-75B" => "512MB PC-2100 Nanya DDR-266 Unbuffered CL2.5 2Rx8",

	"NT1GT72U4P...U-5A" => "1GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 1Rx4",
	"NT1GT72U4P...V-5A" => "1GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 1Rx4",
	"NT1GT72U4P...V-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Registered CL5 1Rx4",
	"NT2GT72U4N...V-5A" => "2GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 2Rx4",
	"NT2GT72U4N...V-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Registered CL5 2Rx4",

	"NT256T64UH4...Y-37B?" => "256MB PC2-4200 Nanya DDR2-533 Unbuffered CL4 1Rx16",
	"NT512T64U88..(F|B|BY)-5A" => "512MB PC2-3200 Nanya DDR2-400 Unbuffered CL3 1Rx8",
	"NT512T64U88..(F|B|BY)-37B?" => "512MB PC2-4200 Nanya DDR2-533 Unbuffered CL4 1Rx8",
	"NT512T72U89...Y-37B?" => "512MB PC2-4200 Nanya DDR2-533 ECC Unbuffered CL4 1Rx8",
	"NT512T72U89...Y-3C" => "512MB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 1Rx8",
	"NT1GT64U8H..(F|B|BY)-37B?" => "1GB PC2-4200 Nanya DDR2-533 Unbuffered CL4 2Rx8",
	"NT1GT64U8H..(F|B|BY)-3C" => "1GB PC2-5300 Nanya DDR2-667 Unbuffered CL5 2Rx8",
	"NT1GT64U8H..(F|B|BY)-25C" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL5 2Rx8",
	"NT1GT64U8H..(F|B|BY)-25D" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL6 2Rx8",
	"NT1GT72U8P...Y-37B" => "1GB PC2-4200 Nanya DDR2-533 ECC Unbuffered CL4 2Rx8",
	"NT1GT72U8P...Y-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 2Rx8",
	"NT1GT64U88...Y-AC" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL5 1Rx8",
	"NT1GT64U88...Y-AD" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL6 1Rx8",
	"NT2GT72U8P...Y-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 2Rx8",
	"NT2GT72U8P...Y-AC" => "2GB PC2-6400 Nanya DDR2-800 ECC Unbuffered CL5 2Rx8",
	"NT2GT72U8P...Y-AD" => "2GB PC2-6400 Nanya DDR2-800 ECC Unbuffered CL6 2Rx8",

	"NT1GT72U8P..BD-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT AMB
	"NT1GT72U8P..BN-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel AMB
	"NT2GT72U4N..BD-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT AMB
	"NT2GT72U4N..BN-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel AMB
	"NT2GT72U8P..BN-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel AMB
	"NT2GT72U8P..BD-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT AMB
	"NT4GT72U4N..BD-3C" => "4GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT AMB
	#        ^^   ^
	#        ||   |- AMB: D=IDT, N=Intel, E=NEC
	#        ||----- #chips: 4=4, 8=8, 9=9, H=16, P=18, N=36
	#        |------ 4=x4, 8=x8, H=x16
	#
	"NT4GTT72U4P..UN-3C" => "4GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4 DDP",	# Intel AMB
	# 2Rx4 DDP ==> 36 chips / 2 (DDP) == 18 chips

	# Elixir (brand of Nanya)
	# http://www.elixir-memory.com/product_elixir_1.asp?pro_Name=elixir&pro_Class=DDR+SDRAM&pro_Type=All
	"M2U51264DS8HC3G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",
	"M2Y51264DS8HC3G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",
	"M2Y51264DS88C1G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 1Rx8",
	"M2Y1G64DS8HC1G-5T" => "1GB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",

	"M2U51264TU88A2B-37" => "512MB PC2-4200 Elixir DDR2-533 Unbuffered CL4 1Rx8",
	"M2Y51264TU88A4B-3C" => "512MB PC2-5300 Elixir DDR2-667 Unbuffered CL5 1Rx8",
	"M2Y1G64TU8HB4B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",
	"M2Y1G64TU8HA2B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",
	"M2Y1G64TU8HA4B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",

	# http://www.samsung.com/Products/Semiconductor/Support/ProductLists/index.htm
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/PartNumberDecoder.htm
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/DDR_SDRAM_Module.pdf
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/ddr2_product_guide_aug_04.pdf
	# http://www.samsung.com/Products/Semiconductor/common/product_list.aspx?family_cd=DDR020201
	# http://www.samsung.com/global/system/business/semiconductor/family/2007/8/30/746403DDR_SDRAM_Module.pdf
	"M3 12L6420...-CB0" => "512MB PC-2100 Samsung DDR-266 ECC Registered CL2.5 1Rx4",
	"M3 12L6523...-CB3" => "512MB PC-2700 Samsung DDR-333 ECC Registered CL2.5 1Rx8",
	"M3 12L2920...-CB3" => "1GB PC-2700 Samsung DDR-333 ECC Registered CL2.5 1Rx4",
	"M3 68L1624...-CB3" => "128MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx16",
	"M3 68L3223...-CB0" => "256MB PC-2100 Samsung DDR-266 Unbuffered CL2.5 1Rx8",
	"M3 68L3223...-CB3" => "256MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx8",
	"M3 68L3223...-CCC" => "256MB PC-3200 Samsung DDR-400 Unbuffered CL3 1Rx8",
	"M3 68L6523...-CB3" => "512MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx8",
	"M3 68L6523...-CCC" => "512MB PC-3200 Samsung DDR-400 Unbuffered CL3 1Rx8",
	"M3 68L6423...-CCC" => "512MB PC-3200 Samsung DDR-400 Unbuffered CL3 2Rx8",
	"M3 68L2923...-CCC" => "1GB PC-3200 Samsung DDR-400 Unbuffered CL3 2Rx8",
	#         ^
	#         |-- 0=x4, 3=x8, 4=x16

	# http://www.samsung.com/global/business/semiconductor/products/dram/downloads/ddr2_product_guide_apr_07.pdf
	"M3 93T6450...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T6453...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx8",
	"M3 93T6553...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx8",
	"M3 93T6553...-CE6" => "512MB PC2-5300 Samsung DDR2-667 ECC Registered CL5 1Rx8",
	"M3 93T2950...-CCC" => "1GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T2950...-CE6" => "1GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 1Rx4",
	"M3 93T2953...-CCC" => "1GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx8",
	"M3 93T5750...-CCC" => "2GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx4",
	"M3 93T5750...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Registered CL4 2Rx4",
	"M3 93T5660...-CCC" => "2GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T5660...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Registered CL4 1Rx4",
	"M3 93T5750...-CE6" => "2GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 2Rx4",
	"M3 93T5160...-CE6" => "4GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 2Rx4",

	"M3 78T3253...-CD5" => "256MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 1Rx8",
	"M3 78T6453...-CD5" => "512MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 2Rx8",
	"M3 78T6553...-CD5" => "512MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 1Rx8",
	"M3 78T6553...-CE6" => "512MB PC2-5300 Samsung DDR2-667 Unbuffered CL5 1Rx8",
	"M3 78T2953...-CD5" => "1GB PC2-4200 Samsung DDR2-533 Unbuffered CL4 2Rx8",
	"M3 78T2953...-CE6" => "1GB PC2-5300 Samsung DDR2-667 Unbuffered CL5 2Rx8",
	"M3 78T2953...-CF7" => "1GB PC2-6400 Samsung DDR2-800 Unbuffered CL6 2Rx8",
	"M3 78T2863...-CE6" => "1GB PC2-5300 Samsung DDR2-667 Unbuffered CL5 1Rx8",
	"M3 78T2863...-CF7" => "1GB PC2-6400 Samsung DDR2-800 Unbuffered CL6 1Rx8",
	"M3 91T2953...-CD5" => "1GB PC2-4200 Samsung DDR2-533 ECC Unbuffered CL4 2Rx8",
	"M3 91T2953...-CE6" => "1GB PC2-5300 Samsung DDR2-667 ECC Unbuffered CL5 2Rx8",
	"M3 91T5663...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Unbuffered CL4 2Rx8",
	"M3 91T5663...-CE6" => "2GB PC2-5300 Samsung DDR2-667 ECC Unbuffered CL5 2Rx8",

	# http://www.samsung.com/global/system/business/semiconductor/family/2008/5/26/041421DDR2_SDRAM_Module.pdf
	# http://www.samsung.com/Products/Semiconductor/common/product_list.aspx?family_cd=DDR020204
	"M395T6553...-CE60" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel C0 AMB
	"M395T6553...-CE61" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT A1.5 AMB
	"M395T6553...-CE66" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT C1 AMB
	"M395T6553...-CE6." => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# wildcard AMB

	"M395T2953...-CD50" => "1GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel C0 AMB
	"M395T2953...-CD51" => "1GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx8",	# IDT A1.5 AMB
	"M395T2953...-CE60" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel C0 AMB
	"M395T2953...-CE61" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT A1.5 AMB
	"M395T2953...-CE62" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# NEC B5 AMB
	"M395T2953...-CE65" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"M395T2953...-CE66" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"M395T2953...-CE6." => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# wildcard AMB

	"M395T5750...-CD50.?" => "2GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel C0 AMB
	"M395T5750...-CD51" => "2GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx4",	# IDT A1.5 AMB
	"M395T5750...-CE60.?" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel C0 AMB
	"M395T5750...-CE61" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT A1.5 AMB
	"M395T5750...-CE62" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# NEC B5 AMB
	"M395T5750...-CE65" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"M395T5750...-CE66" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"M395T5750...-CE6." => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# wildcard AMB

	"M395T5663...-CE65" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"M395T5663...-CE66" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"M395T5663...-CE6." => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# wildcard AMB

	"M395T5160...-CE65" => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"M395T5160...-CE6." => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# wildcard AMB
	"M395T5166...-CE65" => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	#        ^
	#        |-- 0=x4, 3=x8, 4=x16, 6=x4:stack, 7=x8:stack

	# http://www.amd.com/us-en/assets/content_type/DownloadableAssets/Opteron_Registered_DIMM_AVL.pdf
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/DDR_SDRAM_Module.pdf
	"M3 12L5720CZ0-CB3" => "2GB PC-2700 Samsung DDR-333 ECC Registered CL2.5 2Rx4",
	"M3 12L5720CZ3-CCC" => "2GB PC-3200 Samsung DDR-400 ECC Registered CL3 2Rx4",

	# http://download.micron.com/pdf/guide/modguide.pdf
	# http://www.micron.com/products/modules/ddr2sdram/partlist.aspx?pincount=240-pin&version=Registered&package=DIMM
	# http://www.micron.com/products/search.aspx
	# http://www.micron.com/products/modules/
	"9VDDF6472G-335.." => "512MB PC-2700 Micron DDR-333 ECC Registered CL2.5 1Rx8",
	"8LSDT3264AG-133.." => "256MB PC133 Micron SDRAM Unbuffered CL3 1Rx8",
	"18LSDT6472G-133.." => "512MB PC133 Micron SDRAM ECC Registered CL3 1Rx4",
	"18VDDT6472G-265.." => "512MB PC-2100 Micron DDR-266 ECC Registered CL2.5 1Rx4",
	"18VDDF12872G-335.." => "1GB PC-2700 Micron DDR-333 ECC Registered CL2.5 1Rx4",

	# http://www.micron.com/products/modules/udimm/partlist?tech=DDR%20SDRAM
	"4VDDT1664AG-335.." => "128MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx16",
	"8VDDT3264AG-265.." => "256MB PC-2100 Micron DDR-266 Unbuffered CL2.5 1Rx8",
	"8VDDT3264AG-335.." => "256MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx8",
	"8VDDT3264AG-40B.." => "256MB PC-3200 Micron DDR-400 Unbuffered CL3 1Rx8",
	"8VDDT6464AG-335.." => "512MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx8",
	"16VDDT6464AY-40B.." => "512MB PC-3200 Micron DDR-400 Unbuffered CL3 2Rx8",

	"9HTF3272Y-40E.." => "256MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx8",
	"9HTF6472Y-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx8",
	"18HTF6472DY-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx8",
	"18HTF6472Y-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"18HTF12872Y-40E.." => "1GB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"18HTF12872DY-40E.." => "1GB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx8",
	"18HTF12872PY-667.." => "1GB PC2-5300 Micron DDR2-667 ECC Registered CL5 1Rx4",
	"18HTF25672Y-40E.." => "2GB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"36HTF25672Y-40E.." => "2GB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx4",		# Non-Parity
	"36HTF25672PY-667.." => "2GB PC2-5300 Micron DDR2-667 ECC Registered CL5 2Rx4",		# Parity
	"36HTF51272PY-667.." => "4GB PC2-5300 Micron DDR2-667 ECC Registered CL5 2Rx4",		# Parity

	"4HTF3264AY-53E.." => "256MB PC2-4200 Micron DDR2-533 Unbuffered CL4 1Rx16",
	"8HTF6464AY-53E.." => "512MB PC2-4200 Micron DDR2-533 Unbuffered CL4 1Rx8",
	"9HTF6472AY-53E.." => "512MB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 1Rx8",
	"8HTF12864AY-667.." => "1GB PC2-5300 Micron DDR2-667 Unbuffered CL5 1Rx8",
	"8HTF12864AY-800.." => "1GB PC2-6400 Micron DDR2-800 Unbuffered CL6 1Rx8",
	"16HTF12864AY-667.." => "1GB PC2-5300 Micron DDR2-667 Unbuffered CL5 2Rx8",
	"18HTF12872AY-53E.." => "1GB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 2Rx8",
	"18HTF25672AY-53E.." => "2GB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 2Rx8",

	"MT9HTF6472FY-53E.." => "512MB PC2-4200 Micron DDR2-533 ECC Fully Buffered CL4 1Rx8",
	"18HTF12872FDY-53EB" => "1GB PC2-4200 Micron DDR2-533 ECC Fully Buffered CL4 2Rx8",
	"18HF12872FD667...." => "1GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"36HTF25672F667...." => "2GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"36HTF51272F667...." => "4GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 2Rx4",

	# http://www.elpida.com/en/products/index.html
	# http://www.elpida.com/pdfs/ECT-TS-1971.pdf
	"EBD25UC8AMFA-5B" => "256MB PC-3200 Elpida DDR-400 Unbuffered CL3 1Rx8",

	"EBE51UD8A.WA-6E-E" => "512MB PC2-5300 Elpida DDR2-667 Unbuffered CL5 1Rx8",
	"EBE11UD8A.WA-6E-E" => "1GB PC2-5300 Elpida DDR2-667 Unbuffered CL5 2Rx8",
	"EBE11UD8A.WA-8E-E" => "1GB PC2-6400 Elpida DDR2-800 Unbuffered CL5 2Rx8",
	"EBE11UD8A.WA-8G-E" => "1GB PC2-6400 Elpida DDR2-800 Unbuffered CL6 2Rx8",

	"EBE51RD8A.FA-4A-E" => "512MB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx8",
	"EBE10RD4A.FA-4A-E" => "1GB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx4",

	"EBE20RE4A.FA-4A-E" => "2GB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx4",
	"EBE20AE4A.FA-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 1Rx4",
	"EBE21AD4A.FA-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 2Rx4",
	"EBE41AE4A.FA-6E-E" => "4GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 2Rx4",
	#   ^^^ ^
	#    || |- 4=x4, 8=x8, 6=x16
	#    ||--- A=registered/addr parity, R=registered
	#    |---- density/rank: 10=1G/1R, 11=1G/2R, 20=2G/1R, 21=2G/2R, 41=4G/2R, 51=512M/1R

	# e.g. http://www.elpida.com/pdfs/E0868E30.pdf
	"EBE11FD8A.F.-6E-E" => "1GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx8",
	"EBE21FD4A.F.-5C-E" => "2GB PC2-4200 Elpida DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"EBE21FD4A.F.-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"EBE21FE8A.F.-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx8",

	# http://www.hynix.com/inc/pdfDownload.jsp?path=/upload/products/gl/products/dram/down/DDR2.pdf
	# http://hynix.com/datasheet/eng/module/module_sub.jsp?RK=08&SK=RD&RAM_NAME=DDR2+SDRAM&SUB_RAM=512MB&SUB_RAM1=1GB&SUB_RAM2=2GB&SUB_RAM3=4GB
	# P == lead-free, M == 36 chips?
	"71V32635HCT8-H" => "256MB PC133 Hynix SDRAM Unbuffered CL3 2Rx8",

	"HYMP564R72P?8-E3" => "512MB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx8",
	"HYMP512R72B?P?4-E3" => "1GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx4",
	"HYMP512R72P?8-E3" => "1GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx8",
	"HYMP125R72P?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx4",
	"HYMP125R72MP?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx4",
	"HYMP525R72B?P?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx4",

	"HYMP512P72.P4-Y5" => "1GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 1Rx4",
	"HYMP525P72.P4-Y5" => "2GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4",
	"HYMP125P72.P4-Y5" => "2GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 1Rx4",
	"HYMP151P72.P4-Y5" => "4GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4",
	"HYMP351P72.MP4-Y5" => "4GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4 DDP",
	#       ^   ^ ^
	#       |   | |-- 4=x4, 8=x8, 6=x16
	#       |   |---- M=DDP
	#       |-------- R=registered, P=registered/addr parity

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=03&RK=07&SK=UD&RAM_NAME=DDR%20SDRAM&SUB_RAM1=256MB&SUB_RAM2=512MB&SUB_RAM3=
	# http://www.hynix.com/inc/pdfDownload.jsp?path=/gl/products/down/DDR_MODULE_Old.pdf
	"HYMD216 646[A-D]6J-J" => "128MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 1Rx16",
	"HYMD232 646[A-D]8-H" => "256MB PC-2100 Hynix DDR-266 Unbuffered CL2.5 1Rx8",
	"HYMD232 646[A-D]8J-J" => "256MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 1Rx8",
	"HYMD232 646[A-D]8J-D43" => "256MB PC-3200 Hynix DDR-400 Unbuffered CL3 1Rx8",
	"HYMD264 646[A-D]8J-J" => "512MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 2Rx8",
	"HYMD264 646[A-D]8J-D43" => "512MB PC-3200 Hynix DDR-400 Unbuffered CL3 2Rx8",

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=02&RK=08&SK=UD&RAM_NAME=DDR2%20SDRAM&SUB_RAM=256MB&SUB_RAM1=512MB&SUB_RAM2=
	# http://hynix.com/eng/02_products/01_dram/down/DDR2MODULE.pdf
	"HYMP532U64[A-G]?P6-E3" => "256MB PC2-3200 Hynix DDR2-400 Unbuffered CL3 1Rx16",
	"HYMP564U64[A-G]?P8-C4" => "512MB PC2-4200 Hynix DDR2-533 Unbuffered CL4 1Rx8",
	"HYMP512U64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx8",
	"HYMP112U64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 1Rx8",

	"HYMP564U72[A-G]?P8-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 1Rx8",
	"HYMP564U72[A-G]?P8-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 1Rx8",
	"HYMP512U72[A-G]?P8-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 2Rx8",
	"HYMP512U72[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 2Rx8",
	"HYMP125U72[A-G]?P8-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-S5" => "2GB PC2-6400 Hynix DDR2-800 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-S6" => "2GB PC2-6400 Hynix DDR2-800 ECC Unbuffered CL6 2Rx8",

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=02&RK=08&SK=FB&RAM_NAME=DDR2%20SDRAM
	"HYMP564[BF]72[A-G]?P8D2-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 1Rx8",	# IDT A1.5 AMB
	"HYMP564[BF]72[A-G]?P8N2-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 1Rx8",	# Intel C0 AMB

	"HYMP564[BF]72[A-G]?P8D2-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT A1.5 AMB
	"HYMP564[BF]72[A-G]?P8N2-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel C0 AMB
	"HYMP564[BF]72[A-G]?P8N3-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel D1 AMB

	"HYMP512[BF]72[A-G]?P8D2-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# IDT A1.5 AMB
	"HYMP512[BF]72[A-G]?P8N2-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel C0 AMB
	"HYMP512[BF]72[A-G]?P8N3-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel D1 AMB

	"HYMP512[BF]72[A-G]?P8D2-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT A1.5 AMB
	"HYMP512[BF]72[A-G]?P8D3-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"HYMP512[BF]72[A-G]?P8N2-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel C0 AMB
	"HYMP512[BF]72[A-G]?P8N3-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"HYMP512[BF]72[A-G]?P8E4-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# NEC B5+ AMB

	"HYMP525[BF]72[A-G]?P4D2-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# IDT A1.5 AMB
	"HYMP525[BF]72[A-G]?P4N2-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel C0 AMB
	"HYMP525[BF]72[A-G]?P4N3-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel D1 AMB

	"HYMP525[BF]72[A-G]?P4D2-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT A1.5 AMB
	"HYMP525[BF]72[A-G]?P4D3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"HYMP525[BF]72[A-G]?P4N2-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel C0 AMB
	"HYMP525[BF]72[A-G]?P4N3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"HYMP525[BF]72[A-G]?P4E4-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# NEC B5+ AMB

	"HYMP125[BF]72[A-G]?P8D3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"HYMP125[BF]72[A-G]?P8N3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB

	"HYMP151[BF]72[A-G]?P4D3-Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"HYMP151[BF]72[A-G]?P4N3-Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"HYMP351[BF]72[A-G]?MP4N3Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4 DDP",	# Intel D1 AMB

	# SODIMM
	"HYMP564S64[A-G]?P6-Y5" => "512MB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx16",
	"HYMP512S64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx8",

	# http://www.netlistinc.com/technology_products.aspx?sm=pr
	"NLD127R21203F-D32K" => "1GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 1R",
	"NLD257R21203F-D32K" => "2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R",
	"NLD257R22503F-D32K" => "2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R",
	"NMD257A26407FD53N1" => "2GB PC2-5300 Netlist DDR2-667 ECC Fully Buffered CL5 4Rx8",
	"N.D517A21207FD53.." => "4GB PC2-5300 Netlist DDR2-667 ECC Fully Buffered CL5 4Rx8",

	# http://www.smartm.com/product/productCat.cfm?productCatID=1
	# http://www.smartupgradeconfigurator.com/config/
	"SX5643285D8N6CLICH" => "256MB PC-2700 Smart DDR-333 Unbuffered CL2.5",
	"SM5643285D8N6CLIBH" => "256MB PC-2700 Smart DDR-333 Unbuffered CL2.5",
	"SM5643285D8N6CHIBH" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SM5643285D8N6CHM1H" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SG2567UDR212852HC" => "2GB PC2-5300 Smart DDR2-667 ECC Unbuffered CL5 2Rx8",
	"SM5643285D8N6CHHAH" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SB2567RDR212835IA" => "2GB PC2-3200 Smart DDR2-400 ECC Registered CL3 2R",
	"SB1287RDR212435IA" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SB572284FG8E03BIAH" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SG572284FG8E0DBHBH" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SG5SC82N2G1CDNDSED" => "1GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5",
	"SG5SD42N2G1CDNDSCD" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG5SD42N2G1CDNDSED" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG5SD42N2G1BDDEHCH" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG2567RDR212452IB" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"SG5SE84N2G1CDDGSD" => "4GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"SG5127FBD12852HCDL" => "4GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 4Rx8",
);

my(%drive_models) = (
	# http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/knowndrives.cpp?view=log

	# Toshiba

	"MK4032GAX" => "40GB 5.4K ATA/100 2.5\" Toshiba 8MB",
	"MK4026GAX" => "40GB 5.4K ATA/100 2.5\" Toshiba 16MB",

	# Fujitsu
	# http://www.fujitsu.com/us/services/computing/storage/hdd/support/docs.html

	"MHS2020AT" => "20GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2030AT" => "30GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2040AT" => "40GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2060AT" => "60GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",

	"MHV2040AH" => "40GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2060AH" => "60GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2080AH" => "80GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2100AH" => "100GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2120AH" => "120GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",

	"MHV2040AS" => "40GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2060AS" => "60GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2080AS" => "80GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",

	"MHV2040BS" => "40GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2060BS" => "60GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2080BS" => "80GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",

	"MHV2040BH" => "40GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2060BH" => "60GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2080BH" => "80GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2100BH" => "100GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2120BH" => "120GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",

	# Samsung

	"SP0411N" => "40GB 7.2K ATA/133 Samsung SpinPoint PL40 2MB",
	"SP2514N" => "250GB 7.2K ATA/133 Samsung SpinPoint P120 8MB",
	"SP0411C" => "40GB 7.2K SATA/300 Samsung SpinPoint PL40 2MB",
	"HD040GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",
	"HD080HJ" => "80GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",
	"HD160JJ" => "160GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",

	"HD041GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint S166 2MB",
	"HD042GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD081GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint S166 2MB",
	"HD082GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD120HJ" => "120GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD161HJ" => "160GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",

	"HD080GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD160HJ" => "160GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD320KJ" => "320GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD321KJ" => "320GB 7.2K SATA/300 Samsung SpinPoint T166 16MB",
	"HD500LJ" => "500GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD501LJ" => "500GB 7.2K SATA/300 Samsung SpinPoint T166 16MB",

	"HD161GJ" => "160GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD162GJ" => "160GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD251HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD252HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD321HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD322HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD501IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD502IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD642JJ" => "640GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD753LJ" => "750GB 7.2K SATA/300 Samsung SpinPoint F1 32MB",
	"HD103UJ" => "1TB 7.2K SATA/300 Samsung SpinPoint F1 32MB",

	# need more info on this model
	"HE160HJ" => "160GB 7.2K SATA/300 Samsung 8MB",

	"HE252HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE322HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE502IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE753LJ" => "750GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 32MB",
	"HE103UJ" => "1TB 7.2K SATA/300 Samsung SpinPoint F1 RAID 32MB",

	# http://www.wdc.com/en/products/productcatalog.asp?Language=en

	# http://websupport.wdc.com/products/pf.asp?fid=18
	"WD100EB" => "10GB 5.4K ATA/100 WD Protege 2MB",
	"WD200EB" => "20GB 5.4K ATA/100 WD Protege 2MB",
	"WD300EB" => "30GB 5.4K ATA/100 WD Protege 2MB",
	"WD400EB" => "40GB 5.4K ATA/100 WD Protege 2MB",
	"WD600EB" => "60GB 5.4K ATA/100 WD Protege 2MB",
	"WD800EB" => "80GB 5.4K ATA/100 WD Protege 2MB",

	"WD64AA" => "6GB 5.4K ATA/66 WD Caviar 2MB",
	"WD75AA" => "7GB 5.4K ATA/66 WD Caviar 2MB",
	"WD84AA" => "8GB 5.4K ATA/66 WD Caviar 2MB",
	"WD102AA" => "10GB 5.4K ATA/66 WD Caviar 2MB",
	"WD136AA" => "13GB 5.4K ATA/66 WD Caviar 2MB",
	"WD153AA" => "15GB 5.4K ATA/66 WD Caviar 2MB",
	"WD205AA" => "20GB 5.4K ATA/66 WD Caviar 2MB",
	"WD307AA" => "30GB 5.4K ATA/66 WD Caviar 2MB",
	"WD450AA" => "45GB 5.4K ATA/66 WD Caviar 2MB",

	"WD102BA" => "10GB 7.2K ATA/66 WD Caviar 2MB",
	"WD136BA" => "13GB 7.2K ATA/66 WD Caviar 2MB",
	"WD153BA" => "15GB 7.2K ATA/66 WD Caviar 2MB",
	"WD205BA" => "20GB 7.2K ATA/66 WD Caviar 2MB",

	"WD100AB" => "10GB 5.4K ATA/100 WD Caviar 2MB",
	"WD200AB" => "20GB 5.4K ATA/100 WD Caviar 2MB",
	"WD300AB" => "30GB 5.4K ATA/100 WD Caviar 2MB",
	"WD400AB" => "40GB 5.4K ATA/100 WD Caviar 2MB",
	"WD600AB" => "60GB 5.4K ATA/100 WD Caviar 2MB",
	"WD800AB" => "80GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1000AB" => "100GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1200AB" => "120GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1600AB" => "160GB 5.4K ATA/100 WD Caviar 2MB",

	# http://websupport.wdc.com/products/pf.asp?fid=2
	"WD100BB" => "10GB 7.2K ATA/100 WD Caviar 2MB",
	"WD200BB" => "20GB 7.2K ATA/100 WD Caviar 2MB",
	"WD300BB" => "30GB 7.2K ATA/100 WD Caviar 2MB",
	"WD400BB" => "40GB 7.2K ATA/100 WD Caviar 2MB",
	"WD600BB" => "60GB 7.2K ATA/100 WD Caviar 2MB",
	"WD800BB" => "80GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1000BB" => "100GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1200BB" => "120GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1600BB" => "160GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1800BB" => "180GB 7.2K ATA/100 WD Caviar 2MB",
	"WD2000BB" => "200GB 7.2K ATA/100 WD Caviar 2MB",
	"WD2500BB" => "250GB 7.2K ATA/100 WD Caviar 2MB",
	"WD3000BB" => "300GB 7.2K ATA/100 WD Caviar 2MB",
	"WD3200BB" => "320GB 7.2K ATA/100 WD Caviar 2MB",

	# http://websupport.wdc.com/products/pf.asp?fid=6
	"WD400JB" => "40GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD800JB" => "80GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1000JB" => "100GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1200JB" => "120GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1600JB" => "160GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1800JB" => "180GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2000JB" => "200GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2500JB" => "250GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3000JB" => "300GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3200JB" => "320GB 7.2K ATA/100 WD Caviar SE 8MB",

	# same as JB drives above
	"WD800AAJB" => "80GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1600AAJB" => "160GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2500AAJB" => "250GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3200AAJB" => "320GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD4000AAJB" => "400GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD5000AAJB" => "500GB 7.2K ATA/100 WD Caviar SE 8MB",

	# same as BB line with liquid bearing?
	"WD800LB" => "80GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1200LB" => "120GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1600LB" => "160GB 7.2K ATA/100 WD Caviar 2MB",

	# PB drives sold in korea only?
	"WD800PB" => "80GB 7.2K ATA/100 WD Caviar",

	"WD1200JD" => "120GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD1600JD" => "160GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD2000JD" => "200GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD2500JD" => "250GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD3000JD" => "300GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD3200JD" => "320GB 7.2K SATA/150 WD Caviar SE 8MB",

	"WD400BD" => "40GB 7.2K SATA/150 WD Caviar",
	"WD800BD" => "80GB 7.2K SATA/150 WD Caviar",

	"WD400PD" => "40GB 7.2K SATA/150 WD Caviar SE",
	"WD800PD" => "80GB 7.2K SATA/150 WD Caviar SE",
	"WD1200PD" => "120GB 7.2K SATA/150 WD Caviar SE",
	"WD1600PD" => "160GB 7.2K SATA/150 WD Caviar SE",
	"WD2000PD" => "200GB 7.2K SATA/150 WD Caviar SE",
	"WD2500PD" => "250GB 7.2K SATA/150 WD Caviar SE",

	"WD400JD" => "40GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD800JD" => "80GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1200JS" => "120GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1600JS" => "160GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2000JS" => "200GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2500JS" => "250GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD3000JS" => "300GB 7.2K SATA/300 WD Caviar SE 8MB",

	"WD800AAJS" => "80GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1600AAJS" => "160GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2500AAJS" => "250GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD3200AAJS" => "320GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD4000AAJS" => "400GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD5000AAJS" => "500GB 7.2K SATA/300 WD Caviar SE 8MB",

	"WD1600SB" => "160GB 7.2K ATA/100 WD Caviar RE 8MB",
	"WD2500SB" => "250GB 7.2K ATA/100 WD Caviar RE 8MB",
	"WD3200SB" => "320GB 7.2K ATA/100 WD Caviar RE 8MB",

	"WD1200SD" => "120GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD1600SD" => "160GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD2500SD" => "250GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD3200SD" => "320GB 7.2K SATA/150 WD Caviar RE 8MB",

	"WD1600YD" => "160GB 7.2K SATA/300 WD Caviar RE 16MB",
	"WD2500YD" => "250GB 7.2K SATA/300 WD Caviar RE 16MB",

	"WD4000YR" => "400GB 7.2K SATA/150 WD Caviar RE2 16MB",

	"WD2500KS" => "250GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD3200KS" => "320GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD4000KS" => "400GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD5000KS" => "500GB 7.2K SATA/300 WD Caviar SE16 16MB",

	"WD3200AAKB" => "320GB 7.2K ATA/100 WD Caviar SE16 16MB",	# Now called "Caviar Blue"
	"WD4000AAKB" => "400GB 7.2K ATA/100 WD Caviar SE16 16MB",
	"WD5000AAKB" => "500GB 7.2K ATA/100 WD Caviar SE16 16MB",

	"WD2500AAKS" => "250GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD3200AAKS" => "320GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD4000AAKS" => "400GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD5000AAKS" => "500GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD7500AAKS" => "750GB 7.2K SATA/300 WD Caviar SE16 16MB",

	"WD5000AACS" => "500GB 7.2K SATA/300 WD Caviar GP 16MB",
	"WD7500AACS" => "750GB 7.2K SATA/300 WD Caviar GP 16MB",
	"WD10EACS" => "1TB 7.2K SATA/300 WD Caviar GP 16MB",

	# Enterprise drives

	"WD1600YS" => "160GB 7.2K SATA/300 WD RE 16MB",
	"WD2500YS" => "250GB 7.2K SATA/300 WD RE 16MB",
	"WD3200YS" => "320GB 7.2K SATA/300 WD RE 16MB",

	"WD1601ABYS" => "160GB 7.2K SATA/300 WD RE2 16MB",
	"WD3201ABYS" => "320GB 7.2K SATA/300 WD RE2 16MB",
	"WD4000YS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD4000ABYS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD4001ABYS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD5000YS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD5000ABYS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD5001ABYS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD7500AYYS" => "750GB 7.2K SATA/300 WD RE2 16MB",

	"WD2502ABYS" => "250GB 7.2K SATA/300 WD RE3 16MB",
	"WD3202ABYS" => "320GB 7.2K SATA/300 WD RE3 16MB",
	"WD5002ABYS" => "500GB 7.2K SATA/300 WD RE3 16MB",
	"WD7502ABYS" => "750GB 7.2K SATA/300 WD RE3 32MB",
	"WD1002FBYS" => "1TB 7.2K SATA/300 WD RE3 32MB",

	"WD5000ABPS" => "500GB 7.2K SATA/300 WD RE2-GP 16MB",
	"WD7500AYPS" => "750GB 7.2K SATA/300 WD RE2-GP 16MB",
	"WD1000FYPS" => "1TB 7.2K SATA/300 WD RE2-GP 16MB",

	# Hitachi

	"DK32DJ-18M" => "18GB 10K U320 Hitachi Ultrastar",
	"DK32DJ-36M" => "36GB 10K U320 Hitachi Ultrastar",
	"DK32DJ-72M" => "73GB 10K U320 Hitachi Ultrastar",

	"DK32EJ-36N" => "36GB 10K U320 Hitachi Ultrastar",
	"DK32EJ-72N" => "73GB 10K U320 Hitachi Ultrastar",
	"DK32EJ-14N" => "147GB 10K U320 Hitachi Ultrastar",

	"DNES-309170W" => "9GB 7.2K Ultra IBM Ultrastar 18ES 2MB",
	"DNES-318350W" => "18GB 7.2K Ultra IBM Ultrastar 18ES 2MB",

	"DPSS-309170" => "9GB 10K U160 IBM Ultrastar 36LP",
	"DPSS-318350" => "18GB 10K U160 IBM Ultrastar 36LP",
	"DPSS-336950" => "36GB 10K U160 IBM Ultrastar 36LP",

	"DMVS18M" => "18GB 10K U160 IBM",

	"DMVS18V" => "18GB 10K Ultra2 IBM Ultrastar 18LZX",

	"DDYS-T09170" => "9GB 10K U160 IBM Ultrastar 36LZX",
	"DDYS-T18350" => "18GB 10K U160 IBM Ultrastar 36LZX",
	"DDYS-T36950" => "36GB 10K U160 IBM Ultrastar 36LZX",

	"IC35L009UWD210" => "9GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L018UWD210" => "18GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L036UWD210" => "36GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L073UWD210" => "73GB 10K U160 IBM Ultrastar 73LZX",

	"IC35L009XWD210" => "9GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L018XWD210" => "18GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L036XWD210" => "36GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L073XWD210" => "73GB 10K U320 IBM Ultrastar 73LZX",

	"IC35L018U[CW]DY10" => "18GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L036U[CW]DY10" => "36GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L073U[CW]DY10" => "73GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L146U[CW]DY10" => "146GB 10K U320 IBM Ultrastar 146Z10",

	"HUS151436VL3[68]00" => "36GB 15K U320 Hitachi Ultrastar 15K147",
	"HUS151473VL3[68]00" => "73GB 15K U320 Hitachi Ultrastar 15K147",
	"HUS151414VL3[68]00" => "147GB 15K U320 Hitachi Ultrastar 15K147",

	"HUS151436VLS300" => "36GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",
	"HUS151473VLS300" => "73GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",
	"HUS151414VLS300" => "147GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",

	"HUS153073VLS300" => "73GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",
	"HUS153014VLS300" => "147GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",
	"HUS153030VLS300" => "300GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",

	"HUC101473CSS300" => "73GB 10K SAS/3 2.5\" Hitachi Ultrastar C10K147",
	"HUC101414CSS300" => "147GB 10K SAS/3 2.5\" Hitachi Ultrastar C10K147",

	"DHEA-34330" => "4GB 5.4K ATA/33 IBM Desktar 5",
	"DHEA-36480" => "6GB 5.4K ATA/33 IBM Desktar 5",

	"DHEA-34331" => "4GB 5.4K ATA/33 IBM Desktar 8",
	"DHEA-36481" => "6GB 5.4K ATA/33 IBM Desktar 8",
	"DHEA-38451" => "8GB 5.4K ATA/33 IBM Desktar 8",

	"DPTA-371360" => "14GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-372050" => "20GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-372730" => "27GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-373420" => "34GB 7.2K ATA/66 IBM Deskstar 34GXP",

	"HDS728040PLAT20" => "40GB 7.2K ATA/133 Hitachi Deskstar 7K80",
	"HDS728080PLAT20" => "80GB 7.2K ATA/133 Hitachi Deskstar 7K80",

	"HTS548020M9AT00" => "20GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548040M9AT00" => "40GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548060M9AT00" => "60GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548080M9AT00" => "80GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",

	"HTS541640J9AT00" => "40GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541660J9AT00" => "60GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541680J9AT00" => "80GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541612J9AT00" => "120GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541616J9AT00" => "160GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",

	"HTS541640J9SA00" => "40GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541660J9SA00" => "60GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541680J9SA00" => "80GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541612J9SA00" => "120GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541616J9SA00" => "160GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",

	"HTE542580K9A300" => "80GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542512K9A300" => "120GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542516K9A300" => "160GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542525K9A300" => "250GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",

	"HTS721060G9AT00" => "60GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721080G9AT00" => "80GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721010G9AT00" => "100GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",

	"HTS721060G9SA00" => "60GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721080G9SA00" => "80GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721010G9SA00" => "100GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",


	"HDS721680PLA380" => "80GB 7.2K SATA/300 Hitachi Deskstar 7K160",
	"HDS721616PLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar 7K160",

	"HDS722540VLAT20" => "40GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722580VLAT20" => "80GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722512VLAT20" => "120GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722512VLAT80" => "120GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",
	"HDS722516VLAT20" => "160GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722516VLAT80" => "160GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",
	"HDS722525VLAT80" => "250GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",

	"HDS722540VLSA80" => "40GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722580VLSA80" => "80GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722512VLSA80" => "120GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722516VLSA80" => "160GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722525VLSA80" => "250GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",

	"HDS724040KLAT80" => "400GB 7.2K ATA/133 Hitachi Deskstar 7K400 8MB",
	"HDS724040KLSA80" => "400GB 7.2K SATA/150 Hitachi Deskstar 7K400 8MB",

	"HDS725050KLAT80" => "500GB 7.2K ATA/133 Hitachi Deskstar 7K500 8MB",

	"HDS725050KLA361" => "400GB 7.2K SATA/300 Hitachi Deskstar E7K500 16MB",
	"HDS725050KLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar E7K500 16MB",

	"HDP725016GLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725025GLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725032GLA380" => "320GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725032GLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",
	"HDP725040GLA380" => "400GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725040GLA360" => "400GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",
	"HDP725050GLA380" => "500GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725050GLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",

	"HDT725025VLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725025VLA360" => "250GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725032VLA380" => "320GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725032VLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725040VLA380" => "400GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725040VLA360" => "400GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725050VLA380" => "500GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725050VLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",

	"HDS721075KLA330" => "750GB 7.2K SATA/300 Hitachi Deskstar 7K1000 32MB",
	"HDS721010KLA330" => "1TB 7.2K SATA/300 Hitachi Deskstar 7K1000 32MB",

	"HDT721016SLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721025SLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721032SLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721050SLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721064SLA360" => "640GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721075SLA360" => "750GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721010SLA360" => "1TB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",

	"HDE721050SLA330" => "500GB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",
	"HDE721075SLA330" => "750GB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",
	"HDE721010SLA330" => "1TB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",

	"HUA721050KLA330" => "500GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",
	"HUA721075KLA330" => "750GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",
	"HUA72107A74A" => "750GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",		# megacli on momentum-pr-q95.data.corp.sp1.yahoo.com
	"HUA721010KLA330" => "1TB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",

	"IC35L010AVER07" => "10GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L020AVER07" => "21GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L030AVER07" => "31GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L040AVER07" => "41GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L060AVER07" => "61GB 7.2K ATA/100 IBM Deskstar 60GXP",

	"IC35L020AVVA07" => "20GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L020AVVN07" => "20GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L040AVVA07" => "40GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L040AVVN07" => "40GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L060AVVA07" => "60GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L060AVVN07" => "60GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L080AVVA07" => "80GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L080AVVN07" => "80GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L100AVVN07" => "100GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L100AVVA07" => "100GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L120AVVN07" => "120GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L120AVVA07" => "120GB 7.2K ATA/100 IBM Deskstar 120GXP",

	"IC35L030AVV207" => "30GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L060AVV207" => "40GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L090AVV207" => "80GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L120AVV207" => "120GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L180AVV207" => "180GB 7.2K ATA/100 IBM Deskstar 180GXP",

	"DJNA-351010" => "10GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-351520" => "15GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-352030" => "20GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-352500" => "25GB 5.4K ATA/66 IBM Deskstar 25GP",

	"DJNA-379100" => "9GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-371350" => "13GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-371800" => "18GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-372200" => "22GB 7.2K ATA/66 IBM Deskstar 22GXP",

	"DTTA-350320" => "3GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350403" => "4GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350640" => "6GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350840" => "8GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351010" => "10GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351290" => "12GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351680" => "16GB 5.4K ATA/33 IBM Deskstar 16GP",

	"DTTA-371010" => "10GB 7.2K ATA/33 IBM Deskstar 14GXP",
	"DTTA-371290" => "12GB 7.2K ATA/33 IBM Deskstar 14GXP",
	"DTTA-371440" => "14GB 7.2K ATA/33 IBM Deskstar 14GXP",

	"DTLA-305010" => "10GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305020" => "20GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305030" => "30GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305040" => "40GB 5.4K ATA/100 IBM Deskstar 40GV",

	"DTLA-307015" => "15GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307020" => "20GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307030" => "30GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307045" => "45GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307060" => "60GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307075" => "75GB 7.2K ATA/100 IBM Deskstar 75GXP",

	"ST34501" => "4GB 10K Ultra Seagate Cheetah 4LP",

	"ST34573" => "4GB 7.2K Ultra2 Seagate Barracuda 9LP",
	"ST39173" => "9GB 7.2K Ultra2 Seagate Barracuda 9LP",

	"ST118273" => "18GB 7.2K Ultra2 Seagate Barracuda 18",

	"ST39175" => "9GB 7.2K Ultra2 Seagate Barracuda 18LP",
	"ST318275" => "18GB 7.2K Ultra2 Seagate Barracuda 18LP",

	"ST39236" => "9GB 7.2K U160 Seagate Barracuda 18XL",
	"ST318436" => "18GB 7.2K U160 Seagate Barracuda 18XL",

	"ST150176" => "50GB 7.2K Ultra2 Seagate Barracuda 50",

	"ST39102" => "9GB 10K Ultra2 Seagate Cheetah 9LP",

	"ST39103" => "9GB 10K Ultra2 Seagate Cheetah 18LP",
	"ST318203" => "18GB 10K Ultra2 Seagate Cheetah 18LP",

	"ST39204" => "9GB 10K U160 Seagate Cheetah 18XL",
	"ST318404" => "18GB 10K U160 Seagate Cheetah 18XL",
	"ST336704" => "36GB 10K U160 Seagate Cheetah 36LP",

	"ST318305" => "18GB 10K U160 Seagate Cheetah 18XL",

	"ST318405" => "18GB 10K U160 Seagate 36XL",
	"ST336705" => "36GB 10K U160 Seagate 36XL",

	"ST336605" => "36GB 10K U160 Seagate 73LP",
	"ST373405" => "73GB 10K U160 Seagate 73LP",

	"ST318406" => "18GB 10K U160 Seagate 36ES",
	"ST336706" => "37GB 10K U160 Seagate 36ES",

	"ST336607" => "37GB 10K U320 Seagate 10K.6",
	"ST373307" => "73GB 10K U320 Seagate 10K.6",
	"ST3146807" => "147GB 10K U320 Seagate 10K.6",

	"ST336807" => "36GB 10K U320 Seagate 10K.7",
	"ST373207" => "72GB 10K U320 Seagate 10K.7",
	"ST3146707" => "146GB 10K U320 Seagate 10K.7",
	"ST3300007" => "300GB 10K U320 Seagate 10K.7",

	"ST373355SS" => "72GB 15K SAS/3 Seagate T10",
	"ST3146755SS" => "146GB 15K SAS/3 Seagate T10",
	"ST3300555SS" => "300GB 15K SAS/3 Seagate T10",

	"ST318452" => "18GB 15K U160 Seagate X15 36LP",
	"ST336752" => "36GB 15K U160 Seagate X15 36LP",

	"ST318432" => "18GB 15K U320 Seagate X15 36LP",
	"ST336732" => "36GB 15K U320 Seagate X15 36LP",

	"ST318453" => "18GB 15K U320 Seagate 15K.3",
	"ST336753" => "37GB 15K U320 Seagate 15K.3",
	"ST373453" => "73GB 15K U320 Seagate 15K.3",

	"ST336754SS" => "37GB 15K SAS/3 Seagate 15K.4",
	"ST373454SS" => "73GB 15K SAS/3 Seagate 15K.4",
	"ST3146854SS" => "146GB 15K SAS/3 Seagate 15K.4",

	"ST336754" => "37GB 15K U320 Seagate 15K.4",
	"ST373454" => "73GB 15K U320 Seagate 15K.4",
	"ST3146854" => "146GB 15K U320 Seagate 15K.4",

	"ST373455LC" => "73GB 15K U320 Seagate 15K.5",
	"ST3146855LC" => "146GB 15K U320 Seagate 15K.5",
	"ST3300655LC" => "300GB 15K U320 Seagate 15K.5",

	"ST373455SS" => "73GB 15K SAS/3 Seagate 15K.5",
	"ST3146855SS" => "146GB 15K SAS/3 Seagate 15K.5",
	"ST3300655SS" => "300GB 15K SAS/3 Seagate 15K.5",

	"ST3400755SS" => "400GB 10K SAS/3 Seagate Cheetah NS",

	"ST3146356SS" => "146GB 15K SAS/3 Seagate 15K.6",
	"ST3300656SS" => "300GB 15K SAS/3 Seagate 15K.6",
	"ST3450856SS" => "450GB 15K SAS/3 Seagate 15K.6",

	"ST34520A" => "4GB 7.2K ATA/33 Seagate Medalist Pro",
	"ST36530A" => "6GB 7.2K ATA/33 Seagate Medalist Pro",
	"ST39140A" => "9GB 7.2K ATA/33 Seagate Medalist Pro",

	"ST310211A" => "10GB 5.4K ATA/100 Seagate U5",
	"ST315311A" => "15GB 5.4K ATA/100 Seagate U5",
	"ST320413A" => "20GB 5.4K ATA/100 Seagate U5",
	"ST330621A" => "30GB 5.4K ATA/100 Seagate U5",
	"ST340823A" => "40GB 5.4K ATA/100 Seagate U5",

	"ST320423A" => "10GB 5.4K ATA/66 Seagate U10",
	"ST315323A" => "15GB 5.4K ATA/66 Seagate U10",
	"ST310212A" => "20GB 5.4K ATA/66 Seagate U10",

	"ST36810A" => "7GB 7.2K ATA/66 Seagate Barracuda",
	"ST310220A" => "10GB 7.2K ATA/66 Seagate Barracuda",
	"ST313620A" => "14GB 7.2K ATA/66 Seagate Barracuda",
	"ST320430A" => "20GB 7.2K ATA/66 Seagate Barracuda",
	"ST328040A" => "28GB 7.2K ATA/66 Seagate Barracuda",

	"ST310210A" => "10GB 7.2K ATA/66 Seagate Barracuda II",
	"ST315320A" => "15GB 7.2K ATA/66 Seagate Barracuda II",
	"ST320420A" => "20GB 7.2K ATA/66 Seagate Barracuda II",
	"ST330630A" => "30GB 7.2K ATA/66 Seagate Barracuda II",

	"ST310216A" => "10GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST315324A" => "15GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST320424A" => "20GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST330631A" => "30GB 7.2K ATA/100 Seagate Barracuda II 100",

	"ST320410A" => "20GB 5.4K ATA/100 Seagate U Series",
	"ST330610A" => "30GB 5.4K ATA/100 Seagate U Series",
	"ST340810A" => "40GB 5.4K ATA/100 Seagate U Series",
	"ST360020A" => "60GB 5.4K ATA/100 Seagate U Series",
	"ST380020A" => "80GB 5.4K ATA/100 Seagate U Series",

	"ST310215A" => "10GB 7.2K ATA/100 Seagate Barracuda III",
	"ST315310A" => "15GB 7.2K ATA/100 Seagate Barracuda III",
	"ST320414A" => "20GB 7.2K ATA/100 Seagate Barracuda III",
	"ST330620A" => "30GB 7.2K ATA/100 Seagate Barracuda III",
	"ST340824A" => "40GB 7.2K ATA/100 Seagate Barracuda III",

	"ST320011A" => "20GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST340016A" => "40GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST360021A" => "60GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST380021A" => "80GB 7.2K ATA/100 Seagate Barracuda IV",

	"ST340017A" => "40GB 7.2K ATA/100 Seagate Barracuda V",
	"ST360015A" => "60GB 7.2K ATA/100 Seagate Barracuda V",
	"ST380023A" => "80GB 7.2K ATA/100 Seagate Barracuda V",
	"ST3120023A" => "120GB 7.2K ATA/100 Seagate Barracuda V",

	"ST340014A" => "40GB 7.2K ATA/100 Seagate 7200.7",
	"ST380011A" => "80GB 7.2K ATA/100 Seagate 7200.7",
	"ST380013A" => "80GB 7.2K ATA/100 Seagate 7200.7",
	"ST3120022A" => "120GB 7.2K ATA/100 Seagate 7200.7",
	"ST3160021A" => "160GB 7.2K ATA/100 Seagate 7200.7",

	"ST3120026A" => "120GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3160023A" => "160GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3200021A" => "200GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3200822A" => "200GB 7.2K ATA/100 Seagate 7200.7 Plus",

	"ST340014AS" => "40GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST380011AS" => "80GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST3120022AS" => "120GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST3160021AS" => "160GB 7.2K SATA/150 Seagate 7200.7 2MB",

	"ST380013AS" => "80GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3120026AS" => "120GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3160023AS" => "160GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3200822AS" => "200GB 7.2K SATA/150 Seagate 7200.7 8MB",

	"ST380817AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3120827AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3160827AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",

	# Dell specials?
	"ST340212AS" => "40GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST340812AS" => "40GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST380219AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST380819AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3120228AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST3120828AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3160228AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST3160828AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",

	"ST3250823A" => "250GB 7.2K ATA/100 Seagate 7200.8",
	"ST3300831A" => "300GB 7.2K ATA/100 Seagate 7200.8",
	"ST3400832A" => "400GB 7.2K ATA/100 Seagate 7200.8",

	"ST3200826AS" => "200GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3250823AS" => "250GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3300831AS" => "300GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3400832AS" => "400GB 7.2K SATA/150 Seagate 7200.8 NCQ",

	"ST3402111A" => "40GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3802110A" => "80GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3120213A" => "120GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3120814A" => "120GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3160212A" => "160GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3160812A" => "160GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3200827A" => "200GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3250824A" => "250GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3250624A" => "250GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3300822A" => "300GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3300622A" => "300GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3400833A" => "400GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3400633A" => "400GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3500841A" => "500GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3500641A" => "500GB 7.2K ATA/100 Seagate 7200.9 16MB",

	"ST340211AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST380211AS" => "80GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST380811AS" => "80GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3120211AS" => "120GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3120811AS" => "120GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3160211AS" => "160GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3160811AS" => "160GB 7.2K SATA/300 Seagate 7200.9 8MB",

	"ST3402111AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3402112AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3802110AS" => "80GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3808110AS" => "80GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3120213AS" => "120GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3120813AS" => "120GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3160212AS" => "160GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3160812AS" => "160GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3200827AS" => "200GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3250824AS" => "250GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3250624AS" => "250GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3300822AS" => "300GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3300622AS" => "300GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3400833AS" => "400GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3400633AS" => "400GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3500841AS" => "500GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3500641AS" => "500GB 7.2K SATA/300 Seagate 7200.9 16MB",

	"ST340215A" => "40GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST340815A" => "40GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST380215A" => "80GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST380815A" => "80GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3120215A" => "120GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST3120815A" => "120GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3160215A" => "160GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST3160815A" => "160GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3200820A" => "200GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3250820A" => "250GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3250620A" => "250GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3300820A" => "300GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3300620A" => "300GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3320820A" => "320GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3320620A" => "320GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3400820A" => "400GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3400620A" => "400GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3500830A" => "500GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3500630A" => "500GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3750840A" => "750GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3750640A" => "750GB 7.2K ATA/100 Seagate 7200.10 16MB",


	"ST380215AS" => "80GB 7.2K SATA/300 Seagate 7200.10 2MB",
	"ST380815AS" => "80GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3160215AS" => "160GB 7.2K SATA/300 Seagate 7200.10 2MB",
	"ST3160815AS" => "160GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3160310AS" => "160GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3200820AS" => "200GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3250310AS" => "250GB 7.2K SATA/300 Seagate 7200.10 8MB",	# 1 platter
	"ST3250410AS" => "250GB 7.2K SATA/300 Seagate 7200.10 16MB",	# 1 platter
	"ST3250820AS" => "250GB 7.2K SATA/300 Seagate 7200.10 8MB",	# 2 platters
	"ST3250620AS" => "250GB 7.2K SATA/300 Seagate 7200.10 16MB",	# 2 platters
	"ST3300820AS" => "300GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3300620AS" => "300GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3320820AS" => "320GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3320620AS" => "320GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3400820AS" => "400GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3400620AS" => "400GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3500830AS" => "500GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3500630AS" => "500GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3750840AS" => "750GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3750640AS" => "750GB 7.2K SATA/300 Seagate 7200.10 16MB",

	"ST3160813AS" => "160GB 7.2K SATA/300 Seagate 7200.11 8MB",
	"ST3320613AS" => "320GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3500620AS" => "500GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3500320AS" => "500GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST3640323AS" => "640GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST3750630AS" => "750GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3750330AS" => "750GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31000333AS" => "1TB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31000340AS" => "1TB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31500341AS" => "1.5TB 7.2K SATA/300 Seagate 7200.11 32MB",

	"ST3250823NS" => "250GB 7.2K SATA/150 Seagate NL35 8MB",
	"ST3250623NS" => "250GB 7.2K SATA/150 Seagate NL35 16MB",
	"ST3400832NS" => "400GB 7.2K SATA/150 Seagate NL35 8MB",
	"ST3400632NS" => "400GB 7.2K SATA/150 Seagate NL35 16MB",

	"ST3250824NS" => "250GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3250624NS" => "250GB 7.2K SATA/300 Seagate NL35 16MB",
	"ST3400833NS" => "400GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3400633NS" => "400GB 7.2K SATA/300 Seagate NL35 16MB",
	"ST3500841NS" => "500GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3500641NS" => "500GB 7.2K SATA/300 Seagate NL35 16MB",

	"ST3250820NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 8MB",
	"ST3250620NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3320620NS" => "320GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3400620NS" => "400GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3500630NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3750640NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES 16MB",

	"ST325082-0NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 8MB",
	"ST325062-0NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST332062-0NS" => "320GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST340062-0NS" => "400GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST350063-0NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST375064-0NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES 16MB",

	"ST3160815SV" => "160GB 7.2K SATA/300 Seagate SV35.2 8MB",
	"ST3250820SV" => "250GB 7.2K SATA/300 Seagate SV35.2 8MB",
	"ST3320620SV" => "320GB 7.2K SATA/300 Seagate SV35.2 16MB",
	"ST3500630SV" => "500GB 7.2K SATA/300 Seagate SV35.2 16MB",
	"ST3750640SV" => "750GB 7.2K SATA/300 Seagate SV35.2 16MB",

	"ST3250310SV" => "250GB 7.2K SATA/300 Seagate SV35.3 8MB",
	"ST3500320SV" => "500GB 7.2K SATA/300 Seagate SV35.3 32MB",
	"ST3750330SV" => "750GB 7.2K SATA/300 Seagate SV35.3 32MB",
	"ST31000340SV" => "1TB 7.2K SATA/300 Seagate SV35.3 32MB",

	"ST3250310NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST3500320NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST3750330NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST31000340NS" => "1TB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",

	"ST3500620SS" => "500GB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",
	"ST3750630SS" => "750GB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",
	"ST31000640SS" => "1TB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",

	# http://www.seagate.com/support/disc/manuals/ata/100367120a.pdf
	"ST93811A" => "30GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST94813A" => "40GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST96812A" => "60GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST98823A" => "80GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST9100824A" => "100GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST9120821A" => "120GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",

	"ST9120817AS" => "120GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9160827AS" => "160GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9200827AS" => "200GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9250827AS" => "250GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",

	"ST980310AS" => "80GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9120310AS" => "120GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9160310AS" => "160GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9320320AS" => "320GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",

	"ST96023AS" => "60GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",
	"ST98025AS" => "80GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",
	"ST910021AS" => "100GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",

	"ST980411AS" => "80GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9120411AS" => "120GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9160411AS" => "160GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9250421AS" => "250GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9320421AS" => "320GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",

	"ST936701SS" => "37GB 10K SAS/3 2.5\" Seagate Savvio 8MB",
	"ST973401SS" => "73GB 10K SAS/3 2.5\" Seagate Savvio 8MB",

	"ST936701LC" => "37GB 10K U320 2.5\" Seagate Savvio 8MB",
	"ST973401LC" => "73GB 10K U320 2.5\" Seagate Savvio 8MB",

	"ST936751SS" => "37GB 15K SAS/3 2.5\" Seagate Savvio 15K 16MB",
	"ST973451SS" => "73GB 15K SAS/3 2.5\" Seagate Savvio 15K 16MB",

	"ST973402SS" => "73GB 10K SAS/3 2.5\" Seagate Savvio 10K.2 16MB",
	"ST9146802SS" => "146GB 10K SAS/3 2.5\" Seagate Savvio 10K.2 16MB",

	"MAG3091" => "9GB 10K Ultra2 Fujitsu MAG",
	"MAG3182" => "18GB 10K Ultra2 Fujitsu MAG",

	"MAH3091" => "9GB 7.2K U160 Fujitsu MAH",
	"MAH3182" => "18GB 7.2K U160 Fujitsu MAH",

	"MAJ3091" => "9GB 10K U160 Fujitsu MAJ",
	"MAJ3182" => "18GB 10K U160 Fujitsu MAJ",
	"MAJ3364" => "36GB 10K U160 Fujitsu MAJ",

	"MAN3184" => "18GB 10K U160 Fujitsu MAN",
	"MAN3367" => "36GB 10K U160 Fujitsu MAN",
	"MAN3735" => "73GB 10K U160 Fujitsu MAN",

	"MAM3184" => "18GB 15K U160 Fujitsu MAM",
	"MAM3367" => "36GB 15K U160 Fujitsu MAM",

	"MAS3184" => "18GB 15K U320 Fujitsu MAS",
	"MAS3367" => "36GB 15K U320 Fujitsu MAS",
	"MAS3735" => "73GB 15K U320 Fujitsu MAS",

	"MAP3367" => "36GB 10K U320 Fujitsu MAP",
	"MAP3735" => "73GB 10K U320 Fujitsu MAP",
	"MAP3147" => "147GB 10K U320 Fujitsu MAP",

	"MAT3073" => "73GB 10K U320 Fujitsu MAT",
	"MAT3147" => "147GB 10K U320 Fujitsu MAT",
	"MAT3300" => "300GB 10K U320 Fujitsu MAT",

	"MAW3073" => "73GB 10K U320 Fujitsu MAW",
	"MAW3147" => "147GB 10K U320 Fujitsu MAW",
	"MAW3300" => "300GB 10K U320 Fujitsu MAW",

	"MAU3036" => "36GB 15K U320 Fujitsu MAU",
	"MAU3073" => "73GB 15K U320 Fujitsu MAU",
	"MAU3147" => "147GB 15K U320 Fujitsu MAU",

	"MAX3036" => "36GB 15K U320 Fujitsu MAX",
	"MAX3073" => "73GB 15K U320 Fujitsu MAX",
	"MAX3147" => "147GB 15K U320 Fujitsu MAX",

	"MAX3036RC" => "36GB 15K SAS/3 Fujitsu MAX",
	"MAX3073RC" => "73GB 15K SAS/3 Fujitsu MAX",
	"MAX3147RC" => "147GB 15K SAS/3 Fujitsu MAX",

	"MBA3073NC" => "73GB 15K U320 Fujitsu MBA 16MB",
	"MBA3147NC" => "147GB 15K U320 Fujitsu MBA 16MB",
	"MBA3300NC" => "300GB 15K U320 Fujitsu MBA 16MB",

	"MBA3073RC" => "73GB 15K SAS/3 Fujitsu MBA 16MB",
	"MBA3147RC" => "147GB 15K SAS/3 Fujitsu MBA 16MB",
	"MBA3300RC" => "300GB 15K SAS/3 Fujitsu MBA 16MB",

	"MAV2036" => "36GB 10K SAS/3 2.5\" Fujitsu MAV",
	"MAV2073" => "73GB 10K SAS/3 2.5\" Fujitsu MAV",

	"MAY2036" => "36GB 10K SAS/3 2.5\" Fujitsu MAY",
	"MAY2073" => "73GB 10K SAS/3 2.5\" Fujitsu MAY",

	"MBB2073RC" => "73GB 10K SAS/3 2.5\" Fujitsu MBB 16MB",
	"MBB2147RC" => "147GB 10K SAS/3 2.5\" Fujitsu MBB 16MB",

	"MBC2036RC" => "36GB 15K SAS/3 2.5\" Fujitsu MBC 16MB",
	"MBC2073RC" => "73GB 15K SAS/3 2.5\" Fujitsu MBC 16MB",

	# HP/Compaq

	"BB01813467" => "18GB 7.2K Ultra2 Compaq",

	"BB01823469" => "18GB 7.2K Ultra2 Compaq",

	"BD0096349A" => "9GB 10K U160 Compaq",
	"BD0186349B" => "18GB 10K U160 Compaq",
	"BD0366349C" => "36GB 10K U160 Compaq",

	"BD009635C3" => "9GB 10K U160 Compaq/Fujitsu",
	"BD018635C4" => "18GB 10K U160 Compaq/Fujitsu",
	"BD036635C5" => "36GB 10K U160 Compaq/Fujitsu",

	"BD03663622" => "36GB 10K U160 Compaq",

	"BD01864544" => "18GB 10K U160 Compaq",
	"BD03664545" => "36GB 10K U160 Compaq",
	"BD07264546" => "72GB 10K U160 Compaq",

	"BD01864552" => "18GB 10K U320 Compaq",
	"BD03664553" => "36GB 10K U320 Compaq",

	"BD0186459A" => "18GB 10K U160 Compaq/Fujitsu MAN",
	"BD0366459B" => "36GB 10K U160 Compaq/Fujitsu MAN",
	"BD0726459C" => "72GB 10K U160 Compaq/Fujitsu MAN",

	"BD0366536B" => "36GB 10K U160 Compaq/Seagate 73LP",		# Seagate Cheetah 5
	"BD0726536C" => "72GB 10K U160 Compaq/Seagate 73LP",

	"BD036659CC" => "36GB 10K U160 Compaq",

	"BD07265A22" => "72GB 10K U160 Compaq",

	"BD01865CC4" => "18GB 10K U160 Compaq/Fujitsu MAP",		# MAP3367

	"BD01872377" => "18GB 10K U160 Compaq",

	"BD009734A3" => "9GB 10K U160 Compaq",
	"BD018734A4" => "18GB 10K U160 Compaq",

	"BD009735C6" => "9GB 10K U160 Compaq/Fujitsu",
	"BD018735C7" => "18GB 10K U160 Compaq/Fujitsu",
	"BD036735C8" => "36GB 10K U160 Compaq/Fujitsu",

	"BD01875CC7" => "18GB 10K U160 Compaq/Fujitsu MAP",		# related to BD01865CC4

	"BD03685A24" => "36GB 10K U320 Compaq/Seagate 10K.6",
	"BD07285A25" => "72GB 10K U320 Compaq/Seagate 10K.6",
	"BD14685A26" => "146GB 10K U320 Compaq/Seagate 10K.6",

	"BD03686223" => "36GB 10K U320 Compaq/Fujitsu MAP",
	"BD07286224" => "72GB 10K U320 Compaq/Fujitsu MAP",
	"BD14686225" => "146GB 10K U320 Compaq/Fujitsu MAP",

	"BD036863AC" => "36GB 10K U320 Compaq/Atlas 10K IV",
	"BD072863B2" => "72GB 10K U320 Compaq/Atlas 10K IV",
	"BD146863B3" => "146GB 10K U320 Compaq/Atlas 10K IV",

	"BD07287B4C" => "72GB 10K U320 Compaq/Atlas 10K V",
	"BD14687B52" => "146GB 10K U320 Compaq/Atlas 10K V",
	"BD30087B53" => "300GB 10K U320 Compaq/Atlas 10K V",

	"BD03688272" => "36GB 10K U320 Compaq/Seagate 10K.7",		# ST373207 - looks like a 72G drive!!
	"BD07288277" => "72GB 10K U320 Compaq/Seagate 10K.7",		# ST373207
	"BD14688278" => "146GB 10K U320 Compaq/Seagate 10K.7",		# ST3146707
	"BD30088279" => "300GB 10K U320 Compaq/Seagate 10K.7",		# ST3300007

	"BD300884C2" => "300GB 10K U320 Compaq",			# related to BD3008A527

	"BD0728856A" => "72GB 10K U320 Compaq/Fujitsu MAT",		# MAT3073
	"BD1468856B" => "146GB 10K U320 Compaq/Fujitsu MAT",		# MAT3147
	"BD3008856C" => "300GB 10K U320 Compaq/Fujitsu MAT",		# MAT3300

	"BD07289BB8" => "72GB 10K U320 Compaq/Fujitsu MAW",		# MAW3073
	"BD14689BB9" => "146GB 10K U320 Compaq/Fujitsu MAW",		# MAW3147
	"BD30089BBA" => "300GB 10K U320 Compaq/Fujitsu MAW",		# MAW3300

	"BD0728A4B4" => "72GB 10K U320 Compaq",				# related to BD0729A4B7
	"BD1468A4B5" => "146GB 10K U320 Compaq",
	"BD3008A4B6" => "300GB 10K U320 Compaq",

	"BD0728A4C4" => "72GB 10K U320 Compaq",				# related to BD0729A4C7
	"BD1468A4C5" => "146GB 10K U320 Compaq",
	"BD3008A4C6" => "300GB 10K U320 Compaq",

	"BD03695A27" => "36GB 10K U320 Compaq/Seagate 10K.6",		# related to BD03685A24

	"BD03695CC8" => "36GB 10K U320 Compaq/Fujitsu MAP",		# related to BD03686223
	"BD07296B44" => "72GB 10K U320 Compaq/Fujitsu MAP",		# related to BD07286224

	"BD03697633" => "36GB 10K U320 Compaq/Atlas 10K IV",		# related to BD036863AC

	"BD07297B57" => "72GB 10K U320 Compaq/Atlas 10K V",		# http://www.ad-min.com/productDetail.aspx?A_ID=2222
	"BD14697B58" => "146GB 10K U320 Compaq/Atlas 10K V",

	"BD03698276" => "36GB 10K U320 Compaq/Seagate 10K.7",		# related to BD03688272, real 72G drive
	"BD0729827A" => "72GB 10K U320 Compaq/Seagate 10K.7",
	"BD1469827B" => "146GB 10K U320 Compaq/Seagate 10K.7",
	"BD3009827C" => "300GB 10K U320 Compaq/Seagate 10K.7",

	"BD07298572" => "72GB 10K U320 Compaq/Fujitsu MAT",		# related to BD0728856A
	"BD14698573" => "146GB 10K U320 Compaq/Fujitsu MAT",
	"BD30098574" => "300GB 10K U320 Compaq/Fujitsu MAT",

	"BD0729A4C7" => "72GB 10K U320 Compaq",				# related to BD0728A4C4
	"BD1469A4C8" => "146GB 10K U320 Compaq",
	"BD3009A4C9" => "300GB 10K U320 Compaq",

	"BD07299BBB" => "72GB 10K U320 Compaq",

	# http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=PSD_CN0304W

	"BF01864663" => "18GB 15K U160 Compaq",			# WU3 == U160
	"BF03664664" => "36GB 15K U160 Compaq",			# WU3 == U160

	"BF01865222" => "18GB 15K U160 Compaq",
	"BF03665223" => "36GB 15K U160 Compaq",

	"BF03665A32" => "36GB 15K U160 Compaq",

	"BF07284961" => "72GB 15K U320 Compaq",

	"BF14684970" => "146GB 15K U320 Compaq",

	"BF01885A34" => "18GB 15K U320 Compaq/Seagate 15K.3",
	"BF03685A35" => "36GB 15K U320 Compaq/Seagate 15K.3",
	"BF07285A36" => "72GB 15K U320 Compaq/Seagate 15K.3",	# Seagate ST373453

	"BF018863B4" => "18GB 15K U320 Compaq",
	"BF036863B5" => "36GB 15K U320 Compaq",
	"BF072863B6" => "72GB 15K U320 Compaq",

	"BF018863B8" => "18GB 15K U320 Compaq",
	"BF036863B9" => "36GB 15K U320 Compaq",
	"BF072863BA" => "72GB 15K U320 Compaq",

	"BF0368683B" => "36GB 15K U160 Compaq",
	"BF0728683C" => "72GB 15K U160 Compaq",

	"BF03687B54" => "36GB 15K U320 Compaq",			# related to BF03697B5A
	"BF07287B55" => "72GB 15K U320 Compaq",
	"BF14687B56" => "146GB 15K U320 Compaq",

	"BF03688575" => "36GB 15K U320 Compaq/Fujitsu MAU",	# MAU3036
	"BF07288576" => "72GB 15K U320 Compaq/Fujitsu MAU",	# MAU3073
	"BF14688577" => "146GB 15K U320 Compaq/Fujitsu MAU",	# MAU3147

	"BF00988282" => "9GB 15K U320 Compaq",
	"BF01888283" => "18GB 15K U320 Compaq",

	"BF03688284" => "36GB 15K U320 Compaq/Seagate 15K.4",
	"BF07288285" => "72GB 15K U320 Compaq/Seagate 15K.4",	# ST373454
	"BF14688286" => "146GB 15K U320 Compaq/Seagate 15K.4",

	"BF03689BC3" => "36GB 15K U320 Compaq/Fujitsu MAX",
	"BF07289BC4" => "72GB 15K U320 Compaq/Fujitsu MAX",	# MAX3073
	"BF14689BC5" => "146GB 15K U320 Compaq/Fujitsu MAX",

	"BF0728A4B2" => "72GB 15K U320 Compaq",
	"BF1468A4B3" => "146GB 15K U320 Compaq",

	"BF0368A4B9" => "36GB 15K U320 Compaq",			# related to BF0369A4BC
	"BF0728A4BA" => "72GB 15K U320 Compaq",			# related to BF0729A4C2
	"BF1468A4BB" => "146GB 15K U320 Compaq",		# related to BF1469A4C3

	"BF0368A4CA" => "36GB 15K U320 Compaq/Seagate 15K.4",	#
	"BF0728A4CB" => "72GB 15K U320 Compaq/Seagate 15K.4",	# ST373454
	"BF1468A4CC" => "146GB 15K U320 Compaq/Seagate 15K.4",	#

	"BF0728AFEA" => "72GB 15K U320 Compaq",
	"BF1468AFEB" => "72GB 15K U320 Compaq",			# e.g. pud102.udb.hk2, related to BF14689BC5?

	"BF0368B269" => "36GB 15K U320 Compaq",
	"BF0728B26A" => "72GB 15K U320 Compaq",
	"BF1468B26B" => "146GB 15K U320 Compaq",

	"BF03697B5A" => "36GB 15K U320 Compaq",			# related to BF03687B54
	"BF07297B5B" => "72GB 15K U320 Compaq",
	"BF14697B5C" => "146GB 15K U320 Compaq",

	"BF03698287" => "36GB 15K U320 Compaq",
	"BF07298288" => "72GB 15K U320 Compaq",
	"BF14698289" => "146GB 15K U320 Compaq",

	"BF03698782" => "36GB 15K U320 Compaq/Fujitsu MAS",		# MAS3367

	#
	# http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00305257&jumpid=reg_R1002_USEN
	#

	# search for HP part number at - http://www.lamicrogroup.com/

	"DD400B8541" => "400GB 10K SAS HP/Seagate Cheetah NS",		# ST3400755SS

	"DF146A4941" => "146GB 15K SAS HP",

	"DF036A9843" => "36GB 15K SAS HP/Fujitsu MAX",
	"DF072A9844" => "72GB 15K SAS HP/Fujitsu MAX",			# MAX3073RC
	"DF146A9845" => "146GB 15K SAS HP/Fujitsu MAX",

	"DF036ABAA7" => "36GB 15K SAS HP/Seagate 15K.5",
	"DF072ABAA8" => "72GB 15K SAS HP/Seagate 15K.5",
	"DF146ABAA9" => "146GB 15K SAS HP/Seagate 15K.5",
	"DF300ABAAA" => "300GB 15K SAS HP/Seagate 15K.5",		# ST3300655SS

	"DF072BABUD" => "72GB 15K SAS HP/Hitachi Ultrastar 15K300",	# HUS153073VLS300
	"DF146BABUE" => "146GB 15K SAS HP/Hitachi Ultrastar 15K300",
	"DF300BABUF" => "300GB 15K SAS HP/Hitachi Ultrastar 15K300",

	"DF072BAFDT" => "72GB 15K SAS HP",
	"DF146BAFDU" => "146GB 15K SAS HP",
	"DF300BAFDV" => "300GB 15K SAS HP",

	"DF072BB6BC" => "72GB 15K SAS HP/Seagate 15K.5",
	"DF146BB6C2" => "146GB 15K SAS HP/Seagate 15K.5",		# ST3146855SS
	"DF300BB6C3" => "300GB 15K SAS HP/Seagate 15K.5",

	"DF0146B8052" => "146GB 15K SAS HP/Seagate 15K.6",
	"DF0300B8053" => "300GB 15K SAS HP/Seagate 15K.6",		# ST3300656SS
	"DF0450B8054" => "450GB 15K SAS HP/Seagate 15K.6",		# ST3450856SS

	"DG072A4951" => "73GB 10K SAS 2.5\" HP/Hitachi Ultrastar C10K147",	# HUC101473CSS300
	"DG146A4960" => "147GB 10K SAS 2.5\" HP/Hitachi Ultrastar C10K147",

	"DG072A3515" => "72GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST973402SS
	"DG146A3516" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",

	"DG036A8B53" => "36GB 10K SAS 2.5\" HP/Seagate Savvio 8MB",
	"DG072A8B54" => "72GB 10K SAS 2.5\" HP/Seagate Savvio 8MB",	# ST973401SS

	"DG036A8B5B" => "36GB 10K SAS 2.5\" HP/Fujitsu MAV",		# MAV2036RC
	"DG072A8B5C" => "72GB 10K SAS 2.5\" HP/Fujitsu MAV",

	"DG036A9BB6" => "36GB 10K SAS 2.5\" HP/Fujitsu MAY",
	"DG072A9BB7" => "72GB 10K SAS 2.5\" HP/Fujitsu MAY",		# MAY2073RC

	"DG072ABAB3" => "73GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST973402SS
	"DG146ABAB4" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",

	"DG072BB975" => "73GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",
	"DG146BB976" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST9146802SS

	"DH036ABAA5" => "36GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",
	"DH072ABAA6" => "72GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",	# ST973451SS

	"DH036BB977" => "36GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",
	"DH072BB978" => "72GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",	# ST973451SS

	# SATA

	# FB drives are SATA/150, but these are definitely seagate 7200.10 drives SATA/300
	"FB080C4080" => "80GB 7.2K SATA/150 HP/Seagate 7200.10",	# ST380815AS
	"FB160C4081" => "160GB 7.2K SATA/150 HP/Seagate 7200.10",	# ST3160815AS

	"FJ060C4980" => "60GB 5.4K SATA/150 HP",
	"FJ120C4981" => "120GB 5.4K SATA/150 HP",

	"GB0500C4413" => "500GB 7.2K SATA/300 HP/Seagate Barracude ES 16MB",
	"GB0750C4414" => "750GB 7.2K SATA/300 HP/Seagate Barracuda ES 16MB",	# ST3750640NS

	# looks like GB can be mix of 1.5/3.0 sata
	"GB0250C8045" => "250B 7.2K SATA/150 HP",
	"GB0500C8046" => "500B 7.2K SATA/150 HP",
	"GB0750C8047" => "750B 7.2K SATA/150 HP",

	"GB0160CAABV" => "160GB 7.2K SATA/300 HP",

	"GB0160EAFJE" => "160GB 7.2K SATA/300 HP",
	"GB0250EAFJF" => "250GB 7.2K SATA/300 HP",
	"GB0500EAFJH" => "500GB 7.2K SATA/300 HP",
	"GB0750EAFJK" => "750GB 7.2K SATA/300 HP",
	"GB1000EAFJL" => "1TB 7.2K SATA/300 HP",

	# http://www.maxtor.com/portal/site/Maxtor/menuitem.2d55600ff3a2577ee77bd88591346068/?channelpath=/en_us/Support/Product%20Support/Enterprise%20Storage

	"ATLAS-IV-9" => "9GB 7.2K U160 Quantum Atlas IV",
	"ATLAS-IV-18" => "18GB 7.2K U160 Quantum Atlas IV",
	"ATLAS-IV-36" => "36GB 7.2K U160 Quantum Atlas IV",

	"ATLAS-V-9" => "9GB 7.2K U160 Quantum Atlas V",
	"ATLAS-V-18" => "18GB 7.2K U160 Quantum Atlas V",
	"ATLAS-V-36" => "36GB 7.2K U160 Quantum Atlas V",

	"ATLASU320-18" => "18GB 10K U320 Maxtor Atlas ??",
	"ATLASU320-36" => "36GB 10K U320 Maxtor Atlas ??",

	"ATLAS10K2-TY092" => "9GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY184" => "18GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY367" => "36GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY734" => "73GB 10K U160 Maxtor Atlas 10K II",

	"ATLAS10K3-18" => "18GB 10K U320 Maxtor Atlas 10K III",
	"ATLAS10K3-36" => "36GB 10K U320 Maxtor Atlas 10K III",
	"ATLAS10K3-73" => "73GB 10K U320 Maxtor Atlas 10K III",

	"ATLAS10K4-36" => "36GB 10K U320 Maxtor Atlas 10K IV",
	"ATLAS10K4-73" => "73GB 10K U320 Maxtor Atlas 10K IV",

	"ATLAS10K5-73" => "73GB 10K U320 Maxtor Atlas 10K V",
	"ATLAS10K5-146" => "146GB 10K U320 Maxtor Atlas 10K V",
	"ATLAS10K5-300" => "300GB 10K U320 Maxtor Atlas 10K V",

	# 16MB cache
	"ATLAS10K5-073SAS" => "73GB 10K SAS Maxtor Atlas 10K V",
	"ATLAS10K5-147SAS" => "147GB 10K SAS Maxtor Atlas 10K V",
	"ATLAS10K5-300SAS" => "300GB 10K SAS Maxtor Atlas 10K V",

	# http://www.seagate.com/staticfiles/maxtor/en_us/documentation/manuals/atlas_15k_product_manual.pdf
	"ATLAS15K-18" => "18GB 15K U320 Maxtor Atlas 15K",
	"ATLAS15K-36" => "36GB 15K U320 Maxtor Atlas 15K",
	"ATLAS15K-73" => "73GB 15K U320 Maxtor Atlas 15K",

	"ATLAS15K2-36" => "36GB 15K U320 Maxtor Atlas 15K II",
	"ATLAS15K2-73" => "73GB 15K U320 Maxtor Atlas 15K II",
	"ATLAS15K2-146" => "146GB 15K U320 Maxtor Atlas 15K II",

	# http://www.maxtor.com/_files/maxtor/en_us/documentation/data_sheets/product_line_card.pdf

	"QUANTUM-FIREBALLP-AS10" => "10GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS20" => "20GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS30" => "30GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS40" => "40GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS60" => "60GB 7.2K ATA/100 Fireball Plus AS",

	"2F020J0" => "20GB 5.4K ATA/133 Quantum Fireball 3",
	"2F020L0" => "20GB 5.4K ATA/133 Quantum Fireball 3",
	"2F030J0" => "30GB 5.4K ATA/133 Quantum Fireball 3",
	"2F030L0" => "30GB 5.4K ATA/133 Quantum Fireball 3",
	"2F040J0" => "40GB 5.4K ATA/133 Quantum Fireball 3",
	"2F040L0" => "40GB 5.4K ATA/133 Quantum Fireball 3",

	"2B010H1" => "10GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",
	"2B015H1" => "15GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",
	"2B020H1" => "20GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",

	"93073H4" => "30GB 7.2K ATA/100 Maxtor DiamondMax 60",
	"98196H8" => "80GB 5.4K ATA/100 Maxtor DiamondMax 80",

	"51024U2" => "10GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"51369U3" => "13GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"51536U3" => "15GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"52049U4" => "20GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"52732U6" => "27GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"53073U6" => "30GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"54098U8" => "40GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",

	"51024H2" => "10GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"51369H3" => "13GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"51536H3" => "15GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"52049H4" => "20GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"52732H6" => "27GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"53073H6" => "30GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"54098H8" => "40GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",

	"5T020H2" => "20GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",
	"5T030H3" => "30GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",
	"5T040H4" => "40GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",

	"6E020L0" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6E030L0" => "30GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6E040L0" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6K040L0" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",	# RoHS

	"6N040T0" => "40GB 7.2K SATA/150 Maxtor DiamondMax 8S 2MB",
	"6E040T0" => "40GB 7.2K SATA/150 Maxtor DiamondMax 8S 2MB",

	"6Y060L0" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y080L0" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y120L0" => "120GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y160L0" => "160GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",

	"6Y060P0" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y250P0" => "250GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",

	"6Y060M0" => "60GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y250M0" => "250GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",

	# http://www.seagate.com/staticfiles/maxtor/en_us/documentation/data_sheets/diamondmax_10_data_sheet.pdf
	"6L080L0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 2MB",
	"6B080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",

	"6B200R0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L200R0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6B250R0" => "250GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L250R0" => "250GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6B300R0" => "300GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L300R0" => "300GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",

	"6B080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",

	"6B200S0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L200S0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6B250S0" => "250GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L250S0" => "250GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6B300S0" => "300GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L300S0" => "300GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",

	"6V080E0" => "80GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",
	"6V160E0" => "160GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",
	"6V200E0" => "200GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",

	"6V250F0" => "250GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",
	"6V300F0" => "300GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",
	"6V320F0" => "320GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",

	"6H400R0" => "400GB 7.2K ATA/133 Maxtor DiamondMax 11 16MB",
	"6H500R0" => "500GB 7.2K ATA/133 Maxtor DiamondMax 11 16MB",

	"6H400F0" => "400GB 7.2K SATA/300 Maxtor DiamondMax 11 16MB",
	"6H500F0" => "500GB 7.2K SATA/300 Maxtor DiamondMax 11 16MB",

	"STM3802110A" => "80GB 7.2K ATA/100 Maxtor DiamondMax 20 2MB",

	"STM340211AS" => "40GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",
	"STM380211AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",
	"STM380811AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 8MB",
	"STM380811" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 8MB",
	"STM3160211AS" => "160GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",

	"STM3250820AS" => "250GB 7.2K SATA/300 Maxtor DiamondMax 21 8MB",
	"STM3320820AS" => "250GB 7.2K SATA/300 Maxtor DiamondMax 21 8MB",

	"STM340215AS" => "40GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",
	"STM380215AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",
	"STM3160215AS" => "160GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",

	"7H500R0" => "500GB 7.2K ATA/133 Maxtor MaXLine Pro 500 16MB",
	"7H500F0" => "500GB 7.2K SATA/300 Maxtor MaXLine Pro 500 16MB",

	"4W100H6" => "100GB 5.4K ATA/100 Maxtor DiamondMax 536DX",

	"4D020H1" => "20GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D040H2" => "40GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D060H3" => "60GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D080H4" => "80GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K020H1" => "20GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K040H2" => "40GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K060H3" => "60GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K080H4" => "80GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4G120J6" => "120GB 5.4K ATA/133 Maxtor DiamondMax D540X",
	"4G160J8" => "160GB 5.4K ATA/133 Maxtor DiamondMax D540X",

	"4R060L0" => "60GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R060J0" => "60GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R080L0" => "80GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R080J0" => "80GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R120L0" => "120GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R160L0" => "160GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A160J0" => "160GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A250J0" => "250GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A300J0" => "300GB 5.4K ATA/133 Maxtor DiamondMax 16",

	"6G080E0" => "80GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G160E0" => "160GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G250E0" => "250GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G320E0" => "320GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",

	"6L020J1" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L040J2" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L060J3" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L080J4" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",

	"6L020L1" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L040L2" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L060L3" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L080L4" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",

	"5A250J0" => "250GB 5.4K ATA/133 Maxtor MaXLine II 2MB",
	"5A300J0" => "300GB 5.4K ATA/133 Maxtor MaXLine II 2MB",
	"5A320J0" => "320GB 5.4K ATA/133 Maxtor MaXLine II 2MB",

	"7Y250P0" => "250GB 7.2K ATA/133 Maxtor MaXLine Plus II 8MB",
	"7Y250M0" => "250GB 7.2K SATA/150 Maxtor MaXLine Plus II 8MB",

	"7L250R0" => "250GB 7.2K ATA/133 Maxtor MaXLine III 16MB",
	"7L300R0" => "300GB 7.2K ATA/133 Maxtor MaXLine III 16MB",

	"7L250S0" => "250GB 7.2K SATA/150 Maxtor MaXLine III 16MB",
	"7L300S0" => "300GB 7.2K SATA/150 Maxtor MaXLine III 16MB",

	"7V250F0" => "250GB 7.2K SATA/300 Maxtor MaXLine III 16MB",
	"7V300F0" => "300GB 7.2K SATA/300 Maxtor MaXLine III 16MB",

	# SSD

	"MTRON-MSP-SATA3025-032" => "32GB SSD SATA/150 2.5\" Mtron SSD MOBI 3000",
	"MTRON-MSP-SATA3035-032" => "32GB SSD SATA/150 Mtron SSD MOBI 3000",

	"MTRON-MSP-SATA7025-032" => "32GB SSD SATA/150 2.5\" Mtron SSD Pro 7000",
	"MTRON-MSP-SATA7035-032" => "32GB SSD SATA/150 Mtron SSD Pro 7000",

	"MTRON-MSP-SATA7525-032" => "32GB SSD SATA/300 2.5\" Mtron SSD Pro 7500",
	"MTRON-MSP-SATA7535-032" => "32GB SSD SATA/300 Mtron SSD Pro 7500",

	"SanDisk-SSD-SATA-5000-2.5" => "32GB SSD SATA/150 2.5\" SanDisk SSD 5000",
);

my(@system_models) = (
	{
		"model" => "AOpen i965GMt-LA",
		"ids" => [
			"0x063ea0a0,0x816810ec,0x02",		# Network
			"0x063ea0a0,0x28288086,0x03",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-mPGA478 - Socket P",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"memory-form" => "200pin SO-DIMM DDR2",
		"max-memory" => "4G",
	},
	{
		"model" => "MSI GM965",
		"ugly-model" => "MSI MS-9803",
		"ids" => [
			"0x80301462,0x109a8086,0x00",		# Network
			"0x80301462,0x282[89]8086,0x03",	# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-mPGA478 - Socket P",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"memory-form" => "240pin DDR2",
		"max-memory" => "4G",
	},
	{
		"model" => "Intel X38ML",
		"ids" => [
			"0x34ce8086,0x10a78086,0x02",		# Network
			"0x34d08086,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Intel D945GCLF",
		"ids" => [
			"0x00018086,0x813610ec,0x02",		# Network
			"0x464c8086,0x27c08086,0x01",		# SATA
		],
		"sockets" => 1,
		"dimms" => 1,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "Quanta SU2-3000",
		"ids" => [
			"0x00000000,0x02111166,0x00",		# ATA
			"0x8917152d,0x12298086,0x08",		# Network
			"0x8917152d,0x008f9005,0x02",		# SCSI
		],
		"sockets" => 2,
	},
	{
		"model" => "Gateway SU2-4200",
		"ids" => [
			"0x8920107b,0x02131166,0xa0",			# ATA
			"0x(892d152d|01341028),0x100e8086,0x02",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Quanta SU2-4200",
		"ids" => [
			"0x02201166,0x02131166,0xa0",		# ATA
			"0x892d152d,0x100e8086,0x02",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Asus P5Q-Deluxe",
		"ids" => [
			"0x811a1043,0x432011ab,0x14",		# Network
			"0x81f81043,0x436411ab,0x12",		# Network
			"0x82d41043,0x3a228086,0x00",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1200 non-ECC Unbuffered",
		"max-memory" => "16G",
	},
	{
		"model" => "Asus P5BV-C",
		"ids" => [
			"0x819e1043,0x27df8086,0x01",		# ATA
			"0x81901043,0x27c[013]8086,0x01",	# SATA
			"0x826e1043,0x436411ab,0x12",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte 945GCM-S2C",
		"ids" => [
			"0xe0001458,0x813610ec,0x01",		# Network
			"0xb0021458,0x27c08086,0x01",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Gigabyte G33M-S2L",			# needs to come before Gigabyte P35-S3G
		"ids" => [
			"0xd0001458,0x29c28086,0x02",		# VGA
			"0xb0051458,0x29238086,0x02",		# SATA
			"0xe0001458,0x816810ec,0x01",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1066 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte P35-S3G",
		"ids" => [
			"0xb0051458,0x29238086,0x02",		# SATA
			"0xe0001458,0x816810ec,0x01",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1066 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte EP45-DS3R",
		"ids" => [
			"(0xb0051458,0x3a228086,0x00|0xb0021458,0x3a208086,0x00)",	# SATA
			"0xe0001458,0x816810ec,0x02",					# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1200 non-ECC Unbuffered",
		"max-memory" => "16G",
	},
	{
		"model" => "Gigabyte GA-7A8DRH",
		"ids" => [
			"0xb0101458,0x80[19]d9005,0x10",	# SCSI
			"0x30001458,0x10268086,0x04",		# Network
			"0x10761458,0x10768086,0x00",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "MSI 8480000",
		"ids" => [
			"0x24501462,0x164814e4,0x03",		# Network
			"0x7469(1462|1022),0x74691022,0x03",	# ATA
		],
		"dimms" => 8,
		"memory-type" => "DDR-333 ECC Registered",
		"max-memory" => "16G",
		"sockets" => 2,
		"socket-type" => "AMD-Socket 940",
	},
	{
		"model" => "Tyan S5397",
		"ids" => [
			"0x10001000,0x00581000,0x04",		# SAS
			"0x00008086,0x10968086,0x01",		# Network
			"0x539710f1,0x268[012]08086,0x09",	# SATA
		],
		"dimms" => 16,
		"memory-type" => "DDR2-800 ECC Fully Buffered",
		"max-memory" => "128G",
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		'url' => 'http://www.tyan.com/product_board_detail.aspx?pid=560',
	},
	{
		"model" => "Tyan S5376",
		"ids" => [
			"0x521110f1,0x108b8086,0x03",		# Network
			"0x537610f1,0x292[02]8086,0x02",	# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Intel SRSH4",
		"ids" => [
			"0x34128086,0x12298086,0x0d",		# Network
			"0x34128086,0x100d8086,0x02",		# Network
			"0x34128086,0x801f9005,0x03",		# SCSI
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S4881",
		"ids" => [
			"0x289110f1,0x005310de,0xf2",		# ATA
			"0x164814e4,0x164814e4,0x03",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S4882",
		"ids" => [
			"0x(2b80|36c0)1022,0x74691022,0x03",	# ATA
			"0x10001000,0x00301000,0x07",		# MPT
			"0x164[48]14e4,0x164814e4,0x03",	# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S2880",
		"ids" => [
			"0x6619105a,0x3373105a,0x02",		# SATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2882",			# Must come before Tyan S2881
		"ids" => [
			"0x74691022,0x74691022,0x03",		# ATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
			"0x10408086,0x12298086,0x10",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2881",
		"ids" => [
			"0x74691022,0x74691022,0x03",		# ATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2865",
		"ids" => [
			"0x286510f1,0x005710de,0xa3",
		],
		"sockets" => 1,
	},
	{
		"model" => "Tyan S2722",
		"ids" => [
			"0x10408086,0x12298086,0x10",		# Network
			"0x10018086,0x100f8086,0x01",		# Network
			"0x00000000,0x248b8086,0x02",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2510",
		"ids" => [
			"0x000c8086,0x12298086,0x08",		# Network
			"0x00000000,0x02111166,0x00",		# ATA
			"0x10001000,0x00201000,0x01",		# SCSI
		],
		"sockets" => 2,
		"socket-type" => "Intel-PGA370",
		"dimms" => 4,
		"memory-type" => "PC133 SDRAM ECC Registered",
		"max-memory" => "4G",
	},
	{
		"model" => "Dell GX620",
		"ids" => [
			"0x01ad1028,0x27df8086,0x01",		# ATA
			"0x01ad1028,0x27c[013]8086,0x01",	# SATA
			"0x01ad1028,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX280",
		"ids" => [
			"0x01791028,0x266f8086,0x03",		# ATA
			"0x01791028,0x26518086,0x03",		# SATA
			"0x01791028,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell Precision 360",
		"ids" => [
			"0x01561028,0x24d18086,0x02",		# SATA
			"0x01561028,0x24db8086,0x02",		# ATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR-400 ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Dell SX260",
		"ids" => [
			"0x01381028,0x24cb8086,0x01",		# SATA
			"0x01381028,0x100e8086,0x02",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX270",
		"ids" => [
			"0x01511028,0x24d18086,0x02",		# SATA
			"0x01511028,0x24db8086,0x02",		# ATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell 530",
		"ids" => [
			"0x00d81028,0x244b8086,0x04",		# ATA
			"0x00d81028,0x920010b7,0x78",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX240",
		"ids" => [
			"0x010e1028,0x244b8086,0x12",		# ATA
			"0x00fe1028,0x920010b7,0x78",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX2255",
		"ids" => [
			"0x3024103c,0x05711106,0x80",		# ATA
			"0x3024103c,0x813910ec,0x10",		# Network
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP DX2180",
		"ids" => [
			"0x301e103c,0x26518086,0x04",		# SATA
			"0x301e103c,0x813910ec,0x10",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D290",
		"ids" => [
			"0x301f103c,0x26518086,0x04",		# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX2280",
		"ids" => [
			"0x3022103c,0x27c[013]8086,0x01",	# SATA
			"0x3022103c,0x(8167|8169)10ec,0x10",	# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX5150",
		"ids" => [
			"0x3009103c,0x43791002,0x00",		# SATA
			"0x3009103c,0x167714e4,0x20",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5800",
		"ids" => [
			"0x281e103c,0x10bd8086,0x02",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "HP DC7600",
		"ids" => [
			"0x3011103c,0x160014e4,0x01",		# Network
			"0x3011103c,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D510",
		"ids" => [
			"0x00b[89]0e11,0x24cb8086,0x01",	# ATA
			"0x00120e11,0x103b8086,0x81",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D530",				# must come before HP DC5000
		"ids" => [
			"0x12bc103c,0x24d18086,0x02",		# SATA
			"0x12bc103c,0x24db8086,0x02",		# ATA
			"0x12bc103c,0x169614e4,0x03",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5000",
		"ids" => [
			"0x12bc103c,0x24db8086,0x02",		# ATA
			"0x12bc103c,0x169614e4,0x03",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX6120",
		"ids" => [
			"0x300a103c,0x266f8086,0x03",		# ATA
			"0x300a103c,0x26518086,0x03",		# SATA
			"0x3005103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC7100",
		"ids" => [
			"0x3005103c,0x26518086,0x03",		# SATA
			"0x3005103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		# http://h18000.www1.hp.com/products/quickspecs/12145_ca/12145_ca.pdf
		"model" => "HP DC5100",
		"ids" => [
			"0x300[cd]103c,0x26518086,0x03",		# SATA
			"0x300[56]103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5700",
		"ids" => [
			"0x2808103c,0x28208086,0x02",			# SATA
			"0x2808103c,0x28258086,0x02",			# SATA
			"0x2808103c,0x167b14e4,0x02",			# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Quanta QSSC-295MB",
		"ids" => [
			"0x00008086,0x10bd8086,0x02",		# Network
			"0x8954152d,0x292[02]8086,0x02",	# SATA
			"0x8954152d,0x29268086,0x02",		# SATA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
	},
	{
		"model" => "eSlim SU4-1320",			# Quanta S47 board
		"ugly-model" => "Quanta S47",
		"ids" => [
			"0x8959152d,0x167814e4,0xa3",		# Network
			"0x8959152d,0x292[02]8086,0x02",	# SATA
		],
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
		"sockets" => 1,
	},
	{
		"model" => "Quanta S45",
		"ids" => [
			"(0x00008086,0x10bd8086,0x02|0x00008086,0x108b8086,0x03)",		# Network
			"0x8954152d,0x292[02]8086,0x02",					# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S45",
		"ids" => [
			"(0x00008086,0x10bd8086,0x02|0x00008086,0x108b8086,0x03)",		# Network
			"0x2[89]2[02]8086,0x2[89]2[02]8086,0x02",				# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29S-SAS",
		"ugly-model" => "Dell (F1CH|DCS Platform)",
		"ids" => [
			"0x80868086,0x25d48086,0xb1",		# Chipset
			"0x894b152d,0x00561000,0x02",		# SAS
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29S",
		"ugly-model" => "Dell (F1CH|DCS Platform)",
		"ids" => [
			"0x80868086,0x25d48086,0xb1",		# Chipset
			"0x8949152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29R",
		"ugly-model" => "Dell BlackfordESB2",
		"ids" => [
			"0x8949152d,0x25d48086,0xb1",		# Chipset
			"0x8949152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58R-SAS",			# must come before Dell DCS S58R
		"ugly-model" => "(Intel|Dell) BlackfordESB2",
		"ids" => [
			"0x894b152d,0x25d88086,0xb1",		# Chipset
			"0x894b152d,0x00561000,0x02",		# SAS
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58R",
		"ugly-model" => "(Intel|Dell) BlackfordESB2",
		"ids" => [
			"0x894b152d,0x25d88086,0xb1",		# Chipset
			"0x894b152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58S-SAS",				# must come before Dell DCS S58S
		"ugly-model" => "Quanta S58A?",
		"ids" => [
			"0x80868086,0x25d88086,0xb1",			# Chipset
			"0x894b152d,0x00561000,0x02",			# SAS
			"0x8949152d,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58S",
		"ugly-model" => "Quanta S58A?",
		"ids" => [
			"0x80868086,0x25d88086,0xb1",			# Chipset
			"0x(894b|8950)152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec 5220",
		"ids" => [
			"0x003a1170,0x10968086,0x01",			# Network
			"0x003a1170,0x268[012]8086,0x09",		# SATA
		],
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
		"sockets" => 2,
	},
	{
		"model" => "Inventec Seabream",
		"ids" => [
			"0x00231170,0x(268[012]|269e)8086,0x09",	# ATA/SATA
			"0x00231170,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{
		"model" => "Inventec IR1250",
		"ids" => [
			"0x00221170,0x27df8086,0x01",		# ATA
			"0x00221170,0x165914e4,0x11",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Inventec IR2100",
		"ids" => [
			"0x000c1170,0x47521002,0x27",		# VGA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2300",
		"ugly-model" => "Inventec Tuna",
		"ids" => [
			"0x00161170,0x005310de,0xa2",		# ATA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2350",
		"ids" => [
			"0x00171170,0x005310de,0xa2",		# ATA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2400",
		"ids" => [
			"0x00261170,0x037f10de,0xa3",
			"0x00261170,0x037310de,0xa3",
		],
		"sockets" => 2,		# Socket F
	},
	{
		"model" => "Supermicro X8DTN",
		"ids" => [
			"0x10a715d9,0x10a78086,0x02",			# Network
			"0x2[89]2[02]8086,0x2[89]2[02]8086,0x02",	# SATA
			"0x515e1002,0x515e1002,0x02",			# VGA
		],
		"sockets" => 2,
		"dimms" => 18,
		"memory-type" => "DDR3",
	},
	{
		"model" => "Supermicro X5DP8",
		"ids" => [
			"0x348015d9,0x248b8086,0x02",		# ATA
			"0x005e9005,0x801d9005,0x10",		# SCSI
			"0x10118086,0x10108086,0x01",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR-266 ECC Registered",
		"max-memory" => "16G",
	},
	{
		"model" => "Supermicro P4DP6",
		"ids" => [
			"0x348015d9,0x248b8086,0x02",		# ATA
			"0x900515d9,0x00cf9005,0x01",		# SCSI
			"0x10508086,0x12298086,0x0d",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro PDSBM",
		"ids" => [
			"0xb58015d9,0x27c[013]8086,0x01",	# SATA
			"0x109a15d9,0x109a8086,0x00",		# Network
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Supermicro X7SBL",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0xd88015d9,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Registered",
		"max-memory" => "8G",
		"sata-ports" => 6,
		"form-factor" => "Micro ATX",
	},
	{
		"model" => "Supermicro X7DBP",
		"ids" => [
			"0x838015d9,0x269e8086,0x09",		# ATA
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 8,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "32G",
		"sata-ports" => 6,
		"lan-ports" => 2,
		"serial-ports" => 2,
		"PCI-X" => 2,
		"PCI-E x8" => 2,
		"form-factor" => "41.25cm x 28.52cm",
	},
	{
		"model" => "Supermicro X7DCA-L",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0xdc8015d9,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"sata-ports" => 6,
		"form-factor" => "Micro ATX",
	},
	{
		"model" => "Supermicro X7DCL-3",		# needs to come before X7DCL-i
		"ids" => [
			"0xa48015d9,0x005[89]1000,0x08",	# SAS
			"0xa48015d9,0x292[02]8086,0x02",	# SATA
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
	},
	{
		"model" => "Supermicro X7DCL-i",
		"ugly-model" => '^Supermicro X7DCL$',
		"ids" => [
			"0xa48015d9,0x292[02]8086,0x02",	# SATA
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
	},
	{
		"model" => "Supermicro X7DAL",
		"ids" => [
			"0x948015d9,0x25c08086,0x13",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x948015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro B7DBE",
		"ids" => [
			"0xb08015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBU",
		"ids" => [
			# have some showing up as 0x868015d9,0x26818086,0x09, e.g. fe35.yosup.re1.yahoo.com
			# looks like mistake, so ignore for now
			"0x978[01]15d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DVL-L",
		"ids" => [
			"0xb28015d9,0x25d48086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0xb28015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0xb28015d9,0x002018ca,0x00",		# VGA
		],
		"sockets" => 2,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "16G",
		"sata-ports" => 4,
	},
	{
		"model" => "Supermicro X7DVL-E",
		"ids" => [
			"0x868015d9,0x25d48086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x868015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0x868015d9,0x515e1002,0x02",		# VGA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "16G",
		"sata-ports" => 6,
	},
	{
		"model" => "Supermicro X7DBR-3 SAS",
		"ids" => [
			"0x928015d9,0x25d88086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x928015d9,0x041e9005,0x09",		# SAS
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBR-3 SATA",
		"ids" => [
			"0x928015d9,0x25d88086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x928015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBi+",
		"ids" => [
			"0xb18015d9,0x269e8086,0x09",		# ATA
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "64G",
		"sata-ports" => 6,
	},
	{
		"model" => "Supermicro X7DBR",
		"ids" => [
			"0x848015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
	},
	{
		"model" => "Supermicro X7DB8",
		"ids" => [
			"0x808015d9,0x25d88086,0x92",		# Chipset
			"0x808015d9,0x269b8086,0x09",		# SMBus
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x808015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DB8",
		"ids" => [
			"0x808015d9,0x25d88086,0xb1",		# Chipset
			"0x808015d9,0x269b8086,0x09",		# SMBus
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x808015d9,0x269e8086,0x09",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM dx340",
		"ids" => [
			"0x03251014,0x268[012]8086,0x09",	# SATA
			"0x82171043,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM x3850 M2",
		"ids" => [
			"0x03811014,0x27df8086,0x01",		# ATA
			"0x037c1014,0x163914e4,0x01",		# Network
			"0x03661014,0x00621000,0x03",		# SAS
		],
		"sockets" => 4,
		"dimms" => 32,
		"memory-type" => "DDR2-533 ECC Registered",
		"max-memory" => "256G",
		"chipset" => "IBM X4",
	},
	{
		"model" => "IBM x3550",
		"ids" => [
			"0x02dd1014,0x268[012]8086,0x09",	# SATA
			"0x03421014,0x164c14e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM eServer 306m",
		"ids" => [
			"0x02fd1014,0x27c[013]8086,0x01",	# SATA
			"0x02c61014,0x165914e4,0x11",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Sun Fire X2100",
		"ids" => [
			"0x5348108e,0x005410de,0xf3",
			"0x5348108e,0x005710de,0xa3",
		],
		"sockets" => 1,
	},
	{
		"model" => "Sun Fire V20z",
		"ids" => [
			"0x001017c2,0x74691022,0x03",		# ATA
			"0x001017c2,0x16..14e4,0x0.",		# Network
			"0x001017c2,0x00301000,0x0.",		# SCSI, revs 0x07, 0x08
		],
		"sockets" => 2,
	},
	{
		"model" => "Sun Fire V40z",
		"ids" => [
			"0x002017c2,0x74691022,0x03",		# ATA
			"0x002017c2,0x16a714e4,0x02",		# Network
			"0x002017c2,0x00301000,0x0.",		# SCSI, revs 0x07, 0x08
		],
		"sockets" => 4,
	},
	{
		"model" => "Asus K8N-DRE",
		"ids" => [
			"0x81491043,0x165914e4,0x.1",		# revs 0x11, 0x21
			"0x81621043,0x005510de,0xf3",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA-I SATA",		# before HDAMA-I
		"ids" => [
			"0x164814e4,0x164814e4,0x10",
			"0x3016161f,0x74691022,0x03",
			"0x61141095,0x31141095,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA-I",
		"ids" => [
			"0x164814e4,0x164814e4,0x10",
			"0x3016161f,0x74691022,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA SATA",			# before HDAMA
		"ids" => [
			"0x000c14e4,0x16a614e4,0x02",
			"0x3016161f,0x74691022,0x03",
			"0x61141095,0x31141095,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA",			# before HDAMA
		"ids" => [
			"0x000c14e4,0x16a614e4,0x02",
			"0x3016161f,0x74691022,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1550",
		"ugly-model" => "Dell Cleared System/1000",
		"ids" => [
			"0x00da1028,0x12298086,0x08",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2550",
		"ids" => [
			"0x00d11028,0x164414e4,0x(10|12)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2450",
		"ids" => [
			"0x009b1028,0x12298086,0x08",		# Network
			"0x00a61028,0x00cf9005,0x01",		# SCSI
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1650",
		"ids" => [
			"0x810b1028,0x02121166,0x93",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2650",
		"ids" => [
			"0x01211028,0x164514e4,0x15",		# network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2650",
		"ids" => [
			"0x01211028,0x16a714e4,0x02",		# network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE750",
		"ids" => [
			"0x01651028,0x25a38086,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell PE1750",
		"ids" => [
			"0x014a1028,0x02121166,0x93",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell SC1435",
		"ids" => [
			"0x01eb1028,0x024b1166,0x00",
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{
		"model" => "Dell R200",
		"ids" => [
			"0x023c1028,0x165914e4,0x21",		# network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Dell R300",
		"ids" => [
			"0x020f1028,0x165a14e4,0x00",		# network
		],
		"sockets" => 1,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "24G",
	},
	{
		"model" => "Dell PE860",
		"ids" => [
			"0x01e61028,0x27788086,0x00",
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell PE850",
		"ids" => [
			"0x01b61028,0x27788086,0x00",
		],
		"sockets" => 1,
		"ymodel-type" => "F",
	},
	{
		"model" => "Dell PE1850",
		"ids" => [
			"0x016c1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1955",
		"ids" => [
			"0x01bb1028,0x16ac14e4,0x12", 		# Network
			"0x1f081028,0x00541000,0x01", 		# SAS
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1855",
		"ids" => [
			"0x018a1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2850",
		"ids" => [
			"0x016d1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE6850",
		"ids" => [
			"0x01701028,0x164814e4,0x10",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell R900",
		"ids" => [
			"0x80868086,0x36008086,0x..",		# Chipset
			"0x01f01028,0x164c14e4,0x12",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell PE6950",
		"ids" => [
			"0x01ea1028,0x02141166,0x00",
			"0x01ea1028,0x164c14e4,0x12",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell PE1950 III",
		"ugly-model" => "Dell PowerEdge 1950",
		"ids" => [
			"0x1f101028,0x00581000,0x08",		# SAS
			"0x01b31028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1950",
		"ids" => [
			"0x01b31028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2970",
		"ids" => [
			"0x02051028,0x164c14e4,0x1.",
			"0x02051028,0x024b1166,0x00",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2950",
		"ids" => [
			"0x01b21028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
		"ymodel-type" => "M",
	},
	{
		"model" => "Dell 1600SC",
		"ids" => [
			"0x01351028,0x100e8086,0x02",		# Network
			"0x41351028,0x02121166,0x93",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell SC1425",
		"ids" => [
			"0x019a1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL365 G1",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"model-match" => "DL365",
		"sockets" => 2,
	},
	{
		"model" => "HP DL385 G2",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"model-match" => "DL385",
		"sockets" => 2,
	},
	{
		"model" => "HP DL365 G1/DL385 G2",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"sockets" => 2,
	},
	{
		"model" => "HP BL480c G1",			# needs to go before DL380 G5
		"ids" => [
			"0x703b103c,0x16ac14e4,0x12",		# Network
			"0x703c103c,0x167914e4,0xa3",		# Network
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
		],
		"sockets" => 2,
		"dimms" => 12,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "48G",
	},
	{
		"model" => "HP DL120 G5",
		"ids" => [
			"0x........,0x29f08086,0x..",		# Chipset
			"0x31f4103c,0x292[02]8086,0x02",	# SATA
			"0x7051103c,0x165a14e4,0x00",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
	},
	{
		"model" => "HP DL160 G5",
		"ids" => [
			"0x........,0x40038086,0x..",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
# SAS version hides the following:
#			"0x31fe103c,0x269e8086,0x09",		# ATA
#			"0x31f6103c,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2-800 ECC Fully Buffered",
		"max-memory" => "64G",
	},
	{
		"model" => "HP DL180 G5",			# must come before HP SE1101
		"ugly-model" => "HP ProLiant ML150 G5",
		"ids" => [
			"0x........,0x65c08086,0x80",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
			"0x00000000,0x65e38086,0x80",		# PCI - needed to distinguish from HP SE1101
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP SE1101",
		"ids" => [
			"0x........,0x65c08086,0x80",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP SB600c",				# needs to go before DL380 G5
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
			"0x000000(00|40),0x26908086,0x09",
			"0x3211103c,0x3238103c,0x00",		# CISS
			"0x703b103c,0x16ac14e4,0x12",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
	},
	{
		"model" => "HP BL260c G5",
		"ids" => [
			"0x703c103c,0x167914e4,0xa3",		# Network
			"0x31fe103c,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "16G",
	},
	{
		"model" => "HP DL380 G5",			# needs to go before DL360 G5
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
			"0x000000(00|40),0x26908086,0x09",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP BL460c G1",			# needs to go before DL360 G5
		"ids" => [
			"0x703b103c,0x16ac14e4,0x12",		# Network
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
	},
	{
		"model" => "HP DL360 G5",
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL380 G4",
		"ids" => [
			"0x32010e11,0x24db8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4p",
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"model-match" => "G4p",
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4",
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"model-match" => "G4",
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4/G4p",			# only diff is 4 vs. 6 dimms
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4",
		"ids" => [
			"0x32000e11,0x35908086,0x0a",
			"0x32010e11,0x25a28086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S7000FC4UR",
		"ugly-model" => "Intel MP",
		"ids" => [
			"0x34cc8086,0x515e1002,0x02",		# VGA
			"0x34cc8086,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 4,
		"dimms" => 32,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "256G",
	},
	{
		"model" => "HP DL580 G5",
		"ids" => [
			"0x31f2103c,0x36008086,0x01",		# Chipset
			"0x31fe103c,0x269e8086,0x09",		# ATA
			"0x7038103c,0x164c14e4,0x1.",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL580 G4",
		"ids" => [
			"0x3201103c,0x24db8086,0x02",		# ATA
			"0x1709103c,0x164a14e4,0x02",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML530 G2",			# needs to come before DL580 G2
		"ids" => [
			"0x00000000,0x00111166,0x22",		# Chipset
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
			"0xf6200e11,0x00c09005,0x01",		# SCSI
			"0xb1340e11,0x12298086,0x08",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL580 G2",
		"ids" => [
			"0x00000000,0x00111166,0x22",		# Chipset
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML570",			# needs to go before DL380 G2
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
			"0xb1430e11,0x000a1000,0x02",		# SCSI
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML370 G2",			# identical to 380g2
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
		],
		"model-match" => "ML370",
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL380 G2",
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL180",				# needs to go before DL360 G3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x31fe103c,0x269e8086,0x09",		# ATA
			"0x31f6103c,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL380 G3",			# needs to go before DL360 G3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL560 G1",			# identical to 360g3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"model-match" => "DL560",
		"sockets" => 4,
	},
	{
		"model" => "HP DL360 G3",			# identical to 560g1
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL360 G2",
		"ids" => [
			"0x00850e11,0x164514e4,0x15",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL320 G5",			# could also be a HP DL320s
		"ids" => [
			"0x31fe103c,0x27df8086,0x01",		# ATA
			"0x7039103c,0x166814e4,0xa3",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "HP DL320 G3",
		"ids" => [
			"0x32000e11,0x25888086,0x05",
			"0x32010e11,0x26528086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DL320 G2",
		"ids" => [
			"0x00bb0e11,0x16a614e4,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Compaq DL360",
		"ids" => [
			"0xb1340e11,0x12298086,0x08",		# Network
			"0x....0e11,0x00..1000,0x02",		# SCSI
			"0xb0f30e11,0xa0f00e11,0x00",		# Advanced System Management Controller
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL320",
		"ids" => [
			"0xb1340e11,0x12298086,0x08",		# Network
			"0x00000000,0x02111166,0x00",		# ATA
			"0x001e0e11,0x47521002,0x27",		# VGA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DL140 G2",
		"ids" => [
			"0x3208103c,0x24d[1b]8086,0x02",	# SATA/ATA
			"0x1659....,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL140 G3",
		"ids" => [
			"0x25c08086,0x25c08086,0x..",		# Chipset
			"0x31fe103c,0x269b8086,0x09",		# SMBus
			"0x3260103c,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL145 G3",
		"ids" => [
			"0x703e103c,0x167814e4,0xa3",		# Network
			"0x320b103c,0x02141166,0x00",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL185 G5",
		"ids" => [
			"0x320b103c,0x02141166,0x00",		# SATA
			"0x310f103c,0x164814e4,0x10",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP DL145 G2",
		"ids" => [
			"0x3209103c,0x165914e4,0x11",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL145 G1",
		"ids" => [
			"0x36c01022,0x74691022,0x03",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL585 G2",
		"ids" => [
			"0x1709103c,0x164a14e4,0x02",
			"0x31f8103c,0x005310de,0xa3",
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL585 G1",
		"ids" => [
			"0x32040e11,0x74691022,0x03",
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL385 G1",
		"ids" => [
			"0x32040e11,0x74691022,0x03",
			"0x40910e11,0x00460e11,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL140",
		"ids" => [
			"0x001e0e11,0x47521002,0x27",
			"0x164814e4,0x164814e4,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro PDSMi",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x798015d9,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Supermicro PDSML",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x858015d9,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Supermicro X6DLP",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x108c15d9,0x108c8086,0x03",
			"0x698015d9,0x25a38086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DCT",
		"ids" => [
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
			"0xde8015d9,0x515e1002,0x02",		# VGA
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"sata-ports" => 4,
		"lan-ports" => 2,
		"serial-ports" => 1,
		"PCI-e x16" => 1,
		"form-factor" => "41.66cm x 16.51cm",
	},
	{
		"model" => "Intel S3000PT",
		"ids" => [
			"0x349[ab]8086,0x27c[013]8086,0x01",	# SATA
			"0x349b8086,0x108b8086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel S5000PAL",
		"ids" => [
			"0x346[cd]8086,0x25d88086,0xb1",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5400SF",
		"ids" => [
			"0x34d48086,0x268[012]8086,0x09",	# SATA
			"0x34d48086,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "64G",
	},
	{
		"model" => "Intel S5000XSL",
		"ids" => [
			"0x347a8086,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000VSA",
		"ids" => [
			"0x34848086,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000PSL",
		"ids" => [
			"0x34768086,0x25d88086,0x(92|b1)",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000PSL-SAS",
		"ids" => [
			"0x34788086,0x25d88086,0xb1",
		],
		"sockets" => 2,
	},
	{							# with scsi
		"model" => "Intel SE7520JR2S",			# needs to go before SE7520JR2
		"ids" => [
			"0x10798086,0x10798086,0x03",
			"0x34358086,0x00301000,0x08",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520JR2",
		"ids" => [
			"0x10798086,0x10798086,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520BB2",
		"ugly-model" => "Intel SE7520BB2..?",
		"ids" => [
			"0x347e8086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{							# with scsi
		"model" => "Intel SE7520BD2S",			# needs to go before SE7520BD2
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
			"0x34628086,0x00301000,0xc1",
		],
		"sockets" => 2,
	},
	{							# with sata
		"model" => "Intel SE7520BD2D",			# needs to go before SE7520BD2
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
			"0x34398086,0x24d18086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520BD2",
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7320VP2",
		"ids" => [
			"0x34448086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7320VP2",			# 2nd version of SE7320VP2
		"ids" => [
			"0x34508086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500WV2",			# scsi version of SE7500WV2A
		"ids" => [
			"0x34158086,0x25408086,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500WV2A",
		"ids" => [
			"0x34168086,0x25408086,0x03",
		],
		"sockets" => 2,
	},
	{							# with scsi
		"model" => "Intel SE7501WV2S",			# needs to go before SE7501WV2
		"ids" => [
			"0x341a8086,0x254c8086,0x01",
			"0x341a8086,0x801f9005,0x03",
		],
		"sockets" => 2,
	},
	{							# we had some SE7501WV2S without scsi ids
		"model" => "Intel SE7501WV2",
		"ids" => [
			"0x341a8086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501WV2A",
		"ids" => [
			"0x341b8086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501CW2",
		"ids" => [
			"0x34258086,0x248b8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500CW2",
		"ids" => [
			"0x34198086,0x248b8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501BR2",
		"ids" => [
			"0x34188086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S875WP1",
		"ids" => [
			"0x34288086,0x24d18086,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel CA810E",
		"ids" => [
			"0x43328086,0x71258086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel TR440BX",
		"ids" => [
			"0x71908086,0x71928086,0x03",		# Chipset
			"0x30008086,0x12298086,0x08",		# Network
			"0x00000000,0x71118086,0x01",		# ATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel L440GX+",
		"ids" => [
			"0x00000000,0x71a08086,0x00",
			"0x(00539004|080f9005|ffff9005),0x005f9005,0x00",
			"0x00bc1013,0x00bc1013,0x23",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel N440BX",
		"ids" => [
			"0x00000000,0x71928086,0x..",
			"0x........,0x000f1000,0x37",
			"0x00088086,0x12298086,0x05",
			"0x00000000,0x71118086,0x01",
			"0x........,0x00bc1013,0x23",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S845WD1",
		"ids" => [
			"0x57448086,0x244b8086,0x05",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel SCB20-S",		# SCSI version
		"ids" => [
			"0x340f8086,0x12298086,0x0d",
			"0x340f8086,0x02121166,0x92",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SCB20-A",
		"ids" => [
			"0x34108086,0x12298086,0x0d",
			"0x34108086,0x02121166,0x92",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE6650",
		"ids" => [
			"0x01091028,0x164414e4,0x14",		# network
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq 6400R",
		"ids" => [
			"0xb0dd0e11,0x12298086,0x05",		# network
		],
		"sockets" => 4,
	},

	# Laptops
	{
		"model" => "Dell Latitude D610",
		"ids" => [
			"0x01821028,0x26538086,0x03",		# SATA
			"0x01821028,0x167714e4,0x01",		# Network
			"0x27228086,0x42208086,0x05",		# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-533 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP NC6220",
		"ids" => [
			"0x0944103c,0x266f8086,0x03",		# ATA
			"0x0944103c,0x167d14e4,0x11",		# Network
			"0x12f6103c,0x42208086,0x05",		# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-533 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP NC6400",
		"ids" => [
			"0x30ad103c,0x27df8086,0x01",		# ATA
			"0x30ad103c,0x16fd14e4,0x21",		# Network
			"0x135[bc]103c,0x42228086,0x02",	# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},

);

my(%hostid_synonyms);
my(%hostid_to_pci);
my(%hostid_to_driver);

my(%linux_driver_map) = (
	"lsi" => "mpt",
	"3w" => "twe",
);

sub sig_warn {
        my($err) = @_;
	$err = "$zero: perl-warn: $err";
	$perl_warn .= $err;
	warn $err;
}

sub sig_die {
        my($err) = @_;
	$err = "$zero: perl-die: $err";
	$perl_warn .= $err;
	email('', '');
	die $err;
}

$SIG{'__WARN__'} = 'sig_warn';
$SIG{'__DIE__'} = 'sig_die';

sub watchdog {
	die "watchdog expired; something is taking too long";
}

sub usage {
	print "usage:\t$zero [-dhnv] [-o output-file]\n";
	print "\t-d  show debugging information\n";
	print "\t-h  show usage\n";
	print "\t-n  don't break output lines\n";
	print "\t-v  show version\n";
	print "\t-o  write output to a file\n";
}

sub round {
	my($number) = @_;
	return int($number + 0.5);
}

sub cmp_devs {
	my($aa, $bb) = ($a, $b);
	$aa = $hostid_to_pci{$aa} if ($hostid_to_pci{$aa});
	$bb = $hostid_to_pci{$bb} if ($hostid_to_pci{$bb});
	my($atype, $anum) = ($aa =~ /^(.*?)(\d*)$/);
	my($btype, $bnum) = ($bb =~ /^(.*?)(\d*)$/);
	return ($atype cmp $btype || $anum <=> $bnum);
}

sub parse_enable {
	my($string, $more_yes, $more_no) = @_;
	my($yes) = "yes|on|enabled|1";
	my($no) = "no|off|disabled|0";
	$yes .= "|$more_yes" if $more_yes;
	$no .= "|$more_no" if $more_no;
	if ($string =~ /^($yes)$/i) {
		return 1;
	} elsif ($string =~ /^($no)$/i) {
		return 0;
	} else {
		return;
	}
}

sub parse_value {
	my($string) = @_;
	if ($string =~ /^(\d+)/) {
		return $1;
	}
	return $string;
}

sub parse_bytes {
	my($string, $factor) = @_;
	my($result) = 0;
	my(%exp) = (
		"K" => 1,
		"KB" => 1,
		"M" => 2,
		"MB" => 2,
		"G" => 3,
		"GB" => 3,
		"T" => 4,
		"TB" => 4,
	);
	if ($string =~ /^\s*([\d\.]+)\s*(K|KB|M|MB|G|GB|T|TB)\s*$/i) {
		$result = $1 * (($factor || 1000) ** $exp{uc($2)});
	}
}

sub parse_rpm {
	my($string) = @_;
	my($result) = "";
	my(@rpms) = (4500, 5400, 7200, 10000, 15000);
	my($low) = 0;
	my($high);
	foreach $high (@rpms) {
		if ($string <= $high) {
			if ($high < 10000) {
				$result = sprintf("%3.1fK", $high / 1000);
			} else {
				$result = sprintf("%2dK", $high / 1000);
			}
			last;
		}
	}
	return $result;
}

sub parse_volume_status {
	my($string) = @_;
	my($result);
	if ($string =~ /^(OK|Optimal)$/i) {
		$result = "ok";
	} elsif ($string =~ /^(Degraded)$/i) {
		$result = "degraded";
	} elsif ($string =~ /^(Failed|INTERM RECOVERY|Interim Recovery Mode|BROKEN)$/i) {
		$result = "failed";
	} else {
		$result = $string;
	}
	return $result;
}

sub parse_drive_status {
	my($string) = @_;
	my($result);
	if ($string =~ /^(OK|Ready)/i) {
		$result = "ok";
	} elsif ($string =~ /^(Online)/i) {
		$result = "online";
	} elsif ($string =~ /^(Offline)/i) {
		$result = "offline";
	} elsif ($string =~ /^(Failed)/i) {
		$result = "failed";
	} elsif ($string =~ /^(Rebuild)/i) {
		$result = "rebuilding";
	} else {
		$result = $string;
	}
	return $result;
}

sub parse_drive_interface {
	my($string) = @_;
	my($result) = $string;
	if ($string =~ /Parallel SCSI/) {
		$result = "SCSI";
	} elsif ($string =~ /SAS/) {
		$result = "SAS";
	}
	return $result;
}

sub parse_drive_speed {
	my($string) = @_;
	my($result) = "";
	if ($string =~ /320/) {
		$result = "U320";
	} elsif ($string =~ /SATA 1\.5 Gb\/s/) {
		$result = "SATA-150";
	} elsif ($string =~ /SATA 3\.0 Gb\/s/) {
		$result = "SATA-300";
	}
	return $result;
}

sub canon_raid {
	my($raid) = @_;
	return unless (defined $raid);

	$raid =~ s/RAID 6 \(ADG\)/RAID ADG/;

	$raid =~ s/volume//i;
	$raid =~ s/[^a-z0-9]+$//i;	# nuke garbage from the end

	$raid =~ s/raid\s+raid/raid/ig;

	$raid =~ s/SINGLE/JBOD/i;
	$raid =~ s/RAID Simple/JBOD/i;

	$raid =~ s/^\s+//;
	$raid =~ s/\s+$//;

	$raid =~ s/\-/ /g;

	$raid =~ s/raid(.+)/RAID $1/i;

	# /proc/driver/cciss/cciss0
	$raid =~ s/1\(1\+0/10/;
	$raid =~ s/1\(0\+1/10/;

	# hpacucli
	$raid =~ s/1\+0/10/;
	$raid =~ s/0\+1/10/;

	# aac
	$raid =~ s/0\/1/10/;

	$raid =~ s/\s+/-/g;

	return $raid;
}

sub canon_cpu_vendor {
	my($vendor) = @_;
	if ($vendor =~ /Intel|Pentium|Xeon|Celeron/i) {
		$vendor = 'Intel';
	} elsif ($vendor =~ /AMD/i) {
		$vendor = 'AMD';
	} elsif ($vendor =~ /^I/i) {
		$vendor = 'Intel';
	}
	return $vendor;
}

sub canon_drive {
	my($model) = @_;
	$model =~ s/^\s+//;
	$model =~ s/\s+$//;
	$model =~ s/[\W_]+/-/ig;
	$model =~ s/^\-+//;
	$model =~ s/\-+$//;
	return $model;
}

sub canon_memory {
	my($model) = @_;
	if ($model =~ /^0x/ || (length($model) > 18 && $model =~ /^[0-9a-z]+$/i)) {
		$model =~ s/^0x0*//;
		$model =~ s/(..)/sprintf("%c", hex($1))/ge;
	}
	$model =~ s/\s+/ /g;
	$model =~ s/^\s+//;
	$model =~ s/\s+$//;
	return $model;
}

sub clean_white {
	my($string) = @_;
	$string =~ s/\s+/ /g;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}

sub clean_pci_model {
	my($model) = @_;
	$model =~ s/\(was:.*\)//i;		# freebsd
	$model =~ s/\(rev .*\)$//;		# linux
	$model =~ s/PCI Express/PCI-E/i;
	$model =~ s/(Gigabit) Ethernet( Controller( \(Copper\))?)?/$1/i;
	$model =~ s/(Gigabit) Network Connection/$1/i;
	$model =~ s/\b(Corporation|Corp\.?|Inc|Co\.|Ltd\.|Technologies Inc|Technology Group Ltd\.|Semiconductor Co\.\,)//ig;
	$model =~ s/\s+(Controller|Adapter)$//i;
	return clean_white($model);
}

sub print_bytes {
	my($number, $factor, $int, $round_limit) = @_;
	my(@suffix) = ('', 'K', 'M', 'G', 'T', 'P', 'E');
	my($i) = 0;
	$factor = 1000 unless $factor;
	$round_limit = 10 unless $round_limit;
	# keep going until we're below 1000, otherwise can end up with 1020MB
	# given that we round to one decimal, 1001/1024 will round to 1.0, i.e. we avoid 0.98 type results
	while ($number >= 1000) {
		$number = $number / $factor;
		$i++;
	}
	my($result);
	if ($number >= $round_limit) {
		$result = sprintf("%.0f%sB", $number, $suffix[$i]);
	} elsif ($number) {
		$result = sprintf("%3.1f%sB", $number, $suffix[$i]);
		$result =~ s/\.0// if ($int);
	} else {
		$result = sprintf("0B");
	}
	return $result;
}

sub cpuid {
	my($vendor, $cpuid) = @_;
	$vendor = canon_cpu_vendor($vendor);
	my($family, $model, $stepping, $ext_model, $ext_family);
	$stepping = $cpuid & 0xf;
	$model = ($cpuid >> 4) & 0xf;
	$family = ($cpuid >> 8) & 0xf;
	$ext_model = ($cpuid >> 16) & 0xf;
	$ext_family = ($cpuid >> 20) & 0xff;
	$family += $ext_family;
	$model += ($ext_model << 4);
	return "$vendor-$family,$model,$stepping";
}

sub init_dmesg {
	my($in) = 'DMESG';
	my($line);
	if (-r $dmesg_file) {
		open($in, "< $dmesg_file") || warn("open: $dmesg_file");
		while ($line = <$in>) {
			$dmesg_buf .= $line;
		}
		close($in);
	} else {
		$dmesg_buf = `$dmesg_prog`;
	}
}

my($pci_all) = "";
my($guess_system) = "Unknown";
my($system_model_index);

sub guess_system {
	my($init) = @_;
	my($system) = "";
	my($buf) = $dmesg_buf;
	my($i) = 0;

	my($model);
	foreach $model (@system_models) {
		my($guess) = $model->{'model'};
		my($id);
		my($match) = 1;
		foreach $id (@{$model->{'ids'}}) {
			if (!($pci_all =~ /<$id>/)) {
				$match = 0;
				last;
			}
		}
		if ($match && defined $system_models[$i]{'model-match'}) {
			$match = 0 unless ($init =~ /$system_models[$i]{'model-match'}/);
		}
		if ($match) {
			$system = $guess;
			$guess_system = $guess;
			$system_model_index = $i;
			last;
		}
		$i++;
	}

	if ($guess_system eq "Unknown" && $< == 0) {
		$debug .= "Debug-System:\t$guess_system\n";
	}

	return $system if ($system);

	if ($buf =~ /ServerWorks ROSB4 ATA33/ && $buf =~ /fxp1:/) {
		if ($buf =~ /CMD 649 ATA100/ && $buf =~ /Mach64-GR/) {
			$system = "Compaq ProLiant DL320?";
		} elsif (($buf =~ /sym1:/ || $buf =~ /ida0:/) && $buf =~ /Mach64-GV/) {
			$system = "Compaq ProLiant DL360?";
		} elsif ($buf =~ /Compaq Smart Array 5i/ && $buf =~ /Mach64-GR/) {
			$system = "Compaq ProLiant DL380 G2?";
		}

	} elsif ($buf =~ /ServerWorks CSB5 ATA100/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/ && $buf =~ /Compaq Smart Array 5i/) {
		if ($buf =~ /Broadcom BCM5701/) {
			$system = "Compaq ProLiant DL360 G2?";
		} elsif ($buf =~ /Broadcom BCM5703/) {
			if ($buf =~ /pci6/ && $buf =~ /pci7/) {
				$system = "Compaq ProLiant DL380 G3?";
			} else {
				$system = "HP ProLiant DL360 G3?";
			}
		}

	} elsif ($buf =~ /Broadcom BCM5702/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/) {
		if ($buf =~ /CMD 649 ATA100/) {
			$system = "HP ProLiant DL320 G2?";
		}

	} elsif ($buf =~ /HP Smart Array 6i/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/) {
		if ($buf =~ /Intel 6300ESB ATA100/) {
			$system = "HP ProLiant DL360 G4?";
		} elsif ($buf =~ /Intel ICH5 ATA100/) {
			$system = "HP ProLiant DL380 G4?";
		}

	}
	return ($system || "Unknown");
}


my(%interfaces);
my(%pci_map);
my(%pci_to_driver);
my(%irq_to_pci);
my(%irq_to_driver);
my(%scsi_to_driver);

my($ndrives) = 0;
my($drive_id) = 1;
my(%volumes);
my(%drives);
my(%props);

my(%logical_to_os);
my(%controller_volumes);

sub grok_proc_scsi {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_scsi) {
		my($letter) = 'a';
		my($dev, $channel, $id, $lun, $model, $vendor, $vendor_model);
		$need_arcconf++ if (-e "/proc/scsi/aacraid");
		open($file, "< $proc_scsi") || warn("open: $proc_scsi: $!");
		$debug_storage .= ">> $proc_scsi\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^Host:\s+(\S+)\s+Channel:\s+(\S+)\s+Id:\s+(\S+)\s+Lun:\s+(\S+)/) {
				($dev, $channel, $id, $lun) = ($1, $2, $3, $4);
			} elsif ($line =~ /^Host: (\S+)/) {
				$dev = $1;
			} elsif ($line =~ /^\s*Vendor:\s*(.*)Model:\s*(.*\S)\s*Rev:/) {
				($vendor, $model) = ($1, $2);
				$vendor_model = "$vendor $model";
				$need_megacli++ if ($vendor_model =~ /$perc_names/);	# no /i so that PepperCo doesn't match
				$need_megarc++ if ($vendor_model =~ /MegaRAID/);
				$need_twcli++ if ($vendor_model =~ /(3ware|AMCC)/);
			} elsif ($line =~ /^\s*Type:\s*Direct-Access/) {
				my($vol) = "sd" . $letter++;
				my($device) = vol_to_hostid($vol) || $dev;
				$volumes{$vol}++;
				if ($model =~ /(raid\d+)/i) {
					$props{$vol}{raid} = $1;
				} else {
					$props{$vol}{raid} = "JBOD";
				}
				if ($vendor =~ /^(PepperC|SMSC)/) {
					$props{$vol}{hidden} = 1;
				}
				$props{$vol}{device} = $device if ($device);

				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = canon_drive($vendor_model);
				$props{drives}{$drive_id}{volume} = $vol;
				smartctl($vol, "/dev/$vol", $vendor, $drive_id);
				hdparm($vol, "/dev/$vol", $drive_id);
				$drive_id++;
				$ndrives++;

				if (defined($dev) && defined($channel)) {
					if ($vendor_model =~ /MegaRAID/) {
						my($logical) = sprintf("%s-%d-%d-%d", $dev, $channel, $id, $lun);
						$logical_to_os{'megaraid'}{$logical} = $vol;
					} elsif ($vendor_model =~ /$perc_names/) {
						my($logical) = sprintf("%s-%d-%d-%d", $dev, $channel, $id, $lun);
						$logical_to_os{'perc'}{$logical} = $vol;
					} elsif ($vendor_model =~ /(3ware|AMCC)/i) {
						my($logical) = sprintf("c%d-u%d", ($dev =~ /^scsi(\d+)/), $id);
						$logical_to_os{'tw'}{$logical} = $vol;
					} else {
						$logical_to_os{'other'}{$model} = $vol;
					}
				}
				undef $dev;
				undef $channel;
				undef $id;
				undef $lun;
			}
		}
		close($file);
	}
}

# need to parse /sys/bus/pci/drivers/cciss/ to get pci-id to match with controller

sub grok_proc_cciss {
	my($dir) = 'DIR';
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux" && -d $proc_cciss);

	$need_hpacucli++;

	opendir($dir, $proc_cciss) || warn("opendir: $proc_cciss: $!");
	my(@dirs) = readdir($dir);
	closedir($dir);

	my(@dirs2);
	if (-d $sys_cciss) {
		opendir($dir, $sys_cciss) || warn("opendir: $sys_cciss: $!");
		@dirs2 = readdir($dir);
		closedir($dir);
	}

	$debug_storage .= ">> $proc_cciss\n";

	my($ent, $ent2);
	foreach $ent (@dirs) {
		if ($ent =~ /^cciss\d+$/) {
			open($file, "< $proc_cciss/$ent") || warn("open: $proc_cciss/$ent: $!");
			$debug_storage .= ">>> $proc_cciss/$ent\n";
			while ($line = <$file>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /firmware version: (\S+)/i) {
					$props{$ent}{'firmware'} = $1;
				} elsif ($line =~ /^(cciss\/c\d+d\d+):\s+([\d\.]+)GB\s+(\S+.*)\s*$/) {
					my($vol) = $1;
					$volumes{$vol}++;
					$props{$vol}{raid} = $3;

					$props{$vol}{device} = $ent;
					$controller_volumes{$ent}{$vol}++;

					foreach $ent2 (@dirs2) {
						if ($ent2 =~ /^[0-9a-f]{4}:(.*)/i) {
# not sure when this was added and why.  it only seems to screw things up
# commenting out the next line on 20080329, the other two were commented out long ago
#							$props{$vol}{device} = $1;
#							$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
#							$cleanup{'Disk-Control'}{$num}++;
						}
					}
					
				}
			}
			close($file);
		}
	}
}

sub ccdconfig {
	my($in) = 'CCD';
	my($line);
	my($result) = 0;

	system("which ccdconfig > /dev/null 2>&1");
	return $result if ($?);

	open($in, "ccdconfig -g 2> /dev/null |") || warn "exec ccdconfig: $!";
	$debug_storage .= ">> ccdconfig -g\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result = 1;
		my($vol, $ileave, $flags, @parts) = split(' ', $line);
		if ($flags =~ /MIRROR/) {
			$props{$vol}{raid} = "RAID-1";
		} else {
			$props{$vol}{raid} = "RAID-0";
			$props{$vol}{stripe} = $ileave * 512;
		}
		my($model) = "";
		my($sep) = "partitions: ";
		my($part);
		foreach $part (@parts) {
			$part =~ s,/dev/,,;
			$model .= "$sep$part";
			$sep = ", ";
		}
		$volumes{$vol}++;
		$drives{$vol}{$drive_id}++;
		$props{drives}{$drive_id}{model} = $model;
		$props{drives}{$drive_id}{volume} = $vol;
		$drive_id++;
		$ndrives++;
	}
	close($in);

	return $result;
}

sub grok_mdstat {
	my($file) = 'FILE';
	my($line);
	my($vol, $raid, $rest);

	return unless ($os_type eq "linux");

	if (-e $proc_mdstat) {
		open($file, "< $proc_mdstat") || warn("open: $proc_mdstat: $!");
		$debug_storage .= ">> $proc_mdstat\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(md\d+).*(raid\d+)\s+(.*)/) {
				($vol, $raid, $rest) = ($1, $2, $3);
				$props{$vol}{raid} = $raid;
				my($dev);
				my($model) = "";
				my($sep) = "partitions: ";
				my($part, %parts);
				foreach $dev (split(' ', $rest)) {
					if ($dev =~ /^(.*)\[(\d+)\]/) {
						$parts{$2} = $1;
					}
					$dev =~ s/\[(\d+)\]//;
					$dev =~ s/\d+$//;
					$props{$dev}{inmd}++;
				}
				foreach $part (sort keys %parts) {
					$model .= "$sep$parts{$part}";
					$sep = ", ";
				}
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$ndrives++;
			} elsif ($line =~ /(\S+)\s+chunks/) {
				$props{$vol}{stripe} = parse_bytes($1, 1024);
			}
		}
	}
}

sub grok_proc_partitions {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_partitions) {
		open($file, "< $proc_partitions") || warn("open: $proc_partitions: $!");
		$debug_storage .= ">> $proc_partitions\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+([hs]d[a-z]+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+((cciss|ida)\/c\d+d\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_hpacucli++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(rd\/c\d+d\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_rd++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(md\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_mdstat++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(lvm\S+)(\s+|$)/) {
				$need_lvm++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(dm-\d+)(\s+|$)/) {
				$need_lvm++;
			}
		}
		close($file);
	}
}

sub grok_proc_ide {
	my($dir) = 'DIR';

	return unless ($os_type eq "linux" && -d $proc_ide);

	opendir($dir, $proc_ide) || warn("opendir: $proc_ide: $!");
	my(@dirs1) = readdir($dir);
	closedir($dir);

	my($ent1);
	foreach $ent1 (@dirs1) {
		if ($ent1 =~ /^ide\d+$/ && -d "$proc_ide/$ent1") {
			opendir($dir, "$proc_ide/$ent1") || warn("opendir: $proc_ide/$ent1: $!");
			my(@dirs2) = readdir($dir);
			closedir($dir);

			my($ent2);
			foreach $ent2 (@dirs2) {
				if ($ent2 =~ /^hd([a-z])$/ && -d "$proc_ide/$ent1/$ent2") {
					my($letter) = $1;
					my($vol) = $ent2;
					my($media, $model, $blocks, $cache);
					chomp($media = `cat $proc_ide/$ent1/$vol/media 2> /dev/null`);
					next if ($media =~ /(cdrom|unknown)/i);
					chomp($model = `cat $proc_ide/$ent1/$vol/model 2> /dev/null`);
					chomp($blocks = `cat $proc_ide/$ent1/$vol/capacity 2> /dev/null`);
					chomp($cache = `cat $proc_ide/$ent1/$vol/cache 2> /dev/null`);
					$cache *= 1024 if ($cache);
					$model = canon_drive($model);
					$volumes{$vol}++;
					$props{$vol}{raid} = "JBOD";
					$props{$vol}{bytes} = $blocks * 512;
					$props{$vol}{device} = $ent1;
					$props{$vol}{ms} = ((ord($letter) - ord('a')) % 2) ? "slave" : "master";
					$drives{$vol}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{size} = $blocks * 512;
					$props{drives}{$drive_id}{cache_size} = $cache;
					$props{drives}{$drive_id}{volume} = $vol;

					# override with hdparm info if we have it
					smartctl($vol, "/dev/$vol", "ATA", $drive_id);
					hdparm($vol, "/dev/$vol", $drive_id);

					$drive_id++;
					$ndrives++;
				}
			}
		}
	}
}

sub pci_to_driver {
	my($root) = "/sys/bus/pci/drivers";
	my($dir) = 'DIR';
	my($driver);

	if (-d $root) {
		opendir($dir, "$root") || warn("opendir: $root: $!");
		my(@dirs) = readdir($dir);
		closedir($dir);
		foreach $driver (sort @dirs) {
			next if ($driver =~ /^\.+$/);
			opendir($dir, "$root/$driver") || warn("opendir: $root/$driver: $!");
			my(@files) = readdir($dir);
			closedir($dir);
			my($file);
			my($count) = 0;
			foreach $file (sort @files) {
				if ($file =~ /^0000:(.*)/) {
					my($pci) = $1;
					$pci_to_driver{$pci} = lc($driver);
					$pci_to_driver{$pci} =~ s/\s+/_/g;
					$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
					$pci_to_driver{$pci} .= $count++;
				}
			}
		}
	}
}

sub pci_to_hostid {
	my($pci) = @_;
	my($root) = "/sys/bus/pci/devices";
	my($dir) = 'DIR';
	my($file) = 'FILE';
	my(@dirs1);
	my($hostid);
	my($full_pci) = $pci;

	$full_pci = "0000:$full_pci" unless (-d "$root/$full_pci");
	if (-d "$root/$full_pci") {
		opendir($dir, "$root/$full_pci") || warn("opendir: $root/$full_pci: $!");
		my(@dirs1) = readdir($dir);
		closedir($dir);

		my($ent1);
		foreach $ent1 (sort @dirs1) {
			if ($ent1 =~ /^host\d+$/) {
				if ($hostid) {
					$hostid_synonyms{$ent1} = $hostid;
				} else {
					$hostid = $ent1;
				}

				$hostid_to_pci{$ent1} = $pci;
				if ($pci_to_driver{$pci}) {
					$hostid_to_driver{$ent1} = $pci_to_driver{$pci};
				} elsif (-e "/sys/class/scsi_host/$ent1/proc_name") {
					my($driver) = `cat "/sys/class/scsi_host/$ent1/proc_name"`;
					chomp($driver);
					$driver = $linux_driver_map{$driver} || $driver;
					$driver .= $1 if ($ent1 =~ /(\d+)$/);
					$hostid_to_driver{$ent1} = $pci_to_driver{$pci} = $driver;
				}
			}
		}
	}
	return $hostid;
}

sub vol_to_hostid {
	my($vol) = @_;
	my($root) = "/sys/block";

	if (-l "$root/$vol/device") {
		my($link) = readlink("$root/$vol/device");
		if ($link =~ /\/(host\d+)\//) {
			return $hostid_synonyms{$1} || $1;
		}
	}
	return;
}

my(%ioports_map);
my(%iomem_map);
my(%interrupts_map);
my(%irq_count);

sub grok_proc_ioports {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_interrupts) {
		my($class) = 0;
		my($in) = "LSPCI";
		open($in, "$lspci -vn 2> /dev/null |") || warn("exec: $lspci: $!");
		while ($line = <$in>) {
			if ($line =~ /^([0-9a-f\.\:]+)\s+(Class\s+)?([0-9a-f]+):/i) {
				$class = "0x$3";
			}
			if ($line =~ /irq\s+(\d+)/i) {
				$irq_count{$1}++;
				$irq_count{"$1,$class"}++;
			}
		}
		close($in);

		open($file, "< $proc_interrupts") || warn("open: $proc_interrupts: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*(\d+).*\s+(\S+)$/) {
				$interrupts_map{$1} = lc($2) if ($irq_count{$1} && $irq_count{$1} == 1);
			}
		}
		close($file);
	}
	if (-e $proc_ioports) {
		open($file, "< $proc_ioports") || warn("open: $proc_ioports: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*([0-9a-f]+)-([0-9a-f]+)\s+:\s+(\w+)/) {
				$ioports_map{$1} = lc($3);
			}
		}
		close($file);
	}
	if (-e $proc_iomem) {
		open($file, "< $proc_iomem") || warn("open: $proc_iomem: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*([0-9a-f]+)-([0-9a-f]+)\s+:\s+(\w+)/) {
				$iomem_map{$1} = lc($3);
			}
		}
		close($file);
	}
}

my(%iomem_to_pci);

sub grok_lspci {
	my($in) = "LSPCI";
	my($line);
	my(%driver_count);
	my(%model);

	if ($os_type eq "linux") {
		open($in, "$lspci 2> /dev/null |") || warn("exec: $lspci: $!");
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^([0-9a-f:\.]+) ([^:]+):(.*)/i) {
				my($pci, $class, $model) = ($1, $2, $3);
				if ($model =~ s/: Unknown device [0-9a-f]+$//i) {
					$model .= " $class";
				}
				$model{$pci} = clean_pci_model($model);
			}
		}
		close($in);

		open($in, "$lspci -vn 2> /dev/null |") || warn("exec: $lspci: $!");
		$line = <$in>;
		my($first) = 1;
		my($pci, $class, $chip, $card, $rev, $irq, $irq_class);
		while (1) {
			$debug_lspci .= $line;
			if (eof($in) || $line =~ /^\S+/) {
				if (!$first) {
					my($model) = $model{$pci} || 'Unknown';
					if ($chipsets{"$chip,$rev"}) {
						$other_devices{'Chipset'}{"$chip,$rev"} = $chipsets{"$chip,$rev"};
					} elsif ($chipsets{$chip}) {
						$other_devices{'Chipset'}{$chip} = $chipsets{$chip};
					}
					if ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
					} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
					} elsif ($pci_devices{'storage'}{"0x????????,$chip"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$debug .= "Debug-Disk-Control:\t$dev: $card,$chip,$rev - $model\n";
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"0x????????,$chip"};
					} elsif ($pci_devices{'network'}{"$card,$chip,$rev"}) {
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"$card,$chip,$rev"};
					} elsif ($pci_devices{'network'}{"0x????????,$chip,$rev"}) {
						$debug .= "Debug-Network:\t$pci: $card,$chip,$rev - $model\n";
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"0x????????,$chip,$rev"};
					} elsif ($pci_devices{'network'}{"0x????????,$chip,0x??"}) {
						$debug .= "Debug-Network:\t$pci: $card,$chip,$rev - $model\n";
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"0x????????,$chip,0x??"};
					} elsif ($pci_devices{'other'}{"$card,$chip,$rev"}) {
						$other_devices{$pci_devices{'other'}{"$card,$chip,$rev"}{type}}{"$card,$chip"} = $pci_devices{'other'}{"$card,$chip,$rev"}{model};
					} elsif ($pci_classes{$class}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$debug .= "Debug-" . $pci_classes{$class} . ":\t$dev: $card,$chip,$rev - $model\n";
						$other_devices{$pci_classes{$class}}{$dev} = "$dev: $model";
					}
					$pci_map{$pci} = "$card,$chip,$rev";
					$pci_all .= "<$card,$chip,$rev>";
					$sig_pci .= "$card,$chip,$rev\t$model\n";
				}

				if ($line =~ /^([0-9a-f\.\:]+)\s+(Class\s+)?([0-9a-f]+):\s+([0-9a-f]+):([0-9a-f]+)( \(rev ([0-9a-f]+)\))?/i) {
					($pci, $class, $chip, $card, $rev, $irq, $irq_class) = ($1, "0x$3", "0x$5$4", "0x00000000", $7 ? "0x$7" : "0x00", 0, 0);
					$pci =~ s/^0000://;
					$first = 0;
				}
			} elsif ($line =~ /^\sFlags:.*IRQ\s+(\d+)/i) {
				if ($irq_count{$1} && $irq_count{$1} == 1) {
					$irq = $1;
					$irq_to_pci{$irq} = $pci;
				}
				if ($irq_count{"$1,$class"} && $irq_count{"$1,$class"} == 1) {
					$irq_class = "$1,$class";
					$irq_to_pci{$irq_class} = $pci;
				}
			} elsif ($line =~ /^\s*I\/O ports at ([0-9a-f]+)/i) {
				my($addr) = $1;
				unless ($pci_to_driver{$pci}) {
					my($networks) = "e100|e1000|eepro100|tg3|bnx2|bcm5700|forcedeth|pcnet32|sky2";
					my($match) = "^(lsi|aic79xx|qla2300|cciss|sata_nv|sata_sil|3w|3ware|3w-9xxx|libata|(ide|ioc)\\d+|$networks)\$";
					my($driver);
					if ($ioports_map{$addr} && $ioports_map{$addr} =~ /$match/) {
						$driver = $ioports_map{$addr};
					} elsif ($irq && $interrupts_map{$irq} && $interrupts_map{$irq} =~ /$match/) {
						# use only on last resort, shared interrupts makes this often wrong
						$driver = $interrupts_map{$irq};
					}
					if ($driver) {
						$driver = $linux_driver_map{$driver} || $driver;
						if ($driver =~ /^((ide|ioc)\d+|$networks)$/) {
							$pci_to_driver{$pci} = $driver;
						} else {
							$driver_count{$driver}++;
							$pci_to_driver{$pci} = $driver;
							$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
							$pci_to_driver{$pci} .= ($driver_count{$driver} - 1);
						}
					}
				}
				if ($pci_to_driver{$pci}) {
					$irq_to_driver{$irq_class} = $pci_to_driver{$pci} if ($irq_class);
					$irq_to_driver{$irq} = $pci_to_driver{$pci} if ($irq);
				}
			} elsif ($line =~ /^\s*Memory at ([0-9a-f]+)/i) {
				my($addr) = $1;
				unless ($pci_to_driver{$pci}) {
					my($networks) = "e100|e1000|eepro100|tg3|bnx2|bcm5700|forcedeth|pcnet32|sky2";
					my($match) = "^(megaraid|megasas|aic7xxx|$networks)\$";
					my($driver);
					$iomem_to_pci{$addr} = $pci;
					if ($iomem_map{$addr} && $iomem_map{$addr} =~ /$match/) {
						$driver = $iomem_map{$addr};
					} elsif ($irq && $interrupts_map{$irq} && $interrupts_map{$irq} =~ /$match/) {
						# use only on last resort, shared interrupts makes this often wrong
						$driver = $interrupts_map{$irq};
					}
					if ($driver) {
						$driver = $linux_driver_map{$driver} || $driver;
						if ($driver =~ /^($networks)$/) {
							$pci_to_driver{$pci} = $driver;
						} else {
							$driver_count{$driver}++;
							$pci_to_driver{$pci} = $driver;
							$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
							$pci_to_driver{$pci} .= ($driver_count{$driver} - 1);
						}
					}
				}
				if ($pci_to_driver{$pci}) {
					$irq_to_driver{$irq_class} = $pci_to_driver{$pci} if ($irq_class);
					$irq_to_driver{$irq} = $pci_to_driver{$pci} if ($irq);
				}
			} elsif ($line =~ /^\s*Subsystem: ([0-9a-f]+):([0-9a-f]+)/i) {
				$card = "0x$2$1";
			}
			last if eof($in);
			$line = <$in>;
		}
		close($in);
	}
}

sub grok_pciconf {
	my($in) = "PCICONF";
	my($line);
	my($dev, $class, $card, $chip, $rev);
	my($model);
	my(%vendor, %model);
	my(@lines, $lines);

	system("which $pciconf > /dev/null 2>&1");
	return if ($?);

	open($in, "$pciconf -lv 2> /dev/null |") || warn("exec: $pciconf: $!");
	if (eof($in)) {
		close($in);
		open($in, "$pciconf -l 2> /dev/null |") || warn("exec: $pciconf: $!");
	}
	while ($line = <$in>) {
		$debug_pciconf .= $line;
		push(@lines, $line);
		if ($line =~ /^(\S+)\@.*class=(\S+).*card=(\S+).*chip=(\S+).*rev=(\S+)/) {
			($dev, $class, $card, $chip, $rev) = ($1, $2, $3, $4, $5);
		} elsif ($line =~ /^\s+vendor\s+=\s+'(.*)'$/) {
			$vendor{$dev} = clean_pci_model($1);
		} elsif ($line =~ /^\s+device\s+=\s+'(.*)'$/) {
			$model{$dev} = clean_pci_model($1);
		}
	}
	close($in);

	foreach $line (@lines) {
		if ($line =~ /^(\S+)\@.*class=(\S+).*card=(\S+).*chip=(\S+).*rev=(\S+)/) {
			($dev, $class, $card, $chip, $rev) = ($1, $2, $3, $4, $5);
			$class =~ s/..$//;
			if ($sysctl_dev{$dev} && $sysctl_dev{$dev}{'xmodel'}) {
				$model = $sysctl_dev{$dev}{'xmodel'};
			} elsif ($vendor{$dev} && $model{$dev}) {
				$model = "$vendor{$dev} $model{$dev}";
			} else {
				$model = 'unknown';
			}
			$pci_all .= "<$card,$chip,$rev>";
			$sig_pci .= "$card,$chip,$rev\t$model\n";
			if ($chipsets{"$chip,$rev"}) {
				$other_devices{'Chipset'}{"$chip,$rev"} = $chipsets{"$chip,$rev"};
			} elsif ($chipsets{$chip}) {
				$other_devices{'Chipset'}{$chip} = $chipsets{$chip};
			}
			if ($dev =~ /^(atapci|ciss|amr|ahc|ahd|asr|aac|aacch|mpt|sym|twe|twa|asr|mfi|ida)\d+/ ) {
				$need_cissutil++ if ($1 eq "ciss");
				$need_megarc++ if ($1 eq "amr");
				$need_mfiutil++ if ($1 eq "mfi");
				$need_mptutil++ if ($1 eq "mpt");
				$need_arcconf++ if ($1 eq "aac");
				$need_twcli++ if ($1 eq "twe" || $1 eq "twa");
				if (!$pci_devices{'storage'}{"$card,$chip,$rev"} && !$pci_devices{'storage'}{"$card,$chip"}) {
					$debug .= "Debug-Disk-Control:\t$dev: $model, $card,$chip,$rev\n";
					$card = "0x????????";
				}
				if ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
					$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
				} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
					$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
				} else {
					$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
				}
			} elsif ($dev =~ /^($freebsd_net_devs)/) {
				$interfaces{$dev}{'model'} = $model unless ($model eq 'unknown' && $interfaces{$dev}{'model'});
				if (!$pci_devices{'network'}{"$card,$chip,$rev"}) {
					$debug .= "Debug-Network:\t$dev: ";
					$debug .= "$interfaces{$dev}{'model'}, " if ($interfaces{$dev}{'model'});
					$debug .= "$card,$chip,$rev\n";
					$card = "0x????????";
				}
				if (!$pci_devices{'network'}{"$card,$chip,$rev"}) {
					$rev = "0x??";
				}
				if ($pci_devices{'network'}{"$card,$chip,$rev"}) {
					$interfaces{$dev}{'model'} = $pci_devices{'network'}{"$card,$chip,$rev"};
				}
			} elsif ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
				$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
			} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
				$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
			} elsif ($pci_devices{'network'}{"$card,$chip,$rev"}) {
				$other_devices{'Network'}{$dev} = "$dev: " . $pci_devices{'network'}{"$card,$chip,$rev"};
			} elsif ($pci_devices{'other'}{"$card,$chip,$rev"}) {
				$other_devices{$pci_devices{'other'}{"$card,$chip,$rev"}{type}}{"$card,$chip"} = $pci_devices{'other'}{"$card,$chip,$rev"}{model};
			} elsif ($pci_classes{$class}) {
				$other_devices{$pci_classes{$class}}{$dev} = "$dev: $model";
				$debug .= "Debug-" . $pci_classes{$class} . ":\t$dev: $model, $card,$chip,$rev\n";
			}
		}
	}
}

sub hdparm {
	my($vol, $device, $id) = @_;
	my($result) = 0;
	my($in) = 'HDPARM';
	my($line);

	system("which hdparm > /dev/null 2>&1");
	return $result if ($?);

	# only use hdparm for /dev/hdx and /dev/sdx devices
	return $result if ($device !~ /^\/dev\/(sd|hd)[a-z]+$/);

	open($in, "hdparm -iI $device 2> /dev/null |") || warn("hdparm: $!");
	$debug_storage .= ">> hdparm -iI $device\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$line =~ s/\s*$//;
		if ($line =~ /Model=([^,]+)/i || $line =~ /Model Number:\s*(.*)/i) {
			$props{drives}{$id}{model} = canon_drive($1);
			$result = 1;
		}
		if ($line =~ /SerialNo=([^,]+)/i || $line =~ /Serial Number:\s*(.*)/i) {
			$props{drives}{$id}{serial} = $1;
		}
		if ($line =~ /FwRev=([^,]+)/i || $line =~ /Firmware Revision:\s*(.*)/i) {
			$props{drives}{$id}{firmware} = $1;
		}
		if ($line =~ /^\s*Used: (\S+)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		}
		if ($line =~ /^\s*Likely used: (\d+)/) {
			$props{drives}{$id}{interface} = parse_protocol("ATA-$1");
		}
		if ($line =~ /LBAsects=(((\d+)))/i || $line =~ /LBA(48)?\s*user addressable sectors( =|:)\s*(\d+)/i) {
			$props{drives}{$id}{size} = $3 * 512;
		}
		if ($line =~ /BuffSize=([^,]+)/i)  {
			$props{drives}{$id}{cache_size} = parse_bytes($1, 1024);
		}
		if ($line =~ /WriteCache=([^,]+)/i) {
			$props{drives}{$id}{wcache_enable} = parse_enable($1);
		}
		if ($line =~ /^\s*(\*?)\s*Write cache\s*$/i) {
			$props{drives}{$id}{wcache_support} = 1;
			$props{drives}{$id}{wcache_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Look-ahead\s*$/i) {
			$props{drives}{$id}{readahead_support} = 1;
			$props{drives}{$id}{readahead_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Device-initiated interface power management\s*$/i) {
			$props{drives}{$id}{dipm_support} = 1;
			$props{drives}{$id}{dipm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Host-initiated interface power management\s*$/i) {
			$props{drives}{$id}{hipm_support} = 1;
			$props{drives}{$id}{hipm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Native Command Queueing \(NCQ\)\s*$/i) {
			$props{drives}{$id}{ncq_support} = 1;
			$props{drives}{$id}{ncq_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Queue depth: (\d+)/i) {
			$props{drives}{$id}{ncq_depth} = $1;
		}
		if ($line =~ /^\s*(\*?)\s*SMART feature set\s*$/i) {
			$props{drives}{$id}{smart_support} = 1;
			$props{drives}{$id}{smart_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Automatic Acoustic Management feature set\s*$/i) {
			$props{drives}{$id}{aam_support} = 1;
			$props{drives}{$id}{aam_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Recommended acoustic management value: (\d+), current value: (\d+)/i) {
			$props{drives}{$id}{aam_value} = $2;
		}
		if ($line =~ /^\s*(\*?)\s*Power Management feature set\s*$/i) {
			$props{drives}{$id}{pm_support} = 1;
			$props{drives}{$id}{pm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Advanced Power Management feature set\s*$/i) {
			$props{drives}{$id}{apm_support} = 1;
			$props{drives}{$id}{apm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Advanced power management level: (\d+)/i) {
			$props{drives}{$id}{apm_value} = $1;
		}
		if ($line =~ /^\s*(\*)\s*(SATA-I signaling)/i) {
			$props{drives}{$id}{interface} = parse_protocol($2);
		}
		if ($line =~ /^\s*(\*)\s*(SATA-II signaling)/i) {
			$props{drives}{$id}{interface} = parse_protocol($2);
		}
		if ($line =~ /Logical Unit WWN Device Identifier:\s*(\S+)/i) {
			$props{drives}{$id}{wwn} = "0x$1";
		}
	}
	close($in);
	return $result;
}

sub smartctl {
	my($vol, $device, $vendor, $id) = @_;
	my($result) = 0;
	my($line);
	my($in) = 'SMARTCTL';
	my($dev_opt) = ($vendor =~ /^ATA\s*$/) ? '-d ata' : '';

	system("which smartctl > /dev/null 2>&1");
	return $result if ($?);

	# orginally used "smartctl -a" here.  this causes some PE2950 RHEL boxes to hang/crash
	# e.g. test164.data.corp.sp1.yahoo.com, kr2-ymon-001.ysm.kr2.yahoo.com, s200.del.re1.yahoo.net
	# need to be very careful here.  "smartctl -i" does appear to be safe for all types, but
	# for now we'll be conservative and just use this for ATA drives, which is why we
	# starting using this in the first place.  before we use for anything else, need to
	# be careful.
	#
	# 20080824 - enabling for all types now.  as stated earlier does appear safe

	open($in, "smartctl -i $dev_opt $device 2> /dev/null |") || warn "exec smartctl: $!";
	$debug_storage .= ">> smartctl -i $dev_opt $device\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Device Model:\s*(.*)/) {
			$props{drives}{$id}{model} = canon_drive($1);
		} elsif ($line =~ /^Serial Number:\s*(.*)/) {
			$props{drives}{$id}{serial} = $1;
		} elsif ($line =~ /^Firmware Version:\s*(.*)/) {
			$props{drives}{$id}{firmware} = $1;
		} elsif ($line =~ /^User Capacity:\s*([\d,]+)\s+bytes/) {
			my($size) = $1;
			$size =~ s/,//g;
			$props{drives}{$id}{size} = int($size);
		} elsif ($line =~ /^SMART support is: Available/) {
			$props{drives}{$id}{smart_support} = 1;
		} elsif ($line =~ /^SMART support is: Enabled/) {
			$props{drives}{$id}{smart_enable} = 1;
		}
		$result = 1;
	}
	close($in);
	return $result;
}

# new lshal - 0.5.11rc2 on fedora9
#	- info.bus seems to have gone away, only used now for usb?
#	- linux.sysfs_path_device is gone

sub grok_lshal {
	my($in) = "LSHAL";
	my($line);
	my(%hal, $udi);
	my(%cleanup);

	open($in, "$lshal 2> /dev/null |") || warn("exec: $lshal: $!");
	while ($line = <$in>) {
		$debug_lshal .= $line;
		chomp($line);
		if ($line =~ /^udi = \'([^\']*)\'/) {
			$udi = $1;
		} elsif ($line =~ /^\s+(\S+)\s+=\s+\'([^\']*)\'/) {
			$hal{$udi}{$1} = $2;
		} elsif ($line =~ /^\s+(\S+)\s+=\s+(\S+)/) {
			$hal{$udi}{$1} = $2;
		}
	}
	close($in);
	foreach $udi (keys %hal) {
		if ($hal{$udi}{'pci.product_id'}) {
			my($num) = substr($hal{$udi}{'linux.sysfs_path'}, -7);
			my($id) = $pci_map{$num};
			if ($hal{$udi}{'net.interface'}) {
				my($model) = $other_devices{'Network'}{$num};
				$model =~ s/^$num: //;
				$interfaces{$hal{$udi}{'net.interface'}}{'model'} = $model;
				$cleanup{'Network'}{$num}++;
				$success{'grok_lshal_network'}++;
			}
			$need_arcconf++ if ($hal{$udi}{'pci.vendor'} && $hal{$udi}{'pci.vendor'} eq 'Adaptec' && $hal{$udi}{'pci.product'} =~ /AAC/);
		} elsif (($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'ide') || ($hal{$udi}{'info.bus'} && $hal{$udi}{'info.bus'} eq 'ide_host')) {
			my($num, $dev);
			if ($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'ide') {
				# new lshal - 0.5.11rc2
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path'} =~ /(.{7})\/(ide\d+)\//);
			} else {
				# legacy lshal
				($num, $dev) = ($hal{$udi}{'ide_host.linux.sysfs_path'} =~ /(.{7})\/([^\/]+)$/);
			}
			if ($num =~ /^[0-9a-f:\.]+$/) {
				# guard against case - linux.sysfs_path = '/sys/devices/ide0/0.0'
				# which happens with lshal 0.5.11rc2 and ide == compatible in bios
				my($model) = $other_devices{'Disk-Control'}{$num};
				$dev =~ s/^.*\///;
				$model =~ s/^$num: //;
				$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
				$cleanup{'Disk-Control'}{$num}++;
			}
		} elsif (($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'scsi_host') || ($hal{$udi}{'info.bus'} && $hal{$udi}{'info.bus'} eq 'scsi_host')) {
			my($num, $dev);
			if ($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'scsi_host') {
				# new lshal - 0.5.11rc2
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path'} =~ /(.{7})\/([^\/]+)$/);
			} else {
				# legacy lshal
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path_device'} =~ /(.{7})\/([^\/]+)$/);
			}
			unless ($hal{$udi}{'info.parent'} =~ /usb/) {
				# check for synonyms for this hostid
				$dev = pci_to_hostid($num) || $dev;
				unless (defined $other_devices{'Disk-Control'}{$dev}) {
					my($model) = $other_devices{'Disk-Control'}{$num};
					$dev =~ s/^.*\///;
					$model =~ s/^$num: //;
					$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
					$cleanup{'Disk-Control'}{$num}++;
				}
			}
		} elsif ($hal{$udi}{'storage.drive_type'} && $hal{$udi}{'storage.drive_type'} eq 'disk') {
			my($devpath) = $hal{$udi}{'block.device'};
			my($vol) = $devpath;
			$vol =~ s/.*\///;
			$volumes{$vol}++;

			my($firmware, $serial, $model);
			$firmware = $hal{$udi}{'storage.firmware_version'};
			$serial = $hal{$udi}{'storage.serial'};
			$model = canon_drive($hal{$udi}{'storage.model'});
			if ($model =~ /(raid\d+)/i) {
				$props{$vol}{raid} = $1;
			} else {
				$props{$vol}{raid} = "JBOD";
			}

			if ($hal{$udi}{'storage.bus'} eq 'ide') {
				my($node) = $udi;
				my($ms) = 'unknown';
				while ($hal{$node}{'info.parent'}) {
					$node = $hal{$node}{'info.parent'};
					if ($hal{$node}{'info.product'}) {
						if ($hal{$node}{'info.product'} =~ /IDE device \((slave|master)\)/) {
							$ms = $1;
						}
					}
					if ($hal{$node}{'linux.subsystem'} && $hal{$node}{'linux.subsystem'} eq 'ide') {
						# new lshal - 0.5.11rc2 on fedora9
						my($dev) = ($hal{$node}{'linux.sysfs_path'} =~ /\/(ide\d+)\//);
						$props{$vol}{'device'} = $dev;
						$props{$vol}{'ms'} = $ms if ($ms);
						$success{'grok_lshal_ide'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{serial} = $serial;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					} elsif ($hal{$node}{'info.bus'} eq 'ide_host') {
						# legacy lshal
						my($dev) = ($hal{$node}{'ide_host.linux.sysfs_path'} =~ /\/([^\/]+)$/);
						$props{$vol}{'device'} = $dev;
						$props{$vol}{'ms'} = $ms if ($ms);
						$success{'grok_lshal_ide'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{serial} = $serial;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					}
				}
			} elsif ($hal{$udi}{'storage.bus'} eq 'scsi') {
				# good test case - feedb-beta.local.mud

				my($node) = $udi;
				my($extmodel) = $hal{$node}{'storage.vendor'} . ' ' . $hal{$node}{'storage.model'};
				my($scsi_id, $channel, $id, $lun) = (0, 0, 0, 0);

				$need_megacli++ if ($extmodel =~ /$perc_names/);
				$need_megarc++ if ($extmodel =~ /MegaRAID/);
				$need_twcli++ if ($extmodel =~ /(3ware|AMCC)/i);

				if ($hal{$node}{'info.parent'} =~ /scsi_(\d+)_(\d+)_(\d+)_(\d+)/) {
					($scsi_id, $channel, $id, $lun) = ($1, $2, $3, $4);
				} elsif ($hal{$node}{'info.parent'} =~ /scsi_host_scsi_device/) {
					my($parent) = $hal{$node}{'info.parent'};
					($scsi_id, $channel, $id, $lun) = ($hal{$parent}{'scsi.host'}, $hal{$parent}{'scsi.bus'}, $hal{$parent}{'scsi.target'}, $hal{$parent}{'scsi.lun'});
				}

				if ($extmodel =~ /MegaRAID/) {
					my($logical) = sprintf("scsi%d-%d-%d-%d", $scsi_id, 0, $id, 0);
					$logical_to_os{'megaraid'}{$logical} = $vol;
				} elsif ($extmodel =~ /$perc_names/) {
					my($logical) = sprintf("scsi%d-%d-%d-%d", $scsi_id, 0, $id, 0);
					$logical_to_os{'perc'}{$logical} = $vol;
				} elsif ($extmodel =~ /(3ware|AMCC)/i) {
					my($logical) = sprintf("c%d-u%d", $scsi_id, $id);
					$logical_to_os{'tw'}{$logical} = $vol;
				} else {
					$logical_to_os{'other'}{$hal{$node}{'storage.model'}} = $vol;
				}

				while ($hal{$node}{'info.parent'}) {
					if (($hal{$node}{'info.bus'} && $hal{$node}{'info.bus'} eq 'scsi_host') ||
					    ($hal{$node}{'info.category'} && $hal{$node}{'info.category'} eq 'scsi_host')) {
						my($dev) = ($hal{$node}{'linux.sysfs_path'} =~ /\/([^\/]+)$/);
						$props{$vol}{'device'} = $dev;
						$success{'grok_lshal_scsi'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					}
					$node = $hal{$node}{'info.parent'};
				}
			}
		}
	}

	my($type, $num);
	foreach $type (keys %cleanup) {
		foreach $num (keys %{$cleanup{$type}}) {
			delete $other_devices{$type}{$num};
		}
	}
}

my(%lvm_logical_to_physical);

sub lvdisplay {
	my($in) = 'LVM';
	my($line);
	my($result) = 0;
	my($lv) = "unknown";
	my($lv_count) = 0;
	my($lv_size) = 0;
	my($vol) = "unknown";
	my($model) = "unknown";
	my($sep) = '';
	my($looking) = 0;

	system("which lvdisplay > /dev/null 2>&1");
	return $result if ($?);
	
	$debug_storage .= ">> lvm\n";

	open($in, "lvdisplay -m 2> /dev/null |") || warn "exec lvdisplay: $!";
	$debug_storage .= ">>> lvdisply -m\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result = 1;
		if ($line =~ /^Synopsis:/) {
			$result = 0;
			last;
		}
		if ($line =~ /LV Name\s+(\S+)/) {
			$lv = $1;
			$lv =~ s,^/dev/,,;
			$lv =~ s,/,-,g;
			$vol = "dm$lv_count";
			$lv_count++;
		} elsif ($line =~ /LV Size\s+(.*)/) {
			$lv_size = parse_bytes($1, 1000);
		} elsif ($line =~ /Type\s+striped/) {
			$ndrives++;
			$volumes{$vol}++;
			$props{$vol}{bytes} = $lv_size;
			$props{$vol}{raid} = "RAID 1";
			$sep = "partitions: ";
			$model = '';
			$looking++;
		} elsif ($line =~ /Stripe size\s+(.*)/) {
			$props{$vol}{stripe} = parse_bytes($1, 1024);
		} elsif ($line =~ /Physical volume\s+(\S+)/) {
			my($dev) = $1;
			$lvm_logical_to_physical{$lv} = $dev;
			$dev =~ s,^/dev/,,;
			$model .= "$sep$dev";
			$sep = ", ";
		} elsif ($line =~ /^\s*$/) {
			if ($looking) {
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$lvm_logical_to_physical{$lv} = $vol;
			}
			$looking = 0;
		}
	}	
	close($in);

	if (!$result) {
		my($volumes);
		open($in, "lvscan 2> /dev/null |") || warn "exec lvscan: $!";
		$debug_storage .= ">>> lvscan\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /ACTIVE\s*\"(\S+)\"/) {
				$volumes .= " $1";
			}
		}
		close($in);

		if ($volumes) {
			my($next) = 0;
			open($in, "lvdisplay -v $volumes 2> /dev/null |") || warn "exec lvdisplay: $!";
			$debug_storage .= ">>> lvdisplay -v $volumes\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /LV Name\s+(\S+)/) {
					$lv = $1;
				}
				if ($next) {
					if ($line =~ /^\s+(\S+)/) {
						$lvm_logical_to_physical{$lv} = $1;
					}
					$next = 0;
				}
				if ($line =~ /^\s*PV Name/) {
					$next = 1;
				}
			}
			close($in);
		}
	}

	return $result;
}

sub df {
	my($in) = 'DF';
	my($line);
	my($result) = 0;

	system("which df > /dev/null 2>&1");
	return $result if ($?);

	if ($os_type eq "linux") {
		open($in, "df -P -k -l 2> /dev/null |") || warn "exec df: $!";
		$debug_storage .= ">> df -P -k -l\n";
	} else {
		open($in, "df -k -t ufs 2> /dev/null |") || warn "exec df: $!";
		$debug_storage .= ">> df -k -t ufs\n";
	}

	$line = <$in>;
	$debug_storage .= $line;
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result++;
		my($vol, $size, $used, $avail, @rest) = split(' ', $line);
		next unless ($vol =~ m,^/dev/,);
		if ($vol =~ m,^/dev/mapper/(.*),) {
			$vol = $lvm_logical_to_physical{$1} || $vol;
		}
		$vol = $lvm_logical_to_physical{$vol} || $vol;
		$vol =~ s,^/dev/,,;
		if ($os_type eq "linux") {
			if ($vol =~ m,^[hs]d,) {
				$vol =~ s,\d+$,,;
			} else {
				$vol =~ s,p\d+$,,;
			}
		} else {
			$vol =~ s,s\d+[a-e]$,,;		# e.g. /dev/ad0s1e
			$vol =~ s,(\d+)[a-e]$,$1,;	# e.g. /dev/ad0e
			$vol =~ s,p\d+$,,;		# e.g. /dev/mfid0p4
			$need_ccdconfig++ if ($vol =~ /^ccd/);
		}
		$props{$vol}{df}{size} += $size * 1024;
		$props{$vol}{df}{used} += $used;
		$props{$vol}{df}{avail} += $avail;
	}
	close($in);

	return $result;
}

sub other_devices {
	my($line);
	my($type, $device);
	my(@chipsets);
	my($out, @out);
	foreach $type (sort keys %other_devices) {
		foreach $device (sort cmp_devs keys %{$other_devices{$type}}) {
			if ($type eq 'Chipset') {
				push(@chipsets, $other_devices{$type}{$device});
				next;
			}

			$out = '';
			$out .= "$type:\t";
			$out .= "\t" if (length($type) < 7);

			my($description) = $other_devices{$type}{$device};
			my($dev) = $device;
			if ($hostid_to_pci{$dev}) {
				$dev = $hostid_to_pci{$dev};
			}
			if ($dev =~ /^[0-9a-f:\.]+$/) {
				my($new) = $pci_to_driver{$dev} || pci_to_hostid($dev);
				if ($new) {
					if ($type eq 'Network') {
						$description =~ s/^$device/$device ($new)/;
					} else {
						$description =~ s/^$device/$new/;
						$dev = $new;
					}
				}
			}
			$out .= $description;

			if ($type eq 'Disk-Control') {
				if ($props{$dev}{'package'}) {
					if ($props{$dev}{'firmware'}) {
						$out .= ", Package $props{$dev}{'package'}";
					} else {
						$out .= ", FW $props{$dev}{'package'}";
					}
				}
				if ($props{$dev}{'firmware'}) {
					$out .= ", FW $props{$dev}{'firmware'}";
				}
				if ($props{$dev}{'bios'}) {
					$out .= ", BIOS $props{$dev}{'bios'}";
				}
				if ($props{$dev}{'cache-status'} || $props{$dev}{'cache-size'}) {
					$out .= ", Cache";
					$out .= " $props{$dev}{'cache-status'}" if $props{$dev}{'cache-status'};
					if ($props{$dev}{'cache-size'}) {
						$out .= ' ' . print_bytes($props{$dev}{'cache-size'}, 1024, 1);
					} elsif (defined $props{$dev}{'cache-read'}) {
						$out .= ' ' . print_bytes($props{$dev}{'cache-read'}, 1024, 1);
						$out .= '/' . print_bytes($props{$dev}{'cache-write'}, 1024, 1);
						$out .= ' (R/W)';
					}
				}
				if ($props{$dev}{'bbu'}) {
					$out .= ", BBU";
				}
			}
			push(@out, $out);
		}
	}
	foreach $out (sort cmp_other_devices @out) {
		$output .= $out . "\n";
	}
	if (@chipsets) {
		my($chipset, $pretty, $vendor, $last);
		foreach $chipset (sort @chipsets) {
			$chipset =~ s/^(NB|SB|IB): //;
			($vendor) = ($chipset =~ /^(\S+)\s+/);
			if ($last && $vendor eq $last) {
				$chipset =~ s/^$vendor\s+//;
				$pretty .= ", " if $pretty;
			} else {
				$pretty .= "; " if $pretty;
			}
			$pretty .= $chipset;
			$last = $vendor;
		}
		$output .= "Chipset:\t$pretty\n";
	} elsif ($system_model_index && defined $system_models[$system_model_index]{chipset}) {
		$output .= "Chipset:\t$system_models[$system_model_index]{chipset}\n";
	} else {
		$debug .= "Debug-Chipset:\tUnknown\n";
	}
}

sub cmp_other_devices {
	my($atype, $anum) = ($a =~ /^[^:]*:\s*([^:]*?)(\d*)/);
	my($btype, $bnum) = ($b =~ /^[^:]*:\s*([^:]*?)(\d*)/);
	if ($anum && $bnum) {
		return ($atype cmp $btype || $anum <=> $bnum);
	} else {
		return ($atype cmp $btype)
	}
}

sub dmesg_disks {
	my($line);
	my(%disks, $disk);
	my($result, $sep);
	foreach $line (split(/\n/, $dmesg_buf)) {
		if ($line =~ /^((mfid|twed|idad|da|ad|wd|sd)\d+):/) {
			$disks{$1}++;
		}
	}
	$sep = "";
	$result = "";
	foreach $disk (sort { $a cmp $b } keys %disks) {
		$result .= "$sep$disk";
		$sep = " ";
	}
	return $result;
}

my(%dmesg);

sub grok_dmesg {
	my($line);
	foreach $line (split(/\n/, $dmesg_buf)) {
		chomp($line);
		$line =~ s/\(R\)//g;
		if ($line =~ /^CPU:\s+([^\(]*).*\(([\d\.]+-MHz)/i) {
			my($family, $speed) = ($1, $2);
			$family =~ s/\bCPU.*//;
			$family =~ s/\s+$//;
			$speed =~ s/-//g;
			$dmesg{proc}{family} = $family;
			$dmesg{proc}{speed} = $speed;
			if ($family =~ /amd/i) {
				$dmesg{proc}{vendor} = "AMD";
			} elsif ($family =~ /intel/i || $family =~ /xeon/i || $family =~ /pentium/i) {
				$dmesg{proc}{vendor} = "Intel";
			}
		} elsif ($line =~ /^\s*Features\s*=\s*0x([0-9a-f]+)/i) {
			$dmesg{proc}{features} = $1;
		} elsif ($line =~ /Origin\s*=\s*"(\S+)".*Id\s*=\s*0x([0-9a-f]+).*Stepping\s*=\s*(\S+)/i) {
			$dmesg{proc}{origin} = $1;
			$dmesg{proc}{cpuid} = $2;
			$dmesg{proc}{stepping} = $3;
		} elsif ($line =~ /^((atapci|mfi|mpt|ahc|ahd|asr|aac|aacch|sym|ida|twe|twa)\d+): <([^>]*)>.*device/) {
			$need_mfiutil++ if ($2 eq "mfi");
			$need_mptutil++ if ($2 eq "mpt");
			$need_twcli++ if ($2 eq "twe" || $2 eq "twa");
			$other_devices{"Disk-Control"}{$1} = "$1: $3";
			$other_devices{"Disk-Control"}{$1} =~ s/\s+controller\b.*//i;
		} elsif ($line =~ /^(($freebsd_net_devs)\d+): <([^>]*)>.*device/) {
			$interfaces{$1}{'model'} = $3;
			$interfaces{$1}{'model'} =~ s/\s*(Ethernet|Network|,).*//;
		} elsif ($line =~ /^ciss\d+/) {
			$need_cissutil++;
			if ($line =~ /^(ciss\d+): <([^>]*)>.*port.*device/) {
				$other_devices{"Disk-Control"}{$1} = "$1: $2";
			} elsif ($line =~ /^(ciss\d+):\s+firmware\s+([\d\.]+)/) {
				$props{$1}{'firmware'} = $2;
			}
		} elsif ($line =~ /^amr\d+/) {
			$need_megarc++;
			if ($line =~ /^(amr\d+): <([^>]*)>.*Firmware\s+([^,]+),.*BIOS\s+([^,]+)/) {
				$other_devices{"Disk-Control"}{$1} = "$1: $2";
				$props{$1}{'firmware'} = $3;
				$props{$1}{'bios'} = $4;
			}
		}
	}
}

my(%procs);

sub parse_proc {
	my($in, $section, $handle) = @_;
	my($line);
	my($socket) = ++$procs{sockets};
	my($ignore) = 1;
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*Type:\s+(CPU|Central Processor)/i) {
			$ignore = 0;
		} elsif ($line =~ /^\s*Socket Designation:\s+(Slot-2)/i) {
			$ignore = 0 
		} elsif ($line =~ /^\s*current (cpu )?speed:\s+(.*)$/i) {
			my($speed) = $2;
			$speed =~ s/\s+//g;
			$procs{$socket}{speed} = ($speed eq "0MHz") ? 0 : $speed;
		} elsif ($line =~ /^\s*(external )?clock:\s+(.*)$/i) {
			my($clock) = $2;
			$clock =~ s/\s+//g;
			$procs{$socket}{clock} = ($clock eq "0MHz") ? 0 : $clock;
		} elsif ($line =~ /^\s*family:\s+(.*)$/i) {
			$procs{$socket}{family} = $1;
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$procs{$socket}{vendor} = $1;
		} elsif ($line =~ /^\s*cpu present:\s+(.*)$/i) {
			$procs{$socket}{status} = ($1 =~ /yes/i);
		} elsif ($line =~ /^\s*id:\s+(.*)$/i) {
			my($id) = lc($1);
			if ($id =~ / /) {
				# from dmidecode
				$id = join('', reverse(split(' ', $id))) if ($id =~ / /);
			}
			$id =~ s/^0x//;
			$procs{$socket}{cpuid} = substr($id, -8);
			$procs{$socket}{features} = substr($id, 0 ,8);
		} elsif ($line =~ /^\s*status:\s+(.*)$/i) {
			$procs{$socket}{status} = ($1 =~ /\bpopulated\b/i);
		} elsif ($line =~ /^\s*(L\d+ Cache Handle):\s+(.*)$/) {
			$procs{$socket}{$1} = $2;
		}
	}
	if ($ignore) {
		$procs{sockets}--;
		delete $procs{$socket};
	}
}

$smbios{'Memory'}{'Count'} = 0;

sub parse_memory {
	my($in, $section, $sub, $handle) = @_;
	my($count) = $smbios{$section}{'Count'} + 1;
	my($line);
	my($ignore) = 0;
	my($speed) = 0;
	my($size) = 0;
	my($manufacturer) = 0;
	my($part) = 0;
	my($type) = 0;
	my($key);

	if ($sub eq "Device") {
		# override previous Module entries
		if (!$smbios{$section}{$sub} && $count > 1) {
			while ($count) {
				delete $smbios{$section}{$count};
				$count--;
			}
			$count = 1;
		}
		$smbios{$section}{$sub}++;
	} elsif ($smbios{$section}{'Device'}) {
		# ignore if we've already parsed Device entries
		$ignore = 1;
	}

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*(current )?speed:\s+((\d+).*)/i) {
			$speed = ($3 == 0 || $3 > 10000) ? 0 : $2;
			$speed =~ s/\([\d\.]+ ns\)//;
			$speed =~ s/\s+//g;
		} elsif ($line =~ /^\s*(installed )?size:\s+([^\(]*)/i) {
			$size = $2;
		} elsif ($line =~ /^\s*(memory\s+)?type:\s+(.*)$/i) {
			$type = $2;
			$ignore = 1 if ($type eq "Flash");
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$manufacturer = $1;
		} elsif ($line =~ /^\s*part\s+number:\s+(.*)$/i) {
			$part = canon_memory($1);
		} elsif ($line =~ /^\s*form factor: (chip|tsop)/i) {
			$ignore = 1;
		}
	}
	if (!$ignore) {
		$smbios{$section}{'Count'} = $count;
		$smbios{$section}{$count}{'Speed'} = $speed;
		$smbios{$section}{$count}{'Size'} = $size;
		$smbios{$section}{$count}{'Type'} = $type;
		$smbios{$section}{$count}{'Manufacturer'} = $manufacturer;
		$smbios{$section}{$count}{'Part Number'} = $part;
	}
}

sub parse_bios {
	my($in, $section, $handle) = @_;
	my($count) = ++$smbios{$section}{'Count'};
	my($line);
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*vendor:\s+(\S+.*)$/i) {
			$smbios{$section}{$count}{'Vendor'} = $1;
		} elsif ($line =~ /^\s*version:\s+(\S+.*)$/i) {
			my($version) = $1;
			$version =~ s/BIOS Version(:)?\s+//i;
			$version =~ s/\-\[(.*)\]\-/$1/;
			$version =~ s/^'(.*)'$/$1/;
			$version = clean_white($version);
			if ($version =~ s/\s+(BIOS )?Date(:)?\s+(\S+.*)//i) {
				$smbios{$section}{$count}{'Release Date'} = $3;
			} 
			$smbios{$section}{$count}{'Version'} = $version;
		} elsif ($line =~ /^\s*release date:\s+(\S+.*)$/i) {
			$smbios{$section}{$count}{'Release Date'} = $1;
		}
	}
}


sub parse_system {
	my($in, $section, $handle) = @_;
	my($count) = ++$smbios{$section}{'Count'};
	my($line);
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*product( name)?:\s+(.*)$/i) {
			$smbios{$section}{$count}{'Product Name'} = $2;
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$smbios{$section}{$count}{'Manufacturer'} = $1;
		}
	}
}

my(%interface_to_driver);

sub grok_network {
	my($in) = "IFCONFIG";
	my($line);
	my($int);

	open($in, "ifconfig -a |") || warn("exec: ifconfig: $!");
	$debug_network .= ">> ifconfig -a\n";

	if ($os_type eq "freebsd") {
		while ($line = <$in>) {
			$debug_network .= $line;
			chomp($line);
			if ($line =~ /^(\w+):/) {
				$int = $1;
			}
			if ($line =~ /^\s*media:\s*(Ethernet)?\s*(.*)/) {
				my($media) = $2;
				$media =~ s/status.*//;
				$media =~ s/autoselect//;
				$media =~ s/[\(\)]//g;
				$media =~ s/\s+/ /g;
				$media =~ s/^\s+//;
				$media =~ s/\s+$//;
				$interfaces{$int}{media} = $media;
			} elsif ($line =~ /^\s*ether\s*(\S*)/) {
				$interfaces{$int}{mac} = $1;
			}
			if ($line =~ /\bstatus:\s*(.*)/) {
				$interfaces{$int}{status} = $1;
			}
		}
	} elsif ($os_type eq "linux") {
		my($pci) = 0;
		my($debug_network_2) = '';
		while ($line = <$in>) {
			$debug_network .= $line;
			chomp($line);
			if ($line =~ /^(\w+)\s+Link.*HWaddr\s+(\S+)/) {
				$int = $1;
				$interfaces{$int}{mac} = lc($2);
				my($eth) = "ETHTOOL";
				my($eline);
				my($speed, $duplex) = ("", "");
				open($eth, "$ethtool $int 2> /dev/null |") || warn("exec: $ethtool: $!");
				$debug_network_2 .= ">> $ethtool $int\n";
				while ($eline = <$eth>) {
					$debug_network_2 .= $eline;
					chomp($eline);
					if ($eline =~ /Link detected:\s*(\S+)/) {
						# 2.6 kernels only?
# comment out for now 20080201, found a case (mc1020.mail.re1, op951002.inktomisearch.com) running rhel4u6 that
# shows link detected when there is no link.  ifconfig doesn't show RUNNING in this
# case, so maybe ifconfig is more accurate.  if this is still commented out some
# months from now, this should all be nuked.
#						$interfaces{$int}{status} = ($1 eq "yes") ? "active" : "no carrier";
# in order to keep output the same (i.e. no carrier when no link as root), set
# to no carrier as default and let ifconfig decide
						$interfaces{$int}{status} = "no carrier" unless ($interfaces{$int}{status} && $interfaces{$int}{status} eq "active");
					} elsif ($eline =~ /Speed:\s*(\S+)/) {
						$speed = $1;
					} elsif ($eline =~ /Duplex:\s*(\S+)/) {
						$duplex = lc($1);
					}
				}
				$interfaces{$int}{media} = $speed;
				$interfaces{$int}{media} .= " <$duplex-duplex>" if ($duplex);
				close($eth);

				my($driver) = $int;
				open($eth, "$ethtool -i $int 2> /dev/null |") || warn("exec: $ethtool: $!");
				$debug_network_2 .= ">> $ethtool -i $int\n";
				while ($eline = <$eth>) {
					$debug_network_2 .= $eline;
					chomp($eline);
					if ($eline =~ /driver:\s+(\S+)/) {
						$interface_to_driver{$int} = $1;
					} elsif ($eline =~ /bus-info:\s+(\S+)/) {
						$pci = $1;
					}
				}
				close($eth);

				if (!$pci) {
					if (-l "/sys/class/net/$int/driver") {
						$interface_to_driver{$int} = readlink("/sys/class/net/$int/driver");
						$interface_to_driver{$int} =~ s/.*\///;
					}
					if (-l "/sys/class/net/$int/device") {
						$pci = readlink("/sys/class/net/$int/device");
						$pci =~ s/.*\///;
					}
				}
			}
			if (defined $int && $line =~ /RUNNING/) {
				# needed for 2.4 kernels which don't have "Link detected" above
				$interfaces{$int}{status} = "active";
			}
			if (!$pci && defined $int && $line =~ /Interrupt:(\d+)/i) {
				$pci = $irq_to_pci{"$1,0x0200"} if ($irq_to_pci{"$1,0x0200"});
			}
			if (!$pci && defined $int && $line =~ /Memory:([0-9a-f]+)/i) {
				$pci = $iomem_to_pci{$1} if ($iomem_to_pci{$1});
			} 
			if ($line =~ /^\s*$/) {
				if ($int) {
					if (!$pci) {
						if ($dmesg_buf =~ /\b$int:.*probe: addr 0x([0-9a-f]+)*/im) {
							$pci = $iomem_to_pci{$1} if ($iomem_to_pci{$1});
						}
					}
					if (!$interface_to_driver{$int}) {
						if ($pci && $pci_to_driver{$pci}) {
							$interface_to_driver{$int} = $pci_to_driver{$pci};
						} elsif ($dmesg_buf =~ /^(\S+):.*\b$int:.*link is up/im) {
							$interface_to_driver{$int} = $1;
						}
					}
					if ($pci) {
						$pci =~ s/^0000://;
						if ($other_devices{'Network'}{$pci}) {
							my($model) = $other_devices{'Network'}{$pci};
							$model =~ s/^$pci: //;
							$interfaces{$int}{'model'} = $model;
							delete $other_devices{'Network'}{$pci};
						}
					}
				}
				undef $int;
				$pci = 0;
			}
		}
		$debug_network .= $debug_network_2;
	}

	close($in);
}

sub cpuinfo {
	my($in) = 'CPUINFO';
	my($line);

	# prefer data from smbinfo if available
	return unless (-r $cpuinfo);

	my($family, $model, $stepping, $vendor_id, $model_name, $flags, $speed, $cores, $threads);
	my(%physical_id, %core_id);
	my($n) = 0;

	open($in, "< $cpuinfo") || warn "open: $cpuinfo: $!";
	while ($line = <$in>) {
		$debug_cpuinfo .= $line;
		chomp($line);
		if ($line =~ /^cpu family\s*:\s*(\d+)/i) {
			$family = $1;
		} elsif ($line =~ /^model\s*:\s*(\d+)/i) {
			$model = $1;
		} elsif ($line =~ /^stepping\s*:\s*(\d+)/i) {
			$stepping = $1;
		} elsif ($line =~ /^vendor_id\s*:\s*(.*)/i) {
			$vendor_id = $1;
		} elsif ($line =~ /^model name\s*:\s*(.*)/i) {
			$model_name = $1;
		} elsif ($line =~ /^cpu MHz\s*:\s*(.*)/i) {
			$speed = $1;
		} elsif ($line =~ /^physical id\s*:\s*(.*)/i) {
			$physical_id{$1}++;
		} elsif ($line =~ /^core id\s*:\s*(.*)/i) {
			$core_id{$1}++;
		} elsif ($line =~ /^flags\s*:\s*(.*)/i) {
			$flags = $1;
		} elsif ($line =~ /^cpu cores\s*:\s*(.*)/i) {
			$cores = $1;
		} elsif ($line =~ /^\s*$/) {
			if ($family && $model) {
				$n++;
				unless ($procs{$n}{cpuid}) {
					if ($vendor_id =~ /AMD/i && $family >= 15) {
						$procs{$n}{cpuid} = sprintf("%x", (($family - 15) << 20) | (($model >> 4) << 16) | (15 << 8) | (($model & 0xf) << 4) | $stepping);
					} else {
						$procs{$n}{cpuid} = sprintf("%x", ($family << 8) | ($model << 4) | $stepping);
					}
				}
				$procs{$n}{vendor} = $vendor_id;
				$procs{$n}{family} = $model_name;
				$procs{$n}{speed} = $speed;
				$procs{$n}{flags} = $flags;
				# don't set {status} since we don't know if this is a socket
			}
		}
	}

	# $cpu_cores			how many physical cores per socket for this particular cpuid, lookup table
	# $cpu_threads			how many logical cores per socket for this particular cpuid, lookup table

	# $cores			how many cores per chip does cpuinfo think

	# $procs{threads}		how many logical cores for overall system, kernel sees but may be disabled
	# $procs{active_threads}	how many logical cores for overall system that are enabled
	# $procs{cores}			how many physical cores for overall system, some may be disabled in bios/kernel
	# $procs{active_cores}		how many physical cores for overall system that are enabled
	# $procs{sockets}		how many sockets for overall system, might not be populated with chip

	my($id) = cpuid($procs{1}{vendor}, hex($procs{1}{cpuid}));

	my($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = ('', 0, 0, 0);
	if ($cpu_models{$id}) {
		$model_name = canon_cpu_model($model_name);
		if (ref($cpu_models{$id}[0]) eq "ARRAY") {
			my($try);
			foreach $try (@{$cpu_models{$id}}) {
				($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$try->[1]};
				last if ($model_name =~ /$try->[0]/i);
			}
		} else {
			($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$cpu_models{$id}};
		}
	}

	if ($cpu_cores) {
		if ($cores && $cores != $cpu_cores) {
			$debug .= sprintf("Debug-Cores:\t%d (cpuinfo) != %d (lookup)\n", $cores, $cpu_cores);
		}
		$cores = $cpu_cores;
	}

	if (!$cores) {
		if ($model_name =~ /opteron/i && $flags =~ /\bht\b/i) {
			# older kernels don't have the "cpu cores" field, so guess that ht == 2
			$cores = 2;
		}
	}

	my($htt) = 0;
	if (defined $procs{1}{features}) {
		my($features) = $procs{1}{features};
		$htt = (hex($features) >> $feature_htt_bit) & 0x0001;
	} else {
		$htt = ($flags =~ /\bht\b/i);
	}

	if ($cpu_threads) {
		# don't know of a way to actually test to see if hyperthreads are enabled.
		# so this will be wrong if hyperthreading is disabled in bios, in  particular
		# this is a problem with old dl320 g2's that probably don't have bios support,
		# so cpu is HT capable but no way for system to run in that mode.  this one is
		# easy to fix because cpu count is one.  need to use dmidecode for this.

		# well, at least on old kernels, some seem not to show HT in flags, which i
		# guess means kernel doesn't support it.  so let's at least get that case right.

		# actually absense of HT does not imply kernel doesn't support it.  e.g.
		# tp-ms-db1.search.sp1 is running 2.4.21-32.ELsmp, supports HT but doesn't show in cpuinfo
		if ($htt) {
			$threads = $cpu_threads;
		}
	}

	# $n is now number of threads

	# this we're sure about, cpuinfo always gets this right
	$procs{threads} = $n;
	$procs{active_threads} = $n;

	my(@keys) = keys %core_id;
	if ($#keys > -1) {
		$n = $#keys + 1;
	} else {
		if ($n > 1 && $threads) {
			$n = $n / $threads;
		}
	}

	# $n is now number of cores

	$procs{cores} = $n;
	$procs{active_cores} = $n;

	if ($n > 1 && $cores) {
		$n = $n / $cores;
	}

	# $n is now number of chips

	@keys = keys %physical_id;
	$procs{chips} = $#keys + 1;
}

sub mptutil {
	my($result) = 0;
	my($in) = 'MPT';
	my($line);
	my($dev);
	my($adapter, @adapters);
	my($vol);


	system("which $mptutil > /dev/null 2>&1");
	return $result if ($?);
	$have_mptutil = 1;
	$debug_storage .= ">> $mptutil\n";

	open($in, "$mptutil show adapter 2> /dev/null |") || warn "exec $mptutil: $!";
	$debug_storage .= ">>> $mptutil show adapter\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^(mpt(\d+))\s+Adapter/) {
			$adapter = $2;
			$dev = $1;
			push(@adapters, $adapter);
			$got_mptutil = 1;
			$result = 1;
		} elsif ($line =~ /^\s*battery backup: (.*)/i) {
			$props{$dev}{'bbu'} = ($1 eq "present");
		} elsif ($line =~ /^\s*onboard memory: (.*)/i) {
			$props{$dev}{'cache-size'} = parse_bytes($1, 1024);
		}
	}
	close($in);

	open($in, "$mptutil show drives 2> /dev/null |") || warn "exec $mptutil: $!";
	$debug_storage .= ">>> $mptutil show drives\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^(mpt\d+) physical drives:/i) {
			$vol = "$1-spares";
		} elsif ($line =~ /\b(SPARE|UNCONFIGURED)\b.*<([^>]+)>/) {
			my($model) = canon_drive($2);
			$props{$vol}{raid} = "JBOD";
			$drives{$vol}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $vol;
			$drive_id++;
			$volumes{$vol}++;
			$ndrives++;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		my($array);
		my(%array_to_vol);
		my($dev) = "mpt$adapter";

		open($in, "$mptutil -u $adapter show firmware 2> /dev/null |") || warn "exec $mptutil: $!";
		$debug_storage .= ">>> $mptutil -u $adapter show firmware\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BIOS\s+(\S+)/) {
				$props{$dev}{'bios'} = $1;
			} elsif ($line =~ /^APP\s+(\S+)/) {
				$props{$dev}{'firmware'} = $1;
			}
		}
		close($in);

		open($in, "$mptutil -u $adapter show config 2> /dev/null |") || warn "exec $mptutil: $!";
		$debug_storage .= ">>> $mptutil -u $adapter show config\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*volume (da)?(\d+) \(([^\)]*)\) (\S+) (\S+) (\S+)/) {
				$vol = "da$2";
				delete $drives{$vol};
				$props{$vol}{bytes} = parse_bytes($3, 1024);
				$props{$vol}{raid} = $4;
				my($stripe, $status) = ($5, $6);
				if ($stripe =~ /^\d+/) {
					$props{$vol}{stripe} = parse_bytes($stripe, 1024);
					$props{$vol}{status} = parse_volume_status($status);
				} else {
					$props{$vol}{status} = parse_volume_status($stripe);
				}
			} elsif ($line =~ /^\s*dedicated spare/) {
				$vol = '';
			} elsif ($line =~ /^\s*array (\d+)\s*$/) {
				if ($vol) {
					$array_to_vol{$1} = $vol;
				}
			}
		}
		close($in);

		open($in, "$mptutil -u $adapter show config 2> /dev/null |") || warn "exec $mptutil: $!";
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^\s*volume (da)?(\d+)/) {
				$vol = "da$2";
			} elsif ($line =~ /^\s*spare (\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $2;
				my($model) = canon_drive($3) || "Unknown";
				my($spares) = "$dev-spares";
				$drives{$spares}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $4;
				$props{drives}{$drive_id}{volume} = $spares;
				$drive_id++;
				$volumes{$spares}++;
				$props{$spares}{raid} = "JBOD";
				$ndrives++;
				if ($status ne "ONLINE") {
					$props{$spares}{failed}++;
				}
			} elsif ($line =~ /^\s*drive (da)?(\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $3;
				my($model) = canon_drive($4) || "Unknown";
				if ($1) {
					$vol = "da$2";
					delete $drives{$vol};	# nuke camcontrol info
				}
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $5;
				$props{drives}{$drive_id}{status} = parse_drive_status($status);
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
				if ($status eq "REBUILD") {
					$props{$vol}{rebuild}++;
				} elsif ($status eq "MISSING") {
					$props{$vol}{missing}++;
				} elsif ($status ne "ONLINE") {
					# e.g. OFFLINE, FAILED
					$props{$vol}{failed}++;
				}
			}
		}
		close($in);
	}
}

sub mfiutil {
	my($result) = 0;
	my($in) = 'MFI';
	my($line);
	my($dev);
	my($adapter, @adapters);
	my($vol);
	my($try);

	# MegaCli docs at:
	# http://www.lsilogic.com/files/docs/techdocs/storage_stand_prod/sas/mr_sas_sw_ug.pdf

	system("which $mfiutil > /dev/null 2>&1");
	return $result if ($?);
	$have_mfiutil = 1;
	$debug_storage .= ">> $mfiutil\n";

	$try = 0;
	while (1) {
		open($in, "$mfiutil -u $try show adapter 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $try show adapter\n";
		my($found) = 0;
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(mfi(\d+))\s+Adapter/) {
				$adapter = $2;
				$dev = $1;
				push(@adapters, $adapter);
				$got_mfiutil = 1;
				$result = 1;
				$found = 1;
			} elsif ($line =~ /^\s*firmware: (.*)/i) {
				$props{$dev}{'package'} = $1;
			} elsif ($line =~ /^\s*battery backup: (.*)/i) {
				$props{$dev}{'bbu'} = ($1 eq "present");
			} elsif ($line =~ /^\s*onboard memory: (.*)/i) {
				$props{$dev}{'cache-size'} = parse_bytes($1, 1024);
			}
		}
		close($in);
		last if !$found;
		$try++;
	}

	foreach $adapter (@adapters) {
		my($array);
		my(%array_to_vol);
		my($dev) = "mfi$adapter";

		open($in, "$mfiutil -u $adapter show drives 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show drives\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(mfi\d+) physical drives:/i) {
				$vol = "$1-spares";
			} elsif ($line =~ /\b(SPARE|UNCONFIGURED)\b.*<([^>]+)>/) {
				my($model) = canon_drive($2) || "Unknown";
				$props{$vol}{raid} = "JBOD";
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show firmware 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show firmware\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BIOS\s+(\S+)/) {
				$props{$dev}{'bios'} = $1;
			} elsif ($line =~ /^APP\s+(\S+)/) {
				$props{$dev}{'firmware'} = $1;
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show config 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show config\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*volume (mfid)?(\d+) \(([^\)]*)\) (\S+) (\S+) (\S+)/) {
				$vol = "mfid$2";
				delete $drives{$vol};
				$props{$vol}{bytes} = parse_bytes($3, 1024);
				$props{$vol}{raid} = $4;
				my($stripe, $status) = ($5, $6);
				if ($stripe =~ /^\d+/) {
					$props{$vol}{stripe} = parse_bytes($stripe, 1024);
					$props{$vol}{status} = parse_volume_status($status);
				} else {
					$props{$vol}{status} = parse_volume_status($stripe);
				}
			} elsif ($line =~ /^\s*dedicated spare/) {
				$vol = '';
			} elsif ($line =~ /^\s*array (\d+)\s*$/) {
				if ($vol) {
					$array_to_vol{$1} = $vol;
				}
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show config 2> /dev/null |") || warn "exec $mfiutil: $!";
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^\s*array (\d+) of (\d+) drives/) {
				$array = $1;
				$vol = $array_to_vol{$array};
			} elsif ($line =~ /^\s*drive (\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $2;
				my($model) = canon_drive($3) || "Unknown";
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $4;
				$props{drives}{$drive_id}{status} = parse_drive_status($status);
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
				if ($status eq "REBUILD") {
					$props{$vol}{rebuild}++;
				} elsif ($status eq "MISSING") {
					$props{$vol}{missing}++;
				} elsif ($status ne "ONLINE") {
					# e.g. OFFLINE, FAILED
					$props{$vol}{failed}++;
				}
			} elsif ($line =~ /^\s*volume/) {
				last;
			}
		}
		close($in);
	}
}

sub omreport {
	my($omreport) = 'omreport';
	my($result) = 0;
	my($line);
	my($controller, %controllers);
	my($device);
	my(%seen);
	my(%count);

	return 1 if ($got_omreport);

	system("which $omreport > /dev/null 2>&1");
	return $result if ($?);
	$have_omreport = 1;
	$debug_storage .= ">> $omreport\n";

	$count{'megaraid'} = 0;
	$count{'megaraid_sas'} = 0;

	open($in, "$omreport storage controller 2> /dev/null |") || warn "exec $omreport: $!";
	$debug_storage .= ">>> $omreport storage controller\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^\s*$/) {
			$device = "unknown";
		} elsif ($line =~ /^ID\s*:\s*(\d+)\s*$/) {
			$controller = $1;
			$got_omreport = 1;
			$result = 1;
		} elsif ($line =~ /^Name\s*:\s*(.*)/) {
			my($name) = $1;
			if ($name =~ /PERC [34]/) {
				$device = 'megaraid' . $count{'megaraid'}++;
				$controllers{$controller} = $device;
			} elsif ($name =~ /PERC/) {
				$device = 'megaraid_sas' . $count{'megaraid_sas'}++;
				$controllers{$controller} = $device;
			}
		} elsif ($line =~ /^Firmware Version\s*:\s*(.*)/) {
			# don't use this if megarc already set firmware version
			$props{$device}{'package'} = $1 unless ($props{$device}{'firmware'});
		} elsif ($line =~ /^Cache Memory Size\s*:\s*(.*)/) {
			$props{$device}{'cache-size'} = parse_bytes($1, 1024);
		}
	}
	close($in);

	foreach $controller (keys %controllers) {
		$device = $controllers{$controller};
		open($in, "$omreport storage battery controller=$controller 2> /dev/null |") || warn "exec $omreport: $!";
		$debug_storage .= ">>> $omreport storage battery controller=$controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^Status\s*:\s*(\S+)/) {
				$props{$device}{'bbu'} = ($1 eq "Ok");
			}
		}
		close($in);

		my($volume, $name, $raid, $bytes, $stripe, $virtual) = (0, 0, 0, 0, 0, 0);
		my(%virtuals);

		open($in, "$omreport storage vdisk controller=$controller 2> /dev/null |") || warn "exec $omreport: $!";
		$debug_storage .= ">>> $omreport storage vdisk controller=$controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^ID\s*:\s*(\d+)/) {
				$virtual = $1;
			} elsif ($line =~ /^Layout\s*:\s*(\S+)/) {
				$raid = canon_raid($1);
			} elsif ($line =~ /^Name\s*:\s*(.*)/) {
				$name = $1;
			} elsif ($line =~ /^Size\s*:\s*.*\((\d+) bytes\)/) {
				$bytes = $1;
			} elsif ($line =~ /^Device Name\s*:\s*(\S+)/) {
				$volume = $1;
				$volume =~ s/^\/dev\///;
			} elsif ($line =~ /^Stripe Element Size\s*:\s*(.*)/) {
				$stripe = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*$/) {
				if ($name) {
					$volume = "$device-l$virtual" unless $volume;
					$virtuals{$virtual} = $volume;
					$volumes{$volume}++;
					$props{$volume}{bytes} = $bytes;
					$props{$volume}{raid} = $raid;
					$props{$volume}{stripe} = $stripe;
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
					($volume, $name, $raid, $bytes, $stripe, $virtual) = (0, 0, 0, 0, 0, 0);
				}
			}
		}
		close($in);

		foreach $virtual (keys %virtuals) {
			$volume = $virtuals{$virtual};
			my($have_size) = $props{$volume}{bytes};
			my($state);
			my($vendor, $model, $serial, $firmware, $year, $week, $day, $wwn) = ('', '', '', '', '', '', '', '');

			open($in, "$omreport storage adisk controller=$controller vdisk=$virtual 2> /dev/null |") || warn "exec $omreport: $!";
			$debug_storage .= ">>> $omreport storage adisk controller=$controller vdisk=$virtual\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /^State\s*:\s*(.*)/) {
					$state = $1;
					$props{$volume}{failed}++ if ($1 ne 'Online');
				} elsif ($line =~ /^Hot Spare\s*:\s*(.*)/) {
					$props{$volume}{spare}++ if ($1 eq 'Yes');
				} elsif ($line =~ /^Product ID\s*:\s*(.*)/) {
					$model = $1;
				} elsif ($line =~ /^Serial No\.\s*:\s*(.*)/) {
					$serial = $1;
				} elsif ($line =~ /^Revision\s*:\s*(.*)/) {
					$firmware = $1;
				} elsif ($line =~ /^Vendor ID\s*:\s*(.*)/) {
					$vendor = $1;
				} elsif ($line =~ /^Manufacture Day\s*:\s*(.*)/) {
					$day = $1;
				} elsif ($line =~ /^Manufacture Week\s*:\s*(.*)/) {
					$week = $1;
				} elsif ($line =~ /^Manufacture Year\s*:\s*(.*)/) {
					$year = $1;
				} elsif ($line =~ /^SAS Address\s*:\s*(.*)/) {
					$wwn = $1;
				} elsif ($line =~ /^Capacity\s*:\s*.*\((\d+) bytes\)/) {
					$bytes = $1;
					$props{$volume}{bytes} += $bytes unless ($have_size);
				} elsif ($line =~ /^\s*$/) {
					if ($model) {
						$model = canon_drive("$vendor-$model");
						$ndrives++;
						$drives{$volume}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $volume;
						$props{drives}{$drive_id}{serial} = $serial;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{wwn} = $wwn;
						$props{drives}{$drive_id}{size} = $bytes;
						$props{drives}{$drive_id}{date} = sprintf("%04d%02d%02d", $year, $week, $day) if ($year && $week && $day);
						$drive_id++;
					}
					($model, $vendor) = ('', '');
				}
			}
			close($in);
		}
	}

	return $result;
}

sub arcconf {
	my($result) = 0;
	my($line);
	my($controller, @controllers);
	my($volume);
	my(%mapping);

	system("which $arcconf > /dev/null 2>&1");
	return $result if ($?);
	$have_arcconf = 1;
	$debug_storage .= ">> $arcconf\n";

	open($in, "$arcconf getversion 2> /dev/null |") || warn "exec $arcconf: $!";
	$debug_storage .= ">>> $arcconf getversion\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Controller #(\d+)/) {
			push(@controllers, int($1));
			$got_arcconf++;
			$result++;
		}
	}
	close($in);

	foreach $controller (@controllers) {
		open($in, "$arcconf getconfig $controller 2> /dev/null |") || warn "exec $arcconf: $!";
		$debug_storage .= ">>> $arcconf getconfig $controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			last if ($line =~ /^Logical device information/);
		}
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			last if ($line =~ /^Physical Device information/);
			if ($line =~ /Logical device number (\d+)/) {
				# hopefully gets overwritten by "logical device name"
				$volume = "aac-c$controller-l$1";
			} elsif ($line =~ /Logical device name.*: (.*)/) {
				my($logical) = $1;
				# if only one vol, map it
				my(@keys) = keys %{$logical_to_os{'other'}};
				if ($#keys == 0) {
					$logical = $keys[0];
				}
				if ($logical_to_os{'other'}{$logical}) {
					$volume = $logical_to_os{'other'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume};
				}
			} elsif ($line =~ /RAID level.*: (.*)/) {
				$props{$volume}{raid} = "RAID $1";
			} elsif ($line =~ /Status of logical device.*: (.*)/) {
				$props{$volume}{status} = parse_volume_status($1);
			} elsif ($line =~ /Size.*: (.*)/) {
				$props{$volume}{bytes} = parse_bytes($1, 1024);
			} elsif ($line =~ /Stripe-unit size.*: (.*)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			} elsif ($line =~ /Read-cache mode.*: (\S+)/) {
				$props{$volume}{rcache_enable} = parse_enable($1);
			} elsif ($line =~ /Write-cache mode.*: (\S+)/) {
				$props{$volume}{wcache_enable} = parse_enable($1);
			} elsif ($line =~ /Segment \d+.*: .*\((\d+),(\d+)\)/) {
				my($channel, $device) = ($1, $2);
				$mapping{"$channel,$device"} = $volume;
			}
		}
		my($vendor, $model, $hide, $state, $channel);
		my($id) = 0;
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Device is (.*)/) {
				my($type) = $1;
				if ($type =~ /Hard drive/) {
					$hide = 0;
					$id = $drive_id;
				} else {
					$hide = 1;
					$id = 0;
				}
			} elsif ($line =~ /Channel #(\d+)/) {
				$channel = $1;
			} elsif ($line =~ /^\s*State\s+: (.*)/) {
				$state = $1;
				$props{drives}{$id}{status} = parse_drive_status($1);
			} elsif ($line =~ /^\s*Reported Channel,Device\s+: (.*)/) {
				if ($mapping{$1}) {
					$volume = $mapping{$1};
				} else {
					$volume = "aac-c$controller-spares";
					$props{$volume}{raid} = "JBOD";
				}
			} elsif ($line =~ /^\s*Firmware\s+: (.*)/) {
				$props{drives}{$id}{firmware} = $1;
			} elsif ($line =~ /^\s*Serial number\s+: (.*)/) {
				$props{drives}{$id}{serial} = $1;
			} elsif ($line =~ /^\s*Size\s+: (.*)/) {
				$props{drives}{$id}{size} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Write Cache\s+: (.*)/) {
				$props{drives}{$id}{wcache_enable} = parse_enable($1);
			} elsif ($line =~ /^\s*S\.M\.A\.R\.T\.\s+: (.*)/) {
				$props{drives}{$id}{smart_enable} = parse_enable($1);
			} elsif ($line =~ /^\s*Transfer Speed\s+: (.*)/) {
				$props{drives}{$id}{speed} = parse_drive_speed($1);
			} elsif ($line =~ /^\s*Vendor\s+: (.*)/) {
				$vendor = $1;
			} elsif ($line =~ /^\s*Model\s+: (.*)/) {
				$model = canon_drive("$vendor$1");
				if (!$hide) {
					$ndrives++;
					$drives{$volume}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{volume} = $volume;
					$drive_id++;
					$volumes{$volume}++;
					if ($state eq "Rebuilding") {
						$props{$volume}{rebuild}++;
					} elsif ($state ne "Online" && $state ne "Ready") {
						$props{$volume}{failed}++;
					}
				}
			}
		}
		close($in);
	}

	return $result;
}

sub megacli {
	my($adapter, @adapters);
	my($result) = 0;
	my($line);
	my($volume) = "NA";
	my(%seen);
	my($driver) = 'megaraid_sas';

	# MegaCli docs at:
	# http://www.lsilogic.com/files/docs/techdocs/storage_stand_prod/sas/mr_sas_sw_ug.pdf

	my($megacli) = 'MegaCli';
	system("which $megacli > /dev/null 2>&1");
	if ($?) {
		$megacli = 'MegaCli64';
		system("which $megacli > /dev/null 2>&1");
		if ($?) {
			$megacli = 'megacli';
			system("which $megacli > /dev/null 2>&1");
			return $result if ($?);
		}
	}
	$have_megacli = 1;
	$debug_storage .= ">> $megacli\n";

	open($in, "$megacli -AdpAllInfo -aAll 2> /dev/null |") || warn "exec $megacli: $!";
	$debug_storage .= ">>> $megacli -AdpAllInfo -aAll\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Adapter\s+\#(\d+)/) {
			$adapter = int($1);
			push(@adapters, $adapter);
			$got_megacli = 1;
			$result = 1;
		} elsif ($line =~ /^FW Package Build\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'package'} = $1;
		} elsif ($line =~ /^FW Version\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'firmware'} = $1;
		} elsif ($line =~ /^BIOS Version\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'bios'} = $1;
		} elsif ($line =~ /^Serial No\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'serial'} = $1;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"$driver$adapter"}) {
			my(@keys) = keys %{$controller_volumes{"$driver$adapter"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		my($state, $wwn, $raw) = ('', '');

		open($in, "$megacli -cfgDsply -a$adapter 2> /dev/null |") || warn "exec $megacli: $!";
		$debug_storage .= ">>> $megacli -cfgDsply -a$adapter\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BBU: Present/i) {
				$props{"$driver$adapter"}{'bbu'} = 1;
			} elsif ($line =~ /^Memory: (.*)/i) {
				$props{"$driver$adapter"}{'cache-size'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^DISK GROUPS: (\d+)/) {
				$volume = "$driver$adapter-spares";
				$props{$volume}{raid} = "JBOD";
				$volume = ($default_volume || "$driver$adapter-vol$1");

				# make some guesses here for linux on adapter, channel, lun
				my($logical) = sprintf("scsi%d-%d-%d-%d", $adapter, 0, $1, 0);
				if (defined $logical_to_os{'perc'}{$logical}) {
					$volume = $logical_to_os{'perc'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
				} else {
					# if only one vol, map it
					my(@keys) = keys %{$logical_to_os{'perc'}};
					if ($#keys == 0) {
						$volume = $logical_to_os{'perc'}{$keys[0]};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					}
				}

				$volumes{$volume}++;
			} elsif ($line =~ /^RAID Level: Primary-(\d+)/) {
				$props{$volume}{raid} = "RAID $1";
			} elsif ($line =~ /^State:\s*(\S+)/) {
				$props{$volume}{status} = parse_volume_status($1);
			} elsif ($line =~ /^Size:\s*(\S+)/) {
				$props{$volume}{bytes} = parse_bytes($1, 1024);
			} elsif ($line =~ /^Raw Size:\s*(\S+)/) {
				$raw = parse_bytes($1, 1024);
			} elsif ($line =~ /^Stripe Size:\s*(\S+)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			} elsif ($line =~ /^Firmware state:\s+(\S+)/) {
				$state = $1;
			} elsif ($line =~ /^SAS Address\(0\):\s*(\S+)/) {
				$wwn = $1;
			} elsif ($line =~ /^Inquiry Data:\s+(\S+)\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
				my($model, $serial);
				if ($4) {
					$model = canon_drive("$1-$2-$3");
					$serial = $5;
				} else {
					$model = canon_drive("$1-$2");
					$serial = $3;
				}
				$ndrives++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{serial} = $serial;
				$props{drives}{$drive_id}{wwn} = $wwn;
				$props{drives}{$drive_id}{size} = $raw;
				$props{drives}{$drive_id}{status} = parse_drive_status($state);
				$drive_id++;
				$volumes{$volume}++;
				if ($state eq "FAILED") {
					$props{$volume}{failed}++;
				}
			}
		}
		close($in);
	}
	return $result;
}

sub megarc {
	my(%megadrives);
	my($adapter, @adapters);
	my($result) = 0;
	my($line);
	my($volume) = "NA";
	my(%seen);
	my($driver) = ($os_type eq 'linux') ? 'megaraid' : 'amr';

	system("which $megarc > /dev/null 2>&1");
	return $result if ($?);
	$have_megarc = 1;
	$debug_storage .= ">> $megarc\n";

	open($in, "$megarc -AllAdpInfo -noLog 2> /dev/null |") || warn "exec $megarc: $!";
	$debug_storage .= ">>> $megarc -AllAdpInfo\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		last if ($line =~ /^\s*AdapterNo/);
	}
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$got_megarc = 1;
		$result = 1;
		if ($line =~ /^\s*(\d+)/) {
			push(@adapters, int($1));
		} else {
			last;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		unless ($os_type eq "freebsd" && $os_date < 20070130) {
			# armd bug in early freebsd kernels causes data corruption for this query, so don't do it.
			open($in, "$megarc -ctlrInfo -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
			$debug_storage .= ">>> $megarc -ctlrInfo -a$adapter\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /Firmware Version : (\S+)/i) {
					$props{"$driver$adapter"}{'firmware'} = $1;
				}
				if ($line =~ /BIOS Version : (\S+)/i) {
					$props{"$driver$adapter"}{'bios'} = $1;
				}
				if ($line =~ /DRAM : (\S+)/i) {
					$props{"$driver$adapter"}{'cache-size'} = parse_bytes($1, 1024);
				}
			}
			close($in);
		}

		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"$driver$adapter"}) {
			my(@keys) = keys %{$controller_volumes{"$driver$adapter"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		open($in, "$megarc -ldInfo -Lall -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
		$debug_storage .= ">>> $megarc -ldInfo -Lall -a$adapter\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /Logical Drive\s*:\s*(\d+)\s*\(\s*Adapter:\s*(\d+)\s*\)/) {
				$volume = "$driver$adapter-spares";
				$props{$volume}{raid} = "JBOD";
				$volume = ($default_volume || "$driver$adapter-vol$1");

				# make some guesses here for linux on adapter, channel, lun
				my($logical) = sprintf("scsi%d-%d-%d-%d", $adapter, 0, $1, 0);
				if (defined $logical_to_os{'megaraid'}{$logical}) {
					$volume = $logical_to_os{'megaraid'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
				} else {
					# if only one vol, map it
					my(@keys) = keys %{$logical_to_os{'megaraid'}};
					if ($#keys == 0) {
						$volume = $logical_to_os{'megaraid'}{$keys[0]};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					}
				}

				$volumes{$volume}++;
			}
			if ($line =~ /RaidLevel\s*:\s*(\S+)/) {
				$props{$volume}{raid} = "RAID $1";
			}
			if ($line =~ /StripSz\s*:\s*(\S+)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			}
			if ($line =~ /Cache\s*:\s*(\S+)/) {
				$props{$volume}{cachepolicy} = $1;	# CachedIO, DirectIO
			}
			if ($line =~ /WrPolicy\s*:\s*(\S+)/) {
				$props{$volume}{wrpolicy} = $1;		# WriteBack, ?
			}
			if ($line =~ /RdAhead\s*:\s*(\S+)/) {
				$props{$volume}{rdahead} = $1;		# Yes, No
			}
			if ($line =~ /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/) {
				my($status) = $5;
				my($drive) = sprintf("%d-%d-%d", $adapter, int($1), int($2));
				$megadrives{$drive}{vol} = $volume;
				if ($status eq "FAILED") {
					$props{$volume}{failed}++;
				} elsif ($status eq "RBLD") {
					$props{$volume}{rebuild}++;
				}
			}
		}
		close($in);

		open($in, "$megarc -phys -idAll -chAll -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
		$debug_storage .= ">>> $megarc -phys -idAll -chAll -a$adapter\n";
		my($type, $rev);
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Adapter\s+(\d+),\s+Channel\s+(\d+),\s+Target ID\s+(\d+)/) {
				my($drive) = sprintf("%d-%d-%d", int($1), int($2), int($3));
				$volume = $megadrives{$drive}{vol} || "$driver$adapter-spares";
			}
			if ($line =~ /Type\s*:\s+(\S+)/) {
				$type = lc($1);
			}
			if ($line =~ /Revision\s*:\s*(\S+)/) {
				$rev = $1;
			}
			if ($line =~ /Product\s*:\s+(\S+)/ && $type eq "disk") {
				my($model) = canon_drive($1);
				$ndrives++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{firmware} = $rev;
				$drive_id++;
				$volumes{$volume}++;
			}
		}
		close($in);
	}
	return $result;
}

sub mptable {
	my($in) = 'MPTABLE';
	my($line);
	my($result) = 0;

	system("which $mptable > /dev/null 2>&1");
	return $result if ($?);

	open($in, "$mptable 2> /dev/null |") || warn "exec $mptable: $!";
	while ($line = <$in>) {
		last if ($line =~ /^Processors:/);
	}
	while ($line = <$in>) {
		last if ($line =~ /^[-= ]*$/);
		$result = 1;
		$procs{mptable}++;
		if (!$procs{$procs{mptable}}{cpuid}) {
			my(@cols) = split(' ', $line);
			my($family, $model, $step, $flags) = @cols[$#cols-3 .. $#cols];
			if ($family == 6 && $model == 2 && $step == 1) {
				# appears to be the only bug with mptable, otherwise seems reliable
				# e.g. uds1.ads.cnb.yahoo.com, p24.chat.dcn.yahoo.com, mailsrv.bangalore.corp.yahoo.com
				# and it's not just 6b1 that it gets wrong
				# so ignore it here, hopefully get it from dmesg
			} else {
				$procs{$procs{mptable}}{cpuid} = sprintf("0x%x%x%x", $family, $model, $step);
			}
			$flags =~ s/^0x//;
			$procs{$procs{mptable}}{features} = $flags;
		}
	}
	close($in);
	return $result;
}

sub twcli {
	my($in) = 'TWCLI';
	my($line);
	my($controller, %controllers);
	my(%units, %ports);
	my($version) = 0;
	my($result) = 0;
	my(%seen);

	system("which $twcli > /dev/null 2>&1");
	return $result if ($?);
	$have_twcli = 1;
	$debug_storage .= ">> $twcli\n";

	open($in, "$twcli info 2> /dev/null |") || warn "exec $twcli: $!";
	$debug_storage .= ">>> $twcli info\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		last if ($line =~ /^---------/);
	}
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
	 	last if ($line =~ /^\s*$/);
		if ($line =~ /^Controller\s+(\d+):\s+(\S+)/) {
			$controllers{"c$1"} = $2;
			$version = 1;
		} elsif ($line =~ /^(c\d+)\s+(\S+)/) {
			$controllers{$1} = $2;
			$version = 2;
		}
	}
	close($in);

	foreach $controller (keys %controllers) {
		my(%header);
		$got_twcli++;
		$result++;
		open($in, "$twcli info $controller |") || warn "exec $twcli: $!";
		$debug_storage .= ">>> $twcli info $controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			my(@cols) = split(' ', $line);
			if ($line =~ /^Unit/) {
				my($i);
				for ($i=0; $i <= $#cols; $i++) {
					$header{$cols[$i]} = $i;
				}
			} elsif ($line =~ /^u(\d+)/ || $line =~ /^\s*unit\s+(\d+)/i) {
				my($unit) = "u$1";
				my($volume);
				my($type) = ($version == 1) ? $cols[2] : $cols[1];
				my($size) = ($version == 1) ? "$cols[5] $cols[6]" : "$cols[$header{'Size(GB)'}] GB";
				my($stripe) = parse_bytes(($version == 1) ? $cols[4] : $cols[$header{'Stripe'}], 1024);
				if ($type eq "SPARE") {
					$volume = "tw-$controller-spares";
					$props{$volume}{raid} = "JBOD";
				} else {
					$volume = "$controller-$unit";
					if (defined $logical_to_os{'tw'}{$volume}) {
						$volume = $logical_to_os{'tw'}{$volume};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					} else {
						# if only one vol, map it
						my(@keys) = keys %{$logical_to_os{'tw'}};
						if ($#keys == 0) {
							$volume = $logical_to_os{'tw'}{$keys[0]};
							# remove info we got from /proc/scsi/scsi
							delete $drives{$volume} unless $seen{$volume}++;
						}
					}
					$props{$volume}{raid} = $type;
					$props{$volume}{bytes} = parse_bytes($size, 1024);
				}
				$props{$volume}{stripe} = $stripe;
				$volumes{$volume}++;
				$units{$unit}{volume} = $volume;

				# update 3ware controller model if we have enough data
				my($device) = $props{$volume}{device};
				if ($controllers{$controller} && $device) {
					if (defined $other_devices{'Disk-Control'}{$device}) {
						$other_devices{'Disk-Control'}{$device} =~ s/(7xxx\/8xxx|9xxx)/$controllers{$controller}/;
					}
				}
			} elsif ($line =~ /^p(\d+)/ || $line =~ /^\s*port\s+(\d+)/i) {
				my($port) = "p$1";
				my($unit) = "unknown";
				next if ($line =~ /NOT.PRESENT/);
				if ($version == 1) {
					if ($line =~ /unit\s+(\d+)/i) {
						$unit = "u$1";
					} elsif ($line =~ /\(NO UNIT\)/) {
						$unit = "free";
					}
				} else {
					$unit = $cols[2];
				}
				my($volume);
				if (defined($units{$unit}) && $units{$unit}{volume}) {
					$volume = $units{$unit}{volume};
				} else {
					if ($unit eq "-") {
						$volume = "tw-$controller-free";
					} else {
						$volume = "tw-$controller-$unit";
					}
					$volumes{$volume}++;
					$props{$volume}{raid} = "JBOD";
				}
				$props{$volume}{failed}++ if ($line =~ /DEGRADED/);
				$ports{$controller}{$port} = $volume;
			}
		}
		close($in);

		if ($version == 1) {
			my($unit);
			foreach $unit (keys %units) {
				my($volume) = $units{$unit}{volume};
				open($in, "$twcli info $controller $unit |") || warn "exec $twcli: $!";
				$debug_storage .= ">>> $twcli info $controller $unit\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /^Unit Type:\s+(.*)/) {
						$props{$volume}{raid} = $1;
					} elsif ($line =~ /^Size:.*\((\d+)\s+blocks\)/) {
						$props{$volume}{bytes} = $1 * 512;
					} elsif ($line =~ /^Stripe Size:\s*(\S+)/) {
						$props{$volume}{stripe} = parse_bytes($1, 1024);
					}
				}
				close($in);
			}
		}

		my($port);
		foreach $port (keys %{$ports{$controller}}) {
			open($in, "$twcli info $controller $port model |") || warn "exec $twcli: $!";
			$debug_storage .= ">>> $twcli info $controller $port model\n";
			$line = <$in>;
			$debug_storage .= $line;
			chomp($line);
			$line =~ s/^.*model\s*=\s*//i;
			my($model) = canon_drive($line);
			my($volume) = $ports{$controller}{$port};
			$drives{$volume}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $volume;
			$drive_id++;
			close($in);
		}
	}
	return $result;
}

sub smbinfo {
	my($in) = "IN";
	my($line);
	my($result) = 0;

	# smbinfo obsoletes smbiosinfo
	system("which $smbinfo > /dev/null 2>&1");
	if ($?) {
		system("which $smbiosinfo > /dev/null 2>&1");
		return $result if ($?);
		open($in, "$smbiosinfo -l 2> /dev/null |") || warn "exec $smbiosinfo: $!";
	} elsif (-t STDIN) {
		open($in, "$smbinfo -l 2> /dev/null |") || warn "exec $smbinfo: $!";
	} else {
		# older versions of smbinfo don't work when stdin is not a tty, e.g. from cron
		# closing stdin works for some older versions, however ancient versions will stil break
		open($in, "$smbinfo -l <&- 2> /dev/null |") || warn "exec $smbinfo: $!";
	}

	if (!eof($in)) {
		$line = <$in>;
		$result = 1;
		my($comp);
		if ($line =~ /^SMBIOS version/) {
			$line = <$in>;
			while (!eof($in)) {
				$debug_dmidecode .= $line;
				if ($line =~ /^(\S.*):/) {
					$comp = $1;
					parse_bios($in, 'BIOS', 0) if ($comp eq "BIOS Information");
					parse_system($in, 'Base Board', 0) if ($comp eq "Base Board Information");
					parse_system($in, 'System', 0) if ($comp eq "System Information");
					parse_proc($in, 'Processor', 0) if ($comp eq "Processor Information");
					parse_memory($in, 'Memory', 'Device', 0) if ($comp eq "Memory Device");
				}
				$line = <$in>;
			}
		} else {
			while (!eof($in)) {
				$debug_dmidecode .= $line;
				if ($line =~ /^\[\S+\s+([^\]]+)\]/) {
					$comp = $1;
					parse_bios($in, 'BIOS', 0) if ($comp eq "BIOS");
					parse_system($in, 'Base Board', 0) if ($comp eq "Base Board");
					parse_system($in, 'System', 0) if ($comp eq "System");
					parse_proc($in, 'Processor', 0) if ($comp eq "Processor");
					parse_memory($in, 'Memory', 'Device', 0) if ($comp eq "Memory Device");
				}
				$line = <$in>;
			}
		}
	}
	return $result;
}

sub dmidecode {
	my($in) = 'DMIDECODE';
	my($line);
	my($result) = 0;
	my($version) = 0;
	my($section, $count, $attribute, $value, $sep);
	my($handle) = 0;
	my($type) = 0;
	my($key);

	system("which $dmidecode > /dev/null 2>&1");
	return $result if ($?);

	open($in, "$dmidecode 2> /dev/null |") || warn("exec $dmidecode: $!");
	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		$line =~ s/\s+$//;

		if ($handle) {
			if ($line =~ /^(\S+.*)/) {
				$dmi{$handle}{'name'} = $1;
			} elsif ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}

		if ($line =~ /^Handle (\S+), DMI type (\d+)/) {
			$handle = $1;
			$type = $2;
			$dmi{$handle}{'type'} = $type;
			$version = 1;
		} elsif ($line =~ /^\t?(System|Base Board)( Information)?( Block)?$/) {
			$result = 1;
			parse_system($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(BIOS)( Information)( Block)?$/) {
			parse_bios($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(Memory) (Device|Module Information)$/) {
			parse_memory($in, $1, $2, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(Processor)( Information)?$/) {
			parse_proc($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^checksum failed/) {
			last;
		} elsif ($line =~ /^\s*$/) {
			$handle = 0;
		}
	}
	close($in);
	return $result;
}

sub hpacucli {
	my($in) = 'HPACUCLI';
	my($line);
	my($slot, %slots, %logicals, %physicals, %arrays);

	system("which $hpacucli > /dev/null 2>&1");
	return if ($?);
	$have_hpacucli = 1;

	return if system("$hpacucli version > /dev/null 2>&1");
	$debug_storage .= ">> $hpacucli\n";

	open($in, "$hpacucli controller all show 2> /dev/null |") || warn "exec $hpacucli: $!";
	$debug_storage .= ">>> $hpacucli controller all show\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /in\s+slot\s+(\d+)/i) {
			$slots{$1}++;
			$got_hpacucli = 1;
		}
	}
	close($in);

	my($count) = 0;
	foreach $slot (keys %slots) {
		my($dev) = "cciss$count";
		my($total_cache_size) = 0;
		my($ratio) = 0;

		open($in, "$hpacucli controller slot=$slot show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Read Cache Size: (.*)/i) {
				$props{$dev}{'cache-read'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Write Cache Size: (.*)/i) {
				$props{$dev}{'cache-write'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Total Cache Size: (.*)/i) {
				$total_cache_size = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Accelerator Ratio: (\d+)/i) {
				$ratio = $1;
			} elsif ($line =~ /^\s*Battery Pack Count: (\d+)/i) {
				$props{$dev}{'BBU'} = 1 if ($1);
			} elsif ($line =~ /^\s*Firmware Version: (.*)/i) {
				$props{$dev}{'firmware'} = $1;
			} elsif ($line =~ /^\s*Hardware Revision: (.*)/i) {
				$props{$dev}{'revision'} = $1;
			} elsif ($line =~ /^\s*Serial Number: (.*)/i) {
				$props{$dev}{'serial'} = $1;
			} elsif ($line =~ /^\s*Cache Status: (.*)/i) {
				$props{$dev}{'cache-status'} = ($1 eq 'OK') ? 'on' : 'off';
			}
		}
		close($in);

		if ($total_cache_size) {
			$props{$dev}{'cache-read'} = ($ratio / 100) * $total_cache_size;
			$props{$dev}{'cache-write'} = ((100 - $ratio) / 100) * $total_cache_size;
		}

		my(%ids, $id);
		my($array) = "NA";

		open($in, "$hpacucli controller slot=$slot logicaldrive all show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot logicaldrive all show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /array\s+(\S+)/i) {
				$array = $1;
			} elsif ($line =~ /logicaldrive\s+(\d+)\s+\(([^,]*),([^,]*)/) {
				$ids{$1} = $array;
			}
		}
		close($in);

		foreach $id (keys %ids) {
			my($volume, $array, $raid, $size, $stripe, $cache_enable, $status, $uid) = ("ciss-s$slot-$ids{$id}-l$id", $ids{$id}, 0, 0, 0, 'NA', '');

			open($in, "$hpacucli controller slot=$slot logicaldrive $id show |") || warn "exec $hpacucli: $!";
			$debug_storage .= ">>> $hpacucli controller slot=$slot logicaldrive $id show\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /^\s*array\s+(\S+)/) {
					$array = $1;
				} elsif ($line =~ /^\s*Size:\s+(.*)/) {
					$size = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s*Fault Tolerance:\s+(.*)/) {
					$raid = canon_raid("RAID $1");
				} elsif ($line =~ /^\s*Array Accelerator:\s+(.*)/) {
					$cache_enable = parse_enable($1);
				} elsif ($line =~ /^\s*Stripe Size:\s+(.*)/) {
					$stripe = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s*Status:\s+(.*)/) {
					$status = parse_volume_status($1);
				} elsif ($line =~ /^\s*UID:\s+(.*)/) {
					$uid = $1;
				} elsif ($line =~ /^\s*Disk Name:\s+(\S+)/) {
					if ($1 ne 'Unknown') {
						$volume = $1;
						$volume =~ s/^\/dev\///;
					}
				}
			}
			close($in);

			$volumes{$volume}++;
			$logicals{$slot}{$array}{$id} = $volume;
			$props{$volume}{bytes} = $size if ($size);
			$props{$volume}{raid} = $raid if ($raid);
			$props{$volume}{stripe} = $stripe if ($stripe);
			$props{$volume}{status} = $status if ($status);
			$props{$volume}{cache_enable} = $cache_enable;
			$props{$volume}{uid} = $uid;
			$arrays{$slot}{$array}++;
		}

		$array = "NA";

		open($in, "$hpacucli controller slot=$slot physicaldrive all show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot physicaldrive all show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /array\s+(\S+)/i) {
				$array = $1;
			} elsif ($line =~ /physicaldrive\s+(\S+)/) {
				$physicals{$slot}{$array}{$1}++;
			}
		}
		close($in);

		foreach $array (keys %{$arrays{$slot}}) {
			my($volume);
			my(@keys) = keys %{$logicals{$slot}{$array}};
			if ($#keys == 0) {
				# if only one logical in the array, then logical == array
				# so set the volume to the logical, otherwise we'll have an anonymous array volume
				$volume = $logicals{$slot}{$array}{$keys[0]};
			} else {
				# create anonymoums array volume
				$volume = "ciss-s$slot-$array";
				$volumes{$volume}++;
				$props{$volume}{raid} = "JBOD";
				$props{$volume}{bytes} = 0;
			}
			my($have_size) = $props{$volume}{bytes};
			my($drive);
			foreach $drive (keys %{$physicals{$slot}{$array}}) {
				my($size, $rpm, $speed, $interface, $status, $firmware, $serial) = (0, "", "", "", "", "", "");
				my($model) = "unknown";

				open($in, "$hpacucli controller slot=$slot physicaldrive $drive show |") || warn "exec $hpacucli: $!";
				$debug_storage .= ">>> $hpacucli controller slot=$slot physicaldrive $drive show\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /Size:\s+(.*)/i) {
						$size = parse_bytes($1, 1000);
					} elsif ($line =~ /Rotational Speed:\s+(.*)/i) {
						$rpm = parse_rpm($1);
					} elsif ($line =~ /Transfer Speed:\s+(.*)/i) {
						$speed = parse_drive_speed($1);
					} elsif ($line =~ /Status:\s+(.*)/i) {
						$status = parse_drive_status($1);
					} elsif ($line =~ /Interface Type:\s+(.*)/i) {
						$interface = parse_drive_interface($1);
					} elsif ($line =~ /Serial Number:\s+(.*)/i) {
						$serial = $1;
					} elsif ($line =~ /Firmware Revision:\s+(.*)/i) {
						$firmware = $1;
					} elsif ($line =~ /Model:\s+(.*)/i) {
						$model = canon_drive($1);
					}
				}
				close($in);

				if ($status eq "Failed") {
					$props{$volume}{failed}++;
				}

				if ($model eq "unknown") {
					$model = "Unknown";
					$model .= " " . print_bytes($size, 1000, 0) if ($size);
					$model .= " $rpm" if ($rpm);
					$model .= " $interface" if ($interface);
					$model .= " $speed" if ($speed);
				}
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{firmware} = $firmware if ($firmware);
				$props{drives}{$drive_id}{serial} = $serial if ($serial);
				$props{drives}{$drive_id}{rpm} = $rpm if ($rpm);
				$props{drives}{$drive_id}{size} = $size if ($size);
				$props{drives}{$drive_id}{speed} = $speed if ($speed);
				$props{drives}{$drive_id}{interface} = $interface if ($interface);
				$props{drives}{$drive_id}{status} = $status if ($status);
				$drive_id++;
				$props{$volume}{bytes} += $size unless ($have_size);
			}
		}
		$count++;
	}
}

sub cissutil {
	my(%cissdrives);
	my($device, $dev);
	my(%failed);

	system("which $cissutil > /dev/null 2>&1");
	return if ($?);
	$have_cissutil = 1;
	$debug_storage .= ">> $cissutil\n";

	foreach $device (split(' ', `/bin/ls /dev/ciss[0-9]* 2> /dev/null`)) {
		$dev = $device;
		$dev =~ s,^/dev/ciss,,;
		my($volume) = "NA";
		my(@controllers);
		my($controller) = 0;

		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"ciss$dev"}) {
			my(@keys) = keys %{$controller_volumes{"ciss$dev"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		open($in, "$cissutil -c $device -d 2> /dev/null |") || warn "exec on \"$cissutil -c $device -d\" failed";
		$debug_storage .= ">>> $cissutil -c $device -d\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^controller\s+(\d+)\s*(\((.*)\))?/) {
				$controller = $1;
				my($type) = $3; 
				if ($type && !$other_devices{"Disk-Control"}{"ciss$dev"}) {
					$type =~ s/\s+/ /g;
					$type =~ s/^\s*//;
					$type =~ s/\s*$//;
					$other_devices{"Disk-Control"}{"ciss$dev"} = "ciss$dev: $type";
				}
			} elsif ($line =~ /^running firmware\s+([\d\.]+)/i) {
				$props{"ciss$dev"}{"firmware"} = $1;
			} elsif ($line =~ /^cache is:\s+(.*)/i) {
				$props{"ciss$dev"}{'cache-status'} = ($1 =~ /enable/i) ? 'on' : 'off';
			} elsif ($line =~ /^write cache size = (.*)/i) {
				$props{"ciss$dev"}{'cache-write'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^read cache size = (.*)/i) {
				$props{"ciss$dev"}{'cache-read'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^logical drives:\s+(\d+)/) {
				push(@controllers, $controller);
			} elsif ($line =~ /^(da\d+)\s*$/) {
				$volume = $1;
				delete $drives{$volume};	# nuke camcontrol info
				$volumes{$volume}++;
			} elsif ($line =~ /^vol(\d+)\s*$/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$1");
				$volumes{$volume}++;
			} elsif ($line =~ /^(vol(\d+))?\s+blocks\s+available\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$volumes{$volume}++;
				$props{$volume}{bytes} = $3 * 512;
			} elsif ($line =~ /^(vol(\d+))?\s+fault\s+tolerance\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{raid} = $3;
			} elsif ($line =~ /^(vol(\d+))?\s+status\s+(.*)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{status} = parse_volume_status($3);
			} elsif ($line =~ /^(vol(\d+))?\s+stripe\s+size\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{stripe} = parse_bytes($3, 1024);
			} elsif ($line =~ /^(vol(\d+))?\s+failed\s+drives\s+\(([^\)]+)\)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				my($failed) = $3;
				$failed =~ s/\bnone\b//g;
				my(@failed) = split(/,/, $failed);
				$props{$volume}{failed} = $#failed + 1;
				%failed = map { $_ => 1 } @failed;
			} elsif ($line =~ /^vol(\d+)\s+.*\s+(drives|spares)\s+\(([^\)]+)\)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$1");
				my($drive);
				foreach $drive (split(/,/, $3)) {
					$cissdrives{"$dev-$controller-$drive"} = $volume;
				}
			}
		}
		close($in);
	
		foreach $controller (@controllers) {
			my($id) = 0;
			$volume = "ciss$dev-c$controller-NA";

			open($in, "$cissutil -c $device --controller $controller --drives 2> /dev/null |") || warn "exec on $cissutil failed";
			$debug_storage .= ">>> $cissutil -c $device --controller $controller --drives\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				$got_cissutil = 1;
				if ($line =~ /^\((b\d+t\d+) -> (da\d+)/) {
					$volume = $2;
					$ndrives++;
					$id = $drive_id++;
					$props{drives}{$id}{status} = parse_drive_status("failed") if ($failed{$1});
				} elsif ($line =~ /^\(b\d+t\d+ -> free/) {
					$volume = "ciss$dev-c$controller-NA";
					$volumes{$volume}++;
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\((b(\d+)t(\d+))\)/) {
					my($full, $bus, $target) = ($1, $2, $3);
					if ($cissdrives{"$dev-$controller-$full"}) {
						$volume = $cissdrives{"$dev-$controller-$full"};
					} else {
						$volume = "ciss$dev-c$controller-NA";
						$volumes{$volume}++;
					}
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\(b\d+t\d+ -> vol(\d+)/) {
					$volume = ($default_volume || "ciss$dev-c$controller-l$1");
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\s+model\s+(.*)/) {
					my(@model) = split(' ', $1);
					my($model);
					if ($model[0] eq "Unknown" || $model[0] eq "ATA") {
						$model = canon_drive("$model[1],$model[2]");
						$props{drives}{$id}{serial} = $model[3];
						$props{drives}{$id}{firmware} = $model[4];
					} else {
						$model = canon_drive("$model[0],$model[1]");
						$props{drives}{$id}{serial} = $model[2];
						$props{drives}{$id}{firmware} = $model[3];
					}
					$drives{$volume}{$id}++;
					$props{drives}{$id}{model} = $model;
					$props{drives}{$id}{volume} = $volume;
				} elsif ($line =~ /^\s+configured:\s+(\d+)/) {
					$props{$volume}{free}++ unless ($1);
				} elsif ($line =~ /^\s+configured_spare:\s+(\d+)/) {
					$props{$volume}{spare}++ if ($1);
				} elsif ($line =~ /^\s+cache_currently_enabled:\s+(\d+)/) {
					$props{drives}{$id}{wcache_enable} = $1;
				} elsif ($line =~ /^\s+rpm:\s+(\d+)/) {
					$props{drives}{$id}{rpm} = $1;
				} elsif ($line =~ /^\s+size\s+(.*)/) {
					$props{drives}{$id}{size} = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s+SMART:\s+(.*)/) {
					$props{drives}{$id}{smart_support} = parse_enable($1);
				} elsif ($line =~ /^\s+SMART_errors_enabled:\s+(.*)/) {
					$props{drives}{$id}{smart_enable} = parse_enable($1);
				} elsif ($line =~ /^\s+drive_present:\s+(.*)/) {
					if ($1) {
						$props{drives}{$id}{status} = parse_drive_status("online") unless (defined($props{drives}{$id}{status}));
					} else {
						$props{drives}{$id}{status} = parse_drive_status("offline");
					}
				}
			}
			close($in);
		}
	}
}

sub camcontrol {
	my($dev);

	system("which $camcontrol > /dev/null 2>&1");
	return if ($?);

	open($in, "$camcontrol devlist -v 2> /dev/null |") || warn "exec $camcontrol: $!";
	$debug_storage .= ">> $camcontrol devlist -v\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^\<(\S+)\s+(\S+)/) {
			if ($1 eq "COMPAQ") {
				$want_cissutil++;
			} elsif ($1 eq "PE/PV") {
				$want_megarc++;
			}
		}
		if ($line =~ /^scbus[-\d]+\s+on\s+(\S+)\s+bus\s+\d+/) {
			$dev = $1;
		}
		if ($line =~ /\b(da\d+)\b/) {
			my($volume) = $1;
			if ($line =~ /^\<([^>]*)/) {
				my($id) = canon_drive($1);
				$ndrives++;
				$volumes{$volume}++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $id;
				$props{drives}{$drive_id}{volume} = $volume;
				$drive_id++;
				if ($dev) {
					$props{$volume}{device} = $dev;
					$controller_volumes{$dev}{$volume}++;
				}
				if ($line =~ /(raid\s*\S+)/i) {
					$props{$volume}{raid} = $1;
				} else {
					$props{$volume}{raid} = "JBOD";
				}
				if ($dmesg_buf =~ /^$volume:.*?(\d+) (512 byte )?sectors/m) {
					$props{$volume}{bytes} = $1 * 512;
				}
			}
		}
	}
	close($in);
}

sub grok_sysctl_dev {
	my($in) = 'IN';
	my($line);
	my($dev, $key, $val);

	open($in, "sysctl dev |") || warn("sysctl: $!");
	while ($line = <$in>) {
		$debug_sysctl_dev .= $line;
		chomp($line);
		if ($line =~ /^dev\.([^\.]+)\.(\d+)\.\%?([^\:]+)\:\s+(.*)/) {
			($dev, $key, $val) = ($1 . $2, $3, $4);
			$sysctl_dev{$dev}{$key} = $val;
		}
	}
	foreach $dev (keys %sysctl_dev) {
		my($model) = $sysctl_dev{$dev}{desc};
		$model =~ s/\(R\)//g if $model;
		if ($dev =~ /^(atapci|mfi|mpt|ahc|ahd|asr|aac|aacch|sym|ida|ciss|amr)\d+$/) {
			$model =~ s/\s+controller\b.*//i;
			$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
			$sysctl_dev{$dev}{'xmodel'} = $model;
		} elsif ($dev =~ /^(ata|amrd|mfid)\d+$/) {
			$props{$dev}{device} = $sysctl_dev{$dev}{parent};
		} elsif ($dev =~ /^($freebsd_net_devs)\d+$/) {
			if ($1 eq "msk") {
				$interface_to_driver{$dev} = $sysctl_dev{$dev}{parent};
			}
			$model =~ s/\s*(Ethernet|Network|,).*//;
			$interfaces{$dev}{'model'} = $model;
			$sysctl_dev{$dev}{'xmodel'} = $model;
		} else {
			$sysctl_dev{$dev}{'xmodel'} = $model;
		}
	}
}

sub parse_protocol {
	my($proto) = @_;
	my($result);
	if ($proto eq "Serial ATA v1.0" || $proto eq "SATA-I signaling") {
		$result = "SATA-1.0";
	} elsif ($proto eq "Serial ATA II" || $proto eq "SATA-II signaling") {
		$result = "SATA-2.0";
	} elsif ($proto =~ /ATA\/ATAPI revision\s+(\d+)/i) {
		$result = "ATA-$1";
	} else {
		$result = $proto;
		$result =~ s/ATA\/ATAPI/ATA/;
	}
	return $result;
}

sub atacontrol {
	my($in, $id, $disk) = @_;
	my($line);

	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);

		if ($line =~ /device (ad\d+):/) {
			$disk = $1;
		} elsif ($line =~ /^device model\s+(.*)/) {
			my($model) = canon_drive($1);
			$ndrives++;
			$volumes{$disk}++;
			$drives{$disk}{$id}++;
			$props{drives}{$id}{model} = $model;
			$props{drives}{$id}{volume} = $disk;
			$drive_id++;
			$props{$disk}{raid} = "JBOD";
		} elsif ($line =~ /^lba(48)? (not )?supported\s+(\d+) sectors/) {
			my($size) = $3 * 512;
			$props{drives}{$id}{size} = $size;
			if (!$props{$disk}{bytes} || $size > $props{$disk}{bytes}) {
				$props{$disk}{bytes} = $size;
			}
		} elsif ($line =~ /^Protocol\s+(.*)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		} elsif ($line =~ /^(ATA\/ATAPI revision\s*\d+)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		} elsif ($line =~ /^serial number\s+(.*)/) {
			$props{drives}{$id}{serial} = $1;
		} elsif ($line =~ /^firmware revision\s+(.*)/) {
			$props{drives}{$id}{firmware} = $1;
		} elsif ($line =~ /^read ahead\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{readahead_support} = parse_enable($1);
			$props{drives}{$id}{readahead_enable} = parse_enable($2);
		} elsif ($line =~ /^write cache\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{wcache_support} = parse_enable($1);
			$props{drives}{$id}{wcache_enable} = parse_enable($2);
		} elsif ($line =~ /^SMART\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{smart_support} = parse_enable($1);
			$props{drives}{$id}{smart_enable} = parse_enable($2);
		} elsif ($line =~ /^power management\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{pm_support} = parse_enable($1);
			$props{drives}{$id}{pm_enable} = parse_enable($2);
		} elsif ($line =~ /^advanced power management\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{apm_support} = parse_enable($1);
			$props{drives}{$id}{apm_enable} = parse_enable($2);
			$props{drives}{$id}{apm_value} = parse_value($4) if ($3);
		} elsif ($line =~ /^automatic acoustic management\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{aam_support} = parse_enable($1);
			$props{drives}{$id}{aam_enable} = parse_enable($2);
			$props{drives}{$id}{aam_value} = parse_value($4) if ($3);
		} elsif ($line =~ /^Native Command Queuing \(NCQ\)\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{ncq_support} = parse_enable($1);
			$props{drives}{$id}{ncq_enable} = parse_enable($2, '', '-');
			$props{drives}{$id}{ncq_depth} = parse_value($4) if ($3);
		}
	}
}

sub freebsd_disks_unknown {
	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);
	my($disk);
	foreach $disk (split(' ', $sysctl)) {
		next if ($disk =~ /^(md|cd)\d+$/);
		next if ($drives{$disk});

		$ndrives++;
		$volumes{$disk}++;
		$drives{$disk}{$drive_id}++;
		$props{drives}{$drive_id}{model} = "Unknown";
		$props{drives}{$drive_id}{volume} = $disk;
		$drive_id++;
	}
}

sub freebsd_disks {
	my($ac_works) = 1;

	# do this before things like cissutil, megarc (let them override this with more detailed info)
	camcontrol();

	my($which);
	chomp($which = `which $atacontrol` || 0);
	if ($?) {
		$ac_works = 0;
	} else {
		if (($yroot || $jailed) && $os_version < 500000000 && $parent_os !~ /^4/) {
			# 4.x atacontrol hangs on a 6.x kernel
			if (`file $which` =~ /FreeBSD 4/) {
				$ac_works = 0;
			}
		}
	}

	$debug_storage .= ">> $atacontrol\n";

	if ($ac_works) {
		# this works with freebsd 4
		my($i, $j);
		for ($i=0; $i<4; $i++) {
			for ($j=0; $j<2; $j++) {
				open($in, "$atacontrol cap $i $j 2> /dev/null |") || warn "exec on $atacontrol failed";
				$debug_storage .= ">>> $atacontrol cap $i $j\n";
				atacontrol($in, $drive_id, "ata-$i,$j");
				close($in);
			}
		}
	}

	my($grep) = "GREP";
	my($gline);

	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);

	my($disk);
	foreach $disk (split(' ', $sysctl)) {
		next if ($disk =~ /^(md|cd)\d+$/);
		next if ($drives{$disk});

		if ($ac_works) {
			# this works with freebsd 6+
			open($in, "$atacontrol cap $disk 2> /dev/null |") || warn "exec $atacontrol: $!";
			$debug_storage .= ">>> $atacontrol cap $disk\n";
			atacontrol($in, $drive_id, $disk);
			close($in);

			next if ($drives{$disk});

			if ($disk =~ /^ar\d+$/) {
				my($model) = "";
				my($sep) = "partitions: ";
				open($in, "$atacontrol status $disk 2> /dev/null |") || warn "exec $atacontrol: $!";
				$debug_storage .= ">>> $atacontrol status $disk\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /(RAID\d+).*subdisks: (.*) status:/) {
						my($raid, $subs) = ($1, $2);
						my($sub);
						foreach $sub (split(' ', $subs)) {
							$model .= "$sep$sub";
							$sep = ", ";
						}
						$props{$disk}{raid} = $raid;
					} elsif ($line =~ /(RAID\d+)( stripesize=(\d+))? status:\s*(\S+)/) {
						my($raid, $stripe, $status) = ($1, $3, $4);
						$props{$disk}{raid} = $raid;
						$props{$disk}{stripe} = $stripe * 512 if $stripe;
						$props{$disk}{status} = parse_volume_status($status);
					} elsif ($line =~ /^\s*(\d+)\s+(ad\d+)\s+ONLINE/) {
						$model .= "$sep$2";
						$sep = ", ";
					} elsif ($line =~ /^\s*(\d+)\s+\-+\s+MISSING/) {
						$model .= "${sep}down";
						$sep = ", ";
					}
				}
				if ($model) {
					$ndrives++;
					$volumes{$disk}++;
					$drives{$disk}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{volume} = $disk;
					$drive_id++;
					if ($dmesg_buf =~ /^$disk: (\d+)MB/m) {
						$props{$disk}{bytes} = $1 * 1024 * 1024;
					}
				}
			}
			next if ($drives{$disk});
		}
	
		foreach $gline (split(/\n/, $dmesg_buf)) {
			if ($gline =~ /(\s*\d+\s+READY\s+)?\(?\b$disk\)?: ((\d+)MB\s*)?\<([^\>]*)\>(\s+on\s+(\S+))?/i && !$volumes{$disk}) {
				my($size) = $3;
				my($model) = canon_drive($4);
				my($dev) = $6;
				if (defined($sysctl_dev{$disk})) {
					# on freebsd 6.x model/dev should be more accurate from sysctl
					$model = canon_drive($sysctl_dev{$disk}{desc});
					$dev = $sysctl_dev{$disk}{parent};
				}
				$ndrives++;
				$volumes{$disk}++;
				$drives{$disk}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $disk;
				$drive_id++;
				if ($size) {
					$props{$disk}{bytes} = $size * 1024 * 1024;
				}
				if ($gline =~ /\b(raid\s*\S+)/i) {
					$props{$disk}{raid} = $1;
				} else {
					$props{$disk}{raid} = "JBOD";
				}
				if ($dev) {
					$props{$disk}{device} = $dev;
					$controller_volumes{$dev}{$disk}++;
					if ($dev =~ /^twe(\d+)/) {
						my($ctrl) = $1;
						if ($gline =~ /\b$disk: <Unit (\d+)/) {
							$logical_to_os{'tw'}{"c$ctrl-u$1"} = $disk;
						}
					}
				}
			} elsif ($gline =~ /^$disk:.*?(\d+) (512 byte )?sectors/) {
				$props{$disk}{bytes} = $1 * 512;
				if ($gline =~ /(raid\s*\S+)/i) {
					$props{$disk}{raid} = $1;
				}
			}
		}
		next if ($drives{$disk});
	
		# on freebsd 6.x the following should work.  do this after dmesg check as it should have more info.
		if (defined($sysctl_dev{$disk})) {
			my($model) = canon_drive($sysctl_dev{$disk}{desc});
			my($dev) = $sysctl_dev{$disk}{parent};
			$ndrives++;
			$volumes{$disk}++;
			$drives{$disk}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $disk;
			$drive_id++;
			$props{$disk}{device} = $dev;
			$controller_volumes{$dev}{$disk}++;
		}
	}
	
	if ($ac_works) {
		# wire ATA drives
		my($channel);
		open($in, "$atacontrol list 2> /dev/null |") || warn "exec on $atacontrol failed";
		$debug_storage .= ">>> $atacontrol list\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /ATA\s+channel\s+(\d+)/i) {
				$channel = $1;
			} elsif ($line =~ /^\s*(master|slave):\s*(ad\d+)/i) {
				$props{$2}{channel} = "ata$channel";
				$props{$2}{ms} = lc($1);
			}
		}
		close($in);
	}

	# wire from dmesg if we haven't done it yet
	foreach $gline (split(/\n/, $dmesg_buf)) {
		if ($gline =~ /^(ad\d+):.*\s+at\s+(\S+)-(slave|master)/i) {
			if (!defined($props{$1}{channel})) {
				$props{$1}{channel} = $2;
				$props{$1}{ms} = $3;
			}
		} elsif ($gline =~ /^(ata\d+):.*\s+on\s+(ata\S+)/i) {
			$props{$1}{device} = $2 unless $props{$1}{device};
		}
	}
}

# this should probably be elsewhere, but do it last for now so that we always use this if we have it.
# should be very accurate.  certainly more than dmesg output which can get garbled.
sub freebsd_disk_ioctls {
	my($disk);
	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);

	foreach $disk (split(' ', $sysctl)) {
		my($dev) = "/dev/$disk";
		if (-e $dev && open($in, $dev)) {
			my($status);
			my($bytes);
			if ($os_version >= 600000000) {
				$bytes = pack("I2", 0, 0);
				$status = ioctl($in, 0x40086481, $bytes);
				if ($status) {
					my(@bytes) = unpack("I2", $bytes);
					$props{$disk}{bytes} = $bytes[0] + $bytes[1] * 4*1024*1024*1024;
				}
			} elsif ($os_version < 500000000) {
				my($rdev) = (stat($dev))[6];
				my($offset) = round(148 + (16 * ($rdev & 0x07)));
				$bytes = pack("C276", 0);
				$status = ioctl($in, 0x41146465, $bytes);
				if ($status) {
					my(@bytes) = unpack("C${offset}I", $bytes);
					$props{$disk}{bytes} = $bytes[-1] * 512;
				}
			}
			close($in);
		}
	}
}

my(%grub);

sub grok_grub {
	my($conf);
	if (-e $grub) {
		$conf = `cat $grub 2> /dev/null`;
		if ($conf =~ /vmlinuz-$os_version\s.*maxcpus=(\d+)/m) {
			$grub{'maxcpus'} = $1;
		}
	}
}

sub grok_os {
	$os = "";
	$os_up = 0;
	chomp($os = `uname -s`);
	$os_type = lc($os);
	chomp($os .= " " . `uname -r`);
	chomp($os .= " " . `uname -m`);
	if ($os_type eq "freebsd") {
		my($config);
		chomp($config = `uname -v`);
		$config =~ s/.*\///;
		$config =~ s/\s+$//;
		$os .= " " . $config;
		chomp($os_version = `uname -r`);
		if ($os_version =~ /^(\d+)\.\d+-.*(-(\d+))?$/) {
			# $2 is null if stock FreeBSD
			$os_date = $2 ? $3 : "00000000";
			$os_version = "$1$os_date";
		}
		chomp($jailed = `sysctl -n security.jail.jailed 2> /dev/null` || -e "/var/run/yroot-jail.pid" || 0);
		chomp($parent_os = `sysctl -n kern.osrelease 2> /dev/null` || 0);
		if ($jailed) {
			my($host, $key);
			chomp($host = `cat /.yroot_host 2> /dev/null` || 0);
			chomp($key = `cat /etc/ssh/ssh_host_key.pub 2> /dev/null` || 0);
			if ($host) {
				$jailer = $host;
			} elsif ($key =~ /\@(\S+)/) {
				$jailer = $1;
			}
		}
		my($smp);
		# check to see if the sysctl name exists in 4.x or check value in 6.x
		chomp($smp = `sysctl -N machdep.smp_active 2> /dev/null` || `sysctl -n kern.smp.active 2> /dev/null` || 0);
		$os_up = 1 unless ($smp);
	} elsif ($os_type eq "linux") {
		chomp($os_version = `uname -r`);
		if (-e "/etc/redhat-release") {
			# http://kbase.redhat.com/faq/FAQ_43_4422.shtm
			chomp($os_distro = `cat /etc/redhat-release`);
			$os_distro =~ s/Red Hat Enterprise Linux/RHEL/;
			$os_distro =~ s/\s+release\s+/ /;
			$os_distro =~ s/\(.*Update (\d+)\)/U$1/;
		} elsif (-e "/etc/debian_version") {
			chomp($os_distro = `cat /etc/debian_version`);
			$os_distro = "Debian $os_distro";
		} elsif (-e "/etc/gentoo-release") {
			chomp($os_distro = `cat /etc/gentoo-release`);
			$os_distro =~ s/\s+Base System version\s+/ /;
		}
		if ($os_distro) {
			$os = "$os_distro, $os";
		}
	}
	if (-l "/.yroot") {
		my($link) = readlink("/.yroot");
		$link =~ s,^.*/var/yroots/,,;
		$yroot = $link;
	}
}

sub scsi_to_device {
	my($dev) = @_;
	if ($dev =~ /^scsi(\d+)$/) {
		my($id) = $1;
		if ($dmesg_buf =~ /\bscsi\(?$id\)?\s*:.*IRQ[:= ]\s*(\d+)/im) {
			$dev = $irq_to_driver{$1} if ($irq_to_driver{$1});
		}
	}
	return $dev;
}

sub cmp_disks {
	my($atype, $anum) = ($a =~ /^(.*?)(\d*)$/);
	my($btype, $bnum) = ($b =~ /^(.*?)(\d*)$/);
	if ($os_type eq "linux" && $atype =~ /^sd/ && $btype =~ /^sd/) {
		return (length($atype) cmp length($btype) || $atype cmp $btype);
	} else {
		return ($atype cmp $btype || $anum <=> $bnum);
	}
}

sub print_disks {
	my($volume);
	foreach $volume (sort cmp_disks keys %volumes) {
		next if ($volume =~ /^ata-/);
		next unless ($drives{$volume} || $props{$volume}{bytes});
		next if ($props{$volume}{hidden});
		next if ($volume =~ /^ar\d+$/ && !defined($props{$volume}{df}));

		my($out) = "$volume";
		my($out_dev) = "";
		if ($props{$volume}{channel}) {
			my($channel) = $props{$volume}{channel};
			my($dev) = $props{$channel}{device};
			if ($dev) {
				$dev = $pci_to_driver{$dev} || $hostid_to_driver{$dev} || $dev;
				$dev = scsi_to_device($dev);
				$out_dev .= "$dev:";
			}
			$out_dev .= "$props{$volume}{channel}-" . substr($props{$volume}{ms}, 0, 1);
		} elsif ($props{$volume}{device}) {
			my($dev) = $props{$volume}{device};
			$dev = $pci_to_driver{$dev} || $hostid_to_driver{$dev} || $dev;
			$dev = scsi_to_device($dev);
			$out_dev .= $dev;
			$out_dev .= "-" . substr($props{$volume}{'ms'}, 0, 1) if $props{$volume}{'ms'};
		} elsif ($dmesg_buf =~ /^$volume at (\S+)/m) {
			$out_dev .= $1;
		} elsif ($dmesg_buf =~ /^$volume: <.*> on (\S+)/m) {
			$out_dev .= $1;
		}

		next if ($out_dev =~ /^umass/);

		$out .= " ($out_dev)" if ($out_dev);
		$out .= ":";

		if ($props{$volume}{bytes}) {
			$out .= " " . print_bytes($props{$volume}{bytes}, 1000, 0);
		} elsif ($props{$volume}{df}{size}) {
			$out .= " " . print_bytes($props{$volume}{df}{size}, 1000, 0);
		}
		$out .= sprintf(" (%d%%)", $props{$volume}{df}{used} * 100 / ($props{$volume}{df}{used} + $props{$volume}{df}{avail})) if (($props{$volume}{df}{used} || $props{$volume}{df}{avail}) && !defined($props{$volume}{inmd}));
		my($raid) = canon_raid($props{$volume}{raid});
		$out .= " $raid" if ($raid);

# this output is meant for debugger, will hopefully use it later in real output
#		$out .= sprintf(" (stripe %s)", print_bytes($props{$volume}{stripe}, 1024, 1)) if ($props{$volume}{stripe});

		if ($props{$volume}{spare} || $props{$volume}{free} || $props{$volume}{failed} || $props{$volume}{rebuild} || $props{$volume}{missing}) {
			$out .= " (";
			my($sep) = "";
			if ($props{$volume}{spare}) {
				$out .= "$sep$props{$volume}{spare} spare";
				$sep .= ", ";
			}
			if ($props{$volume}{free}) {
				$out .= "$sep$props{$volume}{free} free";
				$sep .= ", ";
			}
			if ($props{$volume}{failed}) {
				$out .= "$sep$props{$volume}{failed} failed";
				$sep .= ", ";
			}
			if ($props{$volume}{rebuild}) {
				$out .= "$sep$props{$volume}{rebuild} rebuild";
				$sep .= ", ";
			}
			if ($props{$volume}{missing}) {
				$out .= "$sep$props{$volume}{rebuild} missing";
				$sep .= ", ";
			}
			$out .= ")";
		}
		$output .= "Disk:\t\t";
		$output .= $out;
		my($key);
		my(%pretty);
		foreach $key (keys %{$drives{$volume}}) {
			my($try);
			my($found) = 0;
			my($model) = $props{drives}{$key}{model};
			# sort by length to find longest match, e.g. some drives just differ in suffix
			foreach $try (sort { length($b) <=> length($a) } keys %drive_models) {
				if ($model =~ /$try/i) {
					$pretty{$drive_models{$try}}++;
					$found++;
					last;
				}
			}
			if (!$found) {
				unless ($model =~ /^($ignore_disk_models)/i) {
					$debug .= "Debug-Disk:\t$model\n";
				}
				if ($model =~ /^((COMPAQ|HP)-)?(BD|BF|DD|DF|DG|DH|FB|FJ)([0-9]{3})([0-9A-Z]{5})/ || $model =~ /^((ATA)-)?(GB)([0-9]{4})([0-9A-Z]{5})/) {
					my($type, $size, $seq) = ($3, $4, $5);
					my($str);
					$size =~ s/^0+//;
					$str = $size . "GB ";
					if ($type eq "BD") {
						$str .= "10K ";
						$str .= "U320 " if (hex($seq) >= 0x85A24);
					} elsif ($type eq "BF") {
						$str .= "15K ";
						$str .= "U320 " if (hex($seq) >= 0x87B54);
					} elsif ($type eq "DD") {
						$str .= "10K SAS ";
					} elsif ($type eq "DF") {
						$str .= "15K SAS ";
					} elsif ($type eq "DG") {
						$str .= "10K SAS 2.5\" ";
					} elsif ($type eq "DH") {
						$str .= "15K SAS 2.5\" ";
					} elsif ($type eq "FB") {
						$str .= "7.2K SATA/150 ";
					} elsif ($type eq "FJ") {
						$str .= "5.4K SATA/150 ";
					} elsif ($type eq "GB") {
						$str .= "7.2K SATA/300 ";
					}
					$str .= "HP";
					$pretty{$str}++;
					$found++;
				}
			}
			if (!$found) {
				$pretty{$model}++;
			}
		}
		my($sep) = " == ";
		foreach $key (sort { $pretty{$b} <=> $pretty{$a} || length($b) <=> length($a) } keys %pretty) {
			if ($volume =~ /^(md|ccd|ar|dm)\d+/) {
				$output .= "$sep$key";
			} else {
				$output .= "$sep$pretty{$key} x $key";
			}
			if ($opts{'n'}) {
				$sep = ", ";
			} else {
				$sep = sprintf("\n\t\t%*s", length($out) + 4, " ");
			}
		}
		$output .= "\n";
	}
}

my($sent_email) = 0;

sub email {
	my($old, $new) = @_;
	my($out) = 'OUT';

	return if ($sent_email);
	open($out, "| sendmail $email") || warn("exec sendmail: $!");
	print $out "To: $email\n";
	print $out "From: hwconfig\@yahoo-inc.com\n";
	print $out "Subject: hwconfig";
	print $out " diff" if ($old ne $new);
	print $out " for $hostname\n";
	print $out "\n";
	print $out $output;
	print $out "Version:\t$version\n";
	print $out "\n$debug" unless ($opts{'d'});
	print $out "Debug-System:\t$guess_system\n" unless ($debug =~ /^Debug-System/m);
	print $out "\n", $stderr, "\n" if ($stderr);
	print $out "\n", $old, "\n" if ($old);
	print $out "\n> perl warnings\n", $perl_warn if ($perl_warn);
	print $out "\n> pciconf\n", $sig_pci, "\n", $debug_pciconf if ($debug_pciconf);
	print $out "\n> lspci\n", $sig_pci, "\n", $debug_lspci if ($debug_lspci);
	print $out "\n> lshal\n", $debug_lshal if ($debug_lshal);
	print $out "\n> sysctl dev\n", $debug_sysctl_dev if ($debug_sysctl_dev);
	print $out "\n> dmidecode\n", $debug_dmidecode if ($debug_dmidecode);
	print $out "\n> cpuinfo\n", $debug_cpuinfo if ($debug_cpuinfo);
	print $out "\n> meminfo\n", $debug_meminfo if ($debug_meminfo);
	print $out "\n> storage\n", $debug_storage if ($debug_storage);
	print $out "\n> network\n", $debug_network if ($debug_network);
	print $out "\n.\n";
	close($out);
	$sent_email++;
}

# Main

$zero = $0;
$zero =~ s,.*/,,;

getopts('h?dmnvo:', \%opts);

if ($opts{'v'}) {
	print "$version\n";
	exit(0);
}
if ($opts{'h'} || $opts{'?'}) {
	usage();
	exit(0);
}

if ($<) {
	$stderr .= "$zero: warning: run as root for more detailed and accurate data\n";
}

$SIG{'ALRM'} = 'watchdog';
alarm($timeout);

if ($opts{'o'}) {
	# avoid infinite reboot by bailing if it looks like last run at reboot didn't complete
	if (-e $lock) {
		# avoid case where stray lock file permanently disables boot run
		# worst case we end up with a single crash after a reboot
		unlink($lock);
		die "$zero: lock file '$lock' already exists, bailing out";
	}
	system("touch $lock");
}

grok_os();
grok_grub();

init_dmesg();

grok_dmesg();

if ($os_type eq 'freebsd') {
	grok_sysctl_dev() if ($os_version >= 600000000);
	grok_pciconf();
} elsif ($os_type eq "linux") {
	pci_to_driver();
	grok_proc_ioports();
	grok_lspci();
	grok_lshal();
}

# YYYY - need to nuke all dmi work if it fails, i.e. clear %smbios and %procs
dmidecode() || smbinfo();

mptable();

my($bios);
if ($smbios{'BIOS'}{1}{'Vendor'}) {
	my($vendor, $version, $date) = ($smbios{'BIOS'}{1}{'Vendor'}, $smbios{'BIOS'}{1}{'Version'}, $smbios{'BIOS'}{1}{'Release Date'});
	$bios = $vendor;
	$bios =~ s/(Award|Dell|Intel|Phoenix).*/$1/;
	$bios =~ s/American Megatrends.*/AMI/;
	$bios =~ s/Hewlett.*/HP/;
	$bios =~ s/\s+/_/g;
	if ($version) {
		$version =~ s/\s+/_/g;
		$bios .= " $version";
	}
	if ($date) {
		$date =~ s/\s+/_/g;
		$bios .= " $date";
	}
}

sub clean_system {
	my($str) = @_;
	$str =~ s/[^ -~]//g;
	$str =~ s/[\.\,]/ /gi;
	$str =~ s/\b(Server|Board|Mainboard|Microtower|Microsystems|Inc|Computer|Corp|Corporation|Technology|Co|Ltd|Manufacturer)\b/ /gi;
	$str =~ s/Small Form Factor//i;
	$str =~ s/\bInc\b/ /gi;
	$str =~ s/Rackable Systems/Rackable/ig;
	$str =~ s/Hewlett-Packard/HP/i;
	$str =~ s/Enterprise Server Division//;
	$str =~ s/TYAN High-End Dual AMD Opteron//;
	$str =~ s/IBM System x iDataPlex/IBM iDataPlex/;
	$str =~ s/Dual AMD Opteron//;
	$str =~ s/TYAN/Tyan/g;
	$str =~ s/GIGABYTE/Gigabyte/g;
	$str =~ s/DELL/Dell/g;
	$str =~ s/MICRO-STAR INTERNATIONAL/MSI/ig;
	$str =~ s/ASUSTeK/Asus/ig;
	$str =~ s/DELUXE/Deluxe/g;
	$str =~ s/RIOWORKS/Arima/g;
	$str =~ s/A Sanmina-SCI Company//g;
	$str =~ s/NEWISYS/Newisys/g;
	$str =~ s/InventecESC/Inventec/g;
	$str =~ s/((HP|Sun|VMware|IBM|Tyan|Dell)\s+)+/$2 /i;
	$str =~ s/HP Compaq/HP/i;
	$str =~ s/\b(uT|CMT|SFF|MT)(\([^\)]+\))?/$1/;
	$str =~ s/\((TM|R)\)//g;
	$str =~ s/DL140 00h/DL140 G1/i;
	$str =~ s/\-?\[[^\]]+\]\-?/ /;
	$str =~ s/\([^\)]+\)/ /;
	$str = clean_white($str);
	return $str;
}

my($system) = 'Unknown';
my($base_board);
my($sys);

if ($smbios{'Base Board'}{1}{'Manufacturer'} && $smbios{'Base Board'}{1}{'Product Name'}) {
	$base_board = clean_system("$smbios{'Base Board'}{1}{'Manufacturer'} $smbios{'Base Board'}{1}{'Product Name'}");
}
if ($smbios{'System'}{1}{'Manufacturer'} && $smbios{'System'}{1}{'Product Name'}) {
	$system = "$smbios{'System'}{1}{'Manufacturer'} $smbios{'System'}{1}{'Product Name'}";
} elsif ($base_board) {
	$system = $base_board;
}

my($guess) = guess_system($system);
$system = $guess if ($system eq 'Unknown');

if ($system eq "Unknown") {
	$system = $smbios{'System'}{1}{'Product Name'} || $smbios{'System'}{1}{'Manufacturer'} || $smbios{'Base Board'}{1}{'Product Name'} || $smbios{'Base Board'}{1}{'Manufacturer'} || $system;
}

$system = clean_system($system);

if (defined($system_model_index)) {
	my($ugly) = $system_models[$system_model_index]{'ugly-model'};
	if ($ugly && $system =~ /^$ugly$/) {
		$system = $system_models[$system_model_index]{'model'};
	}
}

$sys = $system;
$sys =~ s/poweredge\s+(\d+)/PE$1/i;
$sys =~ s/poweredge\s*//i;
$sys =~ s/proliant\s*//i;
$sys = clean_white($sys);

my($procs, $processors);

sub canon_cpu_model {
	my($model) = @_;

	$model =~ s/\@.*//;
	$model =~ s/[\d\.]+(MHz|GHz)//i;
	$model =~ s/\((TM|R|quarter-micron|coppermine|katmai)\)/ /ig;
	$model =~ s/\b(cpu(\s+family|\s+-\s+s)?|processor|genuine|intel|amd)\b/ /ig;
	$model =~ s/dual[\-\s]+core\s+opteron/Opteron/ig;
	$model =~ s/XEON/Xeon/g;
	$model =~ s/\s+/ /g;
	$model =~ s/\s+$//;
	$model =~ s/^\s+//;
	$model =~ s/\/.*//;

	return $model;
}

sub grok_procs {
	if ($os_type eq "linux") {
		cpuinfo();
	}

	my($proc_id) = 1;
	my($index);
	for ($index=1; $index < 1025; $index++) {
		if ($procs{$index} && $procs{$index}{status}) {
			$proc_id = $index;
			last;
		}
	}

	if (!$procs{$proc_id}{vendor}) {
		$procs{$proc_id}{vendor} = $dmesg{proc}{vendor};
	}
	if (!$procs{$proc_id}{family}) {
		$procs{$proc_id}{family} = $dmesg{proc}{family};
	}
	if (!$procs{$proc_id}{speed}) {
		if ($dmesg{proc}{speed}) {
			$procs{$proc_id}{speed} = $dmesg{proc}{speed};
		} else {
			my($tsc) = `sysctl -n machdep.tsc_freq 2> /dev/null`;
			if ($tsc) {
				chomp($tsc);
				$procs{$proc_id}{speed} = sprintf("%4.2fMHz", $tsc / 1000000) if ($tsc);
			}
		}
	}

	my($vendor, $model);
	my($speed) = 0;

	if ($os_type eq "freebsd") {
		chomp($model = `sysctl -n hw.model 2> /dev/null`);
	}
	if (!$model) {
		if ($procs{$proc_id}{family}) {
			$model = $procs{$proc_id}{family};
		} elsif ($vendor) {
			$model = $vendor;
		} else {
			$model = "unknown";
		}
	}

	$vendor = canon_cpu_vendor($procs{$proc_id}{vendor} || $model);

	if ($model =~ /([\d\.]+)(MHz|GHz)/) {
		$speed = round($1 * (($2 eq "GHz") ? 1000 : 1));
	}

	$model = canon_cpu_model($model);

	if (!$model) {
		if ($vendor) {
			$model = $vendor;
		} else {
			$model = "uknown";
		}
	}

	my($cpuid) = hex($procs{$proc_id}{cpuid} || $dmesg{proc}{cpuid} || "0");
	my($id) = cpuid($vendor, $cpuid);

	my($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = ('', 0, 0, 0);
	if ($cpu_models{$id}) {
		if (ref($cpu_models{$id}[0]) eq "ARRAY") {
			my($match) = "$model $speed";
			my($try);
			foreach $try (@{$cpu_models{$id}}) {
				($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$try->[1]};
				last if ($match =~ /$try->[0]/i);
			}
		} else {
			($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$cpu_models{$id}};
		}
	}
	$procs{gen} = $cpu_gen;

	if ($cpu_info && defined($procs{$proc_id}{'L2 Cache Handle'})) {
		my($handle) = $procs{$proc_id}{'L2 Cache Handle'};
		if (defined($dmi{$handle}) && defined($dmi{$handle}{'key'}{'Installed Size'})) {
			my($pretty) = print_bytes(parse_bytes($dmi{$handle}{'key'}{'Installed Size'}, 1024), 1024, 1);
			$cpu_info =~ s/L2: (\S+)/L2: $pretty/;
		}
	}
	if ($cpu_info && defined($procs{$proc_id}{'L3 Cache Handle'})) {
		my($handle) = $procs{$proc_id}{'L3 Cache Handle'};
		if (defined($dmi{$handle}) && defined($dmi{$handle}{'key'}{'Installed Size'})) {
			my($pretty) = print_bytes(parse_bytes($dmi{$handle}{'key'}{'Installed Size'}, 1024), 1024, 1);
			$cpu_info =~ s/L3: (\S+)/L3: $pretty/;
		}
	}

	my($hyper) = "UNKNOWN";
	if ($os_type eq "linux") {
		if ($cpu_cores == 0) {
			# lookup failed, so guess from what's active in cpuinfo
			if ($procs{threads} && $procs{cores}) {
				$cpu_cores = $procs{cores};
				$cpu_threads = ceil($procs{threads} / $procs{cores});
			} else {
				$cpu_cores = 1;
				$cpu_threads = 1;
			}
		}

		if ($procs{1} && $procs{1}{flags} =~ /\bht\b/i) {
			$hyper = "YES_ON";
		} elsif ($procs{threads} > $procs{cores}) {
			# cpu flags not working with Atom procs and dmidecode 2.9
			$hyper = "YES_ON";
		} else {
			$hyper = "NO";
		}
	} elsif ($os_type eq "freebsd") {
		if ($cpu_cores == 0) {
			# lookup failed, no way to tell in freebsd, so set to 1
			$cpu_cores = 1;
			$cpu_threads = 1;
		}

		chomp($procs{active_threads} = `sysctl -n hw.ncpu`);
		$procs{threads} = $procs{active_threads};

		my($sysctl, $allowed, $halt);
		chomp($sysctl = `sysctl -n machdep.hyperthreading_allowed 2> /dev/null`);
		if ($?) {
			$allowed = -1;
		} elsif ($sysctl) {
			$allowed = 1;
		} else {
			$allowed = 0;
		}
		chomp($sysctl = `sysctl -n machdep.hlt_logical_cpus 2> /dev/null`);
		if ($?) {
			$halt = -1;
		} elsif ($sysctl) {
			$halt = 1;
		} else {
			$halt = 0;
		}

		if ($halt == -1) {
			if ($procs{active_threads} == 1) {
				# if we have a UP kernel, have to grok dmesg looking for HTT feature
				if ($allowed != -1) {
					$debug .= "Debug-HT:\tmachdep.hlt_logical_cpus missing\n";
				}
				if ($dmesg_buf =~ /^\s*Features=.*\bHTT\b/m) {
					$hyper = "YES_UP";
				} else {
					$hyper = "NO";
				}
			} else {
				# we have old SMP kernel without machdep.hlt_logical_cpus
				if ($dmesg_buf =~ /^\s*Features=/m && $dmesg_buf !~ /^\s*Features=.*\bHTT\b/m) {
					$hyper = "NO";
				} else {
					# can't tell at this point, leave as UNKNOWN
				}
			}
		} elsif ($halt == 1) {
			$hyper = "YES_OFF";
		} elsif ($halt == 0) {
			if ($allowed == -1) {
				if ($os_version >= 600000000) {
					$hyper = "NO";
				} else {
					$hyper = "YES_ON";
				}
			} elsif ($allowed == 1) {
				$hyper = "YES_ON";
			} elsif ($allowed == 0) {
				$hyper = "YES_OFF";
			}
		}

		# figure out how many threads are really enabled
		chomp($sysctl = `sysctl -n machdep.hlt_cpus 2> /dev/null`);
		if ($?) {
			if ($procs{active_threads} > 1) {
				if ($hyper eq "YES_OFF") {
					if ($halt == 1) {
						$procs{active_threads} = $procs{active_threads} / ($cpu_threads * $cpu_cores);
					} elsif ($halt == 0) {
						$procs{active_threads} = $procs{active_threads} / $cpu_threads;
					}
				}
			}
		} elsif ($sysctl) {
			my($mask) = $sysctl;
			my($n) = $procs{active_threads};
			while ($n) {
				$procs{active_threads}-- if ($mask & 1);
				$mask = $mask >> 1;
				$n--;
			}
		}
	}

	# cpus will report HT even if they don't have it, e.g. dual/quad-core
	if ($cpu_threads == 1) {
		$hyper = "NO";
	} elsif ($hyper eq "NO") {
		$debug .= "Debug-HT:\tmissing HT detect\n" unless ($os_up);
	}

	if (!$speed && $procs{$proc_id}{speed} && $procs{$proc_id}{speed} =~ /^([\d\.]+)(MHz)?$/i) {
		$speed = $1;
	}
	if ($speed >= 1000) {
		$speed = sprintf("%4.2fGHz", $speed / 1000);
	} elsif ($speed >= 100) {
		$speed = sprintf("%.0fMHz", $speed);
	} elsif ($speed > 0) {
		$speed = sprintf("%4.2fMHz", $speed);
	}

	# fix bogus opteron models from HP - http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00606132
	if ($model =~ /Opteron 852/ && $speed =~ /2\.40GHz/i) {
		$model = "Opteron 880";
	} elsif ($model =~ /Opteron 850/ && $speed =~ /2\.20GHz/i) {
		$model = "Opteron 875";
	}

	# fix bogus FSB speeds
	if ($procs{$proc_id}{clock} && $cpu_info) {
		if ($cpu_info =~ /Prestonia/ && $procs{$proc_id}{clock} eq "133MHz") {
			# for HP DL140 with Prestonia Bs
			$procs{$proc_id}{clock} = "533MHz";
			$cpu_info =~ s/Prestonia/Prestonia B/;
		} elsif ($cpu_info =~ /Sossaman/ && $procs{$proc_id}{clock} eq "167MHz") {
			# for Intel SE7520BB2 with Sossaman
			$procs{$proc_id}{clock} = "667MHz";
		}
	}
	
	# how many sockets does this particular system have (assuming we are correct on system type)
	# to be looked up in system property defintion table
	my($sockets) = 0;
	if (defined($system_model_index)) {
		$sockets = $system_models[$system_model_index]{sockets};
	} else {
		if ($procs{sockets}) {
			# got data from smbinfo
			$sockets = $procs{sockets};
		} else {
			# need to guess the number of sockets
			if ($procs{mptable}) {
				$sockets = $procs{mptable};
			} else {
				$sockets = $procs{threads};
			}
			if ($cpu_threads && ($hyper eq "YES_OFF" || $hyper eq "YES_ON")) {
				# if HT is supported, ncpu should be 2x number of sockets
				$sockets = ceil($sockets / $cpu_threads);
			}
			if ($cpu_cores) {
				$sockets = ceil($sockets / $cpu_cores);
			}
			$sockets = 1 if ($sockets < 1);
		}
	}
	if (!$procs{sockets} || $procs{sockets} != $sockets) {
		if ($procs{sockets}) {
			# only warn if we got bad info from smbios
			$debug .= sprintf("Debug-Sockets:\t(smbios=%d) != (lookup=%d) ==> setting sockets=%d\n", $procs{sockets}, $sockets, $sockets);
		}
		# we figured out sockets above, use it
		$procs{sockets} = $sockets;

		# our best guess, make it look like smbios thought we had these minimum number of active cpus
		my($i);
		for ($i=0; $i < ceil($procs{active_threads} / $cpu_cores / $cpu_threads); $i++) {
			$procs{$i+1}{status} = 1;
		}
	}

	# how many active chips do we have in the system
	my($chips) = 0;
	my($proc);
	foreach $proc (keys %procs) {
		$chips++ if $procs{$proc}{status};
	}
	# does /proc/cpuinfo agree?
	if ($procs{chips} && $procs{chips} != $chips) {
		# if not, let's go with /proc/cpuinfo
		$debug .= sprintf("Debug-Chips:\t(smbios=%d) != (cpuinfo=%d) ==> setting chips=%d\n", $chips, $procs{chips}, $procs{chips});
		$chips = $procs{chips};
	}

	if (!$cpu_info && $cpu_cores == 1) {
		# we don't know this cpu from cpuid, so guess at number of cores
		$cpu_cores = ceil($procs{active_threads} / $cpu_threads);
		$cpu_cores = ceil($cpu_cores / $chips) if ($chips);
	}

	# fixup chips and sockets
	if ($procs{sockets} && ($chips > $procs{sockets})) {
		# fix cases that are clearly broken
		# e.g. Tyan S2865 with Opteron 180 dual-core reports 2 sockets in smbios
		$debug .= sprintf("Debug-Sockets:\t(chips=%d) > (smbios=%d) ==> setting chips=%d\n", $chips, $procs{sockets}, $procs{sockets});
		$chips = $procs{sockets};
	}

	# was using active_threads here before, but we really want just threads which should be accurate
	if ($procs{threads} > $chips * $cpu_cores * $cpu_threads) {
		# most likely smbios said we had fewer chips
		# or we didn't have smbios so we guessed at one chip
		$debug .= sprintf("Debug-Chips:\t(threads=%d) > (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> setting chips=%d\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, ceil($procs{threads} / $cpu_cores / $cpu_threads));
		$chips = ceil($procs{threads} / $cpu_cores / $cpu_threads);
		if ($procs{sockets} < $chips) {
			$procs{sockets} = $chips;
		}
	} elsif ($procs{threads} < $chips * $cpu_cores * $cpu_threads) {
		if ($cpu_threads > 1 && $procs{threads} <= $chips * $cpu_cores) {
			if ($hyper ne "YES_OFF") {
				# deal with case where HT is turned off in BIOS
				$hyper = "YES_MISSING";
			}
		} else {
			if ($procs{threads} == 1) {
				# should mean we have UP kernel running
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> running UP kernel?\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads);
			} elsif (defined($grub{'maxcpus'})) {
				# maxcpus is limited in grub.conf
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> maxcpus=%d limited in $grub\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, $grub{'maxcpus'});
			} else {
				# we'll assume cores/threads are accurate, chips is not
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> setting chips=%d\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, ceil($procs{threads} / $cpu_cores / $cpu_threads));
				$chips = ceil($procs{threads} / $cpu_cores / $cpu_threads);
				if ($procs{sockets} < $chips) {
					$procs{sockets} = $chips;
				}
			}
		}
	}

	$procs{cores} = $chips * $cpu_cores;
	$procs{threads} = $chips * $cpu_cores * $cpu_threads;
	if ($procs{cores} > $procs{active_threads}) {
		$procs{active_cores} = $procs{active_threads};
	} else {
		$procs{active_cores} = $procs{cores};
	}

	# sanity check on cores/threads
	if ($procs{active_threads} > $procs{threads}) {
		$debug .= sprintf("Debug-Threads-Active:\t(active_threads=%d) > (threads=%d)\n", $procs{active_threads}, $procs{threads});
	}
	if ($procs{active_cores} > $procs{cores}) {
		$debug .= sprintf("Debug-Cores-Active:\t(active_cores=%d) > (cores=%d)\n", $procs{active_cores}, $procs{cores});
	}
	if ($procs{sockets} && ($procs{cores} > $procs{sockets} * $cpu_cores)) {
		$debug .= sprintf("Debug-Cores:\t(cores=%d) > (sockets=%d * cpu_cores=%d)\n", $procs{cores}, $procs{sockets}, $cpu_cores);
	}
	if ($procs{sockets} && ($procs{threads} > $procs{sockets} * $cpu_cores * $cpu_threads)) {
		$debug .= sprintf("Debug-Threads:\t(threads=%d) > (sockets=%d * cpu_cores=%d * cpu_threads=%d)\n", $procs{threads}, $procs{sockets}, $cpu_cores, $cpu_threads);
	}
	if ($procs{cores} != $chips * $cpu_cores) {
		$debug .= sprintf("Debug-Cores:\t(cores=%d) != (chips=%d * cpu_cores=%d)\n", $procs{cores}, $chips, $cpu_cores);
	}

	# ready for results

	$procs = "$chips x $model";
	$procs .= " $speed" if ($speed);
	if ($chips >= $procs{sockets}) {
		$processors = "$chips x $model";
	} else {
		$processors = "$chips (of $procs{sockets}) x $model";
	}
	$processors .= " $speed" if ($speed);
	$processors .= " $procs{$proc_id}{clock} FSB" if ($procs{$proc_id}{clock});

	my($open, $close) = (" (", "");
	if ($hyper =~ /^YES/ && $vendor ne "AMD") {
		$processors .= $open . "HT ";
		$open = ", ";
		$close = ")";
		if ($hyper eq "YES_ON") {
			$processors .= "enabled";
		} elsif ($hyper eq "YES_OFF") {
			$processors .= "disabled";
		} elsif ($hyper eq "YES_MISSING" || $hyper eq "YES_UP") {
			$processors .= "missing";
		}
	}

	if ($procs{cores} > $chips || $procs{active_cores} < $procs{cores}) {
		if ($procs{active_cores} < $procs{cores}) {
			$processors .= $open . "$procs{active_cores}/$procs{cores} cores";
		} else {
			$processors .= $open . "$procs{active_cores} cores";
		}
		$open = ", ";
		$close = ")";
		if ($procs{threads} && $procs{threads} > $procs{cores}) {
			if ($procs{active_threads} < $procs{threads}) {
				$processors .= $open . "$procs{active_threads}/$procs{threads} threads";
			} else {
				$processors .= $open . "$procs{active_threads} threads";
			}
		}
	} elsif ($procs{threads} > $chips) {
		if ($procs{active_threads} < $procs{threads}) {
			$processors .= $open . "$procs{active_threads}/$procs{threads} threads";
		} else {
			$processors .= $open . "$procs{active_threads} threads";
		}
		$open = ", ";
		$close = ")";
	}
	$processors .= $close;

	if ($cpu_info) {
		$processors .= " - $cpu_info" if ($cpu_info);
	} elsif ($cpuid) {
		$debug .= "Debug-Processors:\t$id\n";
	}

#	my($features) = $procs{$proc_id}{features} || $dmesg{proc}{features};
#	if ($features) {
#		$processors .= " (features = $features)";
#	} elsif ($procs{$proc_id}{flags}) {
#		$processors .= " (flags = $procs{$proc_id}{flags})";
#	}
}

sub memory {
	my($mem, $memory) = ("", "");
	my($total) = 0;
	if (-r $meminfo) {
		my($in) = "MEMINFO";
		my($line);
		open($in, "< $meminfo") || warn "open: $meminfo: $!";
		while ($line = <$in>) {
			$debug_meminfo .= $line;
			chomp($line);
			if ($line =~ /^Mem:\s*(\d+)/i) {
				$total = $1;
			} elsif ($line =~ /^MemTotal:\s*(\d+)\s*kB/i) {
				$total = $1 * 1024 unless $total;
			}
		}
		close($in);
	} else {
		# first check with dmesg
		if ($dmesg_buf =~ /^Physical memory chunk/m) {
			my($line);
			my($match) = 0;
			foreach $line (split(/\n/, $dmesg_buf)) {
				if ($line =~ /^Physical memory chunk/) {
					$match = 1;
				} elsif ($match) {
					if ($line =~ /(\d+) bytes/) {
						$total += $1;
					} else {
						last;
					}
				}
			}
		} elsif ($dmesg_buf =~ /^avail memory = (\d+)/m) {
			$total = $1;
		}

		# now let's see what sysctl hw.* has to say
		my($arch, $pae, $pages, $pagesize, $physmem);
		chomp($arch = `sysctl -n hw.machine_arch 2> /dev/null` || 0);
		chomp($pae = `sysctl -n kern.features.pae 2> /dev/null` || 0);
		chomp($pages = `sysctl -n hw.maxmem 2> /dev/null` || `sysctl -n hw.availpages 2> /dev/null` || 0);
		chomp($pagesize = `sysctl -n hw.pagesize 2> /dev/null` || 0);
		chomp($physmem = `sysctl -n hw.physmem 2> /dev/null` || 0);

		my($try) = 0;
		if ($pae && !$total) {
			# hw.physmem seems to be accurate, but is only 32-bit int, so can't use in case of pae
			# however pages method seems to over estimate, so only use it if we don't have something from dmesg
			$try = $pages * $pagesize;
		}
		if (!$try) {
			$try = $physmem;
		}
		# prefer hw.physmem if we have it, but prefer dmesg if we resort to hw.maxmmem
		$total = $try if ($try > $total);
	}
	$mem = print_bytes($total, 1024, 1, 100);	# probably shouldn't force int=1 here, but didn't have .0 in legacy
	$total = $total / 1024 / 1024;

	if ($smbios{'Memory'}{'Count'}) {
		my($sum) = 0;
		my($i);
		my($device, %device);
		my($key);
		my($sep) = "";
		my($speed) = 0;
		my($type);
	
		for ($i=1; $i <= $smbios{'Memory'}{'Count'}; $i++) {
			my($size) = $smbios{'Memory'}{$i}{'Size'};
			if ($size =~ /(\d+)(MB)?/i) {
				$size = $1 * 1024 * 1024;
				# only use type/speed info if socket is populated
				$type = $smbios{'Memory'}{$i}{'Type'} if ($smbios{'Memory'}{$i}{'Type'});
				$speed = $smbios{'Memory'}{$i}{'Speed'} if ($smbios{'Memory'}{$i}{'Speed'});
			} else {
				$size = 0;
			}
			$sum += $size;
			$key = "$size,";
			$key .= $smbios{'Memory'}{$i}{'Part Number'} if ($smbios{'Memory'}{$i}{'Part Number'});
			$device{$key}++;
		}
		$mem .= " / " if ($mem && $sum);
		$mem .= print_bytes($sum, 1024, 1) if ($sum);
		if ($speed) {
			if ($speed eq "133MHz" && $system =~ /140 G3/) {
				$speed = "533MHz";
			}
		}
		if ($mem) {
			$mem .= " $speed" if ($speed);
			$mem .= " $type" if ($type && $type ne "Unknown" && $type ne "DRAM");

			my(%pretty);
			foreach $key (keys %device) {
				my($size, $part) = ($key =~ /^(.*?),(.*)/);
				my($try);
				my($found) = 0;
				if ($part) {
					foreach $try (keys %memory_models) {
						if ($part =~ /^$try$/i) {
							my($pretty_key) = "$size," . $memory_models{$try};
							$pretty{$pretty_key} += $device{$key};
							$found++;
							last;
						}
					}
					if (!$found) {
						$debug .= "Debug-Memory:\t$part\n";
					}
				}
				if (!$found) {
					$pretty{"$size,"} += $device{$key};
				}
			}

			$sep  = "";
			# sort by memory size first, then number of dimms, then length of description
			foreach $key (sort { (split(/,/,$b))[0] <=> (split(/,/,$a))[0] || $pretty{$b} <=> $pretty{$a} || length($b) <=> length($a) } keys %pretty) {
				my($size, $model) = ($key =~ /^(.*?),(.*)/);
				if ($size) {
					$size = print_bytes($size, 1024, 1);
				} else {
					$size = "empty";
				}
				$memory .= "$sep$pretty{$key} x $size";
				$memory .= " - $model" if ($model);
				$sep = ", ";
			}
		}
	}
	return ($mem, $memory, $total);
}

grok_procs();

my($mem, $memory, $meg) = memory();

if ($os_type eq 'freebsd') {
	freebsd_disks();
} elsif ($os_type eq 'linux') {
	grok_proc_ide() unless $success{'grok_lshal_ide'};
	grok_proc_scsi() unless $success{'grok_lshal_scsi'};
	grok_proc_cciss();
	grok_proc_partitions();
	grok_mdstat() if ($need_mdstat);
}

if ($need_hpacucli || $want_hpacucli) {
	hpacucli();
}

if ($need_twcli || $want_twcli) {
	twcli();
}

if ($need_mptutil || $want_mptutil) {
	mptutil();
}

if ($need_mfiutil || $want_mfiutil) {
	mfiutil();
}

if ($need_megarc || $want_megarc) {
	megarc() || omreport();
}

if ($need_megacli || $want_megacli) {
	megacli() || omreport();
}

if ($need_cissutil || $want_cissutil) {
	cissutil();
}

if ($need_arcconf || $want_arcconf) {
	arcconf() || omreport();
}

if ($os_type eq 'freebsd') {
	freebsd_disks_unknown();
	freebsd_disk_ioctls();
}

my($ymodel) = '';
if ($system_model_index && defined $system_models[$system_model_index]{'ymodel-type'}) {
	$ymodel = sprintf("%s%d%d%d", $system_models[$system_model_index]{'ymodel-type'}, $procs{gen} || 1, log($procs{cores})/log(2), log(($meg/1024)+0.5)/log(2));
}

$output .= "Summary:\t";
#$output .= "$ymodel - " if ($ymodel);
$output .= "$sys, $procs, $mem\n";
$output .= "System:\t\t$system";
$output .= " ($base_board)" if ($base_board && $base_board ne $system && !defined($smbios{'Base Board'}{2}));
$output .= "\nProcessors:\t$processors\n";

if (length($memory) > 70 && !$opts{'n'}) {
	my($sep) = sprintf("\n\t\t%*s", length($mem) + 4, " ");
	$memory =~ s/, /$sep/g;
}
$output .= "Memory:\t\t$mem";
$output .= " == $memory" if ($memory);
$output .= "\n";

lvdisplay() if ($need_lvm);
df();
ccdconfig() if ($need_ccdconfig);

print_disks();

# need this before other_devices()
grok_network();

other_devices();

my($interface);
foreach $interface (sort cmp_devs keys %interfaces) {
	next if ($interface =~ /^(vlan|bond|dummy|vmnet|sw|irda|venet|veth|xenbr|virbr|cipsec|wmaster|mskc|pan)\d+$/);
	my($driver) = $interface_to_driver{$interface};
	$output .= "Network:\t$interface";
	$output .= " ($driver)" if ($driver);
	$output .= ": ";
	if ($driver && $interfaces{$driver} && $interfaces{$driver}{model}) {
		# only example of this is msk0 (interface) -> mskc0 (pci-device/driver)
		$output .= $interfaces{$driver}{model} . ", ";
	} elsif ($interfaces{$interface}{model}) {
		$output .= $interfaces{$interface}{model} . ", ";
	}
	$output .= $interfaces{$interface}{mac} if ($interfaces{$interface}{mac});
	if (defined($interfaces{$interface}{status})) {
		if ($interfaces{$interface}{status} eq "active") {
			$output .= ", $interfaces{$interface}{media}" if ($interfaces{$interface}{media});
		} else {
			$output .= ", $interfaces{$interface}{status}";
		}
	}
	$output .= "\n";
}

if ($os) {
	$output .= "OS:\t\t$os";
	if ($os =~ /amd64|x86_64/) {
		$output .= ", 64-bit";
	} else {
		$output .= ", 32-bit";
	}
	$output .= "\n";
}
$output .= "BIOS:\t\t$bios\n" if ($bios);

if ($os_type eq "linux") {
	chomp($hostname = `hostname -f`);
} else {
	chomp($hostname = `hostname`);
}
if ($hostname) {
	$output .= "Hostname:\t$hostname";
	my($pre, $post) = (" (", "");
	if ($jailed) {
		$output .= $pre . "jailed";
		$output .= " on $jailer" if ($jailer);
		$pre = ", ";
		$post = ")";
	}
	if ($yroot) {
		$output .= $pre . "yroot $yroot";
		$pre = ", ";
		$post = ")";
	}
	$output .= $post . "\n";
}

$output .= "\n$debug" if ($opts{'d'} && $debug);

if ($need_cissutil && !$got_cissutil) {
	$stderr .= "$zero: warning: could not run $cissutil";
	$stderr .= "; please yinst $cissutil" unless ($have_cissutil);
	$stderr .= "\n";
}
if ($need_megarc && !$got_megarc && !$got_omreport) {
	$stderr .= "$zero: warning: could not run $megarc";
	$stderr .= "; please yinst $megarc" unless ($have_megarc);
	$stderr .= "\n";
}
if ($need_megacli && !$got_megacli && !$got_omreport) {
	$stderr .= "$zero: warning: could not run MegaCli";
	$stderr .= "; please yinst megacli" unless ($have_megacli);
	$stderr .= "\n";
}
if ($need_mfiutil && !$got_mfiutil) {
	$stderr .= "$zero: warning: could not run $mfiutil";
	$stderr .= "; please yinst $mfiutil" unless ($have_mfiutil);
	$stderr .= "\n";
}
if ($need_mptutil && !$got_mptutil) {
	$stderr .= "$zero: warning: could not run $mptutil";
	$stderr .= "; please yinst $mptutil" unless ($have_mptutil);
	$stderr .= "\n";
}
if ($need_hpacucli && !$got_hpacucli) {
	$stderr .= "$zero: warning: could not run $hpacucli";
	$stderr .= "; please yinst ports/$hpacucli" unless ($have_hpacucli);
	$stderr .= "\n";
}
if ($need_twcli && !$got_twcli) {
	$stderr .= "$zero: warning: could not run $twcli";
	$stderr .= "; please yinst $twcli" unless ($have_twcli);
	$stderr .= "\n";
}
if ($need_arcconf && !$got_arcconf && !$got_omreport) {
	$stderr .= "$zero: warning: could not run $arcconf";
	$stderr .= "\n";
}

if ($opts{'o'}) {
	my($out) = 'OUT';
	open($out, "> $opts{'o'}") || warn("open: $opts{'o'}: $!");
	print STDERR $stderr if ($stderr);
	print $out $output;
	close($out);
	if (($debug || $perl_warn) && !$jailed) {
		my($old, $new) = ("", "");
		my($tmp) = $opts{'o'};
		$tmp =~ s/^.*\///;
		$tmp = "/var/tmp/$tmp";
		if (-e $tmp) {
			$old = `cat $tmp`;
			# nuke disk usage and hostnames for diff
			$old =~ s/(\s*\d+)\%//gm;
			$old =~ s/^Hostname:.*//m;
			$new = $output;
			$new =~ s/(\s*\d+)\%//gm;
			$new =~ s/^Hostname:.*//m;
		}
		if (! -e $tmp || $old ne $new) {
			email($old, $new);
			open($out, "> $tmp") || warn("open: $tmp: $!");
			print $out $output;
			close($out);
		}
	}
	unlink($lock);
} else {
	print STDERR $stderr if ($stderr);
	print $output;
}

if ($opts{'m'}) {
	email('', '');
}

系统信息脚本

#!/bin/sh
# This program is part of Aspersa (http://code.google.com/p/aspersa/)

# ########################################################################
# A script to summarize system information in a nice way.
# Goals: work well on Linux; create a compact diff-able report that is
# easy to paste into a wiki or email, and easy to scan and compare too.
#
# Usage: $ wget -O- http://aspersa.googlecode.com/svn/trunk/summary |bash
# Options are set through the ASPERSA_SKIP environment variable.  Set this
# variable to a comma-separated list of things you want to omit.
# Options:
#  MOUNT:   Don't print out mounted filesystems and disk fullness.
#  NETWORK: Don't print out information on network controllers & config.
#  PROCESS: Don't print out top processes and vmstat information.
#
# Authors:
#  Baron Schwartz
#  Kevin van Zonneveld (kvz@php.net || http://kevin.vanzonneveld.net)
# ########################################################################

# ########################################################################
# Globals, settings, helper functions
# ########################################################################
POSIXLY_CORRECT=1
export POSIXLY_CORRECT

# The awk code for fuzzy rounding.  (It's used in a few places, so makes sense
# not to duplicate).  It fuzzy-rounds the variable named fuzzy_var.  It goes in
# steps of 5, 10, 25, then repeats by a factor of 10 larger (50, 100, 250), and
# so on, until it finds a number that's large enough.  The pattern is slightly
# broken between the initial 1 and 50, because rounding to the nearest 2.5
# doesn't seem right to me.
fuzzy_formula='
   rounded = 0;
   if (fuzzy_var <= 10 ) {
      rounded   = 1;
   }
   factor = 1;
   while ( rounded == 0 ) {
      if ( fuzzy_var <= 50 * factor ) {
         fuzzy_var = sprintf("%.0f", fuzzy_var / (5 * factor)) * 5 * factor;
         rounded   = 1;
      }
      else if ( fuzzy_var <= 100  * factor) {
         fuzzy_var = sprintf("%.0f", fuzzy_var / (10 * factor)) * 10 * factor;
         rounded   = 1;
      }
      else if ( fuzzy_var <= 250  * factor) {
         fuzzy_var = sprintf("%.0f", fuzzy_var / (25 * factor)) * 25 * factor;
         rounded   = 1;
      }
      factor = factor * 10;
   }'

# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger
# as the number gets larger.  This is to make things easier to diff.
fuzz () {
   echo $1 | $AP_AWK "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}"
}

# The temp files are for storing working results so we don't call commands many
# times (gives inconsistent results, maybe adds load on things I don't want to
# such as RAID controllers).  They must not exist -- if they did, someone would
# symlink them to /etc/passwd and then run this program as root.  Call this
# function with "rm" or "touch" as an argument.
temp_files() {
   for file in /tmp/aspersa /tmp/aspersa2; do
      case "$1" in
      touch)
         if ! touch "${file}"; then
            echo "I can't make my temp file ${file}";
            exit 1;
         fi
         ;;
      rm)
         rm -f "${file}"
         ;;
      esac
   done
}

# Print a space-padded string into $line.  Then translate spaces to hashes, and
# underscores to spaces.  End result is a line of hashes with words at the
# start.
section () {
   echo "$1" | awk '{l=sprintf("#_%-60s", $0 "_"); print l}' | sed -e 's/ /#/g' -e 's/_/ /g'
}

# Print a "name | value" line.
name_val() {
   printf "%12s | %s\n" "$1" "$(echo $2)"
}

# Converts a value to units of power of 2.  Arg 1: the value.  Arg 2: precision (defaults to 2).
shorten() {
   echo $@ | awk '{
      unit = "k";
      size = 1024;
      val  = $1;
      prec = 2;
      if ( $2 ~ /./ ) {
         prec = $2;
      }
      if ( val >= 1099511627776 ) {
         size = 1099511627776;
         unit = "T";
      }
      else if ( val >= 1073741824 ) {
         size = 1073741824;
         unit = "G";
      }
      else if ( val >= 1048576 ) {
         size = 1048576;
         unit = "M";
      }
      printf "%." prec "f%s", val / size, unit;
   }'
}

# ##############################################################################
# Function to take a file and collapse it into an aggregated list.  This
# function works on $1, which it expects to be created with 'sort |
# uniq -c'.  Leading whitespace is deleted.  The result will look like
# "4xabc, 1xdef"  Copy any changes to 'mysql-summary' too.
# ##############################################################################
group_concat () {
   sed -e '{H; $!d}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' ${1}
   # In words: save the whole file into the hold space,
   # {H; $!d}
   # Swap it back into the pattern space,
   # x
   # Join lines with a comma, delete leading whitespace, and put an 'x' between
   # the number and the text that follows,
   # s/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g
   # Collapse whitespace,
   # s/[[:space:]][[:space:]]*/ /g
   # And delete the leading comma-space.
   # s/, //
}

# ##############################################################################
# Functions for parsing specific files and getting desired info from them.
# These are called from within main() and are separated so they can be tested
# easily.  The calling convention is that the data they need to run is prepared
# first by putting it into /tmp/aspersa.  Then code that's testing just needs to
# put sample data into /tmp/aspersa and call it.
# ##############################################################################
   
# ##############################################################################
# Parse Linux's /proc/cpuinfo, which should be stored in /tmp/aspersa.
# ##############################################################################
parse_proc_cpuinfo () {
   # Physical processors are indicated by distinct 'physical id'.  Virtual CPUs
   # are indicated by paragraphs -- one per paragraph.  We assume that all
   # processors are identical, i.e. that there are not some processors with dual
   # cores and some with quad cores.
   virtual=$(grep -c ^processor /tmp/aspersa);
   physical=$(grep 'physical id' /tmp/aspersa | sort -u | wc -l);
   cores=$(grep 'cpu cores' /tmp/aspersa | head -n 1 | cut -d: -f2);

   # Older kernel won't have 'physical id' or 'cpu cores'.
   if [ "${physical}" = "0" ]; then physical=${virtual}; fi
   if [ -z "${cores}" ]; then cores=0; fi

   # Test for HTT; cannot trust the 'ht' flag.  If physical * cores < virtual,
   # then hyperthreading is in use.
   cores=$((${cores} * ${physical}));
   if [ ${cores} -gt 0 -a $cores -lt $virtual ]; then htt=yes; else htt=no; fi

   name_val "Processors" "physical = ${physical}, cores = ${cores}, virtual = ${virtual}, hyperthreading = ${htt}"

   awk -F: '/cpu MHz/{print $2}' /tmp/aspersa \
      | sort | uniq -c > /tmp/aspersa2
   name_val "Speeds" "$(group_concat /tmp/aspersa2)"

   awk -F: '/model name/{print $2}' /tmp/aspersa \
      | sort | uniq -c > /tmp/aspersa2
   name_val "Models" "$(group_concat /tmp/aspersa2)"

   awk -F: '/cache size/{print $2}' /tmp/aspersa \
      | sort | uniq -c > /tmp/aspersa2
   name_val "Caches" "$(group_concat /tmp/aspersa2)"
}

# ##############################################################################
# Parse sysctl -a output on FreeBSD, and format it as CPU info.  The file is the
# first argument.
# ##############################################################################
parse_sysctl_cpu_freebsd() {
   virtual="$(awk '/hw.ncpu/{print $2}' "$1")"
   name_val "Processors" "virtual = ${virtual}"
   name_val "Speeds" "$(awk '/hw.clockrate/{print $2}' "$1")"
   name_val "Models" "$(awk -F: '/hw.model/{print substr($2, 2)}' "$1")"
}

# ##############################################################################
# Parse CPU info from psrinfo -v
# ##############################################################################
parse_psrinfo_cpus() {
   name_val Processors $(grep -c 'Status of .* processor' "$1")
   awk '/operates at/ {
      start = index($0, " at ") + 4;
      end   = length($0) - start - 4
      print substr($0, start, end);
   }' "$1" | sort | uniq -c > /tmp/aspersa2
   name_val "Speeds" "$(group_concat /tmp/aspersa2)"
}

# ##############################################################################
# Parse the output of 'free -b' plus the contents of /proc/meminfo
# ##############################################################################
parse_free_minus_b () {
   physical=$(awk '/Mem:/{print $3}' "${1}")
   swap=$(awk '/Swap:/{print $3}' "${1}")
   virtual=$(shorten $(($physical + $swap)))

   name_val Total   $(shorten $(awk '/Mem:/{print $2}' "${1}"))
   name_val Free    $(shorten $(awk '/Mem:/{print $4}' "${1}"))
   name_val Used    "physical = $(shorten ${physical}), swap = $(shorten ${swap}), virtual = ${virtual}"
   name_val Buffers $(shorten $(awk '/Mem:/{print $6}' "${1}"))
   name_val Caches  $(shorten $(awk '/Mem:/{print $7}' "${1}"))
   name_val Dirty  "$(awk '/Dirty:/ {print $2, $3}' "${1}")"
}

# ##############################################################################
# Parse FreeBSD memory info from sysctl output.
# ##############################################################################
parse_memory_sysctl_freebsd() {
   physical=$(awk '/hw.realmem:/{print $2}' "${1}")
   mem_hw=$(awk '/hw.physmem:/{print $2}' "${1}")
   mem_used=$(awk '
      /hw.physmem/                   { mem_hw       = $2; }
      /vm.stats.vm.v_inactive_count/ { mem_inactive = $2; }
      /vm.stats.vm.v_cache_count/    { mem_cache    = $2; }
      /vm.stats.vm.v_free_count/     { mem_free     = $2; }
      /hw.pagesize/                  { pagesize     = $2; }
      END {
         mem_inactive *= pagesize;
         mem_cache    *= pagesize;
         mem_free     *= pagesize;
         print mem_hw - mem_inactive - mem_cache - mem_free;
      }
   ' "$1");
   name_val Total   $(shorten ${mem_hw} 1)
   name_val Virtual $(shorten ${physical} 1)
   name_val Used    $(shorten ${mem_used} 1)
}

# ##############################################################################
# Parse memory devices from the output of 'dmidecode', which should be stored in
# /tmp/aspersa.
# ##############################################################################
parse_dmidecode_mem_devices () {
   echo "  Locator   Size     Speed             Form Factor   Type          Type Detail"
   echo "  ========= ======== ================= ============= ============= ==========="
   # Print paragraphs containing 'Memory Device\n', extract the desired bits,
   # concatenate them into one long line, then format as a table.  The data
   # comes out in this order for each paragraph:
   # $2  Size         2048 MB
   # $3  Form Factor  <OUT OF SPEC>
   # $4  Locator      DIMM1
   # $5  Type         <OUT OF SPEC>
   # $6  Type Detail  Synchronous
   # $7  Speed        667 MHz (1.5 ns)
   sed    -e '/./{H;$!d;}' \
          -e 'x;/Memory Device\n/!d;' \
          -e 's/: /:/g' \
          -e 's/</{/g' \
          -e 's/>/}/g' \
          -e 's/[ \t]*\n/\n/g' \
       /tmp/aspersa \
       | awk -F: '/Size|Type|Form.Factor|Type.Detail|[^ ]Locator/{printf("|%s", $2)}/Speed/{print "|" $2}' \
       | sed -e 's/No Module Installed/{EMPTY}/' \
       | sort \
       | awk -F'|' '{printf("  %-9s %-8s %-17s %-13s %-13s %-8s\n", $4, $2, $7, $3, $5, $6);}'
}

# ##############################################################################
# Parse the output of 'netstat -antp'
# ##############################################################################
parse_ip_s_link () {
   echo "  interface  rx_bytes rx_packets  rx_errors   tx_bytes tx_packets  tx_errors"
   echo "  ========= ========= ========== ========== ========== ========== =========="

   awk "/^[1-9][0-9]*:/ {
      save[\"iface\"] = substr(\$2, 0, index(\$2, \":\") - 1);
      new = 1;
   }
   \$0 !~ /[^0-9 ]/ {
      if ( new == 1 ) {
         new = 0;
         fuzzy_var = \$1; ${fuzzy_formula} save[\"bytes\"] = fuzzy_var;
         fuzzy_var = \$2; ${fuzzy_formula} save[\"packs\"] = fuzzy_var;
         fuzzy_var = \$3; ${fuzzy_formula} save[\"errs\"]  = fuzzy_var;
      }
      else {
         fuzzy_var = \$1; ${fuzzy_formula} tx_bytes   = fuzzy_var;
         fuzzy_var = \$2; ${fuzzy_formula} tx_packets = fuzzy_var;
         fuzzy_var = \$3; ${fuzzy_formula} tx_errors  = fuzzy_var;
         printf \"  %-8s %10d %10d %10d %10d %10d %10d\\n\", save[\"iface\"], save[\"bytes\"], save[\"packs\"], save[\"errs\"], tx_bytes, tx_packets, tx_errors;
      }
   }" $@
}

# ##############################################################################
# Parse the output of 'netstat -antp' which should be in /tmp/aspersa.
# ##############################################################################
parse_netstat () {
   echo "  Connections from remote IP addresses"
   awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
      print substr($5, 0, index($5, ":") - 1);
   }' /tmp/aspersa | sort | uniq -c \
      | awk "{
         fuzzy_var=\$1;
         ${fuzzy_formula}
         printf \"    %-15s %5d\\n\", \$2, fuzzy_var;
         }" \
      | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4
   echo "  Connections to local IP addresses"
   awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
      print substr($4, 0, index($4, ":") - 1);
   }' /tmp/aspersa | sort | uniq -c \
      | awk "{
         fuzzy_var=\$1;
         ${fuzzy_formula}
         printf \"    %-15s %5d\\n\", \$2, fuzzy_var;
         }" \
      | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4
   echo "  Connections to top 10 local ports"
   awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ {
      print substr($4, index($4, ":") + 1);
   }' /tmp/aspersa | sort | uniq -c | sort -rn | head -n10 \
      | awk "{
         fuzzy_var=\$1;
         ${fuzzy_formula}
         printf \"    %-15s %5d\\n\", \$2, fuzzy_var;
         }" | sort
   echo "  States of connections"
   awk '$1 ~ /^tcp/ {
      print $6;
   }' /tmp/aspersa | sort | uniq -c | sort -rn \
      | awk "{
         fuzzy_var=\$1;
         ${fuzzy_formula}
         printf \"    %-15s %5d\\n\", \$2, fuzzy_var;
         }" | sort
}

# ##############################################################################
# Parse the joined output of 'mount' and 'df -hP'.  $1 = file; $2 = ostype.
# ##############################################################################
parse_filesystems () {
   # Filesystem names and mountpoints can be very long.  We try to align things
   # as nicely as possible by making columns only as wide as needed.  This
   # requires two passes through the file.  The first pass finds the max size of
   # these columns and prints out a printf spec, and the second prints out the
   # file nicely aligned.
   cat > /tmp/aspersa.awk <<-EOF
      BEGIN {
         device     = 10;
         fstype     = 4;
         options    = 4;
      }
      /./ {
         f_device     = \$1;
         f_fstype     = \$10;
         f_options    = substr(\$11, 2, length(\$11) - 2);
         if ( "$2" == "FreeBSD" ) {
            f_fstype  = substr(\$9, 2, length(\$9) - 2);
            f_options = substr(\$0, index(\$0, ",") + 2);
            f_options = substr(f_options, 1, length(f_options) - 1);
         }
         if ( length(f_device) > device ) {
            device=length(f_device);
         }
         if ( length(f_fstype) > fstype ) {
            fstype=length(f_fstype);
         }
         if ( length(f_options) > options ) {
            options=length(f_options);
         }
      }
      END{
         print "%-" device "s %5s %4s %-" fstype "s %-" options "s %s";
      }
	EOF
   spec="$( awk -f /tmp/aspersa.awk "$1" )";
   #awk -f /tmp/aspersa.awk "$1"
   #return;

   cat > /tmp/aspersa.awk <<-EOF
      BEGIN {
         spec="  ${spec}\\n";
         printf spec, "Filesystem", "Size", "Used", "Type", "Opts", "Mountpoint";
      }
      {
         f_fstype     = \$10;
         f_options    = substr(\$11, 2, length(\$11) - 2);
         if ( "$2" == "FreeBSD" ) {
            f_fstype  = substr(\$9, 2, length(\$9) - 2);
            f_options = substr(\$0, index(\$0, ",") + 2);
            f_options = substr(f_options, 1, length(f_options) - 1);
         }
         printf spec, \$1, \$2, \$5, f_fstype, f_options, \$6;
      }
	EOF
   awk -f /tmp/aspersa.awk "$1"
}

# ##############################################################################
# Parse the output of fdisk -l, which should be in /tmp/aspersa; there might be
# multiple fdisk -l outputs in the file.
# ##############################################################################
parse_fdisk () {
   awk '
      BEGIN {
         format="%-12s %4s %10s %10s %18s\n";
         printf(format, "Device", "Type", "Start", "End", "Size");
         printf(format, "============", "====", "==========", "==========", "==================");
      }
      /Disk.*bytes/ {
         disk = substr($2, 1, length($2) - 1);
         size = $5;
         printf(format, disk, "Disk", "", "", size);
      }
      /Units/ {
         units = $9;
      }
      /^\/dev/ {
         if ( $2 == "*" ) {
            start = $3;
            end   = $4;
         }
         else {
            start = $2;
            end   = $3;
         }
         printf(format, $1, "Part", start, end, sprintf("%.0f", (end - start) * units));
      }
   ' /tmp/aspersa
}

# ##############################################################################
# Parse the output of dmesg, which should be in /tmp/aspersa, and detect
# virtualization.
# ##############################################################################
parse_virtualization_dmesg () {
   if grep -qi -e vmware -e vmxnet -e 'paravirtualized kernel on vmi' /tmp/aspersa; then
      echo "VMWare";
   elif grep -qi -e 'paravirtualized kernel on xen' -e 'Xen virtual console' /tmp/aspersa; then
      echo "Xen";
   elif grep -qi qemu /tmp/aspersa; then
      echo "QEmu";
   elif grep -qi 'paravirtualized kernel on KVM' /tmp/aspersa; then
      echo "KVM";
   elif grep -q VBOX /tmp/aspersa; then
      echo "VirtualBox";
   elif grep -qi 'hd.: Virtual .., ATA.*drive' /tmp/aspersa; then
      echo "Microsoft VirtualPC";
   fi
}

# ##############################################################################
# Try to figure out if a system is a guest by looking at prtdiag, smbios, etc.
# ##############################################################################
parse_virtualization_generic() {
   if grep -i -e virtualbox "$1" >/dev/null; then
      echo VirtualBox
   elif grep -i -e vmware "$1" >/dev/null; then
      echo VMWare
   fi
}

# ##############################################################################
# Parse the output of lspci, which should be in /tmp/aspersa, and detect
# Ethernet cards.
# ##############################################################################
parse_ethernet_controller_lspci () {
   grep -i ethernet /tmp/aspersa | cut -d: -f3 | while read line; do
      name_val Controller "${line}"
   done
}

# ##############################################################################
# Parse the output of lspci, which should be in /tmp/aspersa, and detect RAID
# controllers.
# ##############################################################################
parse_raid_controller_lspci () {
   if grep -q "RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS" /tmp/aspersa; then
      echo 'LSI Logic MegaRAID SAS'
   elif grep -q "Fusion-MPT SAS" /tmp/aspersa; then
      echo 'Fusion-MPT SAS'
   elif grep -q "RAID bus controller: LSI Logic / Symbios Logic Unknown" /tmp/aspersa; then
      echo 'LSI Logic Unknown'
   elif grep -q "RAID bus controller: Adaptec AAC-RAID" /tmp/aspersa; then
      echo 'AACRAID'
   elif grep -q "3ware [0-9]* Storage Controller" /tmp/aspersa; then
      echo '3Ware'
   elif grep -q "Hewlett-Packard Company Smart Array" /tmp/aspersa; then
      echo 'HP Smart Array'
   elif grep -q " RAID bus controller: " /tmp/aspersa; then
      awk -F: '/RAID bus controller\:/ {print $3" "$5" "$6}' /tmp/aspersa
   fi
}

# ##############################################################################
# Parse the output of dmesg, which should be in /tmp/aspersa, and detect RAID
# controllers.
# ##############################################################################
parse_raid_controller_dmesg () {
   pat='scsi[0-9].*: .*'
   if grep -qi "${pat}megaraid" /tmp/aspersa; then
      echo 'LSI Logic MegaRAID SAS'
   elif grep -q "Fusion MPT SAS" /tmp/aspersa; then
      echo 'Fusion-MPT SAS'
   elif grep -q "${pat}aacraid" /tmp/aspersa; then
      echo 'AACRAID'
   elif grep -q "${pat}3ware [0-9]* Storage Controller" /tmp/aspersa; then
      echo '3Ware'
   fi
}

# ##############################################################################
# Parse the output of "hpacucli ctrl all show config", which should be stored in
# /tmp/aspersa
# ##############################################################################
parse_hpacucli () {
   grep 'logicaldrive\|physicaldrive' /tmp/aspersa
}

# ##############################################################################
# Parse the output of arcconf, which should be stored in /tmp/aspersa
# ##############################################################################
parse_arcconf () {
   model=$(awk -F: '/Controller Model/{print $2}' /tmp/aspersa)
   chan="$(awk -F: '/Channel description/{print $2}' /tmp/aspersa)"
   cache="$(awk -F: '/Installed memory/{print $2}' /tmp/aspersa)"
   status="$(awk -F: '/Controller Status/{print $2}' /tmp/aspersa)"
   name_val Specs "${model/ /},${chan},${cache} cache,${status}"

   battery=$(grep -A5 'Controller Battery Info' /tmp/aspersa \
      | awk '/Capacity remaining/ {c=$4}
             /Status/             {s=$3}
             /Time remaining/     {t=sprintf("%dd%dh%dm", $7, $9, $11)}
             END                  {printf("%d%%, %s remaining, %s", c, t, s)}')
   name_val Battery "${battery}"

   # ###########################################################################
   # Logical devices
   # ###########################################################################
   echo
   echo "  LogicalDev Size      RAID Disks Stripe Status  Cache"
   echo "  ========== ========= ==== ===== ====== ======= ======="
   for dev in $(awk '/Logical device number/{print $4}' /tmp/aspersa); do
      sed -n -e "/^Logical device .* ${dev}$/,/^$\|^Logical device number/p" \
         /tmp/aspersa \
      | awk '
         /Logical device name/               {d=$5}
         /Size/                              {z=$3 " " $4}
         /RAID level/                        {r=$4}
         /Group [0-9]/                       {g++}
         /Stripe-unit size/                  {p=$4 " " $5}
         /Status of logical/                 {s=$6}
         /Write-cache mode.*Ena.*write-back/ {c="On (WB)"}
         /Write-cache mode.*Ena.*write-thro/ {c="On (WT)"}
         /Write-cache mode.*Disabled/        {c="Off"}
         END {
            printf("  %-10s %-9s %4d %5d %-6s %-7s %-7s\n",
               d, z, r, g, p, s, c);
         }'
   done

   # ###########################################################################
   # Physical devices
   # ###########################################################################
   echo
   echo "  PhysiclDev State   Speed         Vendor  Model        Size        Cache"
   echo "  ========== ======= ============= ======= ============ =========== ======="

   # Find the paragraph with physical devices, tabularize with assoc arrays.
   tempresult=""
   sed -n -e '/Physical Device information/,/^$/p' /tmp/aspersa \
      | awk -F: '
         /Device #[0-9]/ {
            device=substr($0, index($0, "#"));
            devicenames[device]=device;
         }
         /Device is a/ {
            devices[device ",isa"] = substr($0, index($0, "is a") + 5);
         }
         /State/ {
            devices[device ",state"] = substr($2, 2);
         }
         /Transfer Speed/ {
            devices[device ",speed"] = substr($2, 2);
         }
         /Vendor/ {
            devices[device ",vendor"] = substr($2, 2);
         }
         /Model/ {
            devices[device ",model"] = substr($2, 2);
         }
         /Size/ {
            devices[device ",size"] = substr($2, 2);
         }
         /Write Cache/ {
            if ( $2 ~ /Enabled .write-back./ )
               devices[device ",cache"] = "On (WB)";
            else
               if ( $2 ~ /Enabled .write-th/ )
                  devices[device ",cache"] = "On (WT)";
               else
                  devices[device ",cache"] = "Off";
         }
         END {
            for ( device in devicenames ) {
               if ( devices[device ",isa"] ~ /Hard drive/ ) {
                  printf("  %-10s %-7s %-13s %-7s %-12s %-11s %-7s\n",
                     devices[device ",isa"],
                     devices[device ",state"],
                     devices[device ",speed"],
                     devices[device ",vendor"],
                     devices[device ",model"],
                     devices[device ",size"],
                     devices[device ",cache"]);
               }
            }
         }'

}

# ##############################################################################
# Parse the output of "lsiutil -i -s" from /tmp/aspersa
# ##############################################################################
parse_fusionmpt_lsiutil () {
   echo
   awk '/LSI.*Firmware/ { print " ", $0 }' /tmp/aspersa
   grep . /tmp/aspersa | sed -n -e '/B___T___L/,$ {s/^/  /; p}'
}

# ##############################################################################
# Parse the output of MegaCli64 -AdpAllInfo -aALL from /tmp/aspersa.
# ##############################################################################
parse_lsi_megaraid_adapter_info () {
   name=$(awk -F: '/Product Name/{print substr($2, 2)}' /tmp/aspersa);
   int=$(awk '/Host Interface/{print $4}' /tmp/aspersa);
   prt=$(awk '/Number of Backend Port/{print $5}' /tmp/aspersa);
   bbu=$(awk '/^BBU             :/{print $3}' /tmp/aspersa);
   mem=$(awk '/Memory Size/{print $4}' /tmp/aspersa);
   vdr=$(awk '/Virtual Drives/{print $4}' /tmp/aspersa);
   dvd=$(awk '/Degraded/{print $3}' /tmp/aspersa);
   phy=$(awk '/^  Disks/{print $3}' /tmp/aspersa);
   crd=$(awk '/Critical Disks/{print $4}' /tmp/aspersa);
   fad=$(awk '/Failed Disks/{print $4}' /tmp/aspersa);
   name_val Model "${name}, ${int} interface, ${prt} ports"
   name_val Cache "${mem} Memory, BBU ${bbu}"
}

# ##############################################################################
# Parse the output (saved in /tmp/aspersa) of
# /opt/MegaRAID/MegaCli/MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL
# ##############################################################################
parse_lsi_megaraid_bbu_status () {
   charge=$(awk '/Relative State/{print $5}' /tmp/aspersa);
   temp=$(awk '/^Temperature/{print $2}' /tmp/aspersa);
   soh=$(awk '/isSOHGood:/{print $2}' /tmp/aspersa);
   name_val BBU "${charge}% Charged, Temperature ${temp}C, isSOHGood=${soh}"
}

# ##############################################################################
# Parse physical devices from the output (saved in /tmp/aspersa) of
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
# OR, it will also work with the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL
# ##############################################################################
parse_lsi_megaraid_devices () {
   echo
   echo "  PhysiclDev Type State   Errors Vendor  Model        Size"
   echo "  ========== ==== ======= ====== ======= ============ ==========="
   for dev in $(awk '/Device Id/{print $3}' /tmp/aspersa); do
      sed -e '/./{H;$!d;}' -e "x;/Device Id: ${dev}/!d;" /tmp/aspersa \
      | awk '
         /Media Type/                        {d=substr($0, index($0, ":") + 2)}
         /PD Type/                           {t=$3}
         /Firmware state/                    {s=$3}
         /Media Error Count/                 {me=$4}
         /Other Error Count/                 {oe=$4}
         /Predictive Failure Count/          {pe=$4}
         /Inquiry Data/                      {v=$3; m=$4;}
         /Raw Size/                          {z=$3}
         END {
            printf("  %-10s %-4s %-7s %6s %-7s %-12s %-7s\n",
               substr(d, 0, 10), t, s, me "/" oe "/" pe, v, m, z);
         }'
   done
}

# ##############################################################################
# Parse virtual devices from the output (saved in /tmp/aspersa) of
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
# OR, it will also work with the output of
# /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aAll
# ##############################################################################
parse_lsi_megaraid_virtual_devices () {
   # Somewhere on the Internet, I found the following guide to understanding the
   # RAID level, but I don't know the source anymore.
   #    Primary-0, Secondary-0, RAID Level Qualifier-0 = 0
   #    Primary-1, Secondary-0, RAID Level Qualifier-0 = 1
   #    Primary-5, Secondary-0, RAID Level Qualifier-3 = 5
   #    Primary-1, Secondary-3, RAID Level Qualifier-0 = 10
   # I am not sure if this is always correct or not (it seems correct).  The
   # terminology MegaRAID uses is not clear to me, and isn't documented that I
   # am aware of.  Anyone who can clarify the above, please contact me.
   echo
   echo "  VirtualDev Size      RAID Level Disks SpnDpth Stripe Status  Cache"
   echo "  ========== ========= ========== ===== ======= ====== ======= ========="
   awk '
      /^Virtual Disk:/ {
         device              = $3;
         devicenames[device] = device;
      }
      /Number Of Drives/ {
         devices[device ",numdisks"] = substr($0, index($0, ":") + 1);
      }
      /^Name:/ {
         devices[device ",name"] = $2 > "" ? $2 : "(no name)";
      }
      /RAID Level/ {
         devices[device ",primary"]   = substr($3, index($3, "-") + 1, 1);
         devices[device ",secondary"] = substr($4, index($4, "-") + 1, 1);
         devices[device ",qualifier"] = substr($NF, index($NF, "-") + 1, 1);
      }
      /Span Depth/ {
         devices[device ",spandepth"] = substr($2, index($2, ":") + 1);
      }
      /Number of Spans/ {
         devices[device ",numspans"] = $4;
      }
      /^Size:/ {
         devices[device ",size"] = substr($0, index($0, ":") + 1);
      }
      /^State:/ {
         devices[device ",state"] = $2;
      }
      /^Stripe Size:/ {
         devices[device ",stripe"] = $3;
      }
      /^Current Cache Policy/ {
         devices[device ",wpolicy"] = $4 ~ /WriteBack/ ? "WB" : "WT";
         devices[device ",rpolicy"] = $5 ~ /ReadAheadNone/ ? "no RA" : "RA";
      }
      END {
         for ( device in devicenames ) {
            raid = 0;
            if ( devices[device ",primary"] == 1 ) {
               raid = 1;
               if ( devices[device ",secondary"] == 3 ) {
                  raid = 10;
               }
            }
            else {
               if ( devices[device ",primary"] == 5 ) {
                  raid = 5;
               }
            }
            printf("  %-10s %-9s %-10s %5d %7s %6s %-7s %s\n",
               device devices[device ",name"],
               devices[device ",size"],
               raid " (" devices[device ",primary"] "-" devices[device ",secondary"] "-" devices[device ",qualifier"] ")",
               devices[device ",numdisks"],
               devices[device ",spandepth"] "-" devices[device ",numspans"],
               devices[device ",stripe"], devices[device ",state"],
               devices[device ",wpolicy"] ", " devices[device ",rpolicy"]);
         }
      }' /tmp/aspersa
}

# ##############################################################################
# Simplifies vmstat and aligns it nicely.  We don't need the memory stats, the
# system activity is enough.
# ##############################################################################
format_vmstat () {
   cat > /tmp/aspersa.awk <<-EOF
      BEGIN {
         format = "  %2s %2s  %4s %4s %5s %5s %6s %6s %3s %3s %3s %3s %3s\n";
      }
      /procs/ {
         print  "  procs  ---swap-- -----io---- ---system---- --------cpu--------";
      }
      /bo/ {
         printf format, "r", "b", "si", "so", "bi", "bo", "ir", "cs", "us", "sy", "il", "wa", "st";
      }
      \$0 !~ /r/ {
            fuzzy_var = \$1;   ${fuzzy_formula}  r   = fuzzy_var;
            fuzzy_var = \$2;   ${fuzzy_formula}  b   = fuzzy_var;
            fuzzy_var = \$7;   ${fuzzy_formula}  si  = fuzzy_var;
            fuzzy_var = \$8;   ${fuzzy_formula}  so  = fuzzy_var;
            fuzzy_var = \$9;   ${fuzzy_formula}  bi  = fuzzy_var;
            fuzzy_var = \$10;  ${fuzzy_formula}  bo  = fuzzy_var;
            fuzzy_var = \$11;  ${fuzzy_formula}  ir  = fuzzy_var;
            fuzzy_var = \$12;  ${fuzzy_formula}  cs  = fuzzy_var;
            fuzzy_var = \$13;                    us  = fuzzy_var;
            fuzzy_var = \$14;                    sy  = fuzzy_var;
            fuzzy_var = \$15;                    il  = fuzzy_var;
            fuzzy_var = \$16;                    wa  = fuzzy_var;
            fuzzy_var = \$17;                    st  = fuzzy_var;
            printf format, r, b, si, so, bi, bo, ir, cs, us, sy, il, wa, st;
         }
	EOF
      awk -f /tmp/aspersa.awk /tmp/aspersa
   }

# ##############################################################################
# The main() function is called at the end of the script.  This makes it
# testable.  Major bits of parsing are separated into functions for testability.
# As a general rule, we cannot 'cp' files from /proc, because they might be
# empty afterwards.  (I've seen 'cp /proc/cpuinfo' create an empty file.)  But
# 'cat' works okay.
# ##############################################################################
main () {

   # Begin by setting the $PATH to include some common locations that are not
   # always in the $PATH, including the "sbin" locations, and some common
   # locations for proprietary management software, such as RAID controllers.
   export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/libexec"
   export PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin"
   export PATH="${PATH}:/usr/StorMan/:/opt/MegaRAID/MegaCli/";

   # Set up temporary files.
   temp_files "rm"
   temp_files "touch"
   section Aspersa_System_Summary_Report

   # ########################################################################
   # Grab a bunch of stuff and put it into temp files for later.
   # ########################################################################
   sysctl -a > /tmp/aspersa.sysctl 2>/dev/null

   # ########################################################################
   # General date, time, load, etc
   # ########################################################################
   platform="$(uname -s)"
   name_val "Date" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)"
   name_val "Hostname" "$(uname -n)"
   name_val "Uptime" "$(uptime | awk '{print substr($0, index($0, "up") + 3)}')"
   if which dmidecode > /dev/null 2>&1; then
      vendor="$(dmidecode -s system-manufacturer 2>/dev/null | sed 's/ *$//g')"
      if [ "${vendor}" ]; then
         product="$(dmidecode -s system-product-name 2>/dev/null | sed 's/ *$//g')"
         version="$(dmidecode -s system-version 2>/dev/null | sed 's/ *$//g')"
         chassis="$(dmidecode -s chassis-type 2>/dev/null | sed 's/ *$//g')"
         system="${vendor}; ${product}; v${version} (${chassis})"
         name_val "System" "${system}";
         servicetag="$(dmidecode -s system-serial-number 2>/dev/null | sed 's/ *$//g')"
         name_val "Service Tag" "${servicetag:-Not found}";
      fi
   fi
   name_val "Platform" "${platform}"
   if [ "${platform}" = "SunOS" ]; then
      if which zonename >/dev/null 2>&1 ; then
         name_val "Zonename" "$(zonename)"
      fi
   fi

   # Try to find all sorts of different files that say what the release is.
   if [ "${platform}" = "Linux" ]; then
      kernel="$(uname -r)"
      if [ -e /etc/fedora-release ]; then
         release=$(cat /etc/fedora-release);
      elif [ -e /etc/redhat-release ]; then
         release=$(cat /etc/redhat-release);
      elif [ -e /etc/system-release ]; then
         release=$(cat /etc/system-release);
      elif which lsb_release >/dev/null 2>&1; then
         release="$(lsb_release -ds) ($(lsb_release -cs))"
      elif [ -e /etc/lsb-release ]; then
         release=$(grep DISTRIB_DESCRIPTION /etc/lsb-release |awk -F'=' '{print $2}' |sed 's#"##g');
      elif [ -e /etc/debian_version ]; then
         release="Debian-based version $(cat /etc/debian_version)";
         if [ -e /etc/apt/sources.list ]; then
             code=`cat /etc/apt/sources.list |awk  '/^deb/ {print $3}' |awk -F/ '{print $1}'| awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -rn |head -n1 |awk '{print $2}'`
             release="${release} (${code})"
      fi
      elif ls /etc/*release >/dev/null 2>&1; then
         if grep -q DISTRIB_DESCRIPTION /etc/*release; then
            release=$(grep DISTRIB_DESCRIPTION /etc/*release | head -n1);
         else
            release=$(cat /etc/*release | head -n1);
         fi
      fi
   elif [ "${platform}" = "FreeBSD" ]; then
      release="$(uname -r)"
      kernel="$(sysctl -n kern.osrevision)"
   elif [ "${platform}" = "SunOS" ]; then
      release="$(head -n1 /etc/release)"
      if [ -z "${release}" ]; then
         release="$(uname -r)"
      fi
      kernel="$(uname -v)"
   fi
   name_val Release "${release}"
   name_val Kernel "${kernel}"

   CPU_ARCH='32-bit'
   OS_ARCH='32-bit'
   if [ "${platform}" = "Linux" ]; then
      if grep -q ' lm ' /proc/cpuinfo; then
         CPU_ARCH='64-bit'
      fi
   elif [ "${platform}" = "FreeBSD" ]; then
      if sysctl hw.machine_arch | grep -v 'i[36]86' >/dev/null; then
         CPU_ARCH='64-bit'
      fi
   elif [ "${platform}" = "SunOS" ]; then
      if isainfo -b | grep 64 >/dev/null ; then
         CPU_ARCH="64-bit"
      fi
   fi
   if file /bin/sh | grep '64-bit' >/dev/null; then
      OS_ARCH='64-bit'
   fi
   name_val "Architecture" "CPU = $CPU_ARCH, OS = $OS_ARCH"

   # Threading library
   if [ "${platform}" = "Linux" ]; then
      name_val Threading "$(getconf GNU_LIBPTHREAD_VERSION)"
   fi
   if [ -x /lib/libc.so.6 ]; then
      name_val "Compiler" "$(/lib/libc.so.6 | grep 'Compiled by' | cut -c13-)"
   fi

   if [ "${platform}" = "Linux" ]; then
      if getenforce >/dev/null 2>&1; then
         getenforce="$(getenforce 2>&1)";
      fi
      name_val "SELinux" "${getenforce:-No SELinux detected}";
   fi

   # We look in dmesg for virtualization information first, because it's often
   # available to non-root users and usually has telltale signs.  It's most
   # reliable to look at /var/log/dmesg if possible.  There are a number of
   # other ways to find out if a system is virtualized.
   cat /var/log/dmesg > /tmp/aspersa 2>/dev/null
   if [ ! -s /tmp/aspersa ]; then
      dmesg > /tmp/aspersa 2>/dev/null
   fi
   if [ -s /tmp/aspersa ]; then
      virt="$(parse_virtualization_dmesg)"
   fi
   if [ -z "${virt}" ]; then
      if which lspci >/dev/null 2>&1; then
         lspci > /tmp/aspersa 2>/dev/null
         if grep -qi virtualbox /tmp/aspersa; then
            virt=VirtualBox
         elif grep -qi vmware /tmp/aspersa; then
            virt=VMWare
         elif [ -e /proc/user_beancounters ]; then
            virt="OpenVZ/Virtuozzo"
         fi
      fi
   elif [ "${platform}" = "FreeBSD" ]; then
      if ps -o stat | grep J ; then
         virt="FreeBSD Jail"
      fi
   elif [ "${platform}" = "SunOS" ]; then
      if which prtdiag >/dev/null 2>&1 && prtdiag > /tmp/aspersa.prtdiag 2>/dev/null; then
         virt="$(parse_virtualization_generic /tmp/aspersa.prtdiag)"
      elif which smbios >/dev/null 2>&1 && smbios > /tmp/aspersa.smbios 2>/dev/null; then
         virt="$(parse_virtualization_generic /tmp/aspersa.smbios)"
      fi
   fi
   name_val Virtualized "${virt:-No virtualization detected}"

   # ########################################################################
   # Processor/CPU, Memory, Swappiness, dmidecode
   # ########################################################################
   section Processor
   if [ -f /proc/cpuinfo ]; then
      cat /proc/cpuinfo > /tmp/aspersa 2>/dev/null
      parse_proc_cpuinfo
   elif [ "${platform}" = "FreeBSD" ]; then
      parse_sysctl_cpu_freebsd /tmp/aspersa.sysctl
   elif [ "${platform}" = "SunOS" ]; then
      psrinfo -v > /tmp/aspersa
      parse_psrinfo_cpus /tmp/aspersa
      # TODO: prtconf -v actually prints the CPU model name etc.
   fi

   section Memory
   if [ "${platform}" = "Linux" ]; then
      free -b > /tmp/aspersa
      cat /proc/meminfo >> /tmp/aspersa
      parse_free_minus_b /tmp/aspersa
   elif [ "${platform}" = "FreeBSD" ]; then
      parse_memory_sysctl_freebsd /tmp/aspersa.sysctl
   elif [ "${platform}" = "SunOS" ]; then
      name_val Memory "$(prtconf | awk -F: '/Memory/{print $2}')"
   fi

   rss=$(ps -eo rss 2>/dev/null | awk '/[0-9]/{total += $1 * 1024} END {print total}')
   name_val UsedRSS "$(shorten ${rss} 1)"

   if [ "${platform}" = "Linux" ]; then
      name_val Swappiness "$(sysctl vm.swappiness 2>&1)"
      name_val DirtyPolicy "$(sysctl vm.dirty_ratio 2>&1), $(sysctl vm.dirty_background_ratio 2>&1)"
      if sysctl vm.dirty_bytes > /dev/null 2>&1; then
         name_val DirtyStatus "$(sysctl vm.dirty_bytes 2>&1), $(sysctl vm.dirty_background_bytes 2>&1)"
      fi
   fi

   if which dmidecode >/dev/null 2>&1 && dmidecode > /tmp/aspersa 2>/dev/null; then
      parse_dmidecode_mem_devices
   fi

   # ########################################################################
   # Disks, RAID, Filesystems
   # ########################################################################
   # TODO: Add info about software RAID

   if echo "${ASPERSA_SKIP}" | grep -v MOUNT >/dev/null; then
      if [ "${platform}" != "SunOS" ]; then
         section "Mounted_Filesystems"
         cmd="df -h"
         if [ "${platform}" = "Linux" ]; then
            cmd="df -h -P"
         fi
         $cmd | sort > /tmp/aspersa2
         mount | sort | join /tmp/aspersa2 - > /tmp/aspersa
         parse_filesystems /tmp/aspersa "${platform}"
      fi
   fi

   if [ "${platform}" = "Linux" ]; then
      section "Disk_Schedulers_And_Queue_Size"
      echo "" > /tmp/aspersa
      for disk in $(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]'); do
         if [ -e "/sys/block/${disk}/queue/scheduler" ]; then
            name_val "${disk}" "$(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)"
            fdisk -l "/dev/${disk}" >> /tmp/aspersa 2>/dev/null
         fi
      done

      # Relies on /tmp/aspersa having data from the Disk Schedulers loop.
      section "Disk_Partioning"
      parse_fdisk

      section "Kernel_Inode_State"
      for file in dentry-state file-nr inode-nr; do
         name_val "${file}" "$(cat /proc/sys/fs/${file} 2>&1)"
      done

      section "LVM_Volumes"

      if which lvs >/dev/null 2>&1 && test -x "$(which lvs)"; then
         lvs 2>&1
      else
         echo "Cannot execute 'lvs'";
      fi
   fi

   section "RAID_Controller"

   # ########################################################################
   # We look in lspci first because it's more reliable, then dmesg, because it's
   # often available to non-root users.  It's most reliable to look at
   # /var/log/dmesg if possible.
   # ########################################################################
   if which lspci >/dev/null 2>&1 && lspci > /tmp/aspersa 2>/dev/null; then
      controller="$(parse_raid_controller_lspci)"
   fi
   if [ -z "${controller}" ]; then
      cat /var/log/dmesg > /tmp/aspersa 2>/dev/null
      if [ ! -s /tmp/aspersa ]; then
         dmesg > /tmp/aspersa 2>/dev/null
      fi
      controller="$(parse_raid_controller_dmesg)"
   fi

   name_val Controller "${controller:-No RAID controller detected}"

   # ########################################################################
   # Attempt to get, parse, and print RAID controller status from possibly
   # proprietary management software.  Any executables that are normally stored
   # in a weird location, such as /usr/StorMan/arcconf, should have their
   # location added to $PATH at the beginning of main().
   # ########################################################################
   notfound=""
   if [ "${controller}" = "AACRAID" ]; then
      if arcconf getconfig 1 > /tmp/aspersa 2>/dev/null; then
         parse_arcconf
      elif ! which arcconf >/dev/null 2>&1; then
         notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/"
      fi
   elif [ "${controller}" = "HP Smart Array" ]; then
      if hpacucli ctrl all show config > /tmp/aspersa 2>/dev/null; then
         parse_hpacucli
      elif ! which hpacucli >/dev/null 2>&1; then
         notfound="your package repository or the manufacturer's website"
      fi
   elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then
      if MegaCli64 -AdpAllInfo -aALL -NoLog > /tmp/aspersa 2>/dev/null; then
         parse_lsi_megaraid_adapter_info
      elif ! which MegaCli64 >/dev/null 2>&1; then
         notfound="your package repository or the manufacturer's website"
      fi
      if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > /tmp/aspersa 2>/dev/null; then
         parse_lsi_megaraid_bbu_status
      fi
      if MegaCli64 -LdPdInfo -aALL -NoLog > /tmp/aspersa 2>/dev/null; then
         parse_lsi_megaraid_virtual_devices
         parse_lsi_megaraid_devices
      fi
   fi

   if [ "${notfound}" ]; then
      echo "   RAID controller software not found; try getting it from"
      echo "   ${notfound}"
   fi

   if echo "${ASPERSA_SKIP}" | grep -v NETWORK >/dev/null; then
      # #####################################################################
      # Network stuff
      # #####################################################################
      if [ "${platform}" = "Linux" ]; then
         section Network_Config
         if which lspci > /dev/null 2>&1 && lspci > /tmp/aspersa 2>/dev/null; then
            parse_ethernet_controller_lspci
         fi
         if sysctl net.ipv4.tcp_fin_timeout > /dev/null 2>&1; then
            name_val "FIN Timeout" "$(sysctl net.ipv4.tcp_fin_timeout)"
            name_val "Port Range" "$(sysctl net.ipv4.ip_local_port_range)"
         fi
      fi

      # TODO cat /proc/sys/net/ipv4/ip_conntrack_max ; it might be
      # /proc/sys/net/netfilter/nf_conntrack_max or /proc/sys/net/nf_conntrack_max
      # in new kernels like Fedora 12?

      if which ip >/dev/null 2>&1 && ip -s link > /tmp/aspersa 2>/dev/null; then
         section Interface_Statistics
         parse_ip_s_link /tmp/aspersa
      fi

      if [ "${platform}" = "Linux" ]; then
         section Network_Connections
         if netstat -antp > /tmp/aspersa 2>/dev/null; then
            parse_netstat
         fi
      fi
   fi

   # ########################################################################
   # Processes, load, etc
   # ########################################################################
   if echo "${ASPERSA_SKIP}" | grep -v PROCESS >/dev/null; then
      section Top_Processes
      if which prstat > /dev/null 2>&1; then
         prstat | head
      elif which top > /dev/null 2>&1 ; then
         cmd="top -bn 1"
         if [ "${platform}" = "FreeBSD" ]; then
            cmd="top -b -d 1"
         fi
         $cmd | sed -e 's# *$##g' -e '/./{H;$!d;}' -e 'x;/PID/!d;' | grep . | head
      fi
      if which vmstat > /dev/null 2>&1 ; then
         section "Simplified_and_fuzzy_rounded_vmstat_(wait_please)"
         vmstat 1 5 > /tmp/aspersa
         if [ "${platform}" = "Linux" ]; then
            format_vmstat
         else
            # TODO: simplify/format for other platforms
            cat /tmp/aspersa
         fi
      fi
   fi

   # ########################################################################
   # All done.  Signal the end so it's explicit.
   # ########################################################################
   temp_files "rm"
   temp_files "check"
   section The_End
}

# Execute the program if it was not included from another file.  This makes it
# possible to include without executing, and thus test.
if [ "$(basename "$0")" = "summary" ] || [ "$(basename "$0")" = "bash" -a "$_" = "$0" ]; then
    main $@
fi