#!/usr/bin/env perl ##################################################################### # Hardware Probe 1.6 # A tool to probe for hardware, check operability and find drivers # # WWW (Linux): https://github.com/linuxhw # WWW (BSD): https://github.com/bsdhw # # Copyright (C) 2014-2023 Linux Hardware Project # # Written by Andrey Ponomarenko (ABI Laboratory, LSB Infrastructure, # Broad Test technology, IEEE certified software test # engineer, ROSA Linux distribution) # # LinkedIn: https://www.linkedin.com/in/andreyponomarenko # # PLATFORMS # ========= # Linux (Fedora, CentOS, RHEL, Ubuntu, Debian, Mint, MX, Arch, # Gentoo, ROSA, Mandriva, Clear Linux, Alpine ...) # # BSD (FreeBSD, OpenBSD, NetBSD, GhostBSD, NomadBSD, DragonFly, # MidnightBSD, FuryBSD, FreeNAS, pfSense, OPNsense, # XigmaNAS ...) # # LICENSE # ======= # This work is dual-licensed under LGPL 2.1 (or any later version) # and BSD-4-Clause. You can choose between one of them if you use # this work. # # LGPL-2.1-or-later # ================= # This library is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see: # # https://www.gnu.org/licenses/ # # BSD-4-Clause # ============ # This is free software: you can redistribute it and/or modify it # under the terms of the BSD 4-Clause License. # # This software is distributed WITHOUT ANY WARRANTY; without even # the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. # # You should have received a copy of the BSD 4-Clause License along # with this library. If not, see: # # https://spdx.org/licenses/BSD-4-Clause.html # ##################################################################### use Getopt::Long; Getopt::Long::Configure ("posix_default", "no_ignore_case"); use File::Path qw(mkpath rmtree); use File::Temp qw(tempdir); use File::Copy qw(copy move); use File::Basename qw(basename dirname); use Cwd qw(abs_path cwd); my $TOOL_VERSION = "1.6"; my $TOOL_RELEASE = "5"; my $URL_LINUX = "https://linux-hardware.org"; my $URL_BSD = "https://bsd-hardware.info"; my $URL = $URL_LINUX; my $GITHUB = "https://github.com/linuxhw/hw-probe"; my $LOCALE = "C"; my $ORIG_DIR = cwd(); my $TMP_DIR = tempdir(CLEANUP=>1); my $TMP_LOCAL = ".tmp_".basename($TMP_DIR); my $SNAP_DESKTOP = (defined $ENV{"BAMF_DESKTOP_FILE_HINT"}); my $FLATPAK_DESKTOP = (grep { $_ eq "-flatpak" } @ARGV); my $BY_DESKTOP = 0; my @ARGV_COPY = @ARGV; my $CmdName = basename($0); if($CmdName=~/\A\d+\Z/) { # Run from STDIN $CmdName = "hw-probe"; } my $CmdExample = "sudo -E $CmdName"; my $GlobalSubject = "Linux"; if(isBSD($^O)) { $CmdExample = $CmdName; $GlobalSubject = "BSD"; } my $ShortUsage = "Hardware Probe $TOOL_VERSION A tool to probe for hardware, check operability and find drivers License: LGPL-2.1-or-later OR BSD-4-Clause Usage: $CmdExample [options] Example: $CmdExample -all -upload\n\n"; my %Opt; GetOptions("h|help!" => \$Opt{"Help"}, "v|version!" => \$Opt{"ShowVersion"}, "dumpversion!" => \$Opt{"DumpVersion"}, # Main "all!" => \$Opt{"All"}, "log-level=s" => \$Opt{"LogLevel"}, "minimal|mini|min!" => \$Opt{"Minimal"}, "maximal|maxi|max!" => \$Opt{"Maximal"}, "enable=s" => \$Opt{"Enable"}, "disable=s" => \$Opt{"Disable"}, "check!" => \$Opt{"Check"}, "check-extended!" => \$Opt{"CheckExtended"}, "check-graphics!" => \$Opt{"CheckGraphics"}, "check-hdd!" => \$Opt{"CheckHdd"}, "limit-check-hdd=s" => \$Opt{"LimitCheckHdd"}, "check-memory!" => \$Opt{"CheckMemory"}, "check-cpu!" => \$Opt{"CheckCpu"}, "check-7z!" => \$Opt{"Check7z"}, "id|name=s" => \$Opt{"PC_Name"}, "upload|confirm-upload-of-hashed-ids!" => \$Opt{"Upload"}, "hwinfo-path=s" => \$Opt{"HWInfoPath"}, "log!" => \$Opt{"ShowLog"}, "proxy=s" => \$Opt{"Proxy"}, # Inventory "inventory|inventory-id|i|group|g=s" => \$Opt{"Group"}, "generate-inventory|generate-inventory-id|get-inventory-id|get-group!" => \$Opt{"GenerateGroup"}, "email=s" => \$Opt{"Email"}, # Monitoring "start|start-monitoring!" => \$Opt{"StartMonitoring"}, "stop|stop-monitoring!" => \$Opt{"StopMonitoring"}, "remind-inventory!" => \$Opt{"RemindGroup"}, "monitoring!" => \$Opt{"Monitoring"}, # Other "src|source=s" => \$Opt{"Source"}, "save=s" => \$Opt{"Save"}, "fix=s" => \$Opt{"FixProbe"}, "show-devices!" => \$Opt{"ShowDevices"}, "show-host!" => \$Opt{"ShowHost"}, "show!" => \$Opt{"Show"}, "compact!" => \$Opt{"Compact"}, "verbose!" => \$Opt{"Verbose"}, "printers!" => \$Opt{"Printers"}, "scanners!" => \$Opt{"Scanners"}, "pci-ids=s" => \$Opt{"PciIDs"}, "usb-ids=s" => \$Opt{"UsbIDs"}, "sdio-ids=s" => \$Opt{"SdioIDs"}, "pnp-ids=s" => \$Opt{"PnpIDs"}, "list!" => \$Opt{"ListProbes"}, "save-uploaded!" => \$Opt{"SaveUploaded"}, "debug|d!" => \$Opt{"Debug"}, "dump-acpi!" => \$Opt{"DumpACPI"}, "decode-acpi!" => \$Opt{"DecodeACPI"}, # Obsolete "probe!" => \$Opt{"Probe"}, "logs!" => \$Opt{"Logs"}, # Private "docker!" => \$Opt{"Docker"}, "appimage!" => \$Opt{"AppImage"}, "snap!" => \$Opt{"Snap"}, "flatpak!" => \$Opt{"Flatpak"}, "from-gui!" => \$Opt{"FromGUI"}, "low-compress!" => \$Opt{"LowCompress"}, "high-compress!" => \$Opt{"HighCompress"}, "identify-drive=s" => \$Opt{"IdentifyDrive"}, "identify-monitor=s" => \$Opt{"IdentifyMonitor"}, "show-dmesg=s" => \$Opt{"ShowDmesg"}, "bsd!" => \$Opt{"ForBSD"}, "decode-acpi-from=s" => \$Opt{"DecodeACPI_From"}, "decode-acpi-to=s" => \$Opt{"DecodeACPI_To"}, "fix-edid!" => \$Opt{"FixEdid"}, "rm-log=s" => \$Opt{"RmLog"}, "truncate-log=s" => \$Opt{"TruncateLog"}, "install-deps!" => \$Opt{"InstallDeps"}, "nodeps!" => \$Opt{"SkipDeps"}, "rm-obsolete!" => \$Opt{"RmObsolete"}, "output=s" => \$Opt{"OutputDir"} ) or die "\n".$ShortUsage; if($#ARGV_COPY==-1) { # Run from STDIN if (-t STDIN) { $Opt{"ShowVersion"} = 1; } else { print "Executing hw-probe -all -upload\n\n"; $Opt{"All"} = 1; $Opt{"Upload"} = 1; } } elsif($#ARGV_COPY==0 and grep { $ARGV_COPY[0] eq $_ } ("-snap", "-flatpak")) { # Run by desktop file my $Gui = "hw-probe-pyqt5-gui"; if(not checkCmd($Gui)) { $Gui = "hw-probe-pyqt6-gui"; } if(checkCmd($Gui)) { print "Executing $Gui\n\n"; if($ARGV_COPY[0] eq "-flatpak") { system("HW_PROBE_FLATPAK=1 $Gui"); } else { system("HW_PROBE_SNAP=1 $Gui"); } exitStatus(0); } print "Executing hw-probe -all -upload\n\n"; $Opt{"All"} = 1; $Opt{"Upload"} = 1; if($SNAP_DESKTOP or $FLATPAK_DESKTOP) { # Desktop $BY_DESKTOP = 1; } } my $PROBE_DIR = "/root/HW_PROBE"; if($Opt{"Snap"}) { $PROBE_DIR = $ENV{"SNAP_USER_COMMON"}."/HW_PROBE"; } elsif($Opt{"Flatpak"}) { $TMP_DIR = "/var".$TMP_DIR; mkpath($TMP_DIR); $PROBE_DIR = $ENV{"XDG_DATA_HOME"}."/HW_PROBE"; } if($Opt{"OutputDir"}) { $PROBE_DIR = $Opt{"OutputDir"}; mkpath($PROBE_DIR); } my $LATEST_DIR = $PROBE_DIR."/LATEST"; my $TMP_PROBE_DIR = $LATEST_DIR."/".basename($TMP_DIR); my $TMP_PROBE = $TMP_PROBE_DIR."/hw.info"; my $PROBE_LOG = $PROBE_DIR."/LOG"; my ($DATA_DIR, $LOG_DIR, $TEST_DIR) = initDataDir($LATEST_DIR."/hw.info"); my $HelpMessage=" NAME: Hardware Probe ($CmdName) A tool to probe for hardware, check operability and find drivers DESCRIPTION: Hardware Probe ($CmdName) is a tool to probe for hardware, check its operability and upload result to the $GlobalSubject hardware database. By creating probes you contribute to the \"HDD/SSD Desktop-Class Reliability Test\" study: https://github.com/linuxhw/SMART USAGE: $CmdExample [options] EXAMPLE: $CmdExample -all -upload PRIVACY: Private information (including the username, machine's hostname, IP addresses, MAC addresses, UUIDs and serial numbers) is NOT uploaded to the database. The tool uploads 32-byte prefix of salted SHA512 hash of MAC addresses and serial numbers to properly identify unique computers and hard drives. UUIDs are decorated in the same way, but formatted like regular UUIDs in order to save readability of logs. All the data is uploaded securely via HTTPS. INFORMATION OPTIONS: -h|-help Print this help. -v|-version Print version info. -dumpversion Print the tool version ($TOOL_VERSION) and don't do anything else. GENERAL OPTIONS: -all Probe for hardware. -log-level N Set the logging level to N. Available values: - minimal - default - maximal -minimal|-min Collect minimal number of logs. Equal to --log-level=min. -maximal|-max Collect maximal number of logs. Equal to --log-level=max. -enable LIST Comma separated list of logs to enable in addition to current log level. -disable LIST Comma separated list of logs to disable in current log level. Some logs cannot be disabled. For example, you can disable collecting of 'fstab', but you cannot disable logging of 'smartctl'. -check Check devices operability. Test performance of CPU, memory, graphics cards and drives. -check-extended Perform extended check. -upload Upload result to the hardware database. You will get a permanent URL to view the probe. By use of this option you confirm uploading of 32-byte prefix of salted SHA512 hash of MAC addresses and serial numbers to prevent duplication of computers in the DB. -hwinfo-path PATH Path to a local hwinfo binary. -proxy ADDRESS:PORT Set outgoing http/https proxy using syntax: proxy.domain.local:3128 INVENTORY OPTIONS: -i|-inventory-id ID Mark the probe by inventory ID. -generate-inventory-id Generate new inventory ID. -email ADDR Email for node status notifications. MONITORING OPTIONS: -start Start monitoring of the node. -stop Stop monitoring of the node. -remind-inventory Remind node inventory ID. OTHER OPTIONS: -save DIR Save probe package to DIR. This is useful if you are offline and need to upload a probe later (with the help of -src option). -src|-source PATH A probe to upload. -fix PATH Update list of devices and host info in the probe using probe data. -show-devices Show devices list. -show Show host info and devices list. -show-host Show host info only. -verbose Use with -show option to show type and status of the device. -printers Probe for printers. -scanners Probe for scanners. -id|-name DESC Any description of the probe. -pci-ids PATH -usb-ids PATH -sdio-ids PATH -pnp-ids PATH Path to {pci,usb,sdio,pnp}.ids file to read missed device names. -list List executed probes (for debugging). -clean Do nothing. Obsolete option. -save-uploaded Save uploaded probes. -debug|-d Do nothing. Obsolete option. -dump-acpi Probe for ACPI table. -decode-acpi Decode ACPI table. -import DIR Import probes from the database to DIR for offline use. If you are using Snap or Flatpak package, then DIR will be created in the sandbox data directory. Provide inventory ID by -i option in order to import your inventory. DATA LOCATION: Probes info is saved in the $PROBE_DIR directory. "; # Hardware my %HW; my %HW_Count; my %LongID; my %DevBySysID; my %KernMod; my %WorkMod; my %WLanInterface; my %EthernetInterface; my %PermanentAddr; my %ExtraConnection; my %HDD; my %HDD_Info; my %MMC; my %MMC_Info; my %BlockCapacity; my $Board_ID; my $Bios_ID; my $CPU_ID; my $CDROM_ID; my %ComponentID; my %Monitor_ID; my %DeviceIDByNum; my %DeviceNumByID; my %DriveNumByFile; my %DeviceAttached; my %GraphicsCards; my %GraphicsCards_All; my %GraphicsCards_InUse; my %UsedNetworkDev; my %DevAttachedRecursive; my %DevAttachedRecursive_R; my %DevAttached; my %DevAttached_R; my %DrmAttached; my $SnapNoBlockDevices = 0; my $MIN_BAT_CAPACITY = 30; my @G_DRIVERS_INTEL = ("i915", "i915_bpo", "gma500_gfx"); my @G_DRIVERS = ("nvidia", "nouveau", "radeon", "amdgpu", "fglrx", @G_DRIVERS_INTEL); my %DriverVendor = ( "i915" => "8086", "nouveau" => "10de", "nvidia" => "10de", "radeon" => "1002", "amdgpu" => "1002", "fglrx" => "1002" ); foreach (@G_DRIVERS_INTEL) { $DriverVendor{$_} = "8086"; } my $PCI_DISK_BUS = "nvme"; # System my %Sys; # Settings my $Admin = ($>==0); # Fixing my $FixProbe_Pkg; my $FixProbe_Logs; my $FixProbe_Tests; # Probe my $RecentProbe; # PCI and USB IDs my %PciInfo = ("I"=>{}, "D"=>{}, "V"=>{}, "C"=>{}); $PciInfo{"V"} = { "8086" => "Intel", "17aa" => "Lenovo", "14a4" => "Lite-On", "1987" => "Phison", "144d" => "Samsung", "1b4b" => "SanDisk", "126f" => "Silicon Motion", "1c5c" => "SK hynix", "1179" => "Toshiba" }; my %UsbInfo; my %UsbVendor; my %UsbClass; # JEDEC IDS my %VendorJedec = ( "A Force" => ["7F7F7F7F7F7F7F02", "7F7F7F6DFFFFFFFF"], "ADATA" => ["04CB", "7F7F7F7FCB000000"], "Aeneon" => ["7F7F7F7F7F570000"], "AMD" => ["80010000830B"], "Apacer" => ["017A", "7F7A", "7A01"], "ASint" => ["06C1", "c106", "7F7F7F7F7F7FC100"], "ATP" => ["86E3"], "A-TECH" => ["0080000080AD"], "Avant" => ["7F7F7F7F7FF70000", "85F7", "F705"], "Carry" => ["070E"], "Catalyst" => ["314F000000000000"], "Centon" => ["7F7F7F1900000000"], "CFD" => ["0770000080AD"], "CSX" => ["855D", "7F7F7F7F7F5D0000"], "Corsair" => ["029E", "0215", "9E02", "7F7F9E0000000000", "009C36160000"], "Crucial" => ["1315", "059B", "859B", "9B85", "9B05", "0D9B", "09B8", "7F7F7F7F7F9B0000", "7F7F7F7F7F9BFFFF", "0000000000009B85", "859<", "009D36160000", "009C2B0C0000", "9B000D1000000000", "F7F7F7F7F7B90000", "9B"], "Dane-Elec" => ["7FDA"], "DERLAR" => ["8AA2"], "Elpida" => ["00FE", "01FE", "02FE", "FE02", "7F7FFE0000000000", "0000000000FE7F7F", "000000000000FE02", "000000000FE02"], "Essencore" => ["0898000080AD"], "EUDAR" => ["847C"], "EVGA" => ["08D9"], "Foxline" => ["88F2", "8ABA"], "G-Alantic" => ["08F7"], "G.Skill" => ["04CD", "CD04", "7F7F7F7FCD000000", "04=>", "=>04", "EC9D0B160000"], "GIGABYTE" => ["89F2"], "Golden Empire" => ["7F7F7F1300000000", "8A45", "8313000080AD"], "Goldenmars" => ["7F7F7F7F7F620000"], "GOODRAM" => ["075D", "7F7F7F7F7F7F7F5D", "5D07"], "Infineon" => ["C100", "C10", "C1494E46494E454F"], "Innodisk" => ["86F1"], "Itaucom" => ["7F7F310000000000"], "Hexon" => ["7F7F7F7F7FDC0000"], "High Bridge" => ["07E9"], "Hikvision" => ["0B2C"], "KANMEIQi" => ["0B29"], "KINGBOX" => ["7F7F7F7F51000000"], "KingFast" => ["897A"], "Kingmax" => ["7F7F7F2500000000", "7F7F7F2500000000", "8325000080AD", "83250000830B"], "Kingston" => ["0198", "7F98", "9804", "F789", "9806", "9805", "00000B160000", "009C162D0000", "009C23240000"], "KingTiger" => ["7F7F7F7F7F7F7F10"], "Kllisre" => ["89C2"], "Kreton" => ["85E3", "7F7F7F7F7FE30000"], "Lexar" => ["8A76", "8A7600000000", "8A7600008A76"], "MAXSUN" => ["89A2"], "MCI Computer" => ["7F7F640000000000"], "Melco" => ["7F7F7F8300000000"], "Micron" => ["002C", "00C2", "802C", "857F", "878A", "2C00", "C200", "2CFF", "2C80", "2C0", "C20", "2C", "08D0", "009C162C0000", "FFFFFFFFFFFFFF2C", "0000000000002C80", "0000000002C80", "2C080905DF3FF327", "2C1600000000", "00002C0F0000"], "Mougol" => ["4B0", "4B00000000000000"], "MTASE" => ["8AAE"], "Multilaser"=> ["08B6"], "Nanya" => ["830B", "030B", "0B83", "0B0D", "7F7F7F0B000000", "7F7F7F0B00000000", "F7F7F7B000000000", "0000000000000B83"], "Netlist" => ["7F7F7F1600000000"], "OCZ" => ["84B0", "7F7F7F7FB0000000"], "Patriot" => ["4D41", "8502", "7F7F7F7F7F020000"], "Pioneer" => ["0B89"], "PNY" => ["01BA", "7FBA", "BA01"], "Positivo Informatica" => ["7F7F7F7F16000000"], "pqi" => ["853E", "7F7F7F7F7F3E0000"], "PRINCETON" => ["7F7F7F8A00000000"], "PUSKILL" => ["8AAD"], "Qimonda" => ["7F7F7F7F7F510000", "5145", "F7F7F7F7F7150000", "80C1", "85517FB38551", "855180B38551"], "Qumo" => ["02B5"], "Ramaxel" => ["7F7F7F7F43000000", "0443", "04430000802C", "7F7F7F7F7F000000", "000000437F7F7F7F"], "Reboto" => ["0080000080CE"], "Samsung" => ["EC00", "00CE", "80CE", "00EC", "CE00", "CE80", "0CE", "EC0", "CE0", "000000000000CE80", "CE80", "CE30", "00000000000000CE", "CE01", "009C360B0000", "0000CE020000", "009C0B160000", "09B0", "090D", "000000000CE80", "7F7F7F7F3B000000"], "SanMax" => ["86E9"], "SemsoTai" => ["09C8"], "Sesame" => ["0B13"], "Silicon Power" => ["86D3", "7F7F7F7F7F7FD300"], "SiS" => ["7F7F7F7F7F7FA800"], "SK hynix" => ["00AD", "00DA", "DA00", "6F2B", "80AD", "AD00", "ADFF", "AD01", "AD80", "000000000000AD80", "00000000000000AD", "AD0", "DA0", "0AD8", "009C35230000", "009C2B160000", "0000AD010000", "0000000080AD", "08CD", "2E0400000000"], "Smart" => ["7F94", "000000000000947F", "0194000080CE"], "Smart Modular" => ["019400000A00"], "Super Talent" => ["004D415500000000", "8634000082B5", "7F7F7F7F7F7F3400"], "Swissbit" => ["7F7F7FDA00000000"], "TakeMS" => ["7F7F7F5800000000", "7F7F7F58FFFFFFFF"], "Team" => ["04EF", "EF04", "7F7F7F7FEF000000"], "Teikon" => ["079E", "9E07"], "Textorm" => ["8C97"], "TIMETEC" => ["8C26"], "Transcend" => ["014F", "7F4F"], "TwinMOS" => ["866B", "066B"], "Unifosa" => ["0707000002FE"], "Unigen" => ["7FCE"], "Uroad" => ["07DC"], "Veineda" => ["89D0"], "V-Color" => ["066D"], "V-GeN" => ["0A94", "8A94"], "Walton Chaintech" => ["05D6", "7F7F7F7F7FD60000"] # APOGEE ); my %JedecVendor; foreach my $V (sort keys(%VendorJedec)) { foreach (sort @{$VendorJedec{$V}}) { $JedecVendor{$_} = $V; } } my %VendorRam = ( "A Force" => ["1GX64", "1GX72", "128X64K", "256X64M", "25664Y", "51264V"], "ADATA" => ["AD7", "AM1", "EL7YG", "HYOPE"], "Advantech" => ["SQR"], "Aeneon" => ["AET"], "AMD" => ["AE32G", "AE34G", "AP34G", "AE38G", "AP38G", "AV34G", "R33", "R53", "R5S", "R7S", "R9S", "R73", "R74", "R93", "R94"], "Atermiter"=> ["Atermite"], "ATP" => ["AQ12M", "AQ28M", "AQ56M", "AW12P"], "A-TECH" => ["ATECH"], "Axiom" => ["51264Y"], "Apacer" => ["76.", "78.", "D12."], "Apotop" => ["S3A", "U3A"], "Asgard" => ["VMA45U"], "ASint" => ["B1YJ", "B2YJ", "B3KJ", "C1RE", "C2RE", "SSA", "SSY", "SSZ", "SLA", "SLB", "SLZ"], "ATLA" => ["AD3SHT"], "Avant" => ["F64", "H64", "J64", "W64"], "Catalyst" => ["02GN"], "Centon" => ["MICT"], "CFD" => ["D4N"], "Chun Well" => ["D4U"], "Corsair" => ["CMD", "CMG", "CMH", "CMK", "CML", "CMR", "CMS", "CMT", "CMU", "CMV", "CMW", "CMX", "CMZ", "CM2X", "CM3X", "CM4X", "VS2G"], "Crucial" => ["BLS", "BLT", "BLE", "BL4G", "BL5", "BL8G", "BL16G", "BL32G", "BL256", "BLM8G", "BLM16G", "CB4G", "CB8G", "CB16G", "CT256", "CT512", "CT1024", "CT2048", "CT4G", "CT8G", "CT16G", "CT32G", "CT10", "CT12864", "CT2G", "RM256", "RM512", "ST256", "ST512", "ST1024", "ZC256", "RM1024"], "CSX" => ["V01L", "V01D"], "Dane-Elec" => ["SS3D106"], "Dexcom" => ["L23 06/11 DEXCOM"], "Dynet" => ["DNHMAU"], "DSL" => ["D3SH"], "Elpida" => ["EBJ"], "Eluktro" => ["MEM-12800-8GB-PRO"], "Essencore"=> ["IM44GU", "KD48GS"], "Exceleram"=> ["E301", "E302", "E408", "E404", "E470", "EPH4"], "EVGA" => ["08G-D3", "16G-D4-2400-MR"], "Foxline" => ["FL1", "FL2"], "G-Alantic"=> ["D3SS"], "G.Skill" => ["F2-", "F3-", "F4-", "F5-"], "GeIL" => ["CL9-9", "CL9-10", "CL10-10", "CL10-11", "CL11-11", "CL11-12"], # Golden Empire "GIGABYTE" => ["AR32", "AR36", "GP-GR"], "GigaRam" => ["GR4GU"], "Gloway" => ["TYA", "TYP"], "Goldkey" => ["BKH", "GKE", "GKH"], "Goldenmars" => ["GMT"], "GOODRAM" => ["GL213", "GR32", "GR400", "GR667", "GR800", "GR1", "GR2", "GY1", "IRX", "IR1", "IR2", "W-MEM"], "HBS" => ["HB2", "HB3"], "High Bridge" => ["HB3SU"], "Hikvision" => ["HKED"], "HMD" => ["HMD"], "HomeNet" => ["HomeNet"], "HP" => ["2E2M5AA", "7EH"], "Huananzhi" => ["HNM"], "Hypertec" => ["G2BT", "G2RT"], "ISD Technology Limited" => ["IMT41"], "INNOVATION PC" => ["INNO"], "Innodisk" => ["M4S0"], "Juhor" => ["JHD"], "JOY-IT" => ["QLA304G"], "KANMEIQi" => ["KAi-D"], "Kembona" => ["KBN"], "Kimtigo" => ["KT4G", "KT8G"], "KingFast" => ["KF"], "Kingmax" => ["FLF", "FLG", "FSF", "FSG", "GLAH", "GLLG", "GSLG4"], "KingSpec" => ["KS"], "Kingston" => ["KHX", "ACR", "ASU", "BRAP", "D3L16", "HP53", "KMK", "KN2M", "SNY", "TSB", "CL4-", "CL7-", "CL9-", "CL11-", "CL15-", "CL16-", "CL-17-", "1024636"], "KingTiger"=> ["KingTiger000000000", "KingTige"], "KLEVV" => ["KD4"], "Kllisre" => ["KRE-"], "KomputerBay" => ["KB_8G", "KB8G"], "Kreton" => ["516230", "51631x", "51634x", "51731x"], "Lexar" => ["LD4"], "Magnum Tech" => ["MAGNUMTECH"], "MAXSUN" => ["MSD"], "MDT" => ["MDT"], "Memory Solution" => ["MS3S"], "Memox" => ["LN-SD"], "MemoWise" => ["MW0"], "Micron" => ["16G2", "16HTF", "16JS", "16JT", "16ATF", "16KTF", "18HF", "36JS", "4ATF", "53E", "864AY", "8ATF", "8JSF", "8JTF", "4KTF", "8KTF", "EDF8132", "MT5", "MT62F1"], "Mushkin" => ["991769", "992017", "991529", "991558", "991713", "992070", "991705", "MB[A/B]", "MR[A/B]"], "Multilaser" => ["MD401", "MD451", "MS301", "MS351"], "Nanya" => ["NT1", "NT2", "NT4", "NT8", "M2F", "M2N", "M2S"], "Neo Forza"=> ["NMGD", "NMS", "NMUD"], "Netac" => ["NS0"], "Netlist" => ["NL8"], "Novatech" => ["N3D", "N3S"], "OCZ" => ["OCZ"], "Panasonic"=> ["CFW5W"], "Panram" => ["PUD"], "Patriot" => ["PSA", "PSD", "R6N", "1600EL", "1600LL", "1866EL", "186C0", "2000EL", "2133 CL11 Series", "2666 C15 Series", "2666 C16 Series", "2800 C16 Series", "3200 C16 Series"], "Pioneer" => ["APS"], "PNY" => ["64C0M", "4GBH", "8GBF1X", "8GBH2X", "8GBU1X", "64D0J", "16GF2X", "16GU1X"], "PQI" => ["BN109062", "MFPD"], "PSC" => ["AS8"], "Puskill" => ["PJ16"], "Qbex" => ["B9C40", "C2NA0"], "Qimonda" => ["64T1280", "64T64", "64T12", "72T256"], "Quadratica" => ["QDE"], "Qumo" => ["QUM"], "Rahonix" => ["RXD"], "Ramaxel" => ["RMT", "RMN", "RMR", "RMS", "RMU"], "Ramos" => ["EMB", "EWB", "RMB"], "RyzerX" => ["RVR"], "Samsung" => ["23R1G", "M04", "M378", "M393", "M4 70T", "M471", "MSC3S", "K3LK", "K3Q", "K4A", "K4E", "K4F", "K4U", "Ltd:R00MM", "U6E", "UBE"], "Sesame" => ["S939", "S949"], "SGS/Thomson" => ["SD-D2", "SD-D3"], "Silicon Power" => ["D8LR", "DBLT", "DBST", "DCLT", "DCST", "SP0", "ESRD"], "SK hynix" => ["2G25U6", "CG88ME", "HCN", "HMA", "HMT", "HMP", "HYMP", "H5AN", "H54G", "H9CC", "H9HC", "H9JC", "DMT3", "MMXIV", "MPP", "4GBPC1333512", "4GBPC"], "Smart" => ["SF464", "SF472", "SF564", "SG56", "SH564", "SMS4"], "Strontium" => ["EVMT", "SRT"], "Super Talent" => ["SUPERTALENT02", "SUPERTALENT"], "Swissbit" => ["MEN02", "SEU"], "T-FORCE" => ["ZEUS"], "TakeMS" => ["TMS"], "Team" => ["Team-Eli", "Team--El", "Dark", "Vulcan"], "Teikon" => ["TMA", "TML", "TMT"], "Textorm" => ["TXS", "TXU"], "Thermaltake" => ["R009", "R019", "R022"], "Transcend"=> ["JM1", "JM2", "JM320", "JM367", "JM667", "JM800", "TS1", "TS64", "TS128", "TS256", "TS512"], "TwinMOS" => ["9DEEB", "9DHTB", "9DETB", "8DP25KK", "8DE25KK", "7D-23KK", "M2GAO"], "Unifosa" => ["GU3", "GU5", "GU6", "HU5", "HU6"], "Unigen" => ["UG"], "Unknown" => ["GRPFD"], "Uroad" => ["WJD"], "V-Color" => ["TD4", "TL4", "TN4", "VCOLOR"], "V-GeN" => ["D3R", "D4H", "D4R", "D4S"], "Veineda" => ["M08GD16P"], "Veritech" => ["Veritech"], "Visipro" => ["T2G", "T4G86"], "Walton Chaintech" => ["AS2G", "AU2G", "AU4G"], # APOGEE "Wilk Elektronik" => ["IRP"], "Wodposit" => ["WPB"], "ZIFEI" => ["ZFN"] # Shenzhen Zhifeng Weiye Tech ); my $ALL_MEM_VENDORS = "Ankowall|Atermiter|Axiom|BiNFUL|CFD|CompuStocx|DATEN|DERLAR|DeTech|DigiBoard|e2e4|HEXON|imation|JINSHA|KETECH|Kimtigo|KingBank|Kinlstuo|Kllisre|LEADMAX|MARKVISION|MINPO|MLLSE|MTASE|OSCOO|PLEXHD|Princeton|Ramsta|Reboto|RZX|Saikano|SemsoTai|SHARETRONIC|STARKORTIS|SUPER KINGSTEK|Team|Tigo|TIMETEC|TOP MEDIA|TRS Star|Vaseky|ZION"; my %RamVendor; foreach my $V (sort keys(%VendorRam)) { foreach (sort @{$VendorRam{$V}}) { $RamVendor{$_} = $V; } } my %DiskVendor = ( "AGI" => "AGI", "APS-SL" => "Pioneer", "BT58" => "BAITITON", "C3-60G" => "SenDisk", "CSSD-MG4VT" => "CFD", "DX" => "DUEX", "FASTDISK" => "FASTDISK", "FTM" => "Super Talent", "G2 series" => "Micro Center", "G3 Series" => "Myung", "Gen2A400" => "Anobit", "GH-SSD" => "Green House", "GKH84" => "Goldkey", "JAJS" => "Leven", "LIREN" => "Liren", "M2SCF-6M" => "ACPI", "MD0" => "Magnetic Data", "MD" => "MaxDigital", "MKN" => "Mushkin", "MSS4FV" => "acpi", "NFS" => "Neo Forza", "PH6-CE" => "Plextor", "RD-S" => "RECADATA", "RTOTJ" => "Union Memory", "SPCC" => "SPCC", "TEAM" => "Team", "Thinklife" => "Lenovo", "TRO-SSD7" => "Eluktro", "V-GEN" => "V-GeN", "V7 SSD" => "VSEVEN", "Vi550" => "Verbatim", "SWR" => "Win Memory", "WL" => "WD MediaMax", "XUNZHE" => "XUNZHE", "Y6-" => "Yunhaitian", "ZALMAN" => "ZALMAN", "ZTSSD" => "ZOTAC" ); my %VendorDiskPrefix = ( "ACOS" => ["AS-512"], "ADATA" => ["ASU800SS", "AXM", "AXN", "IM2P", "IM2S", "SP600"], "ADLINK" => ["SSO"], "ADTEC" => ["ADC-S25D1S-2TB"], "Advantech"=> ["SATA CVB", "SQF"], "AGB" => ["AGB-6PRO"], "AMD" => ["R3S", "R5M", "R5S"], "Apacer" => ["APSDM"], "Apple" => ["A45ACXBA9TA"], "ASENNO" => ["ASN8", "AS25"], "ASMedia" => ["ASMT1"], "ATP" => ["ATP SATA", "ATP Velocity"], "Avant" => ["NCSC"], "Best Memory" => ["BT-480-535"], "BIWIN" => ["C6308", "G2242", "M6305"], "BlueRay" => ["SDM", "SSD120GM7B", "Ultra M8V"], "BR" => ["BR 60G"], "Cactus" => ["CactusFlashCard"], "CFD" => ["CSSD-S6"], "China" => ["CS2246", "DEPOSM", "EHSA", "ESA3", "FPT310M", "Mit-SSD512A", "MSATA", "NS128GSSD", "OSSD", "QSSEA", "RTMMB", "S41CF", "SH00", "SSD128GBS800", "SSD-HYX-1H", "T480", "TP00"], "CERVOZ" => ["CIS 2S T380"], "Chiprex" => ["S10T", "S8M", "S9M"], "Corsair" => ["CSSD-F", "CSSD-V", "Force MP"], "Crucial" => ["CT", "FCCT", "M4-CT"], "DATSSD" => ["DTGC-C"], "Delkin Devices" => ["ME16"], "Digma" => ["1TB RUN S9", "256GB RUN S9", "512GB RUN S9 512GB", "DGSR", "RUN S9", "RUN Y2"], "DST" => ["DST"], "Emphase" => ["G5RM"], "Espada" => ["C3000.6-M064", "ZF18-64"], "Exascend" => ["EXS"], "ExeGate" => ["EX2"], "Faspeed" => ["H5-240G"], "Foxline" => ["FLD", "FLSSD"], "Gateway" => ["W800SH"], "GEONIX" => ["GEONIX"], "GHIA" => ["GM2256G"], "GK" => ["SM2244LT"], "GOKE" => ["GG2ZT"], "Goldenfir" => ["T650-120", "T650-256"], "GOODRAM" => ["GOODRAM", "IR_SSDPR", "IR-SSDPR", "IRIDIUM", "IRP-SSDPR", "IRP_SSDPR", "SSDPR"], "Greenliant" => ["32GB NANDrive"], "GS Nanotech" => ["256-16 GSPTA", "512-16 GSPTA", "GSP", "GSS", "GS SSD"], "GSemi" => ["GSDSM"], "HAJAAN" => ["HS256E"], "Hanye" => ["W400-256GMP"], "HCiPC" => ["P2MSM"], "HGST" => ["HUH", "HUP", "HUS"], "Hikvision"=> ["HKVSN", "HS-SSD", "M.2 C100"], "Hitachi" => ["HDS", "HDT", "HUA", "HT", "TPH"], "HP" => ["FB0", "FB1", "FK00", "GB0", "GB1000EA", "GJ0", "MB1000", "MB2000", "VB0", "VK0"], "HP Phison" => ["PSSBN"], "HPE" => ["LK04", "LK16", "MB0", "MB8", "MB4000", "MK0", "MM1000", "MM2000", "MR00024", "VR00024", "VR00048"], "Hypertec" => [ "SSD2S120", "SSD2S240" ], "Hyundai" => ["C2S3T"], "IBM/Hitachi" => ["IC25", "IC35"], "Indilinx" => ["IND-S", "InM2"], "Innodisk" => ["DEM24", "DEMSR", "DES25", "mSATA 3TG6"], "Integral" => ["V Series SATA SSD"], "Intel" => ["N18A", "SSDPAMM", "SSDSA2S", "SSDSC2"], "Intenso" => ["JAJ", "lntenso"], "iODD" => ["MST100",], "IPLEX" => ["TITAN256"], "KeepData" => ["GIM128", "GIM512"], "KingDian" => ["SSD-S400"], "KingSpec" => ["ACJ", "ACS", "CHA", "IM032-S220", "KSD-PA", "KSQ120", "NT-32", "P3-512", "PA25-16", "PA25-32", "SPK", "Q-90", "Q-180", "Q-360"], "Kingston" => ["A400", "AS400", "EK60H", "HyperX", "KC600", "KF-S42240", "Kingston", "RBU-SN", "SMSM151S3"], "KIOXIA" => ["KBG", "KXG"], "KOOTION" => ["X12 SSD"], "Kross Elegance" => ["KE-SSD"], "LENSEN" => ["LS600"], "Lite-On" => ["NVMe CA5", "PH2", "PH3", "PH4", "PH5"], "Mach Xtreme" => ["MXSSD"], "MARSHAL" => ["MAL"], "Micron" => ["MTF"], "Mcpoint" => ["MC240"], "MediaRange" => ["MR1"], "MEMXPRO" => ["mSATA M3B"], "MOVESPEED" => ["YSSDJQB", "YSSDMDS"], "MyDigitalSSD" => ["BP5", "SC2 M2", "SB M2", "SB2"], "Netac" => ["N600", "NS512GSS"], "Nfortec" => ["ALCYON"], "NTC" => ["NTC "], "OCZ" => ["AGILITY", "D2C", "D2R", "DEN", "OCZ", "VTX3"], "OSCOO" => ["OSC"], "OWC" => ["Mercury Electra", "Neptune"], "Pacific Sun" => ["PSM-"], "Pasoul" => ["OASDX"], "Phison" => ["P-SSD1800", "S10C", "S11-"], "Phytium" => ["S1200C"], "PNY" => ["69D03", "CS900", "SSD2SC", "SSD9SC"], "Princeton" => ["PSSD"], "Quaroni" => ["QSSD"], "QUMO" => ["Q3DT"], "Ramaxel" => ["RDM", "RTIRB", "RTITF", "RTFM", "RTN"], "RESCUE" => ["RESCUE"], "S3+" => ["S3SSD"], "Samgporse" => ["SP-8"], "Samsung" => ["MBG4", "MCBOE", "MZM", "MZ7", "SG9"], "SanDisk" => ["CF Card", "DB40", "sandisk", "SDCFHS", "SDCFXPS", "SDCFXS", "SDSA6MM", "SDSSDA", "SU04G", "SU08G", "TE2"], "SCY" => ["N10C"], "Seagate" => ["2E256-TU2", "OOS500G", "OOS1000G", "OOS2000G", "ST", "ST_", "XA1920", "XF1230", "ZA1"], "Seapiy" => ["E535N"], "Silicon Motion" => ["AAR", "GSD"], "Silicon Power" => ["SP128GI"], "SK hynix" => ["HFS", "SHGS"], "Smart" => ["SGMST"], "SNR" => ["SNR-ML"], "Solid" => ["SSD0240S", "SSD0256S"], "SSSTC" => ["CVB-CD"], "Supersonic" => ["SUPERSONIC"], "Super Talent" => ["FNX256MORM"], "SXMicro" => ["NF830"], "T&G" => ["TG25S"], "Tammuz" => ["RX440"], "Terabit" => ["T50S3"], "Timetec" => ["30TT", "35TT"], "Toshiba" => ["MG0", "Q200 EX", "SSD0256XQ", "THNSF"], "Transcend"=> ["TS", "USDU1"], "TREKSTOR" => ["TREKSTOR"], "Tronos" => ["TN240G"], "UMIS" => ["RPFTJ"], "Unigen" => ["UGBS"], "Varro" => ["BULLDOZER", "bulldozer"], "VISIPRO" => ["SDV"], "Wibtek" => ["W800S"], "Wicgtyp" => ["M900-128"], "WINTEN" => ["WT200-SSD-256GB"], "WDC" => ["HBS3A", "WD", "WDC WD10", "WD A3"], "X-ENERGY" => ["X-ENERGY"], "Xinsujie" => ["XSJ"], "XPG" => ["SX8200"], "XUM" => ["HX"], "Yeyian" => ["VALK"], "Zebronics" => ["ZEB"], "Zheino" => ["CHN", "CHN "], "ZTC" => ["ZTC-MS", "ZTC-SM"] ); my $ALL_DRIVE_VENDORS = "Aarvex|Acer|ACPI|ADATA|A\-DATA|addlink|ADROITLARK|AFOX|Advantech|AEGO|Agile|AirDisk|ALLIED|AMD|AMP|ANACOMDA|Anobit|Aoluska|Apacer|Apple|ASint|ASUS|ATP|Aura|Avant|AVEXIR|Axiom|AXIOMTEK Corp\.|Azerty|Bamba|BaseTech|BHT|BIOSTAR|BIWIN|Biwintech|Blackpcs|Bliksem|BlitzWolf|BLUERAY|BORY|Brainzap|BRAVEEAGLE|BUFFALO|Centerm|Chiprex|CLOVER|Colorful|Corsair|Crucial|CSD|CWDISK|Dahua|DATARAM|Dell|DEPO|DERLAR|DERLER|DeTech|DGM|DIERYA|Dogeish|DOGFISH|DOGGO|DREVO|DRVEO|EAGET|EDGE|EGON|ELSKY|EMTEC|Espada|EXBOM|ExcelStor Technology|EYOTA|EZCOOL|e2e4|Fanxiang|Faspeed|FASTDISK|FATTYDOVE|FCS|FLEXXON|Fordisk|FORESEE|Force Le|Foxline|FUJITSU|FUTURE TECH|GALAX|Galaxy|GAMER|Geil|GelL|geonix|Getrich|GIGABYTE|Gigastone|Global Memory|GLOWAY|Goldendisk|Goldenfir|Golden memory|Golden\-Memory|Goldkey|GOODRAM|Gost|GOWE|Gritronix|HAGIWARA SOLUTIONS|Hajaan|Hanye|HECTRON|HEORIADY|HGST|Hised|Hitachi|HJDK|Hoodisk|HP|HYPERDISK|Hyundai|i-FlashDisk|IBM-Hitachi|IBM|imation|Indilinx|INDMEM|Inland|Innodisk|InnoLite|INNOVATION[^\x00-\x7F]+IT|INNOVATION IT|INTEL|INTENSO|J\.ZAO|JAMESDONKEY|JASTER|JD|JIAWEI|JIESHUO|JMicron|JUHOR|JUMPER|JWX|KDATA|KimMiDi|Kimtigo|KINGBANK|Kingchuxing|KingDian|KingFast|KINGMAX|KINGPAN|KingPower|KINGRICH|Kingsand|KINGSHARE|King Share|KingSpec|Kingspeed|Kingston|KIOXIA-EXCERIA|KLEVV|KLLISRE|KLONER|KODAK|Kston|KUIJIA|KUU|KVST|Lanbo Flourishing|LDLC|LDNDISK|LenovoSPEED UP|Lenovo|LEQIXIANG|Leven|LEXAR|Lite-On|LITEON|LITEONIT|LONDISK|LuminouTek|LVCARDS|MACROVIP|Magnetic Data|MaiChai|MARSHAL|MARVELL|MAX SPEED|Maximus|Maxmemroy|Maxtor|MediaMax|MENGMI|MicroData|MicroDream|MicroFrom|Micron|Microtech|MidasForce|minisforum|MIXZA|MSI|Mushkin|Myung|Neo Forza|Netac|NFORCE|NN TECNOL[^\x00-\x7F]+GIA|OCZ|OEM|ORICO|Origin Inception|ORTIAL|Ovation|OWC|OXBON|oyunkey|PALIT|Patriot|Pccooler|Pear|PHINOCOM|Phison|Pichau|Platinet|PLEXTOR|PNY|POLION|POWER X|PRETEC|Protectli|PUSKILL|Qianghe|QNIX|QUANTUM|QUMO|Qunion|Radeon|Ramaxel|RAMOS|Ramsta|RCESSD|Reeinno|Redapple|Reeioon|Reletech|RENICE|RevuAhn|ROKOT|Rogueware|RunCore|RZX|Saichi|Samsung Electronics|SAMSUNG|SAMSWEET|SandForce|SanDisk|SanPin|SATADOM|SATAFIRM|SBe|SCY|Seagate|SemsoTai|SenDisk|SETHRISE|SFAS|ShanDianZhe|SHAREVDI|ShiJi|Shinedisk|SILICONMOTION|Silicon Motion|SK hynix|SMART|Smartbuy|SMI|Solidata|SomnAmbulist|SOYO|SPCC|SSSTC|Star Drive|SUNEAST|SUNTRSI|SuperMicro|SuperSSpeed|SuperTalent|Synology|T\-CREATE|T\-FORCE|TAISU|takeMS|TAMMUZ|TANCA|TCSUNB0W|TCSUNBOW|TEAM|Teclast|tecmiyo|Teelkoou|TEUTONS|TEXTORM|TEKET|THU|tigo|Timetec|TOPMORE|TopSunligt|TOSHIBA|Transcend|TurXun|TUSUNBOW|TwinMOS|TXRUI|UDinfo|UMAX|UMIS|UNIC2|V-Gen|Value Tech|Valuetech|Vaseky|VENO SCORP|Veno|VENTUZ|Verbatim|VERICO|VBOX|VICKTER|VICK|ViperTeq|VisionTek|VISIPRO|VIVA|VNYEZ|WALRAM|WDC|Wdstars|Wdxsky|WellcommMaster|Wellcomm Master|Western Digital|Wintec|Wolf Aure|Xinhaike|XPG|XrayDisk|XSTAR|XUNZHE|Yeestor|YMTC|ZEPLIN|Zheino|ZhiTai|ZOTAC|ZOZT|2\-Power"; my $ALL_DRIVE_VENDORS_SHORT = "BR|FiD|HXY|KST|RX7|TMI"; my @DRIVE_ADD_SIZE = ("OCZ", "ADATA", "A-DATA", "PATRIOT", "SPCC", "SAMSUNG", "CORSAIR", "HYPERDISK", "TOSHIBA"); my %VendorDiskModel = ( "Apacer" => ["8GB SATA Flash Drive", "16GB SATA Flash Drive", "32GB SATA Flash Drive", "64GB SATA Flash Drive", "128GB SATA Flash Drive", "256GB SATA Flash Drive", "480GB SATA Flash Drive", "SATA Flash Drive"], "Blackpcs" => ["AS201 SSD"], "China" => [ "128MB ATA Flash Disk", "1GB ATA Flash Disk", "120GB SATA Flash Drive", "120GB SATA Flash Drive_K", "1TB PCS 2.5-inch SSD", "1TB QLC SATA SSD", "1TBE 1TB", "2.5 SATA SSD", "2.5 SSD 240GB", "2.5'' SSD 120GB", "240G SATA III SSD", "256GB PCS 2.5-inch SSD", "256GB PCS SATA M.2 SSD", "256GB QLC SATA SSD", "256GB SATA SSD", "512GB PCS 2.5-inch SSD", "512GB QLC SATA SSD", "AC211-480GB", "AS-120", "AS-240", "ASS-120", "BF9SSD 256S", "BiCS", "BK-32GB MSATA SSD", "BK-64GB MSATA SSD", "BK-128GB MSATA SSD", "BTO-240GB", "C-Series 128G SSD SATA 2", "C500", "CF", "CF120GB", "CHESHI-128G", "CIE M8 M350 128GB W", "CNCTION", "CYX-SSD-S1000", "D128GSSDQ400S", "Destiny4_4000G_DVT", "DHMSR64GD81BC1QC", "EDGSD25240GBOOSTS37M", "EK V100", "EP-SSM280256AA2-TX", "G7-240G PLUS", "FPT330M8SSD512G", "G521N 256GB", "G521N256GB", "G535NW-512G", "GCM2IN2280256C", "Generic S050 Hard drive", "GIM64", "GIM256", "GM016", "GM16", "GM128", "GM1TB", "GN256", "GSDSL128TY2AAQGCX", "HOM128M-46XI2", "HS1TBQE", "IM128-P130", "IM256-P130", "IM256-WG30", "IM3D L06B B0KB", "IT128-P130", "JDa He SATA DISK", "JOYSSD512", "JWX 16GB MSATA", "K1", "KCG SSD", "KR 32G", "Kston128GB", "KZ128", "L06B B0KB", "LS 256G M300", "LS 512GB M300", "M.2 2280 128GB", "M.2 2280-1TB SSD", "M.2 2280 SATA SSD", "M.2 SSD", "M.2 SSD 1TB", "M10C", "M900-1T", "MG BGM.2 NGFF SSD 256GB", "Mit-SSD128A", "MKSCE1BD060M4", "MLC", "MS05", "MS06", "MS07", "MS201-512G", "mSATA SSD", "mSATA-64GB SSD", "Msata", "mSATAS3-0060-XQE8(010)", "mSATA 128GB", "MTG-480GB", "MV-16", "MV-32", "MW02 128GB MSATA", "N86-256GB", "N900-128", "NGFF 2242 32GB SSD", "NGFF 2242 128GB SSD", "NGFF 2242 240GB SSD", "NGFF 2242 512GB SSD", "NGFF 2280 128GB SSD", "NGFF 2280 256GB SSD", "NGFF 2280 512GB", "NGFF 2280 512GB SSD", "NGFF 2280 1TB SSD", "NS256GSSD340", "NT-256 2242", "NT-1TB 2280", "NT-2TB 2242", "OEMSSD240GB", "OOS250G", "PATA SSD", "PSX-NGFF256G", "QST8-512", "R580", "REVOLUTIONS500_256", "S3 SSD", "S500", "s60 60gb", "SATA SSD", "SATA2 32GB SSD", "SCCTS-603-128G SSD", "SHMST6D128GHM11EMC 118000102", "SIC-1TB", "SK 64GB", "SK 128GB", "SK 256GB", "SK 1TB", "SK600-32GB", "sk600 64gb", "sk600 128gb", "Solid", "SSD-128G", "SSD-128GB", "SSD-256GB", "SSD-1TB", "SSD512M2S80", "SSDG2-240G", "SSDG2-256G", "SSDS2TA120G-BULK", "SSE064GMLCC-SBC-2S", "SSE128GMLCT-SBC-2S", "SSE256GMLCT-SBC-2S", "SSE256GMLCT-SBC-4S", "SSV4", "SSV5", "SW-120GB", "SV9SAT6B480GLM21DR", "T240", "TR960GB", "TYPEC 120GB PSSD", "W2EM120GDTA-S71AAB-2Q4-SP", "W552-256G", "W653-64GB", "WPC-240GB", "X160EB", "XJH", "YS SSD", "ZP-128GB", "ZSS11DA02C" ], "EVM" => ["512GB EVM SSD"], "Greenliant" => ["32GB ArmourDrive"], "G.Skill" => ["FM-25S3-120GBP3"], "Innodisk" => ["2.5 SATA SSD 3TE7", "M.2 (S80) 3TE7", "mSATA 3MG2-P", "mSATA 3TE7"], "INNOVATION IT" => ["SSD_M.2_256GB_InnovationIT", "SSD_2.5-inch_TLC_256GB_InnovationIT", "SSD_2.5-inch_TLC_240GB_InnovationIT", "SSD_2.5-inch_TLC_512GB_InnovationIT", "SSD_2.5-inch_1TB_InnovationIT_QLC", "SSD_M.2_512GB_InnovationIT_QLC", "SSD_M.2_1TB_InnovationIT", "SSD_M.2_512GB_InnovationIT", "SSD_2.5-inch_256GB_InnovationIT_QLC", "SSD_2.5-inch_TLC_120GB_InnovationIT"], "Integral" => ["V Series SATA SSD"], "Micron" => ["P400m100-MTFDDAK100MAN 118032953"], "NITEC" => ["G535S 256GB"], "NITRINOnet" => ["240GB SSD NITRINOnet RUS"], "Phison" => ["PS3108S8", "SSDS30256XQC800134237", "S11-64G-PHISON-SSD-B3", "S11-128G-PHISON-SSD-B4", "S11-256G-PHISON-SSD-B3", "S11-256G-PHISON-SSD-B4", "SSE064GPTC0-S81"], "SanDisk" => ["SSD PLUS 480GB"], "Silicon Power" => ["SP-mSATA-64G", "SP SolidState Disk", "ULTIMATE CF CARD"], "Solid" => ["SSD0256S00"], "UNIC2" => ["S5170-256-80"], ); foreach my $V (sort keys(%VendorDiskPrefix)) { foreach (sort @{$VendorDiskPrefix{$V}}) { $DiskVendor{$_} = $V; } } my %DiskModelVendor = (); foreach my $V (sort keys(%VendorDiskModel)) { foreach (sort @{$VendorDiskModel{$V}}) { $DiskModelVendor{$_} = $V; } } # http://standards-oui.ieee.org/oui.txt my %IeeeOui = ( "0014ee" => "WDC", "000c50" => "Seagate", "0004cf" => "Seagate", "00080d" => "Toshiba", "000039" => "Toshiba", "001b44" => "SanDisk", "000cca" => "HGST", "0024e9" => "Samsung", "002538" => "Samsung", "0026b7" => "Kingston", "00000e" => "Fujitsu", "5cd2e4" => "Intel", "002303" => "Lite-On", "6479a7" => "Phison", "ace42e" => "SK hynix", "dc663a" => "Apacer" ); my %SerialVendor = ( "WD" => "WDC", "OCZ" => "OCZ", "PNY" => "PNY" ); my %FirmwareVendor = ( "MZ4O" => "Toshiba", "N1126F" => "Intenso", "S0222A0" => "Patriot", "SFDM104B" => "Apacer", "SFPS881A" => "China", "S0918A0" => "China", "S1211A0" => "Intenso", "S5FAM017" => "Phison", "T0521A0" => "Netac", "T0722A0" => "Netac", "T0910A0" => "Intenso", "T0929A0" => "Intenso", "T1209A0" => "GOODRAM" ); my %MicroCode = ( "SandyBridge" => ["0x206a7"], "Westmere" => ["0x20655"] ); my %MicroCodeMicroArch; foreach my $MicroArch (sort keys(%MicroCode)) { foreach (sort @{$MicroCode{$MicroArch}}) { $MicroCodeMicroArch{$_} = $MicroArch; } } my %MicroArchFamily = ( "AMD" => { # AuthenticAMD "Geode" => { "5" => ["*"] }, "K6" => { "6" => ["*"] }, "K7" => { "7" => ["*"] }, "K8 Hammer" => { "15" => ["*"] }, "K10" => { "16" => ["*"] }, "K8 & K10 hybrid" => { "17" => ["*"] }, "K10 Llano" => { "18" => ["*"] }, "Bobcat" => { "20" => ["*"] }, "Bulldozer" => { "21" => ["1"] }, "Piledriver" => { "21" => ["2", "16", "19"] }, "Steamroller" => { "21" => ["48", "56"] }, "Excavator" => { "21" => ["96", "101", "112"] }, "Jaguar" => { "22" => ["0"] }, "Puma" => { "22" => ["48"] }, "Zen" => { "23" => ["1", "17", "32"] }, "Zen 2" => { "23" => ["49", "96", "113"] }, "Zen+" => { "23" => ["8", "24"] }, "Zen 3" => { "25" => ["33", "80"] } }, "Intel" => { # GenuineIntel "TigerLake" => { "6" => ["140"] }, "IceLake" => { "6" => ["126"] }, "CometLake" => { "6" => ["165", "166"] }, "KabyLake" => { "6" => ["142", "158"] }, "Goldmont plus" => { "6" => ["122"] }, "CannonLake" => { "6" => ["102"] }, "Skylake" => { "6" => ["78", "85", "94"] }, "Goldmont" => { "6" => ["92", "95"] }, "Broadwell" => { "6" => ["61", "71", "79", "86"] }, "Silvermont" => { "6" => ["55", "76", "77"] }, "Haswell" => { "6" => ["60", "63", "69", "70"] }, "IvyBridge" => { "6" => ["58", "62"] }, "Bonnell" => { "6" => ["28", "38", "54"] }, "SandyBridge" => { "6" => ["42", "45"] }, "Westmere" => { "6" => ["37", "44", "47"] }, "Nehalem" => { "6" => ["26", "30", "46"] }, "Penryn" => { "6" => ["23", "29"] }, "Core" => { "6" => ["15", "22"] }, "P6" => { "6" => ["7", "8", "9", "11", "13", "14"] }, "NetBurst" => { "15" => ["1", "2", "3", "4", "6"] } } ); my %MicroArchModel = ( "AMD" => { "Zen" => [ "Ryzen 5 PRO 2500U w/ Radeon Vega Mobile Gfx", "Ryzen 7 1700 Eight-Core Processor" ], "Zen+" => [ "Ryzen 7 2700 Eight-Core Processor", "Ryzen 7 2700X Eight-Core Processor" ], "Zen 2" => [ "Ryzen 5 3600 6-Core Processor" ], }, "Intel" => { "Haswell" => [ "Core i3-4150 CPU @ 3.50GHz" ], "SandyBridge" => [ "Core i5-2430M CPU @ 2.40GHz" ] } ); my %FamilyMicroArch; foreach my $MVendor (sort keys(%MicroArchFamily)) { foreach my $MicroArch (sort keys(%{$MicroArchFamily{$MVendor}})) { foreach my $Family (sort keys(%{$MicroArchFamily{$MVendor}{$MicroArch}})) { foreach (sort @{$MicroArchFamily{$MVendor}{$MicroArch}{$Family}}) { $FamilyMicroArch{$MVendor}{$Family}{$_} = $MicroArch; } } } } my %ModelMicroArch; foreach my $MVendor (sort keys(%MicroArchModel)) { foreach my $MicroArch (sort keys(%{$MicroArchModel{$MVendor}})) { foreach my $Model (sort @{$MicroArchModel{$MVendor}{$MicroArch}}) { $ModelMicroArch{$MVendor}{$Model} = $MicroArch; } } } my $DEFAULT_VENDOR = "China"; # NO NAME my %DistSuffix = ( "res7" => "rels-7", "res6" => "rels-6" ); my %DistPackage = ( "centos-release-5" => "centos-5", "centos-release-6" => "centos-6" ); my @DE_Package = ( [ "budgie-desktop", "Budgie" ], [ "ukui-desktop", "UKUI" ], [ "pantheon-xsession-settings", "Pantheon" ], [ "gnustep ", "GNUstep" ], [ "kdesktop-trinity", "Trinity" ], [ "manjaro-cinnamon-settings", "Cinnamon" ], [ "cinnamon-session", "Cinnamon" ], [ "deepin-manjaro", "Deepin" ], [ "ubuntudde-dde", "Deepin" ], [ "deepin-desktop-base", "Deepin" ], [ "manjaro-mate-settings", "MATE" ], [ "manjaro-lxde-settings", "LXDE" ], [ "task-lxde", "LXDE" ], [ "manjaro-lxqt-extra-settings", "LXQt" ], [ "task-lxqt", "LXQt" ], [ "manjaro-xfce", "XFCE" ], [ "manjaro-kde-settings", "KDE5" ], [ "plasma5-workspace", "KDE5" ], [ "plasma-desktop-5", "KDE5" ], [ "plasma-desktop 5", "KDE5" ], [ "task-plasma5", "KDE5" ], [ "plasma-workspace 4:5", "KDE5" ], [ "plasma5-plasma-workspace", "KDE5" ], [ "mate-session-manager", "MATE" ], [ "lxqt-session", "LXQt" ], [ "plasma-desktop 4:4", "KDE4" ], [ "kde-settings-plasma", "KDE4" ], [ "task-kde4", "KDE4" ], [ "drakconf-kde4", "KDE4" ], [ "kdebase4-workspace", "KDE4" ], [ "unity-session", "Unity" ], [ "gnome-flashback", "GNOME Flashback" ], [ "manjaro-gnome-assets", "GNOME" ], [ "gnome-session", "GNOME" ], [ "manjaro-awesome-settings", "Awesome" ], [ "manjaro-fluxbox-settings", "FluxBox" ], [ "i3-manjaro", "i3" ], [ "i3-wm", "i3" ], [ "manjaro-i3-settings", "i3" ], [ "xfce4-settings", "XFCE" ], # 3b85d1aeb4 XFCE before GNOME [ "task-xfce", "XFCE" ], [ "xfce4-session", "XFCE" ], [ "gnome-desktop", "GNOME" ], [ "openbox-lxde-session", "Openbox" ], [ "manjaro-openbox-settings", "Openbox" ], [ "enlightenment", "Enlightenment" ], [ "lxde", "LXDE" ], # BSD [ "x11/lumina", "Lumina" ], [ "x11/plasma5-plasma-desktop", "KDE5" ], [ "x11/cde", "CDE" ], [ "x11-wm/openbox", "Openbox" ], [ "x11-wm/awesome", "AwesomeWM" ], [ "x11-wm/i3", "i3" ], [ "x11-wm/i3-gaps", "i3" ], [ "x11-wm/fluxbox", "Fluxbox" ], [ "x11-wm/twm", "TWM" ], [ "x11-wm/marco", "Marco" ], [ "x11-wm/stumpwm", "StumpWM" ], [ "x11-wm/windowmaker", "Window Maker" ], [ "x11-wm/compton", "Compton" ], [ "x11-wm/picom", "Picom" ], [ "dwm", "DWM" ], [ "2bwm", "2bwm" ], ); my @DisplayServer_Package = ( [ "x11-server-xwayland", "Wayland" ], [ "xorg-server-xwayland", "Wayland" ], [ "xorg-x11-server-Xwayland", "Wayland" ], [ "xwayland", "Wayland" ], [ "x11-server-xorg", "X11" ], [ "xorg-x11-server-Xorg", "X11" ], [ "xserver-xorg", "X11" ], [ "xorg-x11-server", "X11" ] ); my %DisplayManager_Fix = ( "LIGHTDM" => "LightDM", "SLIM" => "SLiM", "KDE" => "KDM", "LY" => "Ly", "ENTRANCED" => "Entrance" ); my @ALL_DISPLAY_MANAGERS = qw(lightdm tdm sddm xdm gdm gdm3 slim kdm ldm lxdm cdm entranced mdm nodm pcdm wdm xenodm ly); my @DisplayManager_Package = (); foreach my $DM (@ALL_DISPLAY_MANAGERS) { push(@DisplayManager_Package, [$DM, uc($DM)]); } my %ChassisType = ( 1 => "Other", 2 => "Unknown", 3 => "Desktop", 4 => "Low Profile Desktop", 5 => "Pizza Box", 6 => "Mini Tower", 7 => "Tower", 8 => "Portable", 9 => "Laptop", 10 => "Notebook", 11 => "Hand Held", 12 => "Docking Station", 13 => "All In One", 14 => "Sub Notebook", 15 => "Space-saving", 16 => "Lunch Box", 17 => "Main Server Chassis", 18 => "Expansion Chassis", 19 => "Sub Chassis", 20 => "Bus Expansion Chassis", 21 => "Peripheral Chassis", 22 => "RAID Chassis", 23 => "Rack Mount Chassis", 24 => "Sealed-case PC", 25 => "Multi-system", 26 => "CompactPCI", 27 => "AdvancedTCA", 28 => "Blade", 29 => "Blade Enclosing", 30 => "Tablet", 31 => "Convertible", 32 => "Detachable", 33 => "IoT Gateway", 34 => "Embedded PC", 35 => "Mini PC", 36 => "Stick PC" ); my $DESKTOP_TYPE = "desktop|nettop|all in one|box|space\-saving|mini|tower|bus expansion"; my $SERVER_TYPE = "server|rack|blade"; my $MOBILE_TYPE = "notebook|laptop|portable|tablet|convertible|detachable|docking|stick|hand"; my $HID_BATTERY = "wacom|wiimote|hidpp_|controller_|hid\-|steam-controller|power_supply\/bms"; # SDIO IDs my %SdioInfo; my %SdioVendor; # SDIO IDs (Additional) my %AddSdioInfo; my %AddSdioVendor; # PNP IDs my %PnpVendor; my %SdioType = ( "01" => "uart", "02" => "bluetooth", "03" => "bluetooth", "04" => "gps", "05" => "camera", "06" => "phs", "07" => "network", "08" => "ata", "09" => "bluetooth" ); # Fix monitor vendor my %MonVendor = ( "ACI" => "Ancor Communications", # ASUS "ADI" => "ADI", "ADT" => "Advantech", "AGN" => "AG Neovo", "AIC" => "Arnos Instruments", # AG Neovo "ALB" => "Alba", "ALC" => "Alches", "AMH" => "AMH", "AMI" => "Amitech", "AMR" => "JVC", "AMT" => "AMT International", # AMTRAN? "AMW" => "AMW", "AMZ" => "Amazon", "ANX" => "Analogix", "AOP" => "AOpen", "APO" => "ALPD", "APP" => "Apple Computer", "AQS" => "Aquarius", "ARG" => "Alba", "ARM" => "Armaggeddon", "ASM" => "Aosiman", "ATE" => "Megavision", "ATV" => "Ativa", "AVG" => "Avegant", "AXM" => "AXM", "BAL" => "Balance", "BBK" => "BBK", "BBY" => "Insignia", "BEK" => "Beko", "BKM" => "Beike", "BLS" => "BUBALUS", "BMM" => "Proview", "BNQ" => "BenQ", "BOE" => "BOE", "BRA" => "Braview", "BSE" => "Bose", "BTC" => "RS", "BUF" => "Buffalo", "CAL" => "Albatron", "CAS" => "CASIO", "CCE" => "CCE", "CHH" => "Changhong Electric", "CIS" => "Cisco", "CLX" => "Claxan", "CMN" => "Chimei Innolux", "CMO" => "Chi Mei Optoelectronics", "CMS" => "Cosmos", "CND" => "CND", "COB" => "COBY", "COR" => "CPT", # Chunghwa Picture Tubes "COS" => "NVISION", "CPT" => "CPT", "CPQ" => "Compaq Computer", "CTL" => "CTL", "CTX" => "CTX", "CYS" => "Aosiman", "DIC" => "Dinner", "DGV" => "Deco Gear", "DMG" => "Dark Matter", "DNS" => "DNS", "DON" => "DENON", "DSG" => "DSGR", "DSL" => "DisplayLink", "DTB" => "DTEN Board", "DUS" => "VOXICON", "DVA" => "GE", "DVM" => "RoverScan", "DXP" => "DEXP", "EGA" => "Elgato", "EHJ" => "Epson", "ELO" => "Elo Touch", "ENM" => "ENMAR", "ENV" => "Envision Peripherals", "EPI" => "Envision", "EST" => "Estecom", "EQD" => "EQD", "EVE" => "Eve Spectrum", "EXN" => "Extron", "FAC" => "Yuraku", "FDR" => "Founder", "FLU" => "Fluid", "FND" => "Founder", "FSN" => "D&T", "FUJ" => "Fujitsu", "FUS" => "Fujitsu Siemens", "GAM" => "GAOMON", "GBA" => "GABA", "GBT" => "GIGABYTE", "GEC" => "Gechic", "GGF" => "Game Factor", "GMI" => "XGIMI", "GRN" => "Green House", "GSM" => "Goldstar", "GSV" => "G-Story", "GWD" => "GreenWood", "HAN" => "Cbox", "HCD" => "ViewSonic", "HCM" => "HCL", "HED" => "Hedy", "HEI" => "Hyundai", "HII" => "Higer", "HIS" => "Hisense", "HKC" => "HKC", "HKN" => "iQual", "HLT" => "WaveShare", "HSG" => "Hannspree", "HSL" => "Hansol", "HVR" => "HVR", # VR Headsets "HWP" => "HP", "HPC" => "Erisson", "HPN" => "HP", "HRT" => "Hercules", "HSE" => "Hisense", "HSJ" => "Intehill", "HUG" => "Hugon", "HUY" => "HUYINIUDA", "HWV" => "HUAWEI", "HXF" => "BlueCase", "HXV" => "iQual", "HYC" => "Pixio", "IBM" => "IBM", "ICB" => "Pixio", "ICP" => "IC Power", "IGM" => "Videoseven", "IMP" => "Impression", # V7 "INC" => "INCA", "INN" => "PRISM+", "INZ" => "Insignia", "IOC" => "INNOCN", "ITA" => "Easy Living", "ITR" => "INFOTRONIC", "IVM" => "Iiyama", "IVO" => "InfoVision", "JDI" => "JDI", # Japan Display Inc. "JEN" => "Jean", "JVC" => "JVC", "JXC" => "JXC", # Shenzhen JingXingCheng "KDI" => "Seiki", "KDM" => "Korea Data Systems", "KIV" => "Kivi",, "KMR" => "Kramer", "KGN" => "Kogan", "KOA" => "Konka", "KOS" => "KOIOS", "KTC" => "KTC", "LDL" => "LDLC", "LEC" => "Lecoo", "LGD" => "LG Display", "LGP" => "LG Philips", "LHC" => "Denver", "LNX" => "Lanix", "LPL" => "LG Philips", "LPS" => "Lupus Screen", "LTN" => "Lite-On", "LRN" => "Doffler", "MAC" => "MacroSilicon", "MAX" => "Belinea", "MBB" => "MAIBENBEN", "MCE" => "Metz", "MEC" => "Medion Akoya", "MEI" => "Panasonic", "MEK" => "MEK", "MEL" => "Mitsubishi", "MJI" => "Marantz", "MP_" => "Monoprice", "MPC" => "Monoprice", "MSC" => "Syscom", "MSH" => "Microsoft", "MS_" => "Sony", "MST" => "MStar", "MTC" => "Mitac", "MTX" => "Matrox", "MUL" => "Multilaser", "MUS" => "Mecer", "MZI" => "Digital Vision", "NAV" => "Nanovision", "NCI" => "NECCI", "NCP" => "PANDA", # Nanjing CEC Panda "NEC" => "NEC", "NFC" => "NFREN", "NIK" => "Niko", "NLK" => "MStar", "NOA" => "NOA VISION", "NOR" => "Norcent", "NSO" => "Neso", "NUG" => "NU", "NVD" => "Nvidia", "NVI" => "NVISION", "NVT" => "Novatek", "OCM" => "oCOSMO", "OLD" => "Olidata", "ONK" => "Onkyo", "ONN" => "ONN", "ORN" => "Orion", "OTT" => "Ottagono", "OWC" => "OWC", "PBK" => "PCBANK", "PCK" => "SENSY", "PDA" => "PANDA", "PEA" => "Pegatron", "PEB" => "Proview", "PEG" => "PEGA", "PER" => "Turbo-X", "PGE" => "GNR", "PIO" => "Pioneer", "PKB" => "Packard Bell", "PKR" => "Parker", "PLC" => "Philco", "PLN" => "Planar", "PLT" => "PiLot", "PNR" => "Planar", "PNS" => "Pixio", "PRW" => "Promethean", "PTH" => "TVS", "PTS" => "Plain Tree Systems", "QBL" => "QBell", "QDS" => "Quanta Display", "QMX" => "Gericom", "QSM" => "Qushimei", "QUN" => "Lenovo", "RDS" => "KDS", "REC" => "Reconnect", "REG" => "Mobile Pixels", "RJT" => "Ruijiang", "RKU" => "Roku", "ROL" => "Rolsen", "RUB" => "Rubin", "RZR" => "Razer", "SAN" => "Sanyo", "SCE" => "Sun", "SCN" => "Scanport", "SEE" => "SEEYOO", "SHD" => "SmallHD", "SII" => "Skyworth", "SLE" => "Slen", "SNN" => "SUNNY", "SNY" => "Sony", "SPT" => "Sceptre Tech", "SPV" => "Sunplus", "STC" => "Sampo", "STI" => "Semp Toshiba", "STK" => "S2-Tek", "SUN" => "Sun", "SVA" => "SVA", "SVR" => "Sensics", "SYL" => "Sylvania", "SYN" => "Olevia", "SZM" => "Mitac", "TAR" => "Targa Visionary", "TAT" => "Tatung", "TCL" => "TCL", "TEO" => "TEO", "TEU" => "Relisys", "TLX" => "Tianma XM", "TNJ" => "Toppoly", "TOP" => "TopView", "TPV" => "Top Victory", "TRL" => "Royal Information", "TSD" => "TechniMedia", "UMC" => "UMC", "UPS" => "UpStar", "VBX" => "VirtualBox", "VES" => "Vestel Elektronik", "VIT" => "Vita", "VJK" => "HannStar", "VLV" => "Valve", "VSC" => "ViewSonic", "VSN" => "Videoseven", "VTK" => "Viotek", "WAC" => "Wacom", "WAM" => "Pixio", "WIM" => "Wimaxit", "WNX" => "Wincor Nixdorf", "WTC" => "Waytec", "XER" => "Xerox", "XMD" => "Xiaomi", "XMI" => "Mi", "XSC" => "Immer", "XVS" => "XVision", "XYE" => "Xiangye", "YAK" => "Yakumo", "YCT" => "InnoView", "YEY" => "Yeyian", "YLT" => "Panasonic", "YMH" => "Yamaha", "YUK" => "Flipbook", "ZFO" => "Zifro", "ZRN" => "Zoran" ); # LCD monitors my %VendorMon = ( "Acer" => ["ABO", "ACE", "ACR", "API"], "Achieva Shimian" => ["ACB", "ACH"], "AOC" => ["AOC", "NRC", "OTS"], "Arzopa" => ["GBR"], "ASRock" => ["ASR"], "ASUS" => ["ASU", "AUS", "WWW"], "AU Optronics"=> ["AUO", "DMO"], "AVerMedia" => ["AVT"], "AYANEO" => ["AYA"], "Bangho" => ["BGO"], "Beyond TV" => ["TOL"], "Casper" => ["CSP"], "COMPAL" => ["CPL", "WOR"], "Corsair" => ["CRM"], "CRUA" => ["CRU"], "Daewoo" => ["DAE", "DWE"], "Dahua" => ["DAH"], "Dell" => ["DEL", "LNK"], "Dostyle" => ["DOS", "DST"], "Eizo" => ["EIZ", "ENC"], "Element" => ["EIA", "ELE", "EMT"], "Elgato" => ["YUN"], "ELSA" => ["ELA", "ELS"], "Etigroup" => ["ETG"], "eMachines" => ["EMA", "EMI"], "Faytech" => ["FAY"], "FPT" => ["FPT"], "Furrion" => ["FUR"], "Gateway" => ["GTW", "GWY"], "GameMax" => ["GMX"], "Grundig" => ["GRR", "GRU"], "H-Buster" => ["HB\@"], "Haier" => ["HAI", "HAR", "HRE", "SRD"], "HannStar" => ["HSD", "HSP"], "Hitachi" => ["HEC", "HIT", "HTC"], "Huion" => ["HAT", "HUI", "HUN"], "Hyundai ImageQuest" => ["HIQ", "IQT"], "InnoLux Display" => ["CMI", "INL", "INX"], "JINGLITAI" => ["JRP"], "KAZUK" => ["KAZ"], "Lacie" => ["LAC", "LCA"], "Lenovo" => ["LCS", "LEN", "LEO", "LNV", "QUA", "QWA"], "Medion" => ["MEA", "MEB", "MED"], "MSI" => ["MAG", "MSI"], "Newline" => ["NLE"], "Newskill" => ["NSL"], "Nixeus" => ["NIX", "NTI"], "Nreal Air" => ["MRG", "NRL"], "OneMeeting" => ["BDL"], "Optoma" => ["OPT", "OTM"], "PEAQ" => ["PQA"], "Philips" => ["PFL", "PFT", "PHA", "PHG", "PHI", "PHL", "PHP", "PHT"], "Princeton" => ["HTB", "PGS", "PRT"], "Polaroid" => ["MKN", "POL"], "Positivo" => ["NON", "PCL", "POS"], "Prestigio" => ["ASB", "PRE"], "Raritan" => ["RAR"], "Samsung" => ["LGE", "SAM", "SDC", "SEC", "SEM", "SIM", "STN", "_YM"], "SANSUI" => ["XEC"], "Seiki" => ["KDD", "SEK"], "Sharp" => ["SHC", "SHP"], "SOTEC" => ["SOT"], "SuperFrame" => ["SUE"], "TELEFUNKEN" => ["TFK"], "Thomson" => ["PKV", "TMN", "TTE"], "ThundeRobot" => ["TRG"], "Toshiba" => ["LCD", "TOS", "TSB"], "UPlusVision" => ["UPV"], "Valday" => ["XYA"], "Vizio" => ["IZI", "VIZ", "VZO"], "Westinghouse" => ["WDE", "WDT", "WEH", "WET"], "Wipro" => ["WIP"], "Yashi" => ["YSI"], ); foreach my $V (sort keys(%VendorMon)) { foreach (sort @{$VendorMon{$V}}) { $MonVendor{$_} = $V; } } # Repair vendor of some motherboards and mmc devices # It is needed for catalog of public reports on github my %VendorModels = ( "Acer" => [ "ZA10_KB" ], "ASRock" => [ "4CoreDual-VSTA", "4CoreDual-SATA2", "4Core1600-GLAN", "4Core1600-D800", "4CoreN73PV-HD720p", "775XFire-RAID", "775XFire-RAID", "775VM800", "775Twins-HDTV", "775i945GZ", "775i65GV", "775i65PE", "775i48", "939Dual-SATA2", "939NF6G-VSTA", "945GCM-S", "ALiveDual-eSATA2", "ALiveNF4G-DVI", "ALiveNF6P-VSTA", "ALiveNF6G-GLAN", "ALiveNF7G-HDready", "ALiveSATA2-GLAN", "AM2NF6G-VSTA", "G31M-S", "K8NF4G-SATA2", "K8Upgrade-NF3", "K8Upgrade-VM800", "P4VM900-SATA2", "P4VM890", "P4Dual-915GL", "P4i48", "P4i65G", "P4i65GV", "P4VM8", "Wolfdale1333-GLAN", "Wolfdale1333-D667", "775Dual-VSTA", "775Dual-880Pro", "A780GXE/128M" ], "ECS" => [ "848P-A7", "965PLT-A", "H110M4-C2H", "K8M800-M2", "nForce4-A939", "nForce4-A754", "nForce", "nVidia-nForce", "RS480-M" ], "ASUSTek Computer" => [ "C51MCP51", "P5GD1-TMX/S", "RC410-SB450" ], "MSI" => [ "MS-7210", "MS-7030", "MS-7025", "MS-7210 100" ], "SiS Technology" => [ "SiS-661", "SiS-649", "SiS-648FX", "SiS-650GX" ], "Samsung" => [ "AWMB3R", "CJNB4R", "MAG2GC", "MCG8GA", "MCG8GC" ], "SanDisk" => [ "DF4032", "DF4064", "DF4128", "SDW64G", "SL32G" ], "SK hynix" => [ "HBG4a", "HBG4e", "HCG8e" ], "OEM" => [ "EIRD-SAM" ] ); my %VendorByModel; foreach my $V (sort keys(%VendorModels)) { foreach (sort @{$VendorModels{$V}}) { $VendorByModel{$_} = $V; } } my %PciClassType = ( "01" => "storage", "02" => "network", "03" => "graphics card", "04" => "multimedia", "04-00" => "video", "04-01" => "sound", "04-03" => "sound", "05" => "memory controller", "05-00" => "ram memory", "06" => "bridge", "07" => "communication controller", "07-03" => "modem", "08" => "system peripheral", "08-05" => "sd host controller", "09" => "input", "0a" => "docking station", "0b" => "processor", "0b-40" => "co-processor", "0c" => "serial bus controller", "0c-00" => "firewire controller", "0c-03" => "usb controller", "0c-02" => "ssa", "0c-05" => "smbus", "0c-06" => "infiniband", "0c-09" => "canbus", "0d" => "wireless controller", "0d-00" => "irda", "0d-11" => "bluetooth", "0e" => "intelligent controller", "0f" => "communications controller", "10" => "encryption controller", "11" => "signal processing", "12" => "processing accelerators" ); my %UsbClassType = ( "01" => "audio", "02" => "network", "02-02" => "modem", "03" => "human interface", "03-01-01" => "keyboard", "03-01-02" => "mouse", "05" => "physical interface", "06" => "imaging", "07" => "printer", "08" => "storage", "08-06-50" => "disk", "09" => "hub", "0a" => "cdc data", "0b" => "smartcard", "0d" => "content security", "0e" => "video", "dc" => "diagnostic", "e0" => "wireless", "e0-01-01" => "bluetooth", "ef" => "miscellaneous", "58" => "xbox" ); my %BatType = ( "lithium-ion" => "Li-ion", "LiIon" => "Li-ion", "lithium-polymer" => "Li-poly" ); # We are not going to update it anymore # This is resolved at server side my @WrongAddr = ( # MAC/clientHash(MAC) "00-00-00-00-00-00", "9B615E889BC3EDDF63600C8DAA6D56CC", "FF-FF-FF-FF-FF-FF", "2F847FFB96ED2B0B7C2AB39815DC6545", "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00", "47D6D280AB9F429EB219C6991590CEBE", "DEFAULT", "ACA66517F781B3D0DC573F9992EC6E44", # Huawei modem "0C-5B-8F-27-9A-64", "F8AFE52EC893B5F610764246CE0EC5DD", # Qualcomm Atheros AR8151 "00-20-07-01-16-06", "2698F3BD50B6E7317C050EABCBFCDD61", # Realtek RTL8111/8168/8411 "00-0B-0E-0F-00-ED", "B65E4A84BDF8C8FAF775D824E93895E5", "ED-0B-00-00-E0-00", "C8725A03752162516AC1D2736D4BCA7D", "16A64DBFF00A86E93CBF2DBED01DB771", # Realtek RTL8169 "4A0B520A3AE049F53532F8A53170BD2B", # NVIDIA Ethernet Controller "04-4B-80-80-80-03", "390043493F55307CC32EBD5A69443418", "04-4B-80-80-80-04", "5CEE6D893998E9F34E1452DFD0AD4127", "04-4B-80-80-80-F0", "3EEAB05124DE1FB83AD0BEAD31CE981E", # DM9601 Fast Ethernet Adapter "00-E0-4C-53-44-58", "68856DC22FD7A072F83ABA8EA9CC770F", # AR8162 Fast Ethernet vs RTL810xE PCIe Fast Ethernet "D51C765DB99A8E48472B495E83DE44B0", # MCP67 Ethernet vs MCP77 Ethernet "1E82FF14DD3C1B43B1A8D94630C90260", # MCP51 Ethernet vs MCP55 Ethernet "385BFD77E97DC0FD5A18671518FF4251", # RTL-8100/8101L/8139 PCI Fast Ethernet vs AR8152 v2.0 Fast Ethernet "9F797A8831BF6EF57154EE9647731DFC", # ZTE Mobile Broadband Station vs ZXIC Mobile Boardband "D8CE7A717259ADA5053B10C1F7705ECD", # Huawei E398 LTE/UMTS/GSM Modem "58-2C-80-13-92-63", "7D2C0D14714C1351D47DDB71E5A5ED41", "00-1E-10-1F-00-00", "07DECBE266CF024E4BC6DA9960DECDD4", # Qualcomm Atheros Attansic L2 Fast Ethernet "00-13-74-00-5C-38", "9DD557B66D30EAD5D6175340584CB612", # Yota LU150 "00-09-3B-F0-1A-40", "F6E8320D9A80AEE615D4CFA2A7CF40BD", # ME936 LTE/HSDPA+ 4G modem "02-2C-80-13-92-63", "8D0D443BD07047D7664DDD7A7385642A", # Migrated devices "0B4855DFCBE7B2B60B64315E21AC59B8", "3A1ED114C0B16F7FDDA2430FBABC1D82", "86AE125EBD97E64A59E25B250F7B36DE", "00BFE151A76E569ADB46E0DD338B8656", "8AFE7BDBD8B60C9645EDA141A9757E0A", "2DDCA0957AD7C256C77DFC231D80491B", "BBF288DD430B105563756C4194B5142B", # Others "00-DD-00-00-00-00", "631A71585F7CE74AE0C6E575DD1F4B31", "88-88-88-88-87-88", "FD0368E31788DE08AEC3C0F414D65552", "00-00-00-00-00-05", "4291656957E4CF9952D94E3DEF386CBF", "00-FF-00-00-00-00", "779F2E940C240A44289BB71F86A99BE5", "00-00-00-00-00-30", "6A34F992175D0D2ACD794FB107791EBF", "00-00-00-00-00-10", "CB29E07B8A25732D808E4DF3B26718E2", "00-13-74-00-00-00", "E5A433E40C7D5C05E1F82A0C86983656", "00-11-22-33-44-55", "FCE26206D805FEA1EB06C7210F054356", "66-77-44-22-33-11", "87880BCC6946BC2190412EA03A6E9B37", "00-00-00-00-00-03", "C184F6B8763E7AE4985EF4E3AAAD9B32", "66-77-44-22-33-10", "356D3DDEDB928F49B5FFAEBF18BADC65", "00-0B-E0-F0-00-ED", "36E19104CCF3BF32183B47AF9B00FA68", "00-E0-12-34-56-78", "B79FC64470AF23CD8893C1A85520D5A9" ); my @ProtectedLogs = ( "acpidump", "acpidump_decoded", "arcconf", "biosdecode", "cpuid", "dev", "dmidecode", "dmi_id", "drm_info", "edac-util", "edid", "ethtool_p", "fdisk", "glxinfo", "hciconfig", "hdparm", "hwinfo", "ifconfig", "ip_addr", "lsb_release", "lsb-release", "lsblk", "lscpu", "lsmod", "lspci", "lspci_all", "lsusb", "mmcli", "opensc-tool", "os-release", "power_supply", "sensors", "smartctl", "smartctl_megaraid", "smartctl_hpsa", "system-release", "upower", "usb-devices", "xrandr", "xrandr_providers", # *BSD "apm", "atactl", "camcontrol", "devinfo", "diskinfo", "geom", "gpart", "gpart_list", "hwstat", "kldstat", "mfiutil", "modstat", "neofetch", "ofwdump", "pciconf", "pcictl", "pcictl_n", "pcidump", "sysinfo", "usbconfig", "usbctl", "usbdevs", "x86info", # Android "getprop" ); my @ProtectFromRm = ( "dmesg", "dmesg.1" ); my %BusOrder = ( "NVME"=>"M", "IDE"=>"L", "SERIAL"=>"K", "SDIO"=>"J", "SCSI"=>"I", "PCMCIA"=>"H", "PARALLEL"=>"G", "PS/2"=>"F", "INT"=>"E", "SYS"=>"D", "EISA"=>"C", "USB"=>"B", "PCI"=>"A" ); my %TypeOrder = ( "storage"=>"D", "network"=>"C", "sound"=>"B", "graphics card"=>"A" ); my $ALL_CDROM_VENDORS = "AOPEN|ASUS|ASUSTek Computer|ATAPI|BENQ|CDEmu|COMBO|Compaq|Hewlett-Packard|Hitachi|HL-DT-ST|HP|JLMS|LG|Lite-On|LITEON|MAD DOG|MATSHITA|Memorex|MITSUMI|NEC Computers|Optiarc|PBDS|PHILIPS|PIONEER|PLDS|PLEXTOR|QSI|Samsung Electronics|Slimtype|Sony|TEAC|Toshiba|TSSTcorp|ZTE"; my $ALL_VENDORS = "Brother|Canon|Epson|HP|Hewlett\-Packard|Kyocera|Samsung|Xerox"; my $ALL_MON_VENDORS = "Acer|ADI|AGO|ALP|Ancor Communications Inc|AOC|Apple|Arnos Instruments|AU Optronics Corporation|AUS|BBY|BEK|BenQ|BOE Technology Group Co\., Ltd|Chi Mei Optoelectronics corp\.|CHI|CIS|CMN|CNC|COMPAL|COMPAQ|cPATH|CRO|CVTE|DELL|DENON, Ltd\.|Eizo|ELO|EQD|FNI|FUS|Gateway|GRUNDIG|HannStar Display Corp|HII|Hisense|HKC|HP|HPN|IBM|Idek Iiyama|ITR INFOTRONIC|IQT|KOA|Lenovo Group Limited|LGD|LG Electronics|LPL|Maxdata\/Belinea|MEB|Medion|Microstep|MS_ Nvidia|MSH|MST|MStar|NEC|NEX|Nvidia|OEM|ONKYO Corporation|Panasonic|Philips|Pioneer Electronic Corporation|PLN|Princeton Graphics|PRI|PKB|Samsung|Sangyo|Sceptre|SDC|Seiko\/Epson|SEK|SHARP|SONY|STN|TAR|Targa|Tech Concepts|TOSHIBA|Toshiba Matsushita Display Technology Co\., Ltd|UMC|Vestel|ViewSonic|VIZ|Wacom Tech|WDT"; my @KNOWN_BSD = ("clonos", "desktopbsd", "dragonfly", "freenas", "fuguita", "furybsd", "ghostbsd", "hardenedbsd", "hellosystem", "libertybsd", "midnightbsd", "nomadbsd", "opnsense", "os108", "pcbsd", "pfsense", "truenas", "trueos", "xigmanas", "arisblu"); my $KNOWN_BSD_ALL = join("|", @KNOWN_BSD); my $USE_DIGEST = 0; my $USE_DIGEST_ALT = "sha512sum"; my $USE_DUMPER = 0; my $USE_JSON_XS = 0; my $USE_IA = 0; my $HASH_LEN_CLIENT = 32; my $UUID_LEN_CLIENT = 32; my $SALT_CLIENT = "GN-4w?T]>r3FS/*_"; my $MAX_LOG_SIZE = 1048576; # 1Mb my @LARGE_LOGS = ("xorg.log", "xorg.log.1", "dmesg", "dmesg.1", "boot.log"); my $EMPTY_LOG_SIZE = 150; sub getSha512L($$) { my ($String, $Len) = @_; my $Hash = undef; if($USE_DIGEST) { $Hash = Digest::SHA::sha512_hex($String); } else { # No module installed $Hash = qx/echo -n \'$String\' | $USE_DIGEST_ALT 2>&1/; if($Hash=~/([\da-f]{128})/) { $Hash = $1; } else { return $String; } } return substr($Hash, 0, $Len); } sub clientHash(@) { my $Subj = shift(@_); my $Len = $HASH_LEN_CLIENT; if(@_) { $Len = shift(@_); } if(length($Subj)==$Len and $Subj=~/\A[\dA-F]+\Z/) { # Hash return $Subj; } return uc(getSha512L($Subj."+".$SALT_CLIENT, $Len)); } sub encryptSerialsInPaths($) { my $Content = $_[0]; my %DiskSer = (); while($Content=~/((\/|^)(ata|nvme|scsi)-[^\s]*_)(.+?)(\-part|[\s\n,])/mg) { $DiskSer{$4} = 1; } foreach my $Ser (sort keys(%DiskSer)) { my $Enc = clientHash($Ser); if($Enc eq $Ser) { next; } $Content=~s/_\Q$Ser\E\b/_$Enc/g; # /dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_XXXXXXXXXXXXXXX } return $Content; } sub encryptSerials(@) { my $Content = shift(@_); my $Tag = shift(@_); my $Name = undef; if(@_) { $Name = shift(@_); } my $Lower = undef; if(@_) { $Lower = shift(@_); } my %Serials = (); while($Content=~/\Q$Tag\E\s*[:=]\s*"?([^"]+?)"?\s*\n/g) { $Serials{$1} = 1; } foreach my $Ser (sort keys(%Serials)) { if(grep {$Ser eq $_} ("Not Specified", "To Be Filled By O.E.M.", "No Asset Information", "None", "Not Available")) { next; } my $Enc = undef; if($Lower) { $Enc = clientHash(lc($Ser)); } else { $Enc = clientHash($Ser); } if(index($Ser, ":")!=-1 and index($Ser, ".")!=-1) { # 0000:00:1a.0 if($Name and grep { $Name eq $_ } ("hwinfo", "usb-devices")) { $Enc = "..."; } else { next; } } if($Enc eq $Ser) { next; } $Content=~s/(\Q$Tag\E\s*[:=]\s*"?)\Q$Ser\E("?\s*\n)/$1$Enc$2/g; if($Name and $Name eq "hwinfo") { $Content=~s/_\Q$Ser\E\b/_$Enc/g; # /dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_XXXXXXXXXXXXXXX } } return $Content; } sub encryptUUIDs(@) { my $Content = shift(@_); my $Hash = undef; if(@_) { $Hash = shift(@_); } my %UUIDs = (); while($Content=~/([Xa-f\d]{8}-[Xa-f\d]{4}-[Xa-f\d]{4}-[Xa-f\d]{4}-[Xa-f\d]{12}|[a-zA-Z\d]{6}-[a-zA-Z\d]{4}-[a-zA-Z\d]{4}-[a-zA-Z\d]{4}-[a-zA-Z\d]{4}-[a-zA-Z\d]{4}-[a-zA-Z\d]{6}|[a-zA-Z\d]{64})/gi) { $UUIDs{$1} = 1; } foreach my $UUID (sort keys(%UUIDs)) { my $Enc = clientHash(lc($UUID), $UUID_LEN_CLIENT); $Enc = strToUUID($Enc); if($Hash) { $Enc=~s/\A([A-F\d]{5})/HASH_/g; } $Content=~s/\Q$UUID\E/$Enc/g; } %UUIDs = (); while($Content=~/[ \/]([a-fA-F\d]{4}-[a-fA-F\d]{4})\s/g) { $UUIDs{$1} = 1; } foreach my $UUID (sort keys(%UUIDs)) { my $Enc = clientHash(lc($UUID), $UUID_LEN_CLIENT); $Enc=~s/\A(\w{4})(\w{4}).+\Z/$1-$2/; $Content=~s/\Q$UUID\E/$Enc/g; } return $Content; } sub strToUUID($) { my $Str = $_[0]; $Str=~s/\A(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})\Z/$1-$2-$3-$4-$5/; return $Str; } sub hideDevDiskUUIDs($) { return hideByRegexp($_[0], qr/([a-f\d]{8}\Q\x2d\E[a-f\d]{4}\Q\x2d\E[a-f\d]{4}\Q\x2d\E[a-f\d]{4}\Q\x2d\E[a-f\d]{12})/); } sub encryptWWNs($) { my $Content = $_[0]; my %WWNs = (); while($Content=~/\/wwn-0x(.+?)\W/g) { $WWNs{$1} = 1; } foreach my $WWN (sort keys(%WWNs)) { my $Enc = clientHash($WWN); $Content=~s/(wwn-0x)\Q$WWN\E/$1$Enc/g; # wwn-0xXXXXXXXXXXXXXXXX } return $Content; } sub hideWWNs($) { my $Content = $_[0]; $Content=~s/(LU WWN Device Id:\s*\w \w{6} )\w+(\n|\Z)/$1...$2/; $Content=~s/(IEEE EUI-64:\s*\w{6}\s)\w+(\n|\Z)/$1...$2/; return $Content; } sub hideTags($$) { my ($Content, $Tags) = @_; $Content=~s/(($Tags)\s*[:=]\s*).*?(\n|\Z)/$1...$3/g; return $Content; } sub hideAAC($) { my $Content = $_[0]; $Content=~s/(AAC\d+: serial ).+?(\n|\Z)/$1...$2/g; return $Content; } sub hideAudit($) { my $Content = $_[0]; $Content=~s/(acct\=)"[^"]*"/$1=XXX/g; $Content=~s/(hostname\=)[^\s]+ /$1... /g; return $Content; } sub hideEmail($) { my $Content = $_[0]; $Content=~s/([<\(])[^()<>\s]+\@[^()<>\s]+([\)>])/$1\XXX\@\XXX$2/g; $Content=~s/([\s\(\<\'\=]| at | to )[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,}([\:\s\)\>]|\.\n)/$1\XXX\@\XXX$2/g; $Content=~s/ [\w\.\-]+\@[\w\-]+:/ \XXX\@\XXX:/g; # BSD return $Content; } sub hideDmesg(@) { my $Content = shift(@_); my $Hash = undef; if(@_) { $Hash = shift(@_); } if($Opt{"ShowDmesg"}) { $Content=~s/.+\] (audit|kauditd_printk_skb): .+(\n|\Z)//g; $Content=~s/.+ addr=\? terminal=\?.+(\n|\Z)//g; } $Content = hideTags($Content, "SerialNumber"); $Content = hideHostname($Content); $Content = hideIPs($Content); $Content = encryptMACs($Content); $Content = hidePaths($Content); $Content = hideAAC($Content); $Content = encryptUUIDs($Content, $Hash); $Content = hideAudit($Content); $Content = hideEmail($Content); $Content=~s/((SerialNumber|Ethernet address)\s*[:=]\s*).+/$1.../g; $Content=~s/(Serial Number).+/$1.../g; $Content=~s/(\s+s\/n\s+)[^\s]+/$1.../g; $Content=~s/(removable serial\.).+/$1.../g; $Content=~s/((serial|serial_number|serialno)\s*=\s*)[^\s]+/$1.../g; $Content=~s/( serial# ).+/$1.../g; $Content=~s/(- serial #).+/$1.../g; $Content=~s/( SN ).+?( MFG )/$1...$2/g; $Content=~s/( serial_number\().+(\))/$1...$2/g; $Content=~s/( Serial\t\t: )[^\s]+(\n|\Z)/$1...$2/g; $Content=~s/( serial number is | serial number: ).+/$1.../g; $Content=~s/((Autodisabling the use of server inode numbers on|CIFS: Attempting to mount)\s*).+/$1.../g; $Content=~s/( ssid=).+/$1.../g; $Content=~s/( set ssid ).+/$1.../g; $Content=~s/(\[assoc_ssid:).+?(\])/$1...$2/g; $Content=~s/( candidate:\s*)[^(\n]+/$1.../g; $Content=~s/( of user\s*).+/$1.../g; $Content=~s/[^\'\s]+(\s*Secure Boot Module Signature key:\s*)[^\'\s]+/...$1.../g; $Content=~s/(unit=[\w\-]+\@(dev-mapper|media)-)[^\s]+/$1.../g; $Content=~s/(rd.lvm.lv=)[^\s]+/$1.../g; $Content=~s/(Cryptography Setup for\s+)[^\s]+/$1.../g; $Content=~s/(BTRFS: device label\s+).+?(\s+devid)/$1...$2/g; $Content=~s/(volume group\s+)"[^\s\"]+?"/$1"..."/g; $Content=~s/(process) '[^\/].+?' (started with executable stack)/$1 '...' $2/g; $Content=~s/(Bluetooth: hci\d+: )[\w\-\.]+(\n|\Z)/$1...$2/g; $Content=~s/(dracut: Found )\/.+( on)/$1...$2/g; $Content=~s/(Invalid section header).+/$1 '...'/g; $Content=~s/(: dev-mapper-)[^\.:\n]+?\.(\w+:)/$1...$2/g; $Content=~s/( input: ).+?( \(AVRCP\))/$1...$2/g; $Content=~s/(property_set\(".+\.serialno", ").+?("\))/$1...$2/g; $Content=~s/(PropSet Error:\[.+\.serialno:).+?(\])/$1...$2/g; $Content=~s/(snd[-_](ca0106|emu10k1x).+? Serial ).+/$1.../g; $Content=~s/(CIFS:? VFS: \\\\)[^\s]+?( )/$1...$2/g; $Content=~s/(Mounting volume \').+?(\')/$1...$2/g; $Content=~s/(BAD_NETWORK_NAME: ).+/$1.../g; $Content=~s/(Set up automount ).+/$1.../g; $Content=~s/(\/run\/systemd\/generator\/(dev-mapper|media)-)[^\]\s\n,]+/$1.../g; $Content=~s/( server .+? in cell ).+?( )/$1...$2/g; $Content=~s/( for cell ).+( have expired )/$1...$2/g; $Content=~s/(dev-disk-by\\x2d(partuuid-|uuid-|id-wwn))[xa-fA-F\d\\]{12,48}(part\d+|)(\.)/$1...$4/g; $Content=~s/(dev-disk-by\\x2did-(ata|usb|nvme).+?_)[^_]+?(\\x2d.+?\.)/$1...$3/g; $Content=~s/( Checking was requested for )"[^\n]+?( but )/$1...$2/g; $Content=~s/(systemd-fstab-generator\[\d+\]: ).+/$1.../g; $Content=~s/(\/user-)\d+(\.)/$1XXXX$2/g; $Content=~s/(failed to look up) (.+?) (on)/$1 XXX $3/g; if(isBSD() or $Opt{"ForBSD"}) { $Content=~s/(> serial\.).+/$1.../g; $Content=~s/( serial ).+?( type )/$1...$2/g; $Content=~s/(nvme\d: .+, serial ).+/$1.../g; $Content=~s/(USBMIC Serial )\w+/$1.../g; $Content=~s/(uhidpp\d .+ serial ).+/$1.../g; } return $Content; } sub hideHostname($) { my $Content = $_[0]; $Content=~s/(Set hostname to\s+).+/$1.../g; $Content=~s/(Hostname set to\s+).+/$1.../g; return $Content; } sub hideHost($) { my $Content = $_[0]; $Content=~s/(Current Operating System:\s+[^\s]+)\s+[^\s]+/$1 NODE/g; return $Content; } sub hidePaths($) { my $Content = $_[0]; my @Paths = ("storage", "mount", "home", "media", "data", "shares", "vhosts", "mapper", "photos", "snap", "shm", "srv/nfs", "dev/serno", "serno", "exports", "usr/obj", "mnt", "run/user"); if(isBSD()) { push(@Paths, "diskid", "ufsid"); } foreach my $Dir (@Paths) { $Content = hideByRegexp($Content, qr/\Q$Dir\E\/([^\s'")<\\]+)/); } return $Content; } sub hideLVM($) { my $Content = $_[0]; $Content = hideByRegexp($Content, qr/vg_(.+?)\b/); $Content = hideByRegexp($Content, qr/([^\s]+)--vg-[^\s]+/); return $Content; } sub hideIPs($) { my $Content = $_[0]; # IPv4 $Content=~s/\d+\.\d+\.\d+\.\d+/XXX.XXX.XXX.XXX/g; $Content=~s/(XXX\.XXX\.XXX\.XXX):\d+/$1:XXX/g; # IPv6 $Content=~s/[\da-f]+\:\:[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+/XXXX::XXX:XXX:XXX:XXX/gi; $Content=~s/[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+/XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/gi; $Content=~s/[\da-f]+\:[\da-f]+\:[\da-f]+\:[\da-f]+::[\da-f]+/XXX:XXX:XXX:XXX::XXX/gi; $Content=~s/[\da-f]+\:[\da-f]+\:[\da-f]+::[\da-f]+/XXX:XXX:XXX::XXX/gi; return $Content; } sub hideUrls($) { my $Content = $_[0]; $Content=~s{[\w\-]+\.[\w\.\-]+\:\/[^\s]+}{XXX:/XXX}g; $Content=~s{(\w+\:\/+)[^\s]+}{$1\XXX}g; return $Content; } sub hideDf($) { my $Content = $_[0]; my $NewDf = ""; my @DfLines = split(/\n/, $Content); my $BSD = isBSD(); for (my $i = 0; $i <= $#DfLines; $i++) { my $L = $DfLines[$i]; if($i==0) { $NewDf .= $L."\n"; next; } my $HideLine = undef; if($BSD) { $HideLine = ($L!~/\A(Filesystem||build|cgroup|cgroup_root|clr_debug_fuse|dev|devfs|devtmpfs|fdescfs|freenas\-boot|\/(dev|home|run)|kernfs|linprocfs|map|none|overlay|procfs|ptyfs|run|serno|shm|tank|tmpfs|udev|zroot|\s+)/); } else { $HideLine = ($L!~/\A(Filesystem|cgroup|cgroup_root|clr_debug_fuse|dev|devtmpfs|\/(dev|home|run)|none|overlay|run|shm|tmpfs|udev|\s+)/); } if($HideLine and $L!~/\s\/\Z/) { $L = hideByRegexp($L, qr/\A([^\s]+)/); $L = hideByRegexp($L, qr/([^\s]+)\Z/); } $NewDf .= $L."\n"; } $Content = $NewDf; return $Content; } sub hidePass($) { my $Content = $_[0]; $Content=~s/.*password.*/XXXXXXXXXX/gi; return $Content; } sub hideMACs($) { my $Content = $_[0]; $Content=~s/[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}/XX:XX:XX:XX:XX:XX/gi; return $Content; } sub encryptMACs($) { my $Content = $_[0]; my @MACs = ($Content=~/[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}\:[\da-f]{2}/gi); if(isBSD()) { my @FwIp = ($Content=~/[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+\.[\da-f]+/gi); if(@FwIp) { push(@MACs, @FwIp); } } foreach my $MAC (@MACs) { my $Enc = lc($MAC); $Enc=~s/\:/-/g; $Enc = clientHash($Enc); $Content=~s/\Q$MAC\E/$Enc/gi; } return $Content; } sub hideByRegexp(@) { my ($Content, $Regexp) = @_; my $Subj = undef; if(@_) { $Subj = shift(@_); } my @Matches = ($Content=~/$Regexp/gi); my @Skip = ("apex", "bin/", "boot", "cdrom", "data", "live", "livecd", "live-rw", "tmpfs", "control", "shared", "start", "system", "swap", "utab", "vstorage"); foreach my $Match (sort {length($b)<=>length($a)} @Matches) { if(grep {$Match eq $_} @Skip) { next; } if(length($Match) <= 2) { next; } $Content = hideStr($Content, $Match); if($Subj and $Subj eq "systemd") { if($Match=~s/\x2d/-/g) { $Content = hideStr($Content, $Match); } } } return $Content; } sub hideStr($$) { my ($Content, $Str) = @_; my $Hide = "X" x length($Str); $Content=~s/\Q$Str\E/$Hide/g; return $Content; } sub decorateSystemd($) { my $Content = $_[0]; $Content = hideByRegexp($Content, qr/systemd-cryptsetup@(.+?)\.service/, "systemd"); $Content=~s/(\@)\d+(\.)/$1XXXX$2/g; return $Content; } sub exitStatus($) { my $St = $_[0]; if($Opt{"Flatpak"} and $TMP_DIR and -d $TMP_DIR) { rmtree($TMP_DIR); } if($TMP_PROBE_DIR and -d $TMP_PROBE_DIR) { rmtree($TMP_PROBE_DIR); } if($TMP_LOCAL and -d $TMP_LOCAL) { rmtree($TMP_LOCAL); } if($LATEST_DIR and not listDir($LATEST_DIR)) { rmtree($LATEST_DIR); } exit($St); } sub printMsg($$) { my ($Type, $Msg) = @_; if($Type!~/\AINFO/) { $Msg = $Type.": ".$Msg; } if($Type!~/_C\Z/) { $Msg .= "\n"; } if(grep { $Type eq $_ } ("ERROR", "WARNING")) { print STDERR $Msg; } else { print $Msg; } } sub checkModule(@) { my $Name = shift(@_); my $Local = 0; if(@_) { $Local = 1; } my @Dirs = @INC; if($Local) { push(@Dirs, "."); } foreach my $P (@Dirs) { my $Path = "$P/$Name"; if(-e $Path) { return $Path; } } return undef; } sub runCmd($) { my $Cmd = $_[0]; if($Opt{"ListProbes"}) { print "Executing: ".$Cmd."\n"; } my $AddPath = ""; if(isNetBSD()) { $AddPath = "PATH=\$PATH:/usr/sbin:/sbin:/usr/pkg/sbin "; } return `LC_ALL=$LOCALE $AddPath$Cmd`; } sub generateGroup() { my $GroupURL = $URL."/get_group.php"; my $Log = ""; if(checkCmd("curl")) { my $CurlCmd = "curl -s -S -f -POST -F get=group -F email=".$Opt{"Email"}." -F tool_ver=\'$TOOL_VERSION\' -H \"Expect:\" $GroupURL"; # --http1.0 $Log = qx/$CurlCmd 2>&1/; } else { $Log = postRequest($GroupURL, { "get"=>"group", "email"=>$Opt{"Email"}, "tool_ver"=>$TOOL_VERSION }, "NoSSL"); } print $Log; if($?) { my $ECode = $?>>8; printMsg("ERROR", "failed to generate inventory id (group id), curl error code \"".$ECode."\""); exitStatus(1); } if($Log=~/(Group|Inventory) ID: (\w+)/) { my $ID = $2; my $GroupLog = "INVENTORY\n=========\n".localtime(time)."\nInventory ID: $ID\n"; if(-w $PROBE_LOG) { appendFile($PROBE_LOG, $GroupLog."\n"); } } } sub remindGroup() { my $GroupURL = $URL."/remind_group.php"; my $Log = ""; printMsg("INFO", "Please wait..."); if(checkCmd("curl")) { my $CurlCmd = "curl -s -S -f -POST -F hwaddr=".$Sys{"HWaddr"}." -H \"Expect:\" $GroupURL"; # --http1.0 $Log = qx/$CurlCmd 2>&1/; } else { $Log = postRequest($GroupURL, { "hwaddr"=>$Sys{"HWaddr"} }, "NoSSL"); } print $Log; if($?) { my $ECode = $?>>8; printMsg("ERROR", "failed to remind inventory id, curl error code \"".$ECode."\""); exitStatus(1); } } sub postRequest($$$) { my ($UploadURL, $Data, $SSL) = @_; require LWP::UserAgent; my $UAgent = LWP::UserAgent->new(parse_head => 0); if($Opt{"Proxy"}) { $UAgent->proxy([ [ 'http', 'https' ] => "http://".$Opt{"Proxy"} ]); } if($SSL eq "NoSSL" or not checkModule("Mozilla/CA.pm")) { $UploadURL=~s/\Ahttps:/http:/g; $UAgent->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00); } my $Res = $UAgent->post( $UploadURL, Content_Type => "form-data", Content => $Data ); my $Out = $Res->{"_content"}; if(not $Out) { return $Res->{"_headers"}{"x-died"}; } return $Out; } sub getFallbackUrl() { my ($Zone, $FallbackURL) = (); my %ReservedLocalMirror = ( # Reserved local BSDHW database zone "BSD" => undef, # Reserved local LHW database zone "Linux" => "ru" ); if(isBSD()) { if($Zone = $ReservedLocalMirror{"BSD"}) { $FallbackURL = $URL_BSD; } } else { if($Zone = $ReservedLocalMirror{"Linux"}) { $FallbackURL = $URL_LINUX; } } if($Zone and $FallbackURL) { $FallbackURL=~s{\w+\Z}{$Zone}; } return $FallbackURL; } sub saveProbe($) { my $To = $_[0]; $To=~s{/+\Z}{}; my ($Pkg, $HWaddr) = createPackage(); if(not $Pkg) { return; } move($Pkg, $To); print "Saved to: $To/".basename($Pkg)."\n"; } sub uploadData() { my ($Pkg, $HWaddr) = createPackage(); if(not $Pkg) { return; } my $UploadURL = $URL."/upload_result.php"; my $Salt = getSha512L($SALT_CLIENT, 10); # upload package my @Cmd = ("curl", "-s", "-S", "-f", "-POST"); my %Data = (); @Cmd = (@Cmd, "-F file=\@".$Pkg); $Data{"file"} = [$Pkg]; @Cmd = (@Cmd, "-F hwaddr=$HWaddr"); $Data{"hwaddr"} = $HWaddr; if($Opt{"Debug"}) { @Cmd = (@Cmd, "-F debug=1"); $Data{"debug"} = "1"; } if($Opt{"Docker"}) { @Cmd = (@Cmd, "-F docker=1"); $Data{"docker"} = "1"; } if($Opt{"AppImage"}) { @Cmd = (@Cmd, "-F appimage=1"); $Data{"appimage"} = "1"; } if($Opt{"Snap"}) { @Cmd = (@Cmd, "-F snap=1"); $Data{"snap"} = "1"; } if($Opt{"Flatpak"}) { @Cmd = (@Cmd, "-F flatpak=1"); $Data{"flatpak"} = "1"; } if($Opt{"FromGUI"}) { @Cmd = (@Cmd, "-F from_gui"); $Data{"from_gui"} = "1"; } if($Opt{"PC_Name"}) { @Cmd = (@Cmd, "-F id='".$Opt{"PC_Name"}."'"); $Data{"id"} = $Opt{"PC_Name"}; } if($Opt{"Group"}) { @Cmd = (@Cmd, "-F group='".$Opt{"Group"}."'"); $Data{"group"} = $Opt{"Group"}; } if($Opt{"Monitoring"}) { @Cmd = (@Cmd, "-F monitoring=1"); $Data{"monitoring"} = "1"; } if(isBSD()) { @Cmd = (@Cmd, "-F bsd=1"); $Data{"bsd"} = "1"; } @Cmd = (@Cmd, "-F tool_ver=\'$TOOL_VERSION\'"); $Data{"tool_ver"} = $TOOL_VERSION; @Cmd = (@Cmd, "-F tool_rel=\'$TOOL_RELEASE\'"); $Data{"tool_rel"} = $TOOL_RELEASE; @Cmd = (@Cmd, "-F salt=\'$Salt\'"); $Data{"salt"} = $Salt; # fix curl error 22: "The requested URL returned error: 417 Expectation Failed" @Cmd = (@Cmd, "-H", "Expect:"); # @Cmd = (@Cmd, "--http1.0"); @Cmd = (@Cmd, $UploadURL); my $CurlCmd = join(" ", @Cmd); my ($Log, $WWWLog, $HttpsErr, $Err) = (); if(checkCmd("curl")) { $Log = runCmd("$CurlCmd 2>&1"); $Err = $?; if($Err and $Log=~/certificate|ssl/i and $CurlCmd=~s/https/http/) { $Log = runCmd("$CurlCmd 2>&1"); $Err = $?; $HttpsErr = 1; } } else { $Err = 1; } if($Err) { if($Opt{"ListProbes"}) { printMsg("ERROR", "failed to upload by curl"); } if(isNetBSD()) { if(not -e "/etc/openssl/certs" or not listDir("/etc/openssl/certs")) { printMsg("ERROR", "'mozilla-rootcerts-openssl' package is not installed"); } } if($WWWLog = postRequest($UploadURL, \%Data, "NoSSL")) { if(index($WWWLog, "probe=") != -1) { $Err = 0; $Log = $WWWLog; } else { if(index($WWWLog, "Can't locate HTML/HeadParser.pm") != -1) { printMsg("ERROR", "failed to upload data"); printMsg("ERROR", "please add 'libhtml-parser-perl' or 'perl-HTML-Parser' package to your system"); exitStatus(1); } } } } if(my $FallbackURL = getFallbackUrl()) { my $RetryDelay = 1; if($Err and $CurlCmd=~s/\Q$URL\E/$FallbackURL/) { sleep($RetryDelay); $Log = runCmd("$CurlCmd 2>&1"); $Err = $?; if($Err and $Log=~/certificate|ssl/i and $CurlCmd=~s/https/http/) { $Log = runCmd("$CurlCmd 2>&1"); $Err = $?; $HttpsErr = 1; } } if($Err and $UploadURL=~s/\Q$URL\E/$FallbackURL/) { sleep($RetryDelay); if($WWWLog = postRequest($UploadURL, \%Data, "NoSSL")) { if(index($WWWLog, "probe=") != -1) { $Err = 0; $Log = $WWWLog; } } } } if($Err) { if($WWWLog) { print STDERR $WWWLog."\n"; printMsg("ERROR", "failed to upload data"); } else { my $ECode = $Err>>8; print STDERR $Log."\n"; printMsg("ERROR", "failed to upload data, curl error code \"".$ECode."\""); } exitStatus(1); } if($HttpsErr) { $Log=~s/https/http/; $URL=~s/https/http/; } $Log=~s/\s*Private access:\s*http.+?token\=(\w+)\s*/\n/; print $Log; $RecentProbe = undef; if($Log=~/probe\=(\w+)/) { $RecentProbe = $1; } # save uploaded probe and its ID if($RecentProbe) { if($Opt{"SaveUploaded"}) { my $NewProbe = $PROBE_DIR."/".$RecentProbe; if(-d $NewProbe) { printMsg("ERROR", "the probe with ID \'$RecentProbe\' already exists, overwriting ..."); unlink($NewProbe."/hw.info.txz"); } else { mkpath($NewProbe); } if($Opt{"Source"}) { copy($Pkg, $NewProbe); } else { move($Pkg, $NewProbe); } } my $Time = time; my $ProbeUrl = "$URL/?probe=$RecentProbe"; my $ProbeLog = "PROBE\n=====\nDate: ".localtime($Time)." ($Time)\n"; $ProbeLog .= "Probe URL: $ProbeUrl\n"; appendFile($PROBE_LOG, $ProbeLog."\n"); } } sub setupMonitoring() { my $MonitoringURL = $URL."/setup_monitoring.php"; my @Cmd = ("curl", "-s", "-S", "-f", "-POST"); my %Data = (); my $Enable = undef; my $Status = undef; if($Opt{"StartMonitoring"}) { $Enable = 1; $Status = "Enabled"; } elsif($Opt{"StopMonitoring"}) { $Enable = 0; $Status = "Disabled"; } @Cmd = (@Cmd, "-F group=".$Opt{"Group"}); $Data{"group"} = $Opt{"Group"}; @Cmd = (@Cmd, "-F hwaddr=".$Sys{"HWaddr"}); $Data{"hwaddr"} = $Sys{"HWaddr"}; @Cmd = (@Cmd, "-F enable=$Enable"); $Data{"enable"} = $Enable; my $Salt = getSha512L($SALT_CLIENT, 10); @Cmd = (@Cmd, "-F salt=$Salt"); $Data{"salt"} = $Salt; if($RecentProbe) { @Cmd = (@Cmd, "-F init_probe=$RecentProbe"); $Data{"init_probe"} = $RecentProbe; } @Cmd = (@Cmd, "-H \"Expect:\""); @Cmd = (@Cmd, $MonitoringURL); my $Log = ""; if(checkCmd("curl")) { my $CurlCmd = join(" ", @Cmd); $Log = qx/$CurlCmd 2>&1/; } else { $Log = postRequest($MonitoringURL, \%Data, "NoSSL"); } print "\n"; print $Log; print "\n"; if($?) { my $ECode = $?>>8; printMsg("ERROR", "failed to setup monitoring, curl error code \"".$ECode."\""); exitStatus(1); } if($Enable) { my $StatusUrl = undef; if($Log=~/(Status URL: .+)/) { $StatusUrl = $1; } if($StatusUrl) { appendFile($PROBE_LOG, "MONITORING\n==========\n".localtime(time)."\n$Status\n$StatusUrl\n\n"); } else { printMsg("ERROR", "failed to setup monitoring"); exitStatus(1); } } else { if($Log=~/disabled/) { appendFile($PROBE_LOG, "MONITORING\n==========\n".localtime(time)."\n$Status\n\n"); } else { printMsg("ERROR", "failed to setup monitoring"); exitStatus(1); } } my $CronDaily = "/etc/cron.daily"; # add/remove cron entry if(-e $CronDaily) { my $CronScript = $CronDaily."/hw-probe"; if($Enable) { writeFile($CronScript, "#!/bin/bash\n\nhw-probe -all -check-hdd -check-cpu -upload -monitoring -i ".$Opt{"Group"}."\n"); } elsif(-e $CronScript) { unlink($CronScript); } } else { if($Enable) { my $CronTime = "0 0"; if(my $Time = getTimeStamp(time)) { if($Time=~/\A(\d+):(\d+)\Z/) { $CronTime = "$2 $1"; } } system("(EDITOR=cat crontab -e 2>/dev/null | grep -v 'hw-probe' ; echo \"$CronTime * * * hw-probe -all -check-hdd -check-cpu -upload -monitoring -i ".$Opt{"Group"}."\") | crontab -"); } else { system("EDITOR=cat crontab -e 2>/dev/null | grep -v 'hw-probe' | crontab -"); } } } sub cleanData() { if(-d $LATEST_DIR) { rmtree($LATEST_DIR); } } sub readHostAttr($$) { my ($Path, $Attr) = @_; if(readFile($Path."/host")=~/\Q$Attr\E\:([^\n]*)/) { return $1; } return ""; } sub createPackage() { my ($Pkg, $HWaddr) = (); if(not checkCmd("tar")) { printMsg("ERROR", "tar is not installed"); exitStatus(1); } if($Opt{"Source"}) { if(-f $Opt{"Source"}) { if(isPkg($Opt{"Source"})) { $Pkg = $Opt{"Source"}; system("tar", "--directory", $TMP_DIR, "-xf", $Pkg); if($?) { printMsg("ERROR", "failed to extract package (".$?.")"); exitStatus(1); } if(my @Dirs = listDir($TMP_DIR)) { my $Dir = $Dirs[0]; my $Chg = 0; if($Dir ne "hw.info") { # packaged by user move($TMP_DIR."/".$Dir, $TMP_DIR."/hw.info"); $Chg = 1; } if(updateHost($TMP_DIR."/hw.info", "id", $Opt{"PC_Name"})) { $Chg = 1; } $HWaddr = readHostAttr($TMP_DIR."/hw.info", "hwaddr"); if($Chg) { chdir($TMP_DIR); system("tar", "-cJf", "hw.info.txz", "hw.info"); chdir($ORIG_DIR); if($?) { printMsg("ERROR", "failed to create a package (".$?.")"); exitStatus(1); } $Pkg = $TMP_DIR."/hw.info.txz"; } } } else { printMsg("ERROR", "not a package"); exitStatus(1); } } elsif(-d $Opt{"Source"}) { copyFiles($Opt{"Source"}, $TMP_DIR."/hw.info"); updateHost($TMP_DIR."/hw.info", "id", $Opt{"PC_Name"}); $HWaddr = readHostAttr($TMP_DIR."/hw.info", "hwaddr"); chdir($TMP_DIR); system("tar", "-cJf", "hw.info.txz", "hw.info"); chdir($ORIG_DIR); if($?) { printMsg("ERROR", "failed to create a package (".$?.")"); exitStatus(1); } $Pkg = $TMP_DIR."/hw.info.txz"; } else { printMsg("ERROR", "can't access '".$Opt{"Source"}."'"); exitStatus(1); } } else { if(not -d $DATA_DIR) { if($Admin) { printMsg("ERROR", "can't access '".$DATA_DIR."', please make probe first"); } else { printMsg("ERROR", "can't access '".$DATA_DIR."', please run as root"); } exitStatus(1); } if(not -f "$DATA_DIR/devices" and not -f "$DATA_DIR/devices.json") { printMsg("ERROR", "\'$DATA_DIR/devices\' file is not found, please make probe first"); exitStatus(1); } updateHost($DATA_DIR, "id", $Opt{"PC_Name"}); $HWaddr = readHostAttr($DATA_DIR, "hwaddr"); chdir(dirname($DATA_DIR)); # if(isOpenBSD() or isNetBSD() # or (defined $Sys{"Freebsd_release"} and $Sys{"Freebsd_release"} < 7.3)) if(isBSD() or not checkCmd("xz")) { $Pkg = $TMP_DIR."/hw.info.tgz"; system("tar", "-czf", $Pkg, basename($DATA_DIR)); } else { $Pkg = $TMP_DIR."/hw.info.txz"; system("tar", "-cJf", $Pkg, basename($DATA_DIR)); } chdir($ORIG_DIR); } return ($Pkg, $HWaddr); } sub copyFiles($$) { my ($P1, $P2) = @_; mkpath($P2); foreach my $Top (listDir($P1)) { if(-d "$P1/$Top") { # copy subdirectory foreach my $Sub (listDir("$P1/$Top")) { if($Sub=~/~\Z/) { next; } mkpath("$P2/$Top"); copy("$P1/$Top/$Sub", "$P2/$Top/$Sub"); } } else { # copy file if($Top=~/~\Z/) { next; } copy("$P1/$Top", "$P2/$Top"); } } } sub isPkg($) { my $Path = $_[0]; return ($Path=~/\.(tar\.xz|txz|tar\.gz|tgz)\Z/ or `file "$Path"`=~/(XZ|gzip) compressed data/); } sub updateHost($$$) { my ($Path, $Attr, $Val) = @_; if($Val) { if(not -f "$Path/host") { # internal error return 0; } my $Content = readFile($Path."/host"); my $Chg = 0; if($Content!~/(\A|\n)$Attr:/) { if($Content!~/\n\Z/) { $Content .= "\n"; } $Content .= $Attr.":".$Val."\n"; $Chg = 1; } elsif($Content=~/(\A|\n)$Attr:(.*)/) { if($2 ne $Val) { $Content=~s/(\A|\n)$Attr:(.*)/$1$Attr:$Val/; $Chg = 1; } } if($Chg) { writeFile($Path."/host", $Content); print "Added \'$Attr\' to host info\n"; return 1; } } return 0; } sub fixCpuVendor($) { my $Vendor = $_[0]; foreach my $VV ("Intel", "AMD", "ARM") { if($Vendor=~/$VV/) { return $VV; } } if($Vendor=~/Advanced Micro Devices/) { return "AMD"; } if($Vendor=~/unknown/i) { return undef; } return $Vendor; } sub fmtVal($) { my $Val = $_[0]; if(not defined $Val or $Val eq "" or $Val=~/\A\s+\Z/) { return ""; } if($Val!~/[a-z0-9]/i) { # invalid return ""; } $Val=~s/\((R|TM)\)\-/-/gi; $Val=~s/\((R|TM)\)/ /gi; $Val=~s/\x{2122}|\x{AE}|\x{A9}//g; # TM (trade mark), R (registered), C (copyright) special symbols $Val=~s/\303\227/x/g; # multiplication sign $Val=~s/\x{A0}/ /g; # no-break space $Val=~s/\A[_\-\? ]//gi; $Val=~s/[_\-\? ]\Z//gi; $Val=~s/[ ]{2,}/ /g; return $Val; } sub bytesToHuman($) { my $Bytes = $_[0]; $Bytes /= 1000000; # MB if($Bytes>=1000) { $Bytes /= 1000; # GB $Bytes = roundToNearest($Bytes); if($Bytes>=1000) { $Bytes /= 1000; # TB $Bytes = roundToNearest($Bytes); return $Bytes."TB"; } return $Bytes."GB"; } else { $Bytes = roundToNearest($Bytes); } return $Bytes."MB"; } sub toGb($) { my $S = $_[0]; $S=~s/\,/\./; if($S=~/([\d\.]+)([TGM])/) { my ($Res, $Ent) = ($1, $2); if($Ent eq "M") { $Res /= 1000.0; } elsif($Ent eq "T") { $Res *= 1000.0; } return $Res; } return 0; } sub roundFloat($$) { my ($N, $S) = @_; $N = sprintf("%.".$S."f", $N); $N=~s/(\.\d)0\Z/$1/; $N=~s/\.0\Z//; return $N; } sub getPnpVendor($) { my $V = $_[0]; if(defined $MonVendor{$V}) { return $MonVendor{$V}; } # NOTE: this is not reliable # if(not keys(%PnpVendor)) { # readPnpIds(); # } # # if(defined $PnpVendor{$V}) # { # if($PnpVendor{$V}!~/do not use/i) { # return $PnpVendor{$V}; # } # } return $V; } sub readPnpIds() { my $Path = undef; if($Opt{"PnpIDs"}) { $Path = $Opt{"PnpIDs"}; } else { $Path = "/usr/share/hwdata/pnp.ids"; # ROSA Fresh, RELS } if(not -e $Path) { $Path = "/usr/share/misc/pnp.ids"; # ROSA Marathon } if(not -e $Path) { return; } foreach my $Line (split(/\n/, readFile($Path))) { if($Line=~/\A([A-Z]+)\s+(.*?)\Z/) { $PnpVendor{$1} = $2; } } } sub getPciVendor($) { my $V = $_[0]; if(defined $PciInfo{"V"}{$V}) { return $PciInfo{"V"}{$V}; } if(not keys(%{$PciInfo{"V"}})) { readVendorIds(); } if(defined $PciInfo{"V"}{$V}) { return $PciInfo{"V"}{$V}; } return; } sub readVendorIds() { my $Path = "/usr/share/hwdata/pci.ids"; if($Opt{"PciIDs"}) { $Path = $Opt{"PciIDs"}; } elsif(-e "ids/pci.ids") { $Path = "ids/pci.ids"; } if(not -e $Path) { return; } foreach my $Line (split(/\n/, readFile($Path))) { if($Line=~/\A(\w{4})\s+(.*?)\Z/) { $PciInfo{"V"}{$1} = $2; } } } sub getSdioType($) { my $Class = $_[0]; return $SdioType{$Class}; } sub getClassType($$) { my ($Bus, $Class) = @_; while($Class) { if($Bus eq "pci") { if(defined $PciClassType{$Class}) { return $PciClassType{$Class}; } } elsif($Bus eq "usb") { if(defined $UsbClassType{$Class}) { return $UsbClassType{$Class}; } } if($Class!~s/\-\w+?\Z//) { last; } } return ""; } sub fixRootHub($$$) { my ($V, $D, $Dev) = @_; if(grep {$V eq $_} ("8086", "0000") and $D eq "0000") { $V = "1d6b"; $Dev->{"Vendor"} = "BSD"; if($Dev->{"Device"}=~/UHCI|OHCI/i) { $D = "0001"; } elsif($Dev->{"Device"}=~/EHCI/i) { $D = "0002"; } elsif($Dev->{"Device"}=~/XHCI/i) { $D = "0003"; } } return ($V, $D); } sub getDefaultType($$$) { my ($Bus, $DId, $Device) = @_; foreach my $Name ($Device->{"Device"}, $Device->{"SDevice"}) { if(not $Name) { next; } if($Bus eq "usb") { if($Name=~/camera|webcam|web cam/i) { return "camera"; } elsif($Name=~/card reader/i) { return "card reader"; } elsif($Name=~/fingerprint (reader|scanner|sensor|device|coprocessor)|swipe sensor/i) { return "fingerprint reader"; } elsif($Name=~/smartcard/i) { return "smartcard reader"; } elsif($Name=~/USB Scanner|CanoScan|FlatbedScanner|Scanjet|EPSON Scanner/i) { return "scanner"; } elsif($Name=~/bluetooth/i) { return "bluetooth"; } elsif($Name=~/(\A| )(WLAN|NIC|11n Adapter)( |\Z)|Wireless.*Adapter|Wireless Network|Wireless LAN|WiMAX|WiFi|802\.11|Mobile Broadband|Ethernet/i) { return "network"; } elsif($Name=~/converter/i) { return "converter"; } elsif($Name=~/(\A| )UPS(\Z| )/) { return "ups"; } elsif($Name=~/TouchScreen/i) { return "touchscreen"; } elsif($Name=~/Touchpad|Touch Pad/i) { return "touchpad"; } elsif($Name=~/(\A| )Phone(\Z| )/i) { return "phone"; } elsif($Name=~/Gamepad/i) { return "gamepad"; } elsif($Name=~/DVB-T/) { return "dvb card"; } } elsif($Bus eq "pci") { if($Name=~/card reader/i) { return "card reader"; } elsif($Name=~/I\/O card/i) { return "i/o card"; } elsif($Name=~/PCI Bridge/i) { return "bridge"; } elsif($Name=~/(\A|\s)SD Host Controller/i) { return "sd host controller"; } elsif($Name=~/High Definition Audio Controller/i and $Name!~/HD Graphics/i) { return "sound"; } } elsif($Bus eq "pcmcia") { if($Name=~/Bay8Controller/i) { return "smartcard reader"; } } } if($Bus eq "usb") { if(defined $Device->{"ActiveDriver"}{"btusb"}) { return "bluetooth"; } if($Device->{"Vendor"}=~/AuthenTec|Validity Sensors/ or $Device->{"Vendor"}=~/Synaptics/ and $DId=~/00(81|9a|9b|a2|a8|bb|bd|be|c7|c9|df|e7|e9|f0|f9)/ or $Device->{"Vendor"}=~/Goodix/ and $DId=~/533c/ or $Device->{"Vendor"}=~/Elan/ and $DId=~/0c00/ or $Device->{"Vendor"}=~/EgisTec/ and $DId=~/0575/) { return "fingerprint reader"; } } return ""; } sub addCapacity($$) { my ($Device, $Capacity) = @_; $Capacity=~s/\.0\d+//; $Capacity=~s/(\.\d)\d+/$1/; $Capacity=~s/\s+//g; if($Capacity) { if($Device!~/(\A|\s|\-)[\d\.\,]+\s*([MGT]B|[MGT])(\s|\Z)/ and $Device!~/reader|bridge|\/sd\/|adapter/i and $Device!~/\Q$Capacity\E/i) { return " ".$Capacity; } } return ""; } sub decodeEdid($) { my $Edid = $_[0]; my $TmpFile = $TMP_DIR."/hex-edid"; writeFile($TmpFile, $Edid); my $Res = qx/edid-decode $TmpFile 2>&1/; unlink($TmpFile); return $Res; } sub countDevice($$) { my ($DevId, $DevType) = @_; if(not defined $HW_Count{$DevId}{$DevType}) { $HW_Count{$DevId}{$DevType} = 1; } else { $HW_Count{$DevId}{$DevType} += 1; } } sub setDevCount($$$) { my ($DevId, $DevType, $Count) = @_; $HW_Count{$DevId}{$DevType} = $Count; } sub getDeviceCount($) { my $DevId = $_[0]; if(defined $HW_Count{$DevId}) { foreach (keys(%{$HW_Count{$DevId}})) { return $HW_Count{$DevId}{$_}; } } return 0; } sub getSysVer($) { if($_[0]=~/\-(\d.*)\Z/) { return int($1); } return undef; } sub probeHW() { if($Opt{"FixProbe"}) { print "Fixing probe ... "; } else { if(enabledLog("hwinfo") and not defined $Opt{"HWInfoPath"} and not checkCmd("hwinfo")) { printMsg("ERROR", "'hwinfo' is not installed"); exitStatus(1); } if($Opt{"HWLogs"}) { my %CmdPackage = ( "dmidecode" => "dmidecode", "lspci" => "pciutils", "lsusb" => "usbutils", "lscpu" => "lscpu", "smartctl" => "smartmontools", "edid-decode" => "edid-decode", "hwstat" => "hwstat", "curl" => "curl", "usbctl" => "usbutil", "cpuid" => "cpuid", "shasum" => "p5-Digest-SHA" ); my @NeedProgs = ("dmidecode", "smartctl"); if(isOpenBSD()) { push(@NeedProgs, "lscpu", "usbctl", "curl"); # we have pcidump and usbdevs on OpenBSD by default if($Sys{"System_version"} < 6.3) { @NeedProgs = grep {$_!~/lscpu/} @NeedProgs; } if($Sys{"Arch"}!~/i386|amd64/) { @NeedProgs = grep {$_!~/dmidecode/} @NeedProgs; if($Sys{"System_version"} eq "6.3") { @NeedProgs = grep {$_!~/lscpu/} @NeedProgs; } } } elsif(isNetBSD()) { push(@NeedProgs, "usbctl", "curl"); # we have pcictl and usbdevs on NetBSD by default, TODO: cpuid? if($Sys{"Arch"}!~/i386|amd64|aarch64/) { @NeedProgs = grep {$_!~/dmidecode/} @NeedProgs; } if($Sys{"Arch"}=~/aarch64/ and $Sys{"System_version"} < 9.0) { @NeedProgs = grep {$_!~/dmidecode/} @NeedProgs; } } elsif($Sys{"System"}=~/midnightbsd/) { push(@NeedProgs, "cpuid", "curl"); # using cpuid instead of lscpu on MidnightBSD due to huge recursive deps list } elsif($Sys{"System"}=~/dragonfly/) { push(@NeedProgs, "hwstat", "lscpu", "curl"); } elsif($Sys{"System"}=~/pfsense|opnsense/) { push(@NeedProgs, "curl"); # no hwstat and lscpu on pfSense if($Sys{"Arch"}!~/i386|amd64/) { @NeedProgs = grep {$_!~/dmidecode/} @NeedProgs; } } elsif(defined $Sys{"Freebsd_release"}) { push(@NeedProgs, "hwstat", "lscpu", "curl"); # we have pciconf and usbconfig (since 8.0) on FreeBSD by default if($Sys{"Freebsd_release"} < 11.2) { @NeedProgs = grep {$_!~/lscpu/} @NeedProgs; push(@NeedProgs, "cpuid"); } if($Sys{"Freebsd_release"} < 8.1) { @NeedProgs = grep {$_!~/hwstat/} @NeedProgs; } if($Sys{"Freebsd_release"} < 8.0) { push(@NeedProgs, "usbctl"); } if($Sys{"Freebsd_release"} < 7.0) { # Use Perl instead of openssl push(@NeedProgs, "shasum"); } if($Sys{"Freebsd_release"} < 5.2) { @NeedProgs = grep {$_!~/smartctl/} @NeedProgs; } if($Sys{"Arch"}!~/i386|amd64/) { @NeedProgs = grep {$_!~/dmidecode|lscpu/} @NeedProgs; } if($Sys{"System"}!~/$KNOWN_BSD_ALL|freebsd/) { # Unknown FreeBSD-based @NeedProgs = grep {$_!~/hwstat|lscpu/} @NeedProgs; } } elsif(isBSD()) { # Unknown BSD push(@NeedProgs, "curl"); if($Sys{"Arch"}!~/i386|amd64/) { @NeedProgs = grep {$_!~/dmidecode/} @NeedProgs; } } else { # Linux push(@NeedProgs, "lspci", "lsusb", "edid-decode"); } my @NeedToInstall = (); foreach my $Prog (@NeedProgs) { if(enabledLog($Prog) and not checkCmd($Prog)) { if(isBSD()) { if(not defined $Opt{"InstallDeps"}) { printMsg("ERROR", "'".$CmdPackage{$Prog}."' package is not installed"); } push(@NeedToInstall, $CmdPackage{$Prog}) } else { printMsg("WARNING", "'".$CmdPackage{$Prog}."' package is not installed"); } } } if(@NeedToInstall) { my $NeedCmd = "pkg install"; if(isOpenBSD()) { $NeedCmd = "pkg_add"; if($Sys{"System_version"} < 6.5) { # for old OpenBSD versions $NeedCmd = "PKG_PATH=https://ftp.nluug.nl/OpenBSD/".$Sys{"System_version"}."/packages/".$Sys{"Arch"}." pkg_add"; } } elsif($Sys{"System"}=~/midnightbsd/) { $NeedCmd = "mport install"; } elsif(isNetBSD()) { $NeedCmd = "pkgin install"; } elsif(defined $Sys{"Freebsd_release"}) { if($Sys{"Freebsd_release"} < 10.0) { $NeedCmd = "pkg_add"; } if($Sys{"Freebsd_release"} < 9.3) { $NeedCmd = "env PACKAGESITE='http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/ports/".$Sys{"Arch"}."/packages-".$Sys{"Freebsd_release"}."-release/Latest/' pkg_add -r"; } } $NeedCmd .= " ".join(" ", @NeedToInstall); if(defined $Opt{"InstallDeps"}) { printMsg("INFO", "Installing dependencies ..."); system($NeedCmd); } else { printMsg("TIP", "install missed packages by command (auto-install by adding `-install-deps` option):\n\n $NeedCmd\n"); } if(not $Opt{"SkipDeps"}) { exitStatus(1); } } elsif(defined $Opt{"InstallDeps"}) { printMsg("INFO", "Nothing to install"); exitStatus(0); } } print "Probe for hardware ... "; if($Opt{"ListProbes"}) { print "\n"; } } # Dev listing my $DevFiles = ""; if($Opt{"FixProbe"}) { $DevFiles = readFile($FixProbe_Logs."/dev"); } else { listProbe("logs", "dev"); $DevFiles = runCmd("find /dev -ls 2>/dev/null"); if($DevFiles) { $DevFiles=~s/(\A|\n).*?\d+ \//$1\//g; $DevFiles = join("\n", sort split(/\n/, $DevFiles)); } else { # Alpine $DevFiles = runCmd("ls -lR /dev"); $DevFiles=~s/total \d+\n//g; $DevFiles=~s/(\A|\n).*?\s+\d+\s+\d\d:\d\d\s+/$1/g; } $DevFiles = encryptSerialsInPaths($DevFiles); $DevFiles = encryptWWNs($DevFiles); $DevFiles = hideByRegexp($DevFiles, qr/\/by-partlabel\/([^\s]+)/); $DevFiles = hideByRegexp($DevFiles, qr/\/by-partuuid\/([a-f\d]{8})\-\d\d/); $DevFiles = hideByRegexp($DevFiles, qr/nvme-eui\.([a-f\d]+)/); $DevFiles = hideLVM($DevFiles); $DevFiles = hideByRegexp($DevFiles, qr/\/([^\s\/]+?)-vg/); $DevFiles = hidePaths($DevFiles); $DevFiles = encryptUUIDs($DevFiles); writeLog($LOG_DIR."/dev", $DevFiles); } my @DevUUIDs = $DevFiles=~/by-uuid\/([^\s]{36})/g; if($Sys{"Kernel"}=~/raspi/) { my @MMCUuids = ($DevFiles=~/\/mmc-(\w+0x[a-f\d]{8}) /g); push(@DevUUIDs, @MMCUuids); } if(@DevUUIDs) { $Sys{"Uuid"} = getSysUUID(@DevUUIDs); } if(not $Sys{"System"} or $Sys{"System"}=~/freedesktop/) { if(index($DevFiles, "/dev/chromeos-low-mem") != -1) { $Sys{"System"} = "chrome_os"; } elsif(index($DevFiles, "eos-swap") != -1) { $Sys{"System"} = "endless"; } } my %DevIdByName = (); my %DevNameById = (); my $InDevById = 0; foreach my $Line (split(/\n/, $DevFiles)) { if(not $InDevById and index($Line, "/dev/disk/by-id")==0) { $InDevById = 1; } if($InDevById) { if(not $Line or index($Line, "/dev/disk/by-uuid")==0) { last; } if($Line=~/\A(\/dev\/disk\/by-id\/|)((ata|usb|nvme|wwn)\-[^\/]+)\s+\-\>\s+.*?(\w+)\Z/) { my ($DFile, $DIdent) = ($4, $2); if($DIdent!~/\Awwn\-/) { $DevIdByName{$DFile} = $DIdent; } $DevNameById{$DIdent} = "/dev/".$DFile; } } } # Loaded modules my $Lsmod = ""; if($Opt{"FixProbe"}) { $Lsmod = readFile($FixProbe_Logs."/lsmod"); } elsif(enabledLog("lsmod") and checkCmd("lsmod")) { listProbe("logs", "lsmod"); if($Opt{"Snap"} or $Opt{"Flatpak"}) { $Lsmod = "Module Size Used by\n"; foreach my $L (split(/\n/, readFile("/proc/modules"))) { if($L=~/\A(\w+)\s+(\d+)\s+(\d+)\s+([\w,-]+)/) { my ($Mod, $Size, $Used, $By) = ($1, $2, $3, $4); $By=~s/[,-]\Z//; # if($By) { # $By = join(",", sort split(/,/, $By)); # } $Lsmod .= $Mod; my $Sp = 28 - length($Size) - length($Mod); if($Sp<4) { $Sp = 4; } foreach (1 .. $Sp) { $Lsmod .= " "; } $Lsmod .= $Size." ".$Used." ".$By."\n"; } } } else { $Lsmod = runCmd("lsmod 2>&1"); if(length($Lsmod)<60) { $Lsmod = ""; } } if($Lsmod) { # Sort, but save title my $FL = ""; if($Lsmod=~s/\A(.*?)\n//) { $FL = $1; } $Lsmod = $FL."\n".join("\n", sort split(/\n/, $Lsmod)); } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lsmod", $Lsmod); } } my $Kldstat = ""; if($Opt{"FixProbe"}) { $Kldstat = readFile($FixProbe_Logs."/kldstat"); } elsif(enabledLog("kldstat") and checkCmd("kldstat")) { listProbe("logs", "kldstat"); $Kldstat = runCmd("kldstat 2>/dev/null"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/kldstat", $Kldstat); } } my $Kldstat_v = ""; if($Opt{"FixProbe"}) { $Kldstat_v = readFile($FixProbe_Logs."/kldstat_v"); } elsif(enabledLog("kldstat_v") and checkCmd("kldstat")) { listProbe("logs", "kldstat_v"); $Kldstat_v = runCmd("kldstat -v 2>/dev/null"); if($Opt{"HWLogs"} and $Kldstat_v) { writeLog($LOG_DIR."/kldstat_v", $Kldstat_v); } } # TODO: clarification of the condition is required # if($Kldstat_v=~/bcm2835_/) # { # $Sys{"Type"} = "system on chip"; # $Sys{"Model"} = "Raspberry Pi"; # } # if($Kldstat_v=~/rockchip_dwmmc/) # { # $Sys{"Type"} = "system on chip"; # $Sys{"Vendor"} = "Pine Microsystems"; # $Sys{"Model"} = "Rockpro64"; # } my $Modstat = ""; if($Opt{"FixProbe"}) { $Modstat = readFile($FixProbe_Logs."/modstat"); } elsif(enabledLog("modstat") and checkCmd("modstat")) { listProbe("logs", "modstat"); $Modstat = runCmd("modstat 2>/dev/null"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/modstat", $Modstat); } } my $Sndstat = ""; if($Opt{"FixProbe"}) { $Sndstat = readFile($FixProbe_Logs."/sndstat"); } elsif(enabledLog("sndstat")) { listProbe("logs", "sndstat"); $Sndstat = readFile("/dev/sndstat"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/sndstat", $Sndstat); } } my $Neofetch = ""; if($Opt{"FixProbe"}) { $Neofetch = readFile($FixProbe_Logs."/neofetch"); } elsif(enabledLog("neofetch") and checkCmd("neofetch")) { listProbe("logs", "neofetch"); $Neofetch = runCmd("neofetch --json 2>/dev/null"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/neofetch", $Neofetch); } } if($Neofetch and $USE_JSON_XS) { require Encode; eval { $Neofetch = JSON::XS::decode_json(Encode::encode_utf8($Neofetch)); } or do { $Neofetch = (); print STDERR "ERROR: failed to parse neofetch log\n"; }; if(not $Sys{"Wm"} and $Neofetch) { $Sys{"Wm"} = $Neofetch->{"WM"}; } } my $KernelConfig = ""; if($Opt{"FixProbe"}) { $KernelConfig = readFile($FixProbe_Logs."/config"); } elsif(enabledLog("config") and checkCmd("config") and -e "/boot/kernel/kernel") { listProbe("logs", "config"); $KernelConfig = runCmd("config -x /boot/kernel/kernel 2>/dev/null"); if($Opt{"HWLogs"} and $KernelConfig) { writeLog($LOG_DIR."/config", $KernelConfig); } } my @KernDrvs = (); foreach my $Line (split(/\n/, $Lsmod)) { if($Line=~/(\w+)\s+(\d+)\s+(\d+)/) { my ($Name, $Use) = ($1, $3); $KernMod{$Name} = $Use; push(@KernDrvs, $Name); if($Use) { $WorkMod{$Name} = 1; } } } if(not $Sys{"Type"} and not $Sys{"Model"}) { if(defined $KernMod{"raspberrypi_hwmon"}) { $Sys{"Type"} = "system on chip"; $Sys{"Model"} = "Raspberry Pi"; ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); } } my $Lsblk = ""; if($Opt{"FixProbe"}) { $Lsblk = readFile($FixProbe_Logs."/lsblk"); } elsif(enabledLog("lsblk") and checkCmd("lsblk")) { listProbe("logs", "lsblk"); my $LsblkCmd = undef; if(isBSD()) { $LsblkCmd = "lsblk"; } else { $LsblkCmd = "lsblk -al -o NAME,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINT,MODEL,PTTYPE,PARTUUID"; if($Opt{"Flatpak"}) { $LsblkCmd .= " 2>/dev/null"; } else { $LsblkCmd .= " 2>&1"; } } $Lsblk = runCmd($LsblkCmd); if($Lsblk=~/unknown column/) { # CentOS 6: no PARTUUID column if($LsblkCmd=~s/\,PARTUUID//g) { $Lsblk = runCmd($LsblkCmd); } } if($Lsblk=~/unknown column/) { if($LsblkCmd=~s/\,PTTYPE//g) { $Lsblk = runCmd($LsblkCmd); } } if($Opt{"Snap"} and $Lsblk=~/Permission denied/) { $Lsblk = ""; } $Lsblk = hideByRegexp($Lsblk, qr/(.+?)\s+[^\s]+?\s+crypt\s+/); $Lsblk = hidePaths($Lsblk); $Lsblk = hideLVM($Lsblk); $Lsblk = encryptUUIDs($Lsblk); $Lsblk = hideByRegexp($Lsblk, qr/\s([a-f\d]{8})\-\d\d\n/); # PARTUUID $Lsblk=~s/(\s)[A-Z\d]{16}(\s)/$1XXXXXXXXXXXXXXXX$2/g; # ntfs UUID writeLog($LOG_DIR."/lsblk", $Lsblk); } if($Lsblk) { foreach my $Line (split(/\n/, $Lsblk)) { if($Line=~/\blive-/) { next; } my @L = split(/\s+/, $Line); if($L[0]=~/\A(sd[a-z]+|nvme\d+n\d+|mmcblk\d+|mtdblock\d+)\Z/) { my $HDD_File = "/dev/".$L[0]; my $HDD_Size = $L[1]; if(index($HDD_Size, ":")!=-1) { # old lsblk log $HDD_Size = $L[3]; $HDD_Size=~s/\,/./; } if($HDD_Size!~/\A\d.*[A-Z]\Z/) { next; } $HDD_Size=~s/\.X/\.0/; if($HDD_Size=~/\A([\d\.]+)([A-Z]+)\Z/) { my ($N, $S) = ($1, $2); if($S eq "T") { $N = $N*1.11111111111; } elsif($S eq "G") { $N = $N*1.07355; } elsif($S eq "X") { # incorrect anonymization next; } $HDD_Size = sprintf("%.1f", $N).$S; $HDD_Size=~s/\.\d+//; } if($HDD_Size!~/B\Z/) { $HDD_Size .= "B"; } if($HDD_Size eq "1000GB") { $HDD_Size = "1TB"; } $HDD_Size = fixCapacity($HDD_Size); if($HDD_Size) { $BlockCapacity{$HDD_File} = $HDD_Size; } } } if($Lsblk=~/\sgpt\s/) { $Sys{"Part_scheme"} = "GPT"; } elsif($Lsblk=~/\sdos\s/) { $Sys{"Part_scheme"} = "MBR"; } } if(not $Opt{"FixProbe"} and $Opt{"Logs"}) { if(enabledLog("modinfo") and checkCmd("modinfo")) { listProbe("logs", "modinfo"); my $Modinfo = runCmd("modinfo ".join(" ", @KernDrvs)." 2>&1"); $Modinfo=~s/\n(filename:)/\n\n$1/g; $Modinfo=~s/\n(author|signer|sig_key|sig_hashalgo|vermagic):.+//g; $Modinfo=~s/\ndepends:\s+\n/\n/g; if(index($Modinfo, "signature: ")!=-1) { $Modinfo=~s/:*\n\s+[A-F\d]{2}\:.+//g; } $Modinfo=~s&/lib/modules/[^\/]+/kernel/&&g; writeLog($LOG_DIR."/modinfo", $Modinfo); } } if(not $Opt{"FixProbe"}) { my $RpmLst = "/run/initramfs/live/rpm.lst"; if(-f $RpmLst) { # Live my $Build = `head -n 1 $RpmLst 2>&1`; # iso build No.11506 if($Build=~/(\d+)/) { $Sys{"Build"} = $1; } if($Opt{"Logs"}) { writeLog($LOG_DIR."/build", $Build); } } else { my $RevInfo = "/run/initramfs/live/revision.info"; if(-f $RevInfo) { # DX my $Build = readFile($RevInfo); if($Build=~/RELEASE\:\s*(\d+)/i) { $Sys{"Build"} = $1; } if($Opt{"Logs"}) { writeLog($LOG_DIR."/revision.info", $Build); } } } } my %DriveKind = (); if($Opt{"FixProbe"}) { # Fix drive IDs after uploading my $Smart = readFile($FixProbe_Logs."/smartctl"); my $CurDev = undef; foreach my $SL (split(/\n/, $Smart)) { if(index($SL, "/dev/")==0) { $CurDev = $SL; $DriveKind{$CurDev} = "HDD"; } elsif($CurDev) { if($SL=~/Rotation Rate:.*Solid State Device|\bSSD/) { $DriveKind{$CurDev} = "SSD"; } elsif(index($SL, "NVM Commands")!=-1 or index($SL, "NVMe Log")!=-1) { $DriveKind{$CurDev} = "NVMe"; } } } } # HW Info my $HWInfo = ""; if($Opt{"FixProbe"}) { $HWInfo = readFile($FixProbe_Logs."/hwinfo"); } elsif(enabledLog("hwinfo")) { listProbe("logs", "hwinfo"); my @Items = qw(monitor bluetooth bridge camera cdrom chipcard cpu disk dvb fingerprint floppy gfxcard hub ide isapnp isdn joystick keyboard mouse netcard network pci pcmcia scanner scsi smp sound tape tv usb usb-ctrl wlan); my $HWInfoCmd = "hwinfo"; if(defined $Opt{"HWInfoPath"}) { my $HWInfoDir = dirname(dirname($Opt{"HWInfoPath"})); $HWInfoCmd = $Opt{"HWInfoPath"}; if(-d "$HWInfoDir/lib64") { $HWInfoCmd = "LD_LIBRARY_PATH=\"$HWInfoDir/lib64\" ".$HWInfoCmd; } elsif(-d "$HWInfoDir/lib") { $HWInfoCmd = "LD_LIBRARY_PATH=\"$HWInfoDir/lib\" ".$HWInfoCmd; } } if(my $HWInfoVer = qx/$HWInfoCmd --version/) { chomp($HWInfoVer); if($HWInfoVer=~/\A(\d+)\.(\d+)\Z/) { my ($V1, $V2) = (int($1), int($2)); if($V1>21 or $V1==21 and $V2>=34) { # newer than 21.34 push(@Items, "mmc-ctrl"); } } } if($Opt{"LogLevel"} eq "maximal") { # this may hang push(@Items, "framebuffer"); } my $Items = "--".join(" --", @Items); $HWInfo = runCmd("$HWInfoCmd $Items 2>/dev/null"); if(not $HWInfo) { # incorrect option printMsg("WARNING", "incorrect hwinfo option passed, using --all"); $HWInfo = runCmd($HWInfoCmd." --all 2>&1"); } $HWInfo = hideTags($HWInfo, "UUID|Asset Tag"); $HWInfo = encryptMACs($HWInfo); if(index($HWInfo, "Serial ID:")) { $HWInfo = encryptSerials($HWInfo, "Serial ID", "hwinfo"); } $HWInfo = encryptSerialsInPaths($HWInfo); $HWInfo = encryptWWNs($HWInfo); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/hwinfo", $HWInfo); } } foreach my $Info (split(/\n\n/, $HWInfo)) { my %Device = (); my ($DevNum, $Bus) = (); my ($V, $D, $SV, $SD, $C) = (); if($Info=~s/(\d+):\s*([^ ]+)//) { # 37: PCI 700.0: 0200 Ethernet controller $DevNum = $1; $Bus = lc($2); } $Bus=~s/\(.*?\)//g; if($Bus eq "adb") { # adb:0001-0001-macintosh-mouse-button-emulation next; } if($Info=~s/:\s*\w+\s+(.*)//) { # 37: PCI 700.0: 0200 Ethernet controller if($1 eq "BIOS") { next; } elsif($1 ne "Unclassified device") { $Device{"Type"} = lc($1); $Device{"GeneralType"} = $1; } } if($Device{"Type"} eq "partition") { next; } elsif($Device{"Type"} eq "disk" and $Bus eq "pci") { $Bus = $PCI_DISK_BUS; } $Info=~s/[ ]{2,}/ /g; my $ID = ""; while($Info=~s/[ \t]*([\w ]+?):[ \t]*(.*)//) { my ($Key, $Val) = ($1, $2); if($Key eq "Device" or $Key eq "Vendor" or $Key eq "SubVendor" or $Key eq "SubDevice") { $Key=~s/\ASub/S/; # name mapping if($Bus ne "ide" and $Bus ne $PCI_DISK_BUS) { if($Val=~s/\A(\w+) 0x/0x/) { $Bus = $1; } if($Val=~s/0x(\w{4})\s*//) { if($Key eq "Vendor") { $V = $1; } elsif($Key eq "Device") { $D = $1; } elsif($Key eq "SVendor") { $SV = $1; } elsif($Key eq "SDevice") { $SD = $1; } } } if($Val=~/\"(.*)\"/) { $Device{$Key} = fmtVal($1); } if($Device{"Type"} eq "cpu") { # fix cpu if($Key eq "Vendor") { $Device{$Key} = fixCpuVendor($Device{$Key}); } } if(not $V) { if($Val=~/(\w+)\s+\".*\"/) { $V = $1; } elsif($Val!~/\".*\"/) { $V = fmtVal($Val); } } } elsif($Key eq "Model") { if($Val=~/\A(.*?)\s*\"(.*)\"/) { $D = $1; $Device{"Model"} = fmtVal($2); } } elsif($Key eq "Hardware Class") { if(lc($Val) ne "unknown") { $Device{"Type"} = $Val; } } elsif($Key eq "Driver") { while($Val=~s/\"([\w\-]+)\"//) { my $Dr = $1; $Dr=~s/\-/_/g; $Device{"ActiveDriver_Common"}{$Dr} = keys(%{$Device{"ActiveDriver_Common"}}); } } elsif($Key eq "Driver Status") { if($Val=~/(.*) is active/) { my $Dr = $1; $Dr=~s/\-/_/g; $Device{"ActiveDriver"}{$Dr} = keys(%{$Device{"ActiveDriver"}}); } } elsif($Key eq "Module Alias") { if($Val=~/\"(.*)\"/) { $Val = $1; if($Val=~/ic(\w\w)isc(\w\w)ip(\w\w)\Z/) { # usb $C = devID($1, $2, $3); } elsif($Val=~/bc(\w\w)sc(\w\w)i(\w\w)\Z/) { # pci $C = devID($1, $2, $3); } $Device{$Key} = $Val; } } elsif($Key eq "Resolution" and $Device{"Type"} eq "monitor") { # monitor if($Val=~s/\@.*//) { if(not defined $Device{"Resolution"} or getXRes($Val)>getXRes($Device{"Resolution"})) { $Device{$Key} = $Val; $Device{$Key}=~s/ //g; } } } elsif($Key eq "Size" and $Device{"Type"} eq "monitor") { # monitor if($Val ne "1600x900 mm") { $Device{$Key} = $Val; $Device{$Key}=~s/ //g; } } elsif($Key eq "Size") { # disk if($Val=~/(\d+) sectors a (\d+) bytes/) { $Device{"Capacity"} = bytesToHuman($1*$2); } } elsif($Key eq "Capacity") { # disk if($Val=~s/\s*\((.*?)\)//) { my $Bytes = $1; $Bytes=~s/ bytes//g; $Val = bytesToHuman($Bytes); } $Val=~s/ //g; $Device{$Key} = $Val; } elsif($Key eq "Attached to") { $Device{"Attached"} = $Val; if($Bus eq "ide" or $Bus eq $PCI_DISK_BUS) { # FIXME: check for PATA # if($Val=~/SATA/) # { # $Bus = "sata"; # } } if($Val=~/#(\d+)/) { $DeviceAttached{$DevNum} = $1; } } elsif($Key eq "Device Files") { foreach my $F (split(/,\s*/, $Val)) { if(index($F, "nvme-nvme")==-1 and $F=~/by-id\/((ata|nvme|usb|mmc)-.*)\Z/) { $Device{"FsId"} = $1; } $Device{"AllFiles"}{$F} = 1; } } elsif($Key eq "Device File") { if($Device{"Type"} eq "disk" or $Device{"Type"} eq "storage device") { if($Val=~s/\s*\((.*)\)//) { $Device{"AdvFile"} = $1; } $Device{"File"} = $Val; if($Bus eq "ide" or $Bus eq $PCI_DISK_BUS or index($Val, "nvme")!=-1 or $Bus eq "usb") { $HDD{$Val} = 0; } elsif(index($Val, "mmcblk")!=-1 and $Val=~/mmcblk\d+\Z/) { $MMC{$Val} = 0; } } elsif($Device{"Type"} eq "network") { $Device{"Files"}{$Val} = 1; if($Bus eq "usb") { $ExtraConnection{$Val} = 1; } } elsif($Device{"Type"} eq "network interface") { if(index($Device{"SysFS Device Link"}, "\/usb")!=-1 or index($Device{"SysFS Device Link"}, "/devices/virtual")!=-1) { $ExtraConnection{$Val} = 1; } } } elsif($Key eq "Serial ID") { # disk if($Val=~/\"(.*)\"/) { $Device{"Serial"} = $1; } } elsif($Key eq "Memory Size") { # framebuffer $Val=~s/(\d)\s+(MB|GB|KB)/$1$2/ig; $Device{"Memory Size"} = $Val; } elsif($Key eq "Platform" and $Device{"Type"} eq "cpu") { # ARM if($Val=~/\"(.*)\"/) { $Device{"Platform"} = $1; } } elsif($Key eq "Link detected") { $Device{"Link detected"} = $Val; } elsif($Key eq "SysFS Device Link" ) { $Device{$Key} = $Val; } elsif($Key eq "SysFS ID" ) { $Device{$Key} = $Val; } } cleanValues(\%Device); if(my $DF = $Device{"File"}) { if($Bus eq "scsi") { if($Device{"Attached"}!~/RAID/) { $HDD{$DF} = 0; } } } if(not $Device{"Device"}) { if(my $Model = $Device{"Model"}) { $Device{"Device"} = $Model; } elsif($Device{"Type"} eq "cpu") { if(my $Platform = $Device{"Platform"}) { $Device{"Device"} = $Platform." Processor"; } elsif($Device{"Vendor"}) { $Device{"Device"} = $Device{"Vendor"}." Processor"; } } } if(defined $Device{"ActiveDriver"}) { $Device{"Driver"} = join(", ", sort {$Device{"ActiveDriver"}{$a} <=> $Device{"ActiveDriver"}{$b}} keys(%{$Device{"ActiveDriver"}})); } elsif(defined $Device{"ActiveDriver_Common"}) { $Device{"Driver"} = join(", ", sort {$Device{"ActiveDriver_Common"}{$a} <=> $Device{"ActiveDriver_Common"}{$b}} keys(%{$Device{"ActiveDriver_Common"}})); } if($Device{"Type"} eq "cpu") { $Bus = "cpu"; } elsif($Device{"Type"} eq "framebuffer") { $Bus = "fb"; next; # disabled } if($Device{"Type"} eq "network") { if($Bus eq "pci") { if($Sys{"NICs"}) { $Sys{"NICs"} += 1; } else { $Sys{"NICs"} = 1; } } foreach my $NF (keys(%{$Device{"Files"}})) { if($NF=~/\Ae/) { $Device{"Kind"} = "Ethernet"; } elsif($NF=~/\Aw/) { $Device{"Kind"} = "WiFi"; } } } if($Device{"Type"} eq "monitor") { if(my $MSize = $Device{"Size"}) { if(my $Inches = computeInch($MSize)) { $Device{"Inches"} = sprintf("%.1f", $Inches); if(my $Density = computeDensity($Device{"Resolution"}, $Inches)) { $Device{"Density"} = roundFloat($Density, 1); } } if(my $Ratio = computeRatio($MSize)) { $Device{"Ratio"} = $Ratio; } if(my $Area = computeArea($MSize)) { $Device{"Area"} = $Area; } if($MSize=~/\A(\d+)/) { $Device{"Width"} = $1; } } if(not $Device{"Ratio"}) { if(my $RatioByRes = computeRatio($Device{"Resolution"})) { $Device{"Ratio"} = $RatioByRes; } } } if($Device{"Type"} eq "disk") { if(not $Device{"Capacity"} and defined $BlockCapacity{$Device{"File"}}) { $Device{"Capacity"} = $BlockCapacity{$Device{"File"}}; } $DriveNumByFile{$Device{"File"}} = $DevNum; if(index($Device{"File"}, "nvme")!=-1) { if(not $Device{"Device"} or $Device{"Device"} eq "Disk" or not $Device{"Vendor"} or not $Device{"Serial"}) { if($Device{"FsId"}=~/\Anvme\-(INTEL|Samsung)_(.+)_([^_]+)\Z/) { $Device{"Vendor"} = ucfirst(lc($1)); $Device{"Model"} = $2; $Device{"Serial"} = $3; $Device{"Model"}=~s/_/ /g; $Device{"Device"} = $Device{"Model"}; if($Device{"Serial"} and $Device{"Serial"}!~/\A[\dA-F]{$HASH_LEN_CLIENT}\Z/) { $Device{"Serial"} = clientHash($Device{"Serial"}); } if($Bus eq "none") { $Bus = $PCI_DISK_BUS; } } } if(not $Device{"Device"} or $Device{"Device"} eq "Disk" or not $Device{"Vendor"} or not $Device{"Serial"}) { # empty info for NVMe drives if($Device{"Model"}) { if(index($Device{"Model"}, $Device{"Device"})!=-1) { if(my $Vnd = guessDeviceVendor($Device{"Model"})) { $Device{"Model"} = $Vnd." Disk"; $Device{"Vendor"} = $Vnd; } else { $Device{"Model"} = undef; } } elsif($Device{"Model"}=~/controller/i) { $Device{"Model"} = undef; } } $HDD_Info{$Device{"File"}} = \%Device; next; } } elsif(index($Device{"File"}, "mmcblk")!=-1) { $Bus = "mmc"; if($Device{"FsId"} and $Device{"FsId"}=~/mmc-(.+?)[_]+(0x[a-f\d]{8})/) { $Device{"Device"} = $1; $Device{"Serial"} = clientHash($2); } else { $Device{"Device"} = "MMC Card"; # $Device{"Serial"} = "000"; } $MMC_Info{$Device{"File"}} = \%Device; next; } if(not $Device{"Attached"}) { next; } } #if($Bus eq "none") #{ # if($Device{"Type"} eq "disk" and $Device{"File"}=~/\/dev\/vd/) { # $Bus = "ide"; # } #} if($Bus eq "none") { if($Device{"Module Alias"}=~/\Aplatform:/) { $Bus = "platform"; } } if($Bus eq "none") { next; } if(not $Device{"Type"}) { $Device{"Type"} = getDefaultType($Bus, $D, \%Device); } # fix type if($Device{"Type"} eq "keyboard") { if($Device{"Device"}=~/mouse/i and $Device{"Device"}!~/keyboard/i) { $Device{"Type"} = "mouse"; } } elsif($Device{"Type"} eq "mouse") { if($Device{"Device"}!~/mouse/i) { if($Device{"Device"}=~/keyboard/i) { $Device{"Type"} = "keyboard"; } elsif($Device{"Device"}=~/touchscreen/i) { $Device{"Type"} = "touchscreen"; } elsif($Device{"Device"}=~/touchpad/i) { $Device{"Type"} = "touchpad"; } } } elsif($Device{"Type"} eq "printer") { if($Device{"Device"}=~/(\A| )MFP( |\Z)/) { $Device{"Type"} = "mfp"; } } elsif($Device{"Type"} eq "disk") { if(defined $Device{"AllFiles"}{"/dev/cdrom"}) { $Device{"Type"} = "cdrom"; if(my $File = $Device{"File"}) { delete($HDD{$File}); } } } # fix vendor if($V eq "1d6b") { $Device{"Vendor"} = "Linux Foundation"; } if(not $Device{"Vendor"}) { if($Bus eq "ps/2") { if($Device{"Type"}=~/touchpad|touchscreen|mouse/) { if($Device{"Device"}=~s/(\w+) (DualPoint Touchpad)/$2/i) { # AlpsPS/2 ALPS DualPoint TouchPad $Device{"Vendor"} = $1; } elsif($Device{"Device"}=~s/(\w+) (Touchpad|TrackPoint|GlidePoint)/$2/i) { $Device{"Vendor"} = $1; } elsif($Device{"Device"}=~s/(PS\/2) (\w+) (Wheel Mouse)/$1 $3/i) { if($2 ne "Generic") { $Device{"Vendor"} = $2; } } elsif($Device{"Device"}=~/(Sony) Vaio Jogdial/i) { $Device{"Vendor"} = $1; } elsif($Device{"Device"}=~/\A(ALPS|Goodix|Wacom) /i) { $Device{"Vendor"} = $1; } if($Device{"Vendor"} eq "FocalTech") { $Device{"Device"}=~s/FocalTech (FocalTech)/$1/i; } } } } if($Device{"Type"} eq "disk" and $Device{"Device"}=~/DVD|\ACD(-|RW)|\ADRW-|\A(DVD|DVDR|DVDRAM) /) { $Device{"Type"} = "cdrom"; } if($Device{"Type"} eq "disk" and $Bus ne "usb") { my $FsId = $Device{"FsId"}; if(not $FsId) { # Docker if($Device{"File"}) { $FsId = $DevIdByName{basename($Device{"File"})}; } } if($FsId) { my $N = $Device{"Device"}; $N=~s/ /_/g; if(my $Serial = $Device{"Serial"}) { if($FsId=~/\Q$N\E(.*?)_\Q$Serial\E/) { my $Suffix = $1; $Suffix=~s/[_]+/ /g; $Suffix=~s/ 1XB\Z/ 1TB/; # wrong anonymization $Device{"Device"} .= $Suffix; } } elsif($FsId=~/\Q$N\E(.*)_(.*?)\Z/) { my $Suffix = $1; my $Ser = $2; $Suffix=~s/[_]+/ /g; $Suffix=~s/ 1XB\Z/ 1TB/; # wrong anonymization $Device{"Device"} .= $Suffix; if(not $Device{"Serial"} and index($Ser, "...") == -1) { $Device{"Serial"} = $Ser; } } } if(defined $DriveKind{$Device{"File"}}) { # NOTE: on fixing probe $Device{"Kind"} = $DriveKind{$Device{"File"}}; } $Device{"Device"} = duplVendor($Device{"Vendor"}, $Device{"Device"}); # Fix incorrect vendor in hwinfo if($Device{"Vendor"} eq "m.2") { $Device{"Device"} .= " ".$Device{"Vendor"}; $Device{"Vendor"} = undef; } elsif($Device{"Vendor"} eq "SK") { $Device{"Device"} = $Device{"Vendor"}." ".$Device{"Device"}; $Device{"Vendor"} = undef; } fixDrive_Pre(\%Device, $Bus); fixDrive(\%Device); } else { if($Device{"Type"} eq "storage device") { if(not $Device{"Vendor"} and my $Vnd = guessDriveVendor($Device{"Device"})) { $Device{"Vendor"} = $Vnd; $Device{"Device"} = duplVendor($Device{"Vendor"}, $Device{"Device"}); } } $Device{"Device"} = duplVendor($Device{"Vendor"}, $Device{"Device"}); if($Device{"Type"} eq "monitor" and not $Device{"Device"}) { $Device{"Device"} = "LCD Monitor"; } } if($Bus eq "usb" or $Bus eq "pci") { $ID = devID($V, $D, $SV, $SD); if($SD) { $LongID{$Bus.":".devID($V, $D)}{$ID} = 1; } if($Device{"Type"} eq "disk") { $Device{"Device"} .= addCapacity($Device{"Device"}, $Device{"Capacity"}); } } else { if($Device{"Device"} eq "Unclassified device") { # PNP devices, etc. next; } if(not $Device{"Device"}) { next; } if($Device{"Type"} eq "monitor") { if(lc($Device{"Device"}) eq lc($Device{"Vendor"})) { $Device{"Device"} = $Device{"GeneralType"}; } if($V) { if(my $Vendor = getPnpVendor($V)) { $Device{"Vendor"} = $Vendor; $Device{"Device"}=~s/\A\Q$Vendor\E(\s+|\-)//ig; } elsif(not $Device{"Vendor"}) { $Device{"Vendor"} = $V; } } } if($Device{"Type"} eq "framebuffer") { if($Device{"Vendor"} eq $Device{"Device"}) { $Device{"Vendor"} = ""; } } # create id if($Device{"Type"} eq "monitor" and $V) { #if(nameID($Device{"Vendor"}) ne $V) #{ # do not add vendor name to id $ID = devID(nameID($Device{"Vendor"})); #} $ID = devID($ID, $V.$D); } else { if(my $Vendor = $Device{"Vendor"}) { $ID = devID(nameID($Vendor)); } else { $ID = devID($V); } $ID = devID($ID, $D, devSuffix(\%Device)); } # additionals to device attributes if($Device{"Type"} eq "monitor") { if($D) { $Device{"Device"} .= " ".uc($V.$D); } if($Device{"Resolution"}) { $Device{"Device"} .= " ".$Device{"Resolution"}; } if($Device{"Size"}) { $Device{"Device"} .= " ".$Device{"Size"}; } if(my $Inches = $Device{"Inches"}) { $Device{"Device"} .= " ".$Inches."-inch"; } } elsif($Device{"Type"} eq "disk") { $Device{"Device"} .= addCapacity($Device{"Device"}, $Device{"Capacity"}); if($Device{"Kind"} eq "SSD" and $Device{"Device"}!~/SSD|Solid State/i) { $Device{"Device"} .= " SSD"; } } elsif($Device{"Type"} eq "cpu") { $Device{"Status"} = "works"; } elsif($Device{"Type"} eq "framebuffer") { $Device{"Device"} .= " ".$Device{"Memory Size"}; } } if($Device{"Type"} eq "network") { if(defined $Device{"Files"}) { foreach my $F (sort keys(%{$Device{"Files"}})) { if(defined $UsedNetworkDev{$F}) { $Device{"Status"} = "works"; } } } if($Device{"Link detected"} eq "yes") { $Device{"Status"} = "works"; } } # delete unused fields delete($Device{"ActiveDriver_Common"}); delete($Device{"ActiveDriver"}); delete($Device{"FsId"}); delete($Device{"Model"}); delete($Device{"GeneralType"}); delete($Device{"Attached"}); delete($Device{"AllFiles"}); delete($Device{"Files"}); delete($Device{"Module Alias"}); delete($Device{"SysFS Device Link"}); if($C) { $Device{"Class"} = $C; } cleanValues(\%Device); $ID = fmtID($ID); if(not $ID) { next; } my $BusID = $Bus.":".$ID; if($Device{"Type"} eq "monitor") { $Monitor_ID{uc($V.$D)} = $ID; } elsif($Device{"Type"} eq "disk" or $Device{"Type"} eq "storage device") { if(my $File = $Device{"File"}) { $HDD{$File} = $BusID; } } if($Bus eq "ps/2" and $Device{"Type"}=~/touchpad/) { if(not $Sys{"Type"} or $Sys{"Type"}=~/$DESKTOP_TYPE|$SERVER_TYPE|other/) { $Sys{"Type"} = "notebook"; } } if($Bus eq "pci" and $Device{"SysFS ID"}=~/\/0000\:([^\/]+)\Z/) { $DevBySysID{$1} = $ID; } delete($Device{"SysFS ID"}); $DeviceIDByNum{$DevNum} = $BusID; $DeviceNumByID{$BusID} = $DevNum; if(not $HW{$BusID}) { $HW{$BusID} = \%Device; } else { # double entry if($Device{"Type"} and not $HW{$BusID}{"Type"}) { $HW{$BusID} = \%Device; } if($HW{$BusID}{"Driver"} eq "nvidiafb") { # two Nvidia cards $HW{$BusID}{"Driver"} = $Device{"Driver"}; } } if($Device{"Type"} and $Device{"Type"}!~/mouse|keyboard|monitor/) { countDevice($BusID, $Device{"Type"}); } if($Device{"Type"} eq "cpu") { if($Device{"Vendor"}) { $CPU_ID = $BusID; } } else { $ComponentID{$Device{"Type"}}{$BusID} = 1; } } my $Sysctl = ""; if($Opt{"FixProbe"}) { $Sysctl = readFile($FixProbe_Logs."/sysctl"); } elsif(enabledLog("sysctl") and checkCmd("sysctl")) { listProbe("logs", "sysctl"); $Sysctl = runCmd("sysctl -a 2>/dev/null"); $Sysctl=~s{()(.+)()}{$1...$3}g; $Sysctl=~s/((kern\.hostname|serialno|-serial|-asset-tag)\s*[:=]\s*).+/$1.../g; $Sysctl=~s/ ([^\s]+) (login|syslogd)/ ... $2/g; $Sysctl=~s/(Serial Number\s+)(.+)/$1.../g; $Sysctl=~s/((sernum|soiikey)=)[^\s]+/$1.../g; foreach my $Hide ("kern.msgbuf", "kern.geom.confxml", "kern.geom.confdot", "kern.geom.conftxt") { $Sysctl=~s/(\Q$Hide\E:).+?(\skern\.)/$1 ...$2/gs; } $Sysctl=~s/(vm\.pmap\.kernel_maps:).+?(\svm\.pmap\.)/$1 ...$2/gs; $Sysctl = encryptUUIDs($Sysctl); $Sysctl = encryptSerials($Sysctl, "kern.hostid"); $Sysctl = encryptMACs($Sysctl); $Sysctl = hideIPs($Sysctl); $Sysctl = hidePaths($Sysctl); $Sysctl = hideEmail($Sysctl); $Sysctl=~s/(debug.tslog_user:)(.|\n)+/$1 ...\n/g; writeLog($LOG_DIR."/sysctl", $Sysctl); if(isOpenBSD()) { if($Sysctl=~/kern\.allowkmem=0/) { printMsg("WARNING", "DMI info cannot be collected, need kern.allowkmem=1 to be set"); } } if($Sysctl=~/kern\.securelevel\s*[=:]\s*([2])/) { printMsg("WARNING", "can't list all devices due to securelevel=$1"); } } if($Sysctl) { if(isOpenBSD()) { if($Sysctl=~/hw\.disknames=(.+)/) { my $DrNames = $1; my @Drs = ($DrNames=~/(\w+):/g); foreach my $Dr (@Drs) { if($Dr=~/\Acd\d+/) { # TODO: detect CDROM } elsif($Dr=~/\Afd\d+/) { # TODO: detect floppy } else { $HDD{"/dev/".$Dr} = 0; } } } } elsif($Sys{"System"}=~/dragonfly|midnightbsd/ or defined $Sys{"Freebsd_release"}) { if($Sysctl=~/kern\.disks: (.+)/) { my @Drs = split(/\s+/, $1); foreach my $Dr (@Drs) { if($Dr!~/\A(md|vn|cd|fd)\d+/) { $HDD{"/dev/".$Dr} = 0; } } } } elsif(isNetBSD()) { if($Sysctl=~/hw\.disknames = (.+)/) { my @Drs = split(/\s+/, $1); foreach my $Dr (@Drs) { if($Dr!~/\A(cd|fd)\d+/) { $HDD{"/dev/".$Dr} = 0; } } } } } my $DevInfo = ""; if($Opt{"FixProbe"}) { $DevInfo = readFile($FixProbe_Logs."/devinfo"); } elsif($Opt{"HWLogs"} and enabledLog("devinfo") and checkCmd("devinfo")) { listProbe("logs", "devinfo"); $DevInfo .= runCmd("devinfo -v 2>&1"); $DevInfo=~s/(sernum=)[^\s]+/$1.../g; writeLog($LOG_DIR."/devinfo", $DevInfo); } if($DevInfo) { my %ParentDev = (); foreach my $Line (split(/\n/, $DevInfo)) { if($Line=~/\A(\s*)(\w+\d+)/) { my $CurDepth = length($1)/2; my $DevFile = $2; $ParentDev{$CurDepth} = $DevFile; foreach (0 .. $CurDepth - 1) { $DevAttachedRecursive{$DevFile}{$ParentDev{$_}} = 1; $DevAttachedRecursive_R{$ParentDev{$_}}{$DevFile} = 1; } if($CurDepth > 0) { $DevAttached{$DevFile} = $ParentDev{$CurDepth - 1}; $DevAttached_R{$ParentDev{$CurDepth - 1}}{$DevFile} = 1; } } } } my $FreeBSD7 = (defined $Sys{"Freebsd_release"} and $Sys{"Freebsd_release"} < 8.0); my $Dmesg = ""; if($Opt{"FixProbe"}) { $Dmesg = readFile($FixProbe_Logs."/dmesg"); if(length($Dmesg) <= 1024 and -e $FixProbe_Logs."/dmesg.1") { $Dmesg = readFile($FixProbe_Logs."/dmesg.1"); } } elsif(checkCmd("dmesg")) { listProbe("logs", "dmesg"); my $DmesgBoot = "/var/run/dmesg.boot"; if(isBSD() and -e $DmesgBoot) { $Dmesg = readFile($DmesgBoot); } else { $Dmesg = runCmd("dmesg 2>&1"); } my $Messages = "/var/log/messages"; if(not isBSD() and -e $Messages and -r $Messages) { if(index($Dmesg, "] Command line:") == -1) { my $KernelMessages = runCmd("grep -I ' kernel: \\[' $Messages | sed 's/.* kernel: \\[/\\[/g'"); $KernelMessages=~s/(.|\n)+(\[ 0.000000\] Linux version)/$2/g; # last boot only if(length($KernelMessages)) { $Dmesg = $KernelMessages; } } } $Dmesg = hideDmesg($Dmesg); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/dmesg", $Dmesg); } } my %PciNumDev = (); my %PciPPb = (); my %UsbAddrDev = (); my %UsbAddrClass = (); if(isBSD()) { if($Dmesg=~/<(PS\/2 Mouse)>.* on (\w+)\d+/) { $HW{"ps/2:mouse"} = {"Device"=>$1, "Driver"=>$2, "Type"=>"mouse"}; } if($Dmesg=~/<(AT Keyboard)>.* on (\w+)\d+/) { $HW{"ps/2:keyboard"} = {"Device"=>$1, "Driver"=>$2, "Type"=>"keyboard"}; } if(isOpenBSD()) { if(not $Bios_ID and $Dmesg=~/bios0: vendor (.+) version "(.+)" date (.+)/) { $Bios_ID = registerBIOS({"Vendor"=>fmtVal($1), "Version"=>$2, "Release Date"=>$3}); } } if(not isOpenBSD() and not isNetBSD()) { my @DrmMap = ($Dmesg=~/(\w+\d+: .+ on vgapci\d+)/g); foreach my $Dr (@DrmMap) { if($Dr=~/\A(\w+\d+): .+ on (vgapci\d+)\Z/) { $DrmAttached{$2}{$1} = 1; } } my @DrmMap2 = ($Dmesg=~/(Initialized .+ for drmn\d+)/g); foreach my $Dr (@DrmMap2) { if($Dr=~/\AInitialized (\w+) .+ for (drmn\d+)\Z/) { $DrmAttached{$2}{$1} = 1; } } my @DrmMap3 = ($Dmesg=~/(drmn\d+: .+)/g); foreach my $Dr (@DrmMap3) { if($Dr=~/\A(drmn\d+): (.+)\Z/) { my ($DrmFile, $DrmDesc) = ($1, $2); if($DrmDesc=~/amdgpu/) { $DrmAttached{$DrmFile}{"amdgpu"} = 1; } } } } my @DriversMap = ($Dmesg=~/(\w+\d+ at \w+\d+)/g); foreach my $Dr (@DriversMap) { if($Dr=~/\A(\w+\d+) at (\w+\d+)\Z/) { my ($DevFile, $Parent) = ($1, $2); $DevAttached{$DevFile} = $Parent; $DevAttached_R{$Parent}{$DevFile} = 1; } } foreach my $DevFile (keys(%DevAttached)) { my $CurDevFile = $DevFile; my %Recur = (); while(defined $DevAttached{$CurDevFile}) { if(defined $Recur{$CurDevFile}) { last; } $DevAttachedRecursive{$DevFile}{$DevAttached{$CurDevFile}} = 1; $Recur{$CurDevFile} = 1; $CurDevFile = $DevAttached{$CurDevFile}; } } if(isOpenBSD() or (isNetBSD() and $Sys{"System_version"} < 9.0)) { my @AllPpb = ($Dmesg=~/\n(pci\d+ at ppb\d+ bus \d+)/g); foreach my $PciLine (@AllPpb) { if($PciLine=~/pci(\d+) at ppb\d+ bus (\d+)/) { $PciPPb{$1} = $2; } } my @AllPci = ($Dmesg=~/\n(\w+\d+ at pci\d+ dev \d+ function \d+)/g); # others are not configured foreach my $PciLine (@AllPci) { if($PciLine=~/\A(\w+\d+) at pci(\d+) dev (\d+) function (\d+)\Z/) { my $DevFile = $1; my ($Pn1, $Pn2, $Pn3) = ($2, $3, $4); if(defined $PciPPb{$Pn1}) { $Pn1 = $PciPPb{$Pn1}; } $PciNumDev{$Pn1.":".$Pn2.":".$Pn3} = $DevFile; } } } if(isNetBSD() or (isOpenBSD() and $Sys{"System_version"} < 6.3)) { my @AllUsb = ($Dmesg=~/[ \n](\w+\d+ at \w+\d+.+? addr \d+)/g); # others are not configured foreach my $UsbLine (@AllUsb) { if($UsbLine=~/\A(\w+\d+) at (\w+\d+).+? addr (\d+)\Z/) { my ($DevFile, $Parent, $UsbAddr) = ($1, $2, $3); $UsbAddrDev{$Parent}{$UsbAddr} = $DevFile; } } my @DevUsb = ($Dmesg=~/[ \n](\w+\d+: .+? addr \d+)/g); # others are not configured foreach my $UsbLine (@DevUsb) { if($UsbLine=~/\A(\w+\d+): .+? addr (\d+)\Z/) { my ($DevFile, $UsbAddr) = ($1, $2); $UsbAddrDev{$DevFile}{$UsbAddr} = $DevFile; } } } elsif($FreeBSD7) { my @AllUsb = ($Dmesg=~/\n(\w+\d+: .+? class [a-f\d]+\/[a-f\d]+.+? addr \d+.+? on \w+\d+)/g); foreach my $UsbLine (@AllUsb) { if($UsbLine=~/\A(\w+\d+): .+? class ([a-f\d]+)\/([a-f\d]+).+? addr (\d+).+? on (\w+\d+)\Z/) { my ($DevFile, $C1, $C2, $UsbAddr, $Parent) = ($1, $2, $3, $4, $5); $UsbAddrDev{$Parent}{$UsbAddr} = $DevFile; $UsbAddrClass{$Parent}{$UsbAddr} = devID((fNum($C1), fNum($C2))); } } } foreach my $D (keys(%HDD)) { my $DFile = basename($D); if($Dmesg=~/\b\Q$DFile\E: <([^<>]+?)>/) { $HDD_Info{$DFile}{"Title"} = $1; } } } my $Sensors = ""; if($Opt{"FixProbe"}) { $Sensors = readFile($FixProbe_Logs."/sensors"); } elsif(enabledLog("sensors") and checkCmd("sensors")) { listProbe("logs", "sensors"); $Sensors = runCmd("sensors 2>/dev/null"); writeLog($LOG_DIR."/sensors", $Sensors); } if($Sensors) { $Sys{"Cpu_temp"} = undef; $Sys{"Cpu_fan"} = undef; foreach my $S (split(/\n\n/, $Sensors)) { if($S=~/k\d+temp-pci|acpitz|cpu\d+_thermal/) { if(not $Sys{"Cpu_temp"} and $S=~/(temp1|Tctl):\s*\+([\d\.]+)/) { $Sys{"Cpu_temp"} = $2; } } elsif($S=~/dell_smm|-(isa|i2c)-/) { my $FanSpeed = undef; foreach my $F ($S=~/(fan\d+|Left side|Right side)\s*:\s*([\d\.]+)/g) { if($F=~/[a-z]/i) { next; } if(not $FanSpeed or $F>$FanSpeed) { $FanSpeed = $F; } } if(defined $FanSpeed) { $Sys{"Cpu_fan"} = $FanSpeed; } foreach my $Sp (split(/\n/, $S)) { if($Sp=~/CPU/ and $Sp=~/temp\d+:\s*\+([\d\.]+)/) { $Sys{"Cpu_temp"} = $1; } } } } if($Sensors=~/(Physical|Package) id 0:\s*\+([\d\.]+)/) { $Sys{"Cpu_temp"} = $2; } else { my $CoreTemp = undef; foreach my $T ($Sensors=~/(Core \d+|Core\d+ Temp):\s*\+([\d\.]+)/g) { if($T=~/[a-z]/i) { next; } if(not $CoreTemp or $T>$CoreTemp) { $CoreTemp = $T; } } if(defined $CoreTemp) { $Sys{"Cpu_temp"} = $CoreTemp; } elsif($Sensors=~/(CPU|CPU Temperature):\s*\+([\d\.]+)/) { $Sys{"Cpu_temp"} = $2; } } if($Sensors=~/(Processor Fan|cpu_fan|CPU FAN Speed|Exhaust)\s*:\s*([\d\.]+)/) { $Sys{"Cpu_fan"} = $2; } if($Sys{"Cpu_temp"} eq "0") { $Sys{"Cpu_temp"} = undef; } if(not defined $Sys{"Cpu_temp"}) { # printMsg("INFO", "failed to identify CPU temperature"); } } my $Uptime = ""; if($Opt{"FixProbe"}) { $Uptime = readFile($FixProbe_Logs."/uptime"); } elsif(enabledLog("uptime") and checkCmd("uptime")) { listProbe("logs", "uptime"); $Uptime = runCmd("uptime"); writeLog($LOG_DIR."/uptime", $Uptime); } if($Uptime) { chomp($Uptime); if($Uptime=~/load average:\s+(.+?)\Z/) { $Sys{"Load_average"} = $1; } } my $Geom = ""; if($Opt{"FixProbe"}) { $Geom = readFile($FixProbe_Logs."/geom"); } elsif(enabledLog("geom") and checkCmd("geom")) { listProbe("logs", "geom"); $Geom = runCmd("geom disk list 2>/dev/null"); $Geom = encryptSerials($Geom, "ident"); $Geom=~s/(lunid:\s*[a-f\d]{7})[a-f\d]{9}\n/$1...\n/g; # WWN $Geom=~s/( SN ).+?( MFG )/$1...$2/g; writeLog($LOG_DIR."/geom", $Geom); } foreach my $DriveInfo (split(/\n\n/, $Geom)) { if($DriveInfo=~/Geom name:\s+(.+)/) { my $DevFile = $1; if($DevFile=~/\Acd\d\Z/) { if(not $CDROM_ID and $DriveInfo=~/descr:\s+(.+)/) { $CDROM_ID = registerCdrom($1, $DevFile); } } elsif($DevFile=~/\Afd\d\Z/) { # TODO: detect floppy } else { $HDD{"/dev/".$DevFile} = 0; } } } my $Sysinfo = ""; if($Opt{"FixProbe"}) { $Sysinfo = readFile($FixProbe_Logs."/sysinfo"); } elsif(enabledLog("sysinfo") and checkCmd("sysinfo")) { listProbe("logs", "sysinfo"); $Sysinfo = runCmd("sysinfo -a -v 1 -c -i 2>/dev/null"); $Sysinfo = encryptUUIDs($Sysinfo); $Sysinfo = encryptMACs($Sysinfo); $Sysinfo = hideIPs($Sysinfo); $Sysinfo=~s/(Serial Number\s+)[^\s]+/$1.../g; if($Sysinfo) { writeLog($LOG_DIR."/sysinfo", $Sysinfo); } } if($Sysinfo) { if($Sysinfo=~/Total real memory available:\s+(\d+) MB/) { $Sys{"Ram_total"} = $1*1024; } if($Sysinfo=~/Logically used memory:\s+(\d+) MB/) { $Sys{"Ram_used"} = $1*1024; } } # UDEV my $Udevadm = ""; if($Opt{"FixProbe"}) { $Udevadm = readFile($FixProbe_Logs."/udev-db"); if(not $Udevadm) { # support for old probes $Udevadm = readFile($FixProbe_Logs."/udevadm"); } if(not $Udevadm) { # support for sdio $Udevadm = readFile($FixProbe_Logs."/sdio"); } } elsif(checkCmd("udevadm") and $Opt{"HWLogs"}) { listProbe("logs", "udev-db"); $Udevadm = runCmd("udevadm info --export-db 2>/dev/null"); $Udevadm = hideTags($Udevadm, "ID_NET_NAME_MAC|ID_SERIAL|ID_SERIAL_SHORT|DEVLINKS|ID_WWN|ID_WWN_WITH_EXTENSION"); $Udevadm=~s/(by\-id\/(ata|usb|nvme|wwn)\-).+/$1.../g; $Udevadm = encryptUUIDs($Udevadm); $Udevadm = encryptSerials($Udevadm, "SERIAL_NUMBER"); if(enabledLog("udev-db")) { writeLog($LOG_DIR."/udev-db", $Udevadm); } else { my $ExtraDevs = ""; foreach my $UL (split(/\n\n/, $Udevadm)) { if($UL=~/sdio/) { $ExtraDevs .= $UL."\n\n"; } } $Udevadm = $ExtraDevs; if($ExtraDevs) { writeLog($LOG_DIR."/sdio", $ExtraDevs); } } } my %HDD_Serial = (); foreach my $Info (split(/\n\n/, $Udevadm)) { my %DInfo = ""; foreach my $Line (split(/\n/, $Info)) { if($Line=~/E:\s+(\w+)\=(.+)/) { $DInfo{$1} = $2; } } my %Device = (); my $Bus = undef; my ($V, $D) = (); my $ID = undef; if($DInfo{"DEVTYPE"} eq "disk" and $DInfo{"ID_TYPE"} eq "disk") { $HDD_Serial{$DInfo{"ID_MODEL"}}{$DInfo{"ID_SERIAL_SHORT"}} = 1; } if($DInfo{"SUBSYSTEM"} eq "sdio") { $Bus = "sdio"; if($DInfo{"SDIO_ID"}=~/(\w\w\w\w)\:(\w\w\w\w)/) { ($V, $D) = (lc($1), lc($2)); if(not keys(%SdioInfo)) { readSdioIds_Sys(); } $Device{"Vendor"} = $SdioVendor{$V}; $Device{"Device"} = $SdioInfo{$V}{$D}; $ID = devID($V, $D); } $Device{"Class"} = $DInfo{"SDIO_CLASS"}; if($Device{"Device"}=~/WLAN|Wireless/i) { $Device{"Class"} = "07"; } $Device{"Driver"} = $DInfo{"DRIVER"}; $Device{"Type"} = getSdioType($Device{"Class"}); } if($ID) { $ID = fmtID($ID); if(not $HW{$Bus.":".$ID}) { $HW{$Bus.":".$ID} = \%Device; } } } # fix incomplete HDD ids (if not root) if((not $Admin or $Opt{"FixProbe"}) and keys(%HDD_Serial)) { foreach my $ID (sort keys(%HW)) { if($ID=~/\Aide:(.+)/) { my $Name = $HW{$ID}{"Device"}; foreach my $FN (sort keys(%HDD_Serial)) { if($FN=~/\Q$Name\E(.+)/i) { my $Missed = $1; foreach my $Ser (sort keys(%{$HDD_Serial{$FN}})) { my $NewID = $ID.devID($Missed)."-serial-".devID($Ser); $HW{$NewID} = $HW{$ID}; $HW{$NewID}{"Device"}=~s/(\Q$Name\E)/$1$Missed/; countDevice($NewID, $HW{$NewID}{"Type"}); } delete($HW{$ID}); } } } } } # PCI (all) my $Lspci_A = ""; if($Opt{"FixProbe"}) { $Lspci_A = readFile($FixProbe_Logs."/lspci_all"); } elsif(enabledLog("lspci_all") and checkCmd("lspci")) { listProbe("logs", "lspci_all"); my $PciLink = createIDsLink("pci"); $Lspci_A = runCmd("lspci -vvnn 2>&1"); if($PciLink) { unlink($PciLink); } $Lspci_A=~s/(Serial Number:?\s+|Manufacture ID:\s+).+/$1.../gi; if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lspci_all", $Lspci_A); } } if(isBSD()) { # collect but do not parse $Lspci_A = ""; } foreach my $Info (split(/\n\n/, $Lspci_A)) { my ($V, $D) = (); my @ID = (); if($Info=~/\w+:\w+\.\w\s+(.*?)\s*\[\w+\]:.*?\[(\w+)\:(\w+)\]/) { ($V, $D) = ($2, $3); } if($Info=~/Subsystem\:.*?\[(\w+)\:(\w+)\]/i) { my ($SV, $SD) = ($1, $2); if($SV ne "0000" or $SD ne "0000") { push(@ID, $SV, $SD); } } my $ID = devID($V, $D, @ID); if($V and $D and @ID) { $LongID{"pci:".devID($V, $D)}{$ID} = 1; } } # PCI my $Lspci = ""; if($Opt{"FixProbe"}) { $Lspci = readFile($FixProbe_Logs."/lspci"); } else { if(checkCmd("lspci")) { listProbe("logs", "lspci"); my $PciLink = createIDsLink("pci"); if(isBSD()) { $Lspci = runCmd("lspci -vmnn 2>&1"); } else { $Lspci = runCmd("lspci -vmnnk 2>&1"); } if($PciLink) { unlink($PciLink); } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lspci", $Lspci); } } if(isBSD()) { # collect but do not parse $Lspci = ""; } foreach my $Info (split(/\n\n/, $Lspci)) { my %Device = (); my (@IDs, @Class) = (); while($Info=~s/(\w+):\s*(.*)//) { if($1 eq "Rev") { next; } if($1 eq "Device" and not defined $Device{$1}) { $Device{"SysFsId"} = $2; } $Device{$1} = $2; } foreach ("Vendor", "Device", "SVendor", "SDevice") { if($Device{$_}=~s/\s*\[(\w{4})\]\s*\Z//) { push(@IDs, $1); } } my $ClassName = $Device{"Class"}; if($ClassName=~s/\s*\[(\w{2})(\w{2})\]//) { push(@Class, $1, $2); if($Device{"ProgIf"}) { push(@Class, $Device{"ProgIf"}); } } delete($Device{"Class"}); delete($Device{"ProgIf"}); cleanValues(\%Device); if($Opt{"PciIDs"} or 1) { if(not $Device{"Device"}) { if(my $Name = $PciInfo{"I"}{$IDs[0]}{$IDs[1]}) { $Device{"Device"} = $Name; } } if(not $Device{"SDevice"}) { if(my $Name = $PciInfo{"D"}{$IDs[0]}{$IDs[1]}{$IDs[2]}{$IDs[3]}) { $Device{"SDevice"} = $Name; } } } $Device{"Class"} = devID(@Class); my $ID = devID(@IDs); if(not $ID) { next; } if($Device{"SysFsId"} and my $OrigID = $DevBySysID{$Device{"SysFsId"}}) { if($ID ne $OrigID) { $ID = $OrigID; } } elsif(my $L_ID = getLongPCI("pci:".$ID)) { my $S_ID = $ID; $ID = $L_ID; if(defined $HW{"pci:".$S_ID}) { $HW{"pci:".$ID} = $HW{"pci:".$S_ID}; delete($HW{"pci:".$S_ID}); } } #if(defined $HW{"pci:".$ID}{"Class"}) { # delete($Device{"Class"}); #} if($Device{"Module"}) { $Device{"Driver"} = $Device{"Module"}; delete($Device{"Module"}); } my $NewDevice = (not defined $HW{"pci:".$ID}); $Device{"Type"} = getDefaultType("pci", $IDs[1], \%Device); if(not $Device{"Type"}) { if($Class[0] eq "02") { # pci:10ec-8136-10ec-8136 $Device{"Type"} = "network"; } } if(not $Device{"Type"}) { if(@Class) { $Device{"Type"} = getClassType("pci", devID(@Class)); } } if(not $Device{"Type"}) { if($Device{"Device"}=~/Ethernet Controller/i) { $Device{"Type"} = "network"; } } if(not $Device{"Type"} and $ClassName ne "Class") { $Device{"Type"} = lc($ClassName); } delete($Device{"SysFsId"}); foreach my $Attr (keys(%Device)) { if(my $Val = $Device{$Attr}) { if(not $NewDevice) { if($Attr eq "Driver") { next; } if($Attr eq "Type" and $HW{"pci:".$ID}{$Attr}) { next; } } $HW{"pci:".$ID}{$Attr} = $Val; } } } my %PciCtlInfo = (); my $PciCtl = ""; if($Opt{"FixProbe"}) { $PciCtl = readFile($FixProbe_Logs."/pcictl"); } elsif(enabledLog("pcictl") and checkCmd("pcictl")) { listProbe("logs", "pcictl"); $PciCtl = runCmd("pcictl pci0 list -N 2>/dev/null"); if(not $PciCtl) { # NetBSD 7.2 $PciCtl = runCmd("pcictl pci0 list 2>/dev/null"); } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/pcictl", $PciCtl); } } foreach my $Info (split(/\n/, $PciCtl)) { if($Info=~/\A([^\s]+):\s+([^\s]+)\s+(.+?)\s+\((\w+)\s+([^,]+).*?\)\s+\[(\w+\d+)\]/) { $PciCtlInfo{$1} = {"Vendor"=>$2, "Device"=>$3, "SubType"=>$4, "Type"=>$5, "File"=>$6}; } elsif($Info=~/\A([^\s]+):\s+([^\s]+)\s+(.+?)\s+\((\w+)\s+([^,]+).*?\)/) { my $BusNum = $1; $PciCtlInfo{$BusNum} = {"Vendor"=>$2, "Device"=>$3, "SubType"=>$4, "Type"=>$5}; my $ShortBus = $BusNum; $ShortBus=~s/0+(\d:)/$1/g; if(my $DevFile = $PciNumDev{$ShortBus}) { $PciCtlInfo{$BusNum}{"File"} = $DevFile; } } } my $PciCtl_n = ""; if($Opt{"FixProbe"}) { $PciCtl_n = readFile($FixProbe_Logs."/pcictl_n"); } elsif(enabledLog("pcictl_n") and checkCmd("pcictl")) { listProbe("logs", "pcictl_n"); $PciCtl_n = runCmd("pcictl pci0 list -Nn 2>/dev/null"); if(not $PciCtl_n) { # NetBSD 7.2 $PciCtl_n = runCmd("pcictl pci0 list -n 2>/dev/null"); } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/pcictl_n", $PciCtl_n); } } foreach my $Info (split(/\n/, $PciCtl_n)) { my ($V, $D, $SV, $SD) = (); my %Device = (); if($Info=~/\A([^\s]+):\s*0x([a-f\d]{4})([a-f\d]{4})\s+\(0x([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})\)/) { if(defined $PciCtlInfo{$1}) { %Device = %{$PciCtlInfo{$1}}; $Device{"Driver"} = $Device{"File"}; $Device{"Driver"}=~s/\d+\Z//; } ($V, $D) = ($3, $2); $Device{"Class"} = devID(($4, $5, $6)); } if($Device{"Class"}) { $Device{"Type"} = getClassType("pci", $Device{"Class"}); } my $ID = devID(($V, $D, $SV, $SD)); my $BusID = "pci:".$ID; foreach my $Attr (keys(%Device)) { $HW{$BusID}{$Attr} = $Device{$Attr}; } countDevice($BusID, $Device{"Type"}); } my $PciDump = ""; if($Opt{"FixProbe"}) { $PciDump = readFile($FixProbe_Logs."/pcidump"); } elsif(enabledLog("pcidump") and checkCmd("pcidump")) { listProbe("logs", "pcidump"); $PciDump = runCmd("pcidump -v 2>/dev/null"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/pcidump", $PciDump); } } $PciDump=~s/\n( \w)/\n\n$1/g; foreach my $Info (split(/\n\n/, $PciDump)) { my ($V, $D, $SV, $SD) = (); my %Device = (); my $BusNum = undef; if($Info=~/\A\s*([\d\:]+?):\s+([^\s]+)\s*(.+?)\n/) { ($Device{"Vendor"}, $Device{"Device"}) = ($2, $3); $BusNum = $1; } if($Info=~/Vendor ID:\s*([a-f\d]{4})/) { $V = $1; } if($Info=~/Product ID:\s*([a-f\d]{4})/) { $D = $1; } if($Info=~/Subsystem Vendor ID:\s*([a-f\d]{4}).+?Product ID:\s*([a-f\d]{4})/) { if($1 ne "0000" or $2 ne "0000") { ($SV, $SD) = ($1, $2); } } my @Class = (); if($Info=~/Class:\s*([a-f\d]{2})/) { push(@Class, $1); } if($Info=~/Subclass:\s*([a-f\d]{2})/) { push(@Class, $1); } if($Info=~/Interface:\s*([a-f\d]{2})/) { push(@Class, $1); } if(@Class) { $Device{"Class"} = devID(@Class); } if($Info=~/Class:\s*[a-f\d]{2}\s+(.+?),/) { $Device{"Type"} = lc($1); } if($Info=~/Subclass:\s*[a-f\d]{2}\s+(.+?),/) { $Device{"SubType"} = $1; } if($Device{"Class"}) { $Device{"Type"} = getClassType("pci", $Device{"Class"}); } if(my $DevFile = $PciNumDev{$BusNum}) { $Device{"File"} = $DevFile; $Device{"Driver"} = $DevFile; $Device{"Driver"}=~s/\d+\Z//; } my $ID = devID(($V, $D, $SV, $SD)); my $BusID = "pci:".$ID; foreach my $Attr (keys(%Device)) { $HW{$BusID}{$Attr} = $Device{$Attr}; } countDevice($BusID, $Device{"Type"}); } my $PciConf = ""; if($Opt{"FixProbe"}) { $PciConf = readFile($FixProbe_Logs."/pciconf"); } elsif(enabledLog("pciconf") and checkCmd("pciconf")) { listProbe("logs", "pciconf"); $PciConf = runCmd("pciconf -l -bcv 2>/dev/null"); # -BbcevV ? $PciConf=~s/\n(\w)/\n\n$1/g; if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/pciconf", $PciConf); } } foreach my $Info (split(/\n\n/, $PciConf)) { my ($V, $D, $SV, $SD) = (); my %Device = (); if($Info=~/chip=0x([a-f\d]{4})([a-f\d]{4})/) { ($D, $V) = ($1, $2); } if($Info=~/card=0x([a-f\d]{4})([a-f\d]{4})/) { if($1 ne "0000") { ($SD, $SV) = ($1, $2); } } if($Info=~/vendor=0x([a-f\d]{4})/) { # FreeBSD 13 $V = $1; } if($Info=~/device=0x([a-f\d]{4})/) { # FreeBSD 13 $D = $1; } if($Info=~/subvendor=0x([a-f\d]{4})/) { # FreeBSD 13 $SV = $1; } if($Info=~/subdevice=0x([a-f\d]{4})/) { # FreeBSD 13 $SD = $1; } if($SD eq "0000") { ($SV, $SD) = (); } if(not $D or not $V) { next; } if($Info=~/class=0x([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/) { $Device{"Class"} = devID(($1, $2, $3)); } if($Info=~/(.+)\@/) { $Device{"Driver"} = $1; $Device{"File"} = $1; $Device{"Files"}{$1} = 1; $Device{"Driver"}=~s/\d+\Z//; if($Device{"Driver"} eq "none") { $Device{"Driver"} = undef; } if($Device{"File"}=~/vgapci/) { $Device{"Driver"} = identifyVideoDriver_BSD($Device{"Driver"}, $Device{"File"}); } } if($Info=~/vendor\s+=\s+'(.+)'/) { $Device{"Vendor"} = $1; } if($Info=~/device\s+=\s+'(.+)'/) { $Device{"Device"} = $1; } if($Info=~/\sclass\s+=\s+(.+)/) { $Device{"Type"} = $1; if($Info=~/subclass\s+=\s+(.+)/) { $Device{"SubType"} = $1; } } if($Device{"Class"}) { $Device{"Type"} = getClassType("pci", $Device{"Class"}); } my $ID = devID(($V, $D, $SV, $SD)); my $BusID = "pci:".$ID; foreach my $Attr (keys(%Device)) { if(ref($HW{$BusID}{$Attr}) eq "HASH" and ref($Device{$Attr}) eq "HASH") { foreach my $SubAttr (keys(%{$Device{$Attr}})) { $HW{$BusID}{$Attr}{$SubAttr} = $Device{$Attr}{$SubAttr}; } } else { $HW{$BusID}{$Attr} = $Device{$Attr}; } } countDevice($BusID, $Device{"Type"}); } if(not $PciConf and not $PciCtl and not $PciDump and $DevInfo) { # NOTE: securelevel=2 foreach my $Line (split(/\n/, $DevInfo)) { if($Line=~/\A\s*(\w+\d+)\s+pnpinfo\s+vendor=0x([a-f\d]{4})\s+device=0x([a-f\d]{4})\s+subvendor=0x([a-f\d]{4})\s+subdevice=0x([a-f\d]{4})\s+class=0x([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/) { my ($DevFile, $V, $D, $SV, $SD, $C1, $C2, $C3) = ($1, $2, $3, $4, $5, $6, $7, $8); my %Device = (); $Device{"File"} = $DevFile; $Device{"Files"}{$DevFile} = 1; $Device{"Driver"} = $DevFile; $Device{"Driver"}=~s/\d+\Z//; $Device{"Class"} = devID(($C1, $C2, $C3)); $Device{"Type"} = getClassType("pci", $Device{"Class"}); my $ID = devID(($V, $D, $SV, $SD)); my $BusID = "pci:".$ID; $HW{$BusID} = \%Device; countDevice($BusID, $Device{"Type"}); } elsif($Line=~/\A\s*(\w+\d+)\s+pnpinfo\s+vendor=0x([a-f\d]{4})\s+product=0x([a-f\d]{4})\s+devclass=0x([a-f\d]{2})\s+devsubclass=0x([a-f\d]{2})\s+devproto=0x([a-f\d]{2})/) { my ($DevFile, $V, $D, $C1, $C2, $C3) = ($1, $2, $3, $4, $5, $6); my %Device = (); $Device{"File"} = $DevFile; $Device{"Files"}{$DevFile} = 1; $Device{"Driver"} = $DevFile; $Device{"Driver"}=~s/\d+\Z//; $Device{"Class"} = devID(($C1, $C2, $C3)); $Device{"Type"} = getClassType("usb", $Device{"Class"}); my $ID = devID(($V, $D)); my $BusID = "usb:".$ID; $HW{$BusID} = \%Device; countDevice($BusID, $Device{"Type"}); } } } # USB my $Lsusb = ""; if($Opt{"FixProbe"}) { $Lsusb = readFile($FixProbe_Logs."/lsusb"); } else { if(checkCmd("lsusb")) { listProbe("logs", "lsusb"); my $UsbLink = createIDsLink("usb"); $Lsusb = runCmd("lsusb -v 2>&1"); if($UsbLink) { unlink($UsbLink); } $Lsusb=~s/(iSerial\s+\d+\s*)[^\s]+$/$1.../mg; $Lsusb = encryptUUIDs($Lsusb); if(length($Lsusb)<60 and $Lsusb=~/unable to initialize/i) { $Lsusb = ""; } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lsusb", $Lsusb); } } if(isBSD()) { # collect but do not parse $Lsusb = ""; } foreach my $Info (split(/\n\n/, $Lsusb)) { my %Device = (); my ($V, $D, @Class) = (); if($Info=~/idVendor[ ]+0x(\w{4})[ ]+(.*)/) { $V = $1; if($2) { $Device{"Vendor"} = fmtVal($2); } } if($Info=~/idProduct[ ]+0x(\w{4})[ ]+(.*)/) { $D = $1; if($2) { $Device{"Device"} = fmtVal($2); } } if($Info=~/bInterfaceClass\s+(\w+)\s+/) { push(@Class, fNum(sprintf('%x', $1))); } if($Info=~/bInterfaceSubClass\s+(\w+)\s+/) { push(@Class, fNum(sprintf('%x', $1))); } if($Info=~/bInterfaceProtocol\s+(\w+)\s+/) { push(@Class, fNum(sprintf('%x', $1))); } $Device{"Class"} = devID(@Class); if(not $V) { # Couldn't open device next; } if(not $D) { # Couldn't open device next; } my $ID = devID($V, $D); #if(defined $HW{"usb:".$ID}{"Class"}) { # delete($Device{"Class"}); #} my $Vendor = $Device{"Vendor"}; my $OldName = $HW{"usb:".$ID}{"Device"}; my $OldVendor = $HW{"usb:".$ID}{"Vendor"}; if($Device{"Device"}=~/\AFlash (Drive|Disk)\Z/i and $OldName) { $Device{"Device"} = $OldName; } else { if($Device{"Device"}) { $Device{"Device"} .= addCapacity($Device{"Device"}, $HW{"usb:".$ID}{"Capacity"}); } } if($Opt{"UsbIDs"}) { if(not $Device{"Device"}) { if(my $Name = $UsbInfo{$V}{$D}) { if($Vendor) { if($Name ne $Vendor) { $Device{"Device"} = $Name; } } else { if($Name ne $OldVendor) { $Device{"Device"} = $Name; } } } } } my $FinalName = $Device{"Device"}; if(not $FinalName) { $FinalName = $OldName; } if($FinalName!~/root hub/i) { if($Info=~/iProduct[ ]+\w+[ ]+(.+)/) { if(my $SubDevice = fmtVal($1)) { if($FinalName) { my $N1 = $FinalName; my $N2 = $SubDevice; $N2=~s/\AUSB //g; if($N1!~/\Q$N2\E/i and $N2!~/\Q$N1\E/i) { $Device{"SDevice"} = $SubDevice; } } else { $Device{"Device"} = $SubDevice; } } } elsif($OldName) { if($FinalName!~/\Q$OldName\E/i and $OldName!~/\Q$FinalName\E/i) { $Device{"SDevice"} = $OldName; } } if($Info=~/iManufacturer[ ]+\w+[ ]+(.+)/) { if(my $SubVendor = fmtVal($1)) { $SubVendor=~s/\AManufacturer\s+//ig; if($Vendor and $SubVendor!~/usb/i and $SubVendor!~/generic/ and $SubVendor ne $Device{"SDevice"} and $SubVendor ne $FinalName) { $Device{"SVendor"} = $SubVendor; } } } } if(not $HW{"usb:".$ID}{"Type"}) { $Device{"Type"} = getDefaultType("usb", $D, \%Device); if(not $Device{"Type"}) { if($Info=~/Camera Sensor/i) { $Device{"Type"} = "camera"; } elsif($Info=~/Uninterruptible Power Supply/i) { $Device{"Type"} = "ups"; } elsif($Info=~/Card Reader/i) { $Device{"Type"} = "card reader"; } } if(not $Device{"Type"}) { if(@Class) { $Device{"Type"} = getClassType("usb", devID(@Class)); } } } cleanValues(\%Device); foreach my $Attr (keys(%Device)) { if(my $Val = $Device{$Attr}) { $HW{"usb:".$ID}{$Attr} = $Val; } } } my $UsbDevs = ""; if($Opt{"FixProbe"}) { $UsbDevs = readFile($FixProbe_Logs."/usbdevs"); } elsif(enabledLog("usbdevs") and checkCmd("usbdevs")) { listProbe("logs", "usbdevs"); $UsbDevs = runCmd("usbdevs -vv 2>/dev/null"); $UsbDevs=~s/(serial\s+)([^\s]+)/$1.../g; if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/usbdevs", $UsbDevs); } } $UsbDevs=~s/(addr |Controller |\s+port \d+ addr)/\n$1/g; my $UsbController = undef; my @UsbCtrls = (); foreach my $Info (split(/\n\n/, $UsbDevs)) { my ($V, $D) = (); my %Device = (); my $UsbAddr = undef; if($Info=~/Controller (.+):/) { push(@UsbCtrls, $1); $UsbController = basename($1); next; } if($Info=~/addr\s+(\d+):.*?,\s*([^,]+?)\(0x([a-f\d]{4})\),\s*([^()]+?)\(0x([a-f\d]{4})\)/i) { # NetBSD, FreeBSD < 8.0, OpenBSD < 6.3 ($V, $D) = ($5, $3); $Device{"Vendor"} = $4; $Device{"Device"} = $2; $UsbAddr = $1; if($Info=~/ ([^,]+)\(0x([a-f\d]{2})\),\s*([^,]+?)\(0x([a-f\d]{2})\)/i) { $Device{"BSDType"} = lc($1); $Device{"SubType"} = lc($3); $Device{"Class"} = devID(($2, $4)); if($Device{"BSDType"}=~/vendor specific/) { $Device{"BSDType"} = undef; } } } elsif($Info=~/addr\s+\d+:\s*([a-f\d]{4}):([a-f\d]{4})\s+([^,]+),\s*(.+)/i) { # OpenBSD ($V, $D) = ($1, $2); $Device{"Vendor"} = $3; $Device{"Device"} = $4; } my @Drs = ($Info=~/driver:\s*([^\s]+)/g); foreach (@Drs) { $Device{"File"} = $_; $Device{"Driver"} = $_; $Device{"Driver"}=~s/\d+\Z//; } if(not $V and not $D) { next; } if($Device{"Vendor"} eq "vendor $V") { $Device{"Vendor"} = undef; } ($V, $D) = fixRootHub($V, $D, \%Device); $Device{"Type"} = getDefaultType("usb", $D, \%Device); if(not $Device{"Type"} and $Device{"Class"} and $Device{"Class"}!~/\Af[fe]/) { $Device{"Type"} = getClassType("usb", $Device{"Class"}); } if(not $Device{"Type"}) { $Device{"Type"} = $Device{"BSDType"}; } if(not $Device{"File"}) { # NetBSD, FreeBSD < 8.0, OpenBSD < 6.3 my $DevFile = undef; if(defined $UsbAddrDev{$UsbController} and defined $UsbAddrDev{$UsbController}{$UsbAddr}) { $DevFile = $UsbAddrDev{$UsbController}{$UsbAddr}; } if(not $DevFile) { LOOP: foreach my $SDev (keys(%{$DevAttached_R{$UsbController}})) { if(defined $UsbAddrDev{$SDev}{$UsbAddr}) { $DevFile = $UsbAddrDev{$SDev}{$UsbAddr}; last; } foreach my $SSDev (keys(%{$DevAttached_R{$SDev}})) { if(defined $UsbAddrDev{$SSDev}{$UsbAddr}) { $DevFile = $UsbAddrDev{$SSDev}{$UsbAddr}; last LOOP; } foreach my $SSSDev (keys(%{$DevAttached_R{$SSDev}})) { if(defined $UsbAddrDev{$SSSDev}{$UsbAddr}) { $DevFile = $UsbAddrDev{$SSSDev}{$UsbAddr}; last LOOP; } foreach my $SSSSDev (keys(%{$DevAttached_R{$SSSDev}})) { if(defined $UsbAddrDev{$SSSSDev}{$UsbAddr}) { $DevFile = $UsbAddrDev{$SSSSDev}{$UsbAddr}; last LOOP; } } } } } } if($DevFile) { $Device{"File"} = $DevFile; $Device{"Driver"} = $DevFile; $Device{"Driver"}=~s/\d+\Z//; } } if(not $Device{"Class"}) { # FreeBSD < 8.0 if(defined $UsbAddrClass{$UsbController} and defined $UsbAddrClass{$UsbController}{$UsbAddr}) { $Device{"Class"} = $UsbAddrClass{$UsbController}{$UsbAddr}; $Device{"Type"} = getClassType("usb", $Device{"Class"}); } } my $ID = devID(($V, $D)); my $BusID = "usb:".$ID; foreach my $Attr (keys(%Device)) { $HW{$BusID}{$Attr} = $Device{$Attr}; } countDevice($BusID, $Device{"Type"}); } my $UsbCtl = ""; if($Opt{"FixProbe"}) { $UsbCtl = readFile($FixProbe_Logs."/usbctl"); } elsif(enabledLog("usbctl") and checkCmd("usbctl")) { listProbe("logs", "usbctl"); foreach (@UsbCtrls) { $UsbCtl .= runCmd("usbctl -f $_ 2>/dev/null")."\n"; } $UsbCtl=~s/(iSerialNumber=)([^\s]+)/$1.../g; if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/usbctl", $UsbCtl); } } if(not (isOpenBSD() or $FreeBSD7 or isNetBSD())) { $UsbCtl = ""; } $UsbCtl=~s/(-----\n)(\w)/$1\n$2/g; $UsbCtl=~s/(\nDEVICE addr )/\n$1/g; foreach my $Info (split(/-----|\n\n\n/, $UsbCtl)) { # detect USB class on OpenBSD if($Info=~/idVendor=0x([a-f\d]{4})\s+idProduct=0x([a-f\d]{4})/) { my ($V, $D) = ($1, $2); my ($C1, $C2, $C3) = (); if($Info=~/bDeviceClass=([^\s+]+)\s+bDeviceSubClass=([^\s+]+)/) { ($C1, $C2) = ($1, $2); if($Info=~/bDeviceProtocol=([^\s+]+)/) { $C3 = $1; } } if($Info=~/bInterfaceClass=([^\s+]+)\s+bInterfaceSubClass=([^\s+]+)/) { ($C1, $C2) = ($1, $2); if($Info=~/bInterfaceProtocol=([^\s+]+)/) { $C3 = $1; } } my %Device = (); if($Info=~/iProduct=.*?\(([^()]+?)\)/) { $Device{"Device"} = $1; } ($V, $D) = fixRootHub($V, $D, \%Device); my $BusId = "usb:".devID(($V, $D)); my $Class = devID((fNum(sprintf('%x', $C1)), fNum(sprintf('%x', $C2)), fNum(sprintf('%x', $C3)))); if($Class eq "03-01-01") { if(defined $DevAttached_R{$HW{$BusId}{"File"}}{"ums0"}) { $Class = "03-01-02" } } my $OldClass = $HW{$BusId}{"Class"}; $HW{$BusId}{"Class"} = $Class; if((not $HW{$BusId}{"Type"} or $OldClass ne $Class) and $Class!~/\Af[fe]/) { $HW{$BusId}{"Type"} = getDefaultType("usb", $D, $HW{$BusId}); if(not $HW{$BusId}{"Type"}) { $HW{$BusId}{"Type"} = getClassType("usb", $Class); } } } } my $UsbConf = ""; if($Opt{"FixProbe"}) { $UsbConf = readFile($FixProbe_Logs."/usbconfig"); } elsif(enabledLog("usbconfig") and checkCmd("usbconfig")) { listProbe("logs", "usbconfig"); $UsbConf = runCmd("usbconfig dump_device_desc show_ifdrv dump_all_config_desc 2>/dev/null"); $UsbConf = hideTags($UsbConf, "iSerialNumber"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/usbconfig", $UsbConf); } } my @UsbConfBlocks = (); if($UsbConf=~/Configuration index/) { $UsbConf=~s/\n(\n\n\n[ ]+Interface \d)/$1/g; @UsbConfBlocks = split(/\n\n\n(\n)+/, $UsbConf); } else { # Support for old probes $UsbConf=~s/(\n\w)/\n$1/g; @UsbConfBlocks = split(/\n\n\n/, $UsbConf); } foreach my $Info (@UsbConfBlocks) { my ($V, $D) = (); my %Device = (); if($Info=~/idVendor\s*=\s*0x([a-f\d]{4})/) { $V = $1; } if($Info=~/idProduct\s*=\s*0x([a-f\d]{4})/) { $D = $1; } if(not $V and not $D) { next; } if($Info=~/iManufacturer\s*=.*<(.+)>/) { $Device{"Vendor"} = $1; } if($Info=~/iProduct\s*=.*<(.+)>/) { $Device{"Device"} = $1; } if($Info=~/:\s+((\w+?)\d+):\s+/) { $Device{"File"} = $1; $Device{"Driver"} = $2; } if($Info=~/bDeviceClass\s*=.*<(.+)>/) { $Device{"BSDType"} = lc($1); if($Device{"BSDType"}=~/probed by (.+) class|vendor specific/) { $Device{"BSDType"} = undef; } } my ($C1, $C2, $C3) = (); if($Info=~/bDeviceClass\s*=\s*0x0*([a-f\d]{2})/) { $C1 = $1; } if($Info=~/bDeviceSubClass\s*=\s*0x0*([a-f\d]{2})/) { $C2 = $1; } if($Info=~/bDeviceProtocol\s*=\s*0x0*([a-f\d]{2})/) { $C3 = $1; } if($Info=~/bInterfaceClass\s*=\s*0x0*([a-f\d]{2})/) { $C1 = $1; } if($Info=~/bInterfaceSubClass\s*=\s*0x0*([a-f\d]{2})/) { $C2 = $1; } if($Info=~/bInterfaceProtocol\s*=\s*0x0*([a-f\d]{2})/) { $C3 = $1; } cleanValues(\%Device); ($V, $D) = fixRootHub($V, $D, \%Device); $Device{"Class"} = devID(($C1, $C2, $C3)); if($Device{"Class"}=~/\Af[fe]/ and my $ExtName = $UsbInfo{$V}{$D}) { $Device{"Device"} = $ExtName; } elsif(not $Device{"BSDType"}) { if(my @Details = $Info=~/iInterface\s*=\s*.*?<(.+)>/g) { @Details = grep {$_!~/no string|Bulk|Interface/} @Details; if(@Details) { $Device{"Device"} = join(" ", $Device{"Device"}, @Details); } } } $Device{"Type"} = getDefaultType("usb", $D, \%Device); if(not $Device{"Type"} and $Device{"Class"} and $Device{"Class"}!~/\Af[fe]/) { $Device{"Type"} = getClassType("usb", $Device{"Class"}); } if(not $Device{"Type"}) { $Device{"Type"} = $Device{"BSDType"}; } my $ID = devID(($V, $D)); my $BusID = "usb:".$ID; foreach my $Attr (keys(%Device)) { $HW{$BusID}{$Attr} = $Device{$Attr}; } countDevice($BusID, $Device{"Type"}); } my $Usb_devices = ""; if($Opt{"FixProbe"}) { $Usb_devices = readFile($FixProbe_Logs."/usb-devices"); } elsif(enabledLog("usb-devices")) { if(checkCmd("usb-devices")) { listProbe("logs", "usb-devices"); $Usb_devices = runCmd("usb-devices -v 2>&1"); $Usb_devices = encryptSerials($Usb_devices, "SerialNumber", "usb-devices"); } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/usb-devices", $Usb_devices); } } foreach my $Info (split(/\n\n/, $Usb_devices)) { my ($V, $D) = (); if($Info=~/Vendor=([^ ]+)/) { $V = $1; } if($Info=~/ProdID=([^ ]+)/) { $D = $1; } my %Driver = (); while($Info=~s/Driver=([\w\-]+)//) { my $Dr = $1; $Dr=~s/\-/_/g; if(not defined $Driver{$Dr}) { $Driver{$Dr} = keys(%Driver); } } if($V and $D) { my $ID = devID($V, $D); if(defined $HW{"usb:".$ID} and not defined $HW{"usb:".$ID}{"Driver"}) { if(my @Dr = keys(%Driver)) { @Dr = sort {$Driver{$a} <=> $Driver{$b}} @Dr; $HW{"usb:".$ID}{"Driver"} = join(", ", @Dr); } } } } # Fix incorrectly detected drivers foreach my $ID (sort keys(%HW)) { my $Driver = $HW{$ID}{"Driver"}; if(not $Driver) { next; } my %Drivers = (); my $Num = 0; foreach my $Dr (split(/\,\s+/, $Driver)) { $Drivers{$Dr} = $Num++; } if(defined $Drivers{"nouveau"}) { if(defined $WorkMod{"nvidia"}) { delete($Drivers{"nouveau"}); $Drivers{"nvidia"} = 1; } } if(defined $Drivers{"nvidia"} and defined $WorkMod{"nvidia"}) { foreach ("nvidiafb", "nvidia_drm") { if(defined $Drivers{$_}) { delete($Drivers{$_}); } } } if(defined $Drivers{"radeon"}) { if(defined $Drivers{"amdgpu"} and defined $WorkMod{"amdgpu"}) { delete($Drivers{"radeon"}); } } foreach my $Dr (@G_DRIVERS) { if(defined $Drivers{$Dr}) { if(defined $KernMod{$Dr} and not defined $WorkMod{$Dr}) { delete($Drivers{$Dr}); } } } foreach my $Dr (sort keys(%Drivers)) { if($Dr eq "nvidiafb") { if(defined $KernMod{$Dr} and not defined $WorkMod{$Dr}) { delete($Drivers{$Dr}); } } elsif($Dr=~/\Anvidia/) { # nvidia346, nvidia_375, etc. if(defined $KernMod{$Dr} and not defined $WorkMod{"nvidia"}) { delete($Drivers{$Dr}); } } } if($Driver ne "wl" and defined $Drivers{"wl"}) { if(not defined $WorkMod{"wl"}) { delete($Drivers{"wl"}); } } $HW{$ID}{"Driver"} = join(", ", sort {$Drivers{$a}<=>$Drivers{$b}} keys(%Drivers)); } # Fix incorrectly detected types foreach my $ID (sort keys(%HW)) { my $Type = $HW{$ID}{"Type"}; my $Type_New = undef; if($Type eq "cdrom" or $Type eq "serial controller") { my $Device = $HW{$ID}{"Device"}; my $SDevice = $HW{$ID}{"SDevice"}; my $Vendor = $HW{$ID}{"Vendor"}; if($Vendor=~/Huawei/i and ($Device=~/modem|mobile|broadband/i or $SDevice=~/modem|mobile|broadband/i)) { $Type_New = "modem"; } } if(defined $Type_New) { $HW{$ID}{"Type"} = $Type_New; } } # Fix status of graphics cards, network devices, etc. my $PCIDrivers = 0; foreach my $ID (sort keys(%HW)) { if($HW{$ID}{"Driver"} and $ID=~/\Apci/) { $PCIDrivers = 1; last; } } my $BSD = isBSD(); foreach my $ID (sort keys(%HW)) { my $DevType = $HW{$ID}{"Type"}; my $Dr = $HW{$ID}{"Driver"}; my $Class = $HW{$ID}{"Class"}; my $Count = getDeviceCount($ID); if(not $Count) { $Count = 1; } if($BSD) { if($DevType eq "network") { if($ID=~/pci:/) { if($Sys{"NICs"}) { $Sys{"NICs"} += $Count; } else { $Sys{"NICs"} = $Count; } } } elsif($DevType=~/camera|video/) { # user-space drivers next; } } if($DevType eq "graphics card") { if($ID=~/\w+:(.+?)\-/) { $GraphicsCards{$1}{$ID} = $Dr; $GraphicsCards_All{$ID} = $Dr; if($Dr) { $GraphicsCards_InUse{$ID} = $Dr; } } } elsif(grep { $DevType eq $_ } ("bluetooth", "camera", "card reader", "chipcard", "communication controller", "dvb card", "fingerprint reader", "smartcard reader", "firewire controller", "flash memory", "modem", "multimedia controller", "network", "sd host controller", "sound", "storage", "system peripheral", "tv card", "video", "wireless", "unclassified device", "unassigned class", "vendor specific", "wireless controller") or not $DevType) { if(grep { $DevType eq $_ } ("sd host controller", "system peripheral") and $HW{$ID}{"Vendor"}=~/Intel/) { next; } if(grep { $DevType eq $_ } ("unclassified device", "unassigned class") and $HW{$ID}{"Device"}=~/ MROM /) { next; } if(grep { $Class eq $_ } ("06-04-01")) { next; } if($ID=~/\A(usb|pci|ide|sdio):/) { if($1 ne "pci" or $PCIDrivers) { if(not $HW{$ID}{"Driver"}) { $HW{$ID}{"Status"} = "failed"; } if($DevType eq "network") { my @Files = ($HW{$ID}{"File"}); if(defined $HW{$ID}{"Files"}) { push(@Files, keys(%{$HW{$ID}{"Files"}})); } foreach my $File (@Files) { my @TotalCards = grep {$HW{$_}{"Type"} eq "network"} keys(%HW); if($HW{$ID}{"Driver"}) { if(defined $UsedNetworkDev{$File} or (keys(%UsedNetworkDev)==1 and $#TotalCards==0)) { $HW{$ID}{"Status"} = "works"; $HW{$ID}{"Link detected"} = "yes"; } } if($EthernetInterface{$File} or $HW{$ID}{"Device"}=~/Ethernet/i) { $HW{$ID}{"Kind"} = "Ethernet"; } elsif($WLanInterface{$File} or $HW{$ID}{"Device"}=~/802\.11|Wireless|Wi-?Fi/i) { $HW{$ID}{"Kind"} = "WiFi"; } elsif($HW{$ID}{"Device"}=~/broadband/i) { $HW{$ID}{"Kind"} = "Modem"; } if($HW{$ID}{"Class"}=~/\A02-80/ or $HW{$ID}{"Device"}=~/Wireless/ or $File=~/\A(ath|iwn)/) { if($File=~s/\A[^\d]+/wlan/) { if($HW{$ID}{"Driver"}) { if(defined $UsedNetworkDev{$File} or (keys(%UsedNetworkDev)==1 and $#TotalCards==0)) { $HW{$ID}{"Status"} = "works"; $HW{$ID}{"Link detected"} = "yes"; } } if($EthernetInterface{$File}) { $HW{$ID}{"Kind"} = "Ethernet"; } elsif($WLanInterface{$File}) { $HW{$ID}{"Kind"} = "WiFi"; } } } } } } } if($HW{$ID}{"Status"} eq "works") { setAttachedStatus($ID, "works"); } } } foreach my $V (sort keys(%GraphicsCards)) { foreach my $ID (sort keys(%{$GraphicsCards{$V}})) { if(index($HW{$ID}{"Device"}, "Secondary")!=-1) { next; } if($HW{$ID}{"Class"} eq "03-80" and keys(%{$GraphicsCards{$V}})>=2) { next; } if(not $GraphicsCards{$V}{$ID} and ($Sys{"Type"}!~/$MOBILE_TYPE/ or keys(%GraphicsCards_All)>=3)) { # not a hybrid graphics # or external full-size PCI card attached to the notebook if(grep { $GraphicsCards_All{$_} } keys(%GraphicsCards_All)) { # some of them are connected next; } } if(grep {$V eq $_} ("1002", "8086")) { if(not $GraphicsCards{$V}{$ID}) { $HW{$ID}{"Status"} = "failed"; } } elsif($V eq "10de") { if(not defined $GraphicsCards{"8086"}) { if(not $GraphicsCards{$V}{$ID}) { $HW{$ID}{"Status"} = "failed"; } } } } } # DMI my $Dmidecode = ""; if($Opt{"FixProbe"}) { $Dmidecode = readFile($FixProbe_Logs."/dmidecode"); } else { if(checkCmd("dmidecode")) { listProbe("logs", "dmidecode"); $Dmidecode = runCmd("dmidecode 2>&1"); $Dmidecode = encryptSerials($Dmidecode, "UUID"); $Dmidecode = encryptSerials($Dmidecode, "Serial Number"); $Dmidecode = encryptSerials($Dmidecode, "Asset Tag"); if(length($Dmidecode) < 2*$EMPTY_LOG_SIZE and index($Dmidecode, "/dev/mem: Permission denied") != -1) { $Dmidecode = undef; } if(not $Dmidecode and isOpenBSD()) { printMsg("WARNING", "failed to run dmidecode"); } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/dmidecode", $Dmidecode); } } my $MemIndex = 0; my %MemIDs = (); my ($CPU_Sockets, $CPU_Cores, $CPU_Threads, $CPU_Family, $CPU_ModelNum, $CPU_Family_Name) = (0, 0, 0, undef, undef, undef); foreach my $Info (split(/\n\n/, $Dmidecode)) { my %Device = (); my $D = ""; my $ID = ""; $Info=~s/[ ]{2,}/ /g; if($Info=~/Chassis Information\n/) { # notebook or desktop if($Info=~/Type:[ ]*(.+?)[ ]*(\n|\Z)/) { if(my $CType = getChassisType($1)) { $Sys{"Type"} = $CType; } } } elsif($Info=~/System Information\n/) { if($Info=~/Manufacturer:[ ]*(.+?)[ ]*(\n|\Z)/) { $Sys{"Vendor"} = fmtVal($1); } if($Info=~/Product Name:[ ]*(.+?)[ ]*(\n|\Z)/) { $Sys{"Model"} = $1; } if($Info=~/Version:[ ]*(.+?)[ ]*(\n|\Z)/) { $Sys{"Version"} = $1; } if($Info=~/SKU Number:[ ]*(.+?)[ ]*(\n|\Z)/) { $Sys{"Sku_number"} = $1; } # clear if(emptyProduct($Sys{"Vendor"})) { $Sys{"Vendor"} = ""; } if(emptyProduct($Sys{"Model"})) { $Sys{"Model"} = ""; } if(emptyProduct($Sys{"Version"})) { $Sys{"Version"} = ""; } ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); $Sys{"Model"} = fixModel($Sys{"Vendor"}, $Sys{"Model"}, $Sys{"Version"}); } elsif($Info=~/Memory Device\n/) # $Info=~/Memory Module Information\n/ { while($Info=~s/([\w ]+):[ \t]*(.+?)[ \t]*(\n|\Z)//) { my ($Key, $Val) = ($1, fmtVal($2)); if(grep { lc($Val) eq $_ } ("unknown", "other")) { next; } if(grep { $Val=~/$_/i } ("OUT OF SPEC", "NOT AVAILABLE")) { next; } if($Key eq "Manufacturer") { $Device{"Vendor"} = nameID($Val, "memory"); } elsif($Key eq "Part Number") { $Device{"Device"} = $Val; } elsif($Key eq "Serial Number") { $Device{"Serial"} = $Val; } elsif($Key eq "Type") { if($Val eq "Reserved") { next; } if(my $FF = $Device{"FF"}) { $Val=~s/ \Q$FF\E\Z//; } $Device{"Kind"} = $Val; } elsif($Key eq "Size") { $Device{"Size"} = $Val; } elsif($Key eq "Speed") { $Device{"Speed"} = $Val; } # Memory Module elsif($Key eq "Installed Size") { $Device{"Size"} = $Val; } elsif($Key eq "Current Speed") { $Device{"Speed"} = $Val; } elsif($Key eq "Form Factor") { $Device{"FF"} = $Val; } elsif($Key eq "Locator" and not $Device{"FF"}) { if($Val=~/\ADIMM/) { $Device{"FF"} = "DIMM"; } } } cleanValues(\%Device); if($Device{"Size"} eq "No Module Installed") { next; } if($Device{"Size"} eq "Not Installed") { next; } if(grep { $Device{"FF"} eq $_ } ("TSOP") # Chip or $Device{"Kind"} eq "Flash") { # TODO: add this kind of devices next; } if($Device{"FF"} eq "DIMM" and $Sys{"Type"}=~/$MOBILE_TYPE/) { $Device{"FF"} = "SODIMM"; } $Device{"Type"} = "memory"; $Device{"Status"} = "works"; my $Inc = 0; if($Device{"Vendor"}) { if($Device{"Vendor"}=~/\A(JEDEC|Unknown) ID:(.+)/) { $Device{"Vendor"} = $2; $Device{"Vendor"}=~s/ //g; } elsif($Device{"Vendor"}=~/\AUnknown - \[0x(.+)\]/) { $Device{"Vendor"} = $1; } elsif($Device{"Vendor"}=~/\AUnknown \((.+)\)/) { $Device{"Vendor"} = $1; } if(defined $JedecVendor{$Device{"Vendor"}} or defined $JedecVendor{uc($Device{"Vendor"})}) { $Device{"Vendor"} = $JedecVendor{$Device{"Vendor"}}; } elsif($Device{"Vendor"}=~/\A0x(.+)\Z/ and defined $JedecVendor{$1}) { $Device{"Vendor"} = $JedecVendor{$1}; } elsif($Device{"Vendor"}=~/\A(0x|)([A-F\d]{4})/i and defined $JedecVendor{$2}) { $Device{"Vendor"} = $JedecVendor{$2}; } } if(not $Device{"Vendor"} or isUnknownRam($Device{"Vendor"}) or isJedecRam($Device{"Vendor"})) { if(my $GuessVendor = guessRamVendor($Device{"Device"})) { $Device{"Vendor"} = $GuessVendor; } } if(not $Device{"Vendor"} or isUnknownRam($Device{"Vendor"}) or isJedecRam($Device{"Vendor"})) { if(defined $RamVendor{$Device{"Device"}}) { $Device{"Vendor"} = $RamVendor{$Device{"Device"}}; } } if(not $Device{"Vendor"}) { $Device{"Vendor"} = "Manufacturer".$MemIndex; $Inc = 1; } if(not $Device{"Device"}) { $Device{"Device"} = "Partnum".$MemIndex; $Inc = 1; } if(not $Device{"Serial"}) { $Device{"Serial"} = "Sernum".$MemIndex; $Inc = 1; } $Device{"Size"}=~s/ //g; $Device{"Speed"}=~s/ (MHz|MT\/s)//; if($Inc) { $MemIndex++; } if(isUnknownRam($Device{"Vendor"})) { $Device{"Vendor"} = undef; } $Device{"Device"} = duplVendor($Device{"Vendor"}, $Device{"Device"}); if($Device{"Device"}=~/\A(\s*)\Z/ or $Device{"Device"}=~/\A(SODIMM)\d+\Z/i or $Device{"Device"}=~/\AArray\d+_(Part)Number\d+\Z/i or $Device{"Device"}=~/\A(Part)Num\d+\Z/i or $Device{"Device"}=~/\A(0x)[\dA-F]+\Z/i or $Device{"Device"}=~/\A(0)0+\Z/i or $Device{"Device"}=~/Module(Part)Number/i or $Device{"Device"}=~/NOT AVAILABLE/i) { $Device{"Device"} = $1; if(not $Device{"Device"} or grep { lc($Device{"Device"}) eq $_ } ("part", "0x", "0", "sodimm")) { $Device{"Device"} = "RAM Module"; } $ID = devID(nameID($Device{"Vendor"}), $Device{"Device"}); if($Device{"Size"} or $Device{"Kind"} or $Device{"Speed"}) { $ID = devID($ID, $Device{"Size"}, $Device{"Kind"}, $Device{"Speed"}, $Device{"FF"}); } if($Device{"Serial"}) { $ID = devID($ID, "serial", $Device{"Serial"}); } } else { $ID = devID(nameID($Device{"Vendor"}), devSuffix(\%Device)); $Device{"Device"} = "RAM ".$Device{"Device"}; } $ID = fmtID($ID); if(defined $MemIDs{$ID}) { # ERROR: the same ID of RAM memory module $ID .= "-".keys(%MemIDs); } $MemIDs{$ID} = 1; my @Add = (); if(my $Size = $Device{"Size"}) { $Size=~s/ //g; $Size=~s/\(.+\)//g; push(@Add, $Size); } if(my $FF = $Device{"FF"}) { push(@Add, $FF); } if(my $Kind = $Device{"Kind"}) { push(@Add, $Kind); } if(my $Speed = $Device{"Speed"}) { $Speed=~s/ //g; push(@Add, $Speed."MT/s"); } if(@Add) { # additionals $Device{"Device"} .= " ".join(" ", @Add); $Device{"Device"}=~s/\A\s+//g; } if($ID) { $HW{"mem:".$ID} = \%Device; # countDevice("mem:".$ID, "memory"); } } elsif($Info=~/Base Board Information\n/) { while($Info=~s/([\w ]+):[ \t]*(.+?)[ \t]*(\n|\Z)//) { my ($Key, $Val) = ($1, $2); if($Key eq "Manufacturer") { $Device{"Vendor"} = fmtVal($Val); } elsif($Key eq "Product Name") { $Device{"Device"} = fmtVal($Val); } elsif($Key eq "Version") { $Device{"Version"} = $Val; } } if($Board_ID) { delete($HW{$Board_ID}); } $Board_ID = registerBoard(\%Device); } elsif($Info=~/BIOS Information\n/) { while($Info=~s/([\w ]+):[ \t]*(.+?)[ \t]*(\n|\Z)//) { my ($Key, $Val) = ($1, $2); if($Key eq "Vendor") { $Device{$Key} = fmtVal($Val); } elsif($Key eq "Version" or $Key eq "Release Date") { $Device{$Key} = $Val; } } if(not $Bios_ID) { $Bios_ID = registerBIOS(\%Device); } } elsif($Info=~/Processor Information\n/) { while($Info=~s/([\w ]+):[ \t]*(.+?)[ \t]*(\n|\Z)//) { my ($Key, $Val) = ($1, $2); if($Key eq "Manufacturer") { $Device{"Vendor"} = fmtVal($Val); $Device{"Vendor"} = fixCpuVendor($Device{"Vendor"}); } elsif($Key eq "Signature") { # Family 6, Model 42, Stepping 7 my @Model = (); if($Val=~/Family\s+(\w+),/) { push(@Model, $1); $CPU_Family = $1; } if($Val=~/Model\s+(\w+),/) { push(@Model, $1); $CPU_ModelNum = $1; } if($Val=~/Stepping\s+(\w+)/) { push(@Model, $1); } $D = join(".", @Model); } elsif($Key eq "Version") { $Device{"Device"} = fmtVal($Val); } elsif($Key eq "Core Count") { $CPU_Cores += $Val; } elsif($Key eq "Thread Count") { $CPU_Threads += $Val; } elsif($Key eq "Family") { $CPU_Family_Name = $Val; } } if(not $Device{"Vendor"}) { next; } if($Device{"Device"} eq "C1") { next; } $CPU_Sockets += 1; cleanValues(\%Device); $Device{"Device"} = duplVendor($Device{"Vendor"}, $Device{"Device"}); if(not $Device{"Device"} and $CPU_Family_Name) { $Device{"Device"} = $CPU_Family_Name; } $Device{"Type"} = "cpu"; $Device{"Status"} = "works"; if(not $CPU_ID) { $ID = devID(nameID($Device{"Vendor"}), $D, devSuffix(\%Device)); $ID = fmtID($ID); if($ID) { $CPU_ID = "cpu:".$ID; $HW{$CPU_ID} = \%Device; } if($CPU_Threads) { setDevCount($CPU_ID, "cpu", $CPU_Threads); } elsif($CPU_Cores) { setDevCount($CPU_ID, "cpu", $CPU_Cores); } } else { # add info foreach (keys(%Device)) { my $Val1 = $HW{$CPU_ID}{$_}; my $Val2 = $Device{$_}; if($Val2 and not $Val1) { $HW{$CPU_ID}{$_} = $Val2; } } } if(my $Microarch = detectMicroarch($Device{"Vendor"}, $CPU_Family, $CPU_ModelNum, $Device{"Device"})) { $Sys{"Microarch"} = $Microarch; } } } if($CPU_Sockets) { $Sys{"Sockets"} = $CPU_Sockets; } if($CPU_Sockets and $CPU_Cores and $CPU_Threads) { $Sys{"Cores"} = $CPU_Cores; if($CPU_Threads>=$CPU_Cores) { $Sys{"Threads"} = $CPU_Threads/$CPU_Cores; } elsif($CPU_Threads>=$CPU_Sockets) { $Sys{"Threads"} = $CPU_Threads/$CPU_Sockets; } } # fix missed or incorrect computer type from DMI if($CPU_ID) { fixFFByCPU($HW{$CPU_ID}{"Device"}); } foreach (keys(%{$ComponentID{"graphics card"}})) { fixFFByGPU($HW{$_}{"Device"}); } foreach (keys(%{$ComponentID{"cdrom"}})) { fixFFByCDRom($HW{$_}{"Device"}); } foreach (keys(%{$ComponentID{"disk"}})) { fixFFByDisk($HW{$_}{"Device"}); } foreach (keys(%{$ComponentID{"monitor"}})) { fixFFByMonitor($HW{$_}{"Device"}); } foreach (keys(%{$ComponentID{"touchpad"}})) { fixFFByTouchpad($_); } if($Sys{"Vendor"} or $Sys{"Model"}) { fixFFByModel($Sys{"Vendor"}, $Sys{"Model"}); } if($Board_ID) { fixFFByBoard($HW{$Board_ID}{"Device"}); if($Sys{"Vendor"}!~/VMware/i and ($Sys{"Type"}=~/$DESKTOP_TYPE|$SERVER_TYPE/ or not $Sys{"Type"})) { my ($MVendor, $MModel) = ($HW{$Board_ID}{"Vendor"}, shortModel($HW{$Board_ID}{"Device"})); if(emptyProduct($MVendor)) { $MVendor = undef; } if(emptyProduct($MModel)) { $MModel = undef; } if($MVendor and $MModel or (not $Sys{"Vendor"} and not $Sys{"Model"})) { $Sys{"Subvendor"} = $Sys{"Vendor"}; $Sys{"Submodel"} = $Sys{"Model"}; $Sys{"Vendor"} = $MVendor; $Sys{"Model"} = $MModel; if($MModel eq "VirtualBox") { $Sys{"Subvendor"} = undef; $Sys{"Submodel"} = undef; } if($Sys{"Subvendor"} eq $Sys{"Vendor"} and $Sys{"Submodel"} eq $Sys{"Model"}) { delete($Sys{"Subvendor"}); delete($Sys{"Submodel"}); } } if($Sys{"Vendor"}) { ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); $Sys{"Model"} = fixModel($Sys{"Vendor"}, $Sys{"Model"}, undef); } ($Sys{"Subvendor"}, $Sys{"Submodel"}) = fixVendorModel($Sys{"Subvendor"}, $Sys{"Submodel"}); $Sys{"Submodel"} = fixModel($Sys{"Subvendor"}, $Sys{"Submodel"}, undef); } } if($Sys{"Vendor"} or $Sys{"Model"}) { fixFFByModel($Sys{"Vendor"}, $Sys{"Model"}); } if($Sysctl and (not $Sys{"Type"} or $Sys{"Type"} eq "desktop")) { if($Sysctl=~/\.acpibat\d+\./) { $Sys{"Type"} = "notebook"; } else { $Sys{"Type"} = "desktop"; } } if(not $Sys{"Type"}) { if(not keys(%WLanInterface)) { $Sys{"Type"} = "desktop"; } } if($Sysctl and (not $Sys{"Model"} or not $Sys{"Vendor"})) { if($Sysctl=~/hw\.product=(.+)/) { $Sys{"Model"} = $1; } if($Sysctl=~/hw\.vendor=(.+)/) { $Sys{"Vendor"} = $1; } if($Sysctl=~/hw\.version=(.+)/) { $Sys{"Version"} = $1; } $Sys{"Model"} = fixModel($Sys{"Vendor"}, $Sys{"Model"}, $Sys{"Version"}); if(not $Board_ID) { $Board_ID = registerBoard({"Vendor"=>fmtVal($Sys{"Vendor"}), "Device"=>$Sys{"Model"}}); if($Board_ID) { fixFFByBoard($HW{$Board_ID}{"Device"}); } } if($Sys{"Vendor"} or $Sys{"Model"}) { fixFFByModel($Sys{"Vendor"}, $Sys{"Model"}); } } if($Sysctl and not $CDROM_ID) { if($Sysctl=~/dev.acd.0.%desc:\s*(.+)/) { my $CdromDescr = $1; $CdromDescr=~s{/.+}{}; $CDROM_ID = registerCdrom($CdromDescr, "acd0"); } } if($Sysctl=~/hw\.physmem[:=]\s*([^\s]+)/) { $Sys{"Ram_total"} = $1/1024; if($Sysctl=~/Free Memory:\s+(.+)/) { $Sys{"Ram_used"} = $Sys{"Ram_total"} - $1; } registerRAM($Sys{"Ram_total"}); } # Printers my %Pr; my $HP_probe = ""; if($Opt{"FixProbe"}) { $HP_probe = readFile($FixProbe_Logs."/hp-probe"); } elsif($Opt{"Printers"} and enabledLog("hp-probe") and checkCmd("hp-probe")) { listProbe("logs", "hp-probe"); # Net $HP_probe = runCmd("hp-probe -bnet -g 2>&1"); $HP_probe .= "\n"; # Usb $HP_probe .= runCmd("hp-probe -busb -g 2>&1"); $HP_probe = clearLog($HP_probe); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/hp-probe", $HP_probe); } } foreach my $Line (split(/\n/, $HP_probe)) { if($Line=~/Found device/) { my %Device = (); my %Attr = (); while($Line=~s/\'([^']*?)\'\s*:\s*\'([^']*?)\'//) { my ($Key, $Val) = ($1, $2); if($Val=~/MDL:(.*?);/) { $Device{"Device"} = $1; } #if($Val=~/MFG:(.*?);/) { # $Device{"Vendor"} = $1; #} $Attr{$Key} = $Val; } $Pr{$Device{"Device"}} = 1; if(not $Device{"Vendor"}) { if(my $Vnd = guessDeviceVendor($Device{"Device"})) { $Device{"Vendor"} = $Vnd; } } if(my $Vendor = $Device{"Vendor"}) { $Device{"Device"} = duplVendor($Vendor, $Device{"Device"}); $Pr{$Device{"Device"}} = 1; } $Device{"Type"} = "printer"; if($Device{"Device"}=~/(\A| )MFP( |\Z)/) { $Device{"Type"} = "mfp"; } # $Device{"Driver"} = "hplip"; my $ID = devID($Device{"Vendor"}, $Device{"Device"}); $ID = fmtID($ID); # additional info if(my $D = $Attr{"product_id"}) { $Device{"Device"} .= " ".$D; } if($ID) { $HW{"net:".$ID} = \%Device; } } } my $Avahi = ""; if($Opt{"FixProbe"}) { if(-f "$FixProbe_Logs/hp-probe") { # i.e. executed with -printers option (-fix) $Avahi = readFile("$FixProbe_Logs/avahi"); } } elsif($Opt{"Printers"} and enabledLog("avahi") and checkCmd("avahi-browse")) { listProbe("logs", "avahi-browse"); $Avahi = runCmd("avahi-browse -a -t 2>&1 | grep 'PDL Printer'"); if($Opt{"HWLogs"} and $Avahi) { writeLog($LOG_DIR."/avahi", $Avahi); } } foreach my $Line (split(/\n/, $Avahi)) { if($Line=~/IPv\d\s+(.*?)\s+PDL Printer/) { my %Device; $Device{"Device"} = $1; $Device{"Device"}=~s/\s*[\(\[].+[\)\]]//g; if($Pr{$Device{"Device"}}) { # already registered next; } if(not $Device{"Vendor"}) { if(my $Vnd = guessDeviceVendor($Device{"Device"})) { $Device{"Vendor"} = $Vnd; } } if(my $Vendor = $Device{"Vendor"}) { $Device{"Device"} = duplVendor($Vendor, $Device{"Device"}); } $Device{"Type"} = "printer"; if($Device{"Device"}=~/(\A| )MFP( |\Z)/) { $Device{"Type"} = "mfp"; } my $ID = devID($Device{"Vendor"}, $Device{"Device"}); $ID = fmtID($ID); if($ID) { $HW{"net:".$ID} = \%Device; } } } # Monitors my $Edid = ""; if($Opt{"FixProbe"}) { $Edid = readFile($FixProbe_Logs."/edid"); my $XRandrLog = $FixProbe_Logs."/xrandr"; my $XOrgLog = $FixProbe_Logs."/xorg.log"; if($Opt{"FixEdid"} and ($Edid or -s $XRandrLog or -s $XOrgLog)) { my %EdidHex = (); my %FoundEdid = (); if(-s $XRandrLog) { my $RCard = undef; foreach my $L (split(/\n/, readFile($XRandrLog))) { if($L=~/([^\s]+)\s+connected /) { $RCard = $1."/edid"; } elsif($RCard) { if($L=~/\s+(\w{32})\Z/) { $FoundEdid{$RCard} .= $1."\n"; } } } } if(not keys(%FoundEdid) and -s $XOrgLog) { my $XCard = "DEFAULT1"; foreach my $L (split(/\n/, readFile($XOrgLog))) { if($L=~/EDID for output ([\w\-]+)/) { $XCard = $1."/edid"; } elsif($XCard) { if($L=~/\s+(\w{32})\Z/) { $FoundEdid{$XCard} .= $1."\n"; } } } } if(index($Edid, "edid-decode")!=-1) { my %OldEdid = (); foreach my $Block (split(/edid-decode /, $Edid)) { if($Block=~/\A\"(.+?)\"/) { my $Card = $1; if($Card!~/edid/) { $Card .= "/edid"; } if(not $OldEdid{$Card}) { foreach my $L (split(/\n/, $Block)) { if($L=~/([a-f0-9]{32})/) { if(not defined $OldEdid{$Card}) { $OldEdid{$Card} = $1; } else { $OldEdid{$Card} .= " ".$1; } } } } if(not $OldEdid{$Card}) { foreach my $B (split(/\n\n/, $Block)) { if($B=~/serial number:/) { foreach my $L (split(/\n/, $B)) { if($L=~/\A[a-z\d\s]+:\s+([a-f\d\s]+?)\Z/) { if(not defined $OldEdid{$Card}) { $OldEdid{$Card} = $1; } else { $OldEdid{$Card} .= " ".$1; } } } last; } } } } } foreach my $C (keys(%OldEdid)) { if($C!~/\A\//) { # from Xrandr or Xorg next; } my $Hex = $OldEdid{$C}; $Hex=~s/\s//g; $Hex=~s/(\w{32})/$1\n/g; if(grep {$FoundEdid{$_}=~/\A$Hex\w+/} keys(%FoundEdid)) { # extended EDID is available next; } $FoundEdid{$C} = $Hex; } } my $FixedContent = ""; foreach my $C (sort {$b=~/\A\// cmp $a=~/\A\//} sort {lc($a) cmp lc($b)} keys(%FoundEdid)) { my $Hex = $FoundEdid{$C}; if(defined $EdidHex{$Hex}) { next; } $EdidHex{$Hex} = 1; my $Decoded = decodeEdid($Hex); if($Decoded=~/No header found/i) { next; } $FixedContent .= "edid-decode \"$C\":\n"; $FixedContent .= "\nEDID (hex):\n".$Hex."\n"; $FixedContent .= $Decoded; $FixedContent .= "\n\n"; } if($FixedContent) { $Edid = $FixedContent; writeFile($FixProbe_Logs."/edid", $FixedContent); } else { if($Edid) { printMsg("WARNING", "failed to fix EDID"); } else { printMsg("WARNING", "failed to detect EDID"); } } } } elsif(enabledLog("edid")) { # NOTE: works for KMS video drivers only listProbe("logs", "edid"); my $EdidDecode = checkCmd("edid-decode"); my $MDir = "/sys/class/drm"; foreach my $Dir (listDir($MDir)) { my $Path = $MDir."/".$Dir."/edid"; if(-f $Path) { my $Dec = ""; my $EdidHex = readFileHex($Path); $EdidHex=~s/(.{32})/$1\n/g; if($EdidDecode) { $Dec = runCmd("edid-decode \"$Path\" 2>/dev/null"); } if($EdidHex) { $Edid .= "edid-decode \"$Path\"\n\n"; $Edid .= "EDID (hex):\n"; $Edid .= $EdidHex."\n"; $Edid .= $Dec."\n\n"; } } } if(not $Edid) { # for nvidia, fglrx if(my $MonEdid = checkCmd("monitor-get-edid")) { if($Admin) { if($EdidDecode) { $Edid .= runCmd("monitor-get-edid 2>/dev/null | edid-decode 2>&1"); } else { # LTS $Edid .= runCmd("monitor-get-edid 2>/dev/null | monitor-parse-edid 2>/dev/null"); } $Edid=~s/\n\n/\n/g; } } } if($Opt{"HWLogs"} and $Edid) { writeLog($LOG_DIR."/edid", $Edid); } } my @Mons = (); if(index($Edid, "edid-decode")!=-1) { @Mons = grep { /\S/ } split(/edid\-decode /, $Edid); } else { @Mons = ($Edid); } foreach my $Info (@Mons) { detectMonitor($Info); } # Battery my $Apm = ""; if($Opt{"FixProbe"}) { $Apm = readFile($FixProbe_Logs."/apm"); } elsif(enabledLog("apm") and checkCmd("apm")) { listProbe("logs", "apm"); $Apm = runCmd("apm 2>/dev/null"); $Apm = encryptSerials($Apm, "Serial number"); if($Opt{"HWLogs"} and $Apm) { writeLog($LOG_DIR."/apm", $Apm); } } my $AcpiConf = ""; if($Opt{"FixProbe"}) { $AcpiConf = readFile($FixProbe_Logs."/acpiconf"); } elsif($Apm and enabledLog("acpiconf") and checkCmd("acpiconf")) { listProbe("logs", "acpiconf"); my @Bats = ($Apm=~/Battery (.+?):/g); foreach my $Bat (@Bats) { $AcpiConf .= "# acpiconf -i $Bat\n"; $AcpiConf .= runCmd("acpiconf -i $Bat 2>/dev/null"); $AcpiConf .= "\n"; } if($Opt{"HWLogs"} and $AcpiConf) { writeLog($LOG_DIR."/acpiconf", $AcpiConf); } } my $Upower = ""; if($Opt{"FixProbe"}) { $Upower = readFile($FixProbe_Logs."/upower"); } elsif(enabledLog("upower") and checkCmd("upower")) { listProbe("logs", "upower"); $Upower = runCmd("upower -d 2>/dev/null"); $Upower = encryptSerials($Upower, "serial"); if($Opt{"HWLogs"} and $Upower) { writeLog($LOG_DIR."/upower", $Upower); } } if($Upower) { foreach my $UPInfo (split(/\n\n/, $Upower)) { if($UPInfo=~/devices\/battery_/ and $UPInfo!~/$HID_BATTERY/i) { my %Device = (); foreach my $Line (split(/\n/, $UPInfo)) { if($Line=~/vendor:[ ]*(.+?)[ ]*\Z/) { $Device{"Vendor"} = fmtVal($1); } elsif($Line=~/model:[ ]*(.+?)[ ]*\Z/) { $Device{"Device"} = fmtVal($1); } elsif($Line=~/serial:[ ]*(.+?)[ ]*\Z/) { $Device{"Serial"} = $1; } elsif($Line=~/energy-full-design:[ ]*(.+?)[ ]*\Z/) { $Device{"Size"} = $1; $Device{"Size"}=~s/\,/\./g; } elsif($Line=~/energy-full:[ ]*(.+?)[ ]*\Z/) { $Device{"CurSize"} = $1; $Device{"CurSize"}=~s/\,/\./g; } elsif($Line=~/technology:[ ]*(.+?)[ ]*\Z/) { $Device{"Technology"} = $1; } elsif($Line=~/capacity:[ ]*(.+?)[ ]*\Z/) { $Device{"Capacity"} = $1; } } if($Device{"Device"}=~/Keyboard|Mouse/i) { next; } if(not $Device{"Size"} or $Device{"Size"} eq "0 Wh") { if(my $C = $Device{"Capacity"}) { $C=~s/\%//; my $F = $Device{"CurSize"}; $F=~s/ Wh//; $Device{"Size"} = ($F*100/$C)." Wh"; } } cleanValues(\%Device); registerBattery(\%Device); } } } my $PSDir = "/sys/class/power_supply"; my $PowerSupply = ""; if($Opt{"FixProbe"}) { $PowerSupply = readFile($FixProbe_Logs."/power_supply"); } elsif(-d $PSDir) { listProbe("logs", "power_supply"); foreach my $Bat (listDir($PSDir)) { my $PSPath = $PSDir."/".$Bat; $PowerSupply .= $PSPath.":\n"; $PowerSupply .= readFile($PSPath."/uevent"); $PowerSupply .= "\n"; } $PowerSupply = encryptSerials($PowerSupply, "SERIAL_NUMBER"); if($Opt{"HWLogs"} and $PowerSupply) { writeLog($LOG_DIR."/power_supply", $PowerSupply); } } if(not $Upower and $PowerSupply) { foreach my $Block (split(/\n\n/, $PowerSupply)) { if(($Block=~/$PSDir\/BAT/i or $Block=~/POWER_SUPPLY_CAPACITY\=/i) and $Block!~/$HID_BATTERY/i) { my %Device = (); if($Block=~/POWER_SUPPLY_MODEL_NAME=(.+)/i) { $Device{"Device"} = $1; } if($Device{"Device"}=~/Keyboard|Mouse/i) { next; } if($Block=~/POWER_SUPPLY_MANUFACTURER=(.+)/i) { $Device{"Vendor"} = $1; } else { if($Device{"Device"}=~s/\A(DELL)\s+//i) { $Device{"Vendor"} = $1; } } if($Block=~/POWER_SUPPLY_TECHNOLOGY=(.+)/i) { $Device{"Technology"} = $1; } if($Block=~/POWER_SUPPLY_ENERGY_FULL_DESIGN=(.+)/i) { if(my $EFullDesign = $1) { $Device{"Size"} = ($EFullDesign/1000000)." Wh"; if($Block=~/POWER_SUPPLY_ENERGY_FULL=(.+)/i) { $Device{"Capacity"} = $1*100/$EFullDesign; } } } if($Block=~/POWER_SUPPLY_CHARGE_FULL=(.+)/i) { $Device{"Charge"} = $1; } if($Block=~/POWER_SUPPLY_CHARGE_FULL_DESIGN=(.+)/i) { $Device{"DesignCharge"} = $1; if($Device{"Charge"} and $Device{"DesignCharge"}) { $Device{"Capacity"} = $Device{"Charge"}*100/$Device{"DesignCharge"}; } } if($Block=~/POWER_SUPPLY_SERIAL_NUMBER=\s*(.+)/i) { $Device{"Serial"} = $1; } if($Block=~/POWER_SUPPLY_VOLTAGE_MIN_DESIGN=(.+)/i) { $Device{"MinVoltage"} = $1; } if($Block=~/POWER_SUPPLY_VOLTAGE_MAX_DESIGN=(.+)/i) { $Device{"MaxVoltage"} = $1; } if(not $Device{"Size"}) { if($Device{"MinVoltage"} and $Device{"DesignCharge"}) { $Device{"Size"} = (($Device{"DesignCharge"}/1000000)*($Device{"MinVoltage"}/1000000))." Wh"; } elsif($Device{"MaxVoltage"} and $Device{"Charge"}) { $Device{"Size"} = (($Device{"Charge"}/1000000)*($Device{"MaxVoltage"}/1000000))." Wh"; } } if(not $Device{"Capacity"}) { if($Block=~/POWER_SUPPLY_HEALTH=Good/i) { $Device{"Status"} = "works"; } } registerBattery(\%Device); } } } # PNP my $Lspnp = ""; if($Opt{"FixProbe"}) { $Lspnp = readFile($FixProbe_Logs."/lspnp"); } elsif(enabledLog("lspnp") and checkCmd("lspnp")) { listProbe("logs", "lspnp"); $Lspnp = runCmd("lspnp -vv 2>&1"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lspnp", $Lspnp); } } # HDD my $Hdparm = ""; if($Opt{"FixProbe"}) { $Hdparm = readFile($FixProbe_Logs."/hdparm"); } elsif($Opt{"HWLogs"} and enabledLog("hdparm")) { if($Admin and checkCmd("hdparm")) { listProbe("logs", "hdparm"); foreach my $Dev (sort keys(%HDD)) { if($Dev=~/\A\/dev\/sr\d+\Z/) { next; } my $Id = $HDD{$Dev}; if(index($Id, "usb:")==0 or index($Id, "scsi:")==0) { next; } my $Output = runCmd("hdparm -I \"$Dev\" 2>/dev/null"); $Output = encryptSerials($Output, "Serial Number"); $Output = encryptSerials($Output, "Unique ID"); $Output = hideTags($Output, "WWN Device Identifier"); if(length($Output)<30) { # empty next; } $Hdparm .= $Output; } } writeLog($LOG_DIR."/hdparm", $Hdparm); } my $EdacUtil = ""; if($Opt{"FixProbe"}) { $EdacUtil = readFile($FixProbe_Logs."/edac-util"); } elsif($Opt{"HWLogs"} and enabledLog("edac-util")) { if($Admin and checkCmd("edac-util")) { listProbe("logs", "edac-util"); $EdacUtil = runCmd("edac-util -vs 2>/dev/null"); $EdacUtil .= runCmd("edac-util -v 2>/dev/null"); writeLog($LOG_DIR."/edac-util", $EdacUtil); } } if($Opt{"HWLogs"}) { if(enabledLog("hddtemp") and checkCmd("hddtemp")) { listProbe("logs", "hddtemp"); if(my $HddTemp = runCmd("hddtemp 2>/dev/null")) { writeLog($LOG_DIR."/hddtemp", $HddTemp); } } } my %Smartctl = (); my $SmartctlCmd = undef; if(not $Opt{"FixProbe"} and $Opt{"HWLogs"}) { if(checkCmd("smartctl")) { $SmartctlCmd = "smartctl"; } if($Opt{"Snap"} or $Opt{"AppImage"} or $Opt{"Flatpak"}) { $SmartctlCmd = findCmd("smartctl"); } } if($Opt{"FixProbe"}) { $Smartctl{"Normal"} = readFile($FixProbe_Logs."/smartctl"); my $CurDev = undef; my %DriveDesc = (); foreach my $SL (split(/\n/, $Smartctl{"Normal"})) { if(index($SL, "/dev/")==0) { $CurDev = $SL; } elsif($CurDev) { $DriveDesc{$CurDev} .= $SL."\n"; } } foreach my $Dev (sort keys(%DriveDesc)) { my $Id = $HDD{$Dev}; if(not $Id) { $Id = detectDrive($DriveDesc{$Dev}, $Dev); } if($Id) { setDriveStatus($DriveDesc{$Dev}, $Id); } } } elsif($Opt{"HWLogs"} and enabledLog("smartctl")) { if($Admin and $SmartctlCmd) # $Admin or $Opt{"Snap"} ? { listProbe("logs", "smartctl"); my %ProbedRAID = (); foreach my $Dev (sort keys(%HDD)) { if($Dev=~/\A\/dev\/sr\d+\Z/) { next; } my $Id = $HDD{$Dev}; if($HW{$Id}{"Driver"}=~/megaraid|hpsa/) { my $RAID = $HW{$Id}{"Device"}; $RAID=~s/\s+\d.+?B\Z//; if($Opt{"ListProbes"}) { printMsg("INFO", "Probing $RAID"); } if(defined $ProbedRAID{$RAID}) { # Do not probe same RAID twice next; } my $RaidMax = 23; if($HW{$Id}{"Driver"}=~/hpsa/) { $RaidMax = 15; } foreach my $N (0 .. $RaidMax) { if($HW{$Id}{"Driver"}=~/megaraid/) { $Smartctl{"MegaRAID"} .= runSmartctl($SmartctlCmd, undef, $Dev, $Dev, "MegaRAID", "-d megaraid,$N", $N); } elsif($HW{$Id}{"Driver"}=~/hpsa/) { $Smartctl{"HPSA"} .= runSmartctl($SmartctlCmd, undef, $Dev, $Dev, "HPSA", "-d cciss,$N", $N); } } $ProbedRAID{$RAID} = 1; next; } my $DiskReport = runSmartctl($SmartctlCmd, $Id, $Dev, $Dev); if(isBSD()) { my $TryNvme = $Dev; if(not $DiskReport and $TryNvme=~s{(nvd|nda)(\d+)\Z}{nvme$2}) { $DiskReport = runSmartctl($SmartctlCmd, $Id, $TryNvme, $Dev); } if(not $DiskReport) { if(isNetBSD()) { $DiskReport = runSmartctl($SmartctlCmd, $Id, $Dev."d", $Dev); } elsif(isOpenBSD()) { $DiskReport = runSmartctl($SmartctlCmd, $Id, $Dev."c", $Dev); } } } $Smartctl{"Normal"} .= $DiskReport; } if($Smartctl{"Normal"}) { writeLog($LOG_DIR."/smartctl", $Smartctl{"Normal"}); } if($Smartctl{"MegaRAID"}) { writeLog($LOG_DIR."/smartctl_megaraid", $Smartctl{"MegaRAID"}); } if($Smartctl{"HPSA"}) { writeLog($LOG_DIR."/smartctl_hpsa", $Smartctl{"HPSA"}); } } else { # write empty writeLog($LOG_DIR."/smartctl", ""); } } my $AtaCtl = ""; if($Opt{"FixProbe"}) { $AtaCtl = readFile($FixProbe_Logs."/atactl"); } elsif($Opt{"HWLogs"} and enabledLog("atactl") and checkCmd("atactl")) { listProbe("logs", "atactl"); foreach my $Dev (sort keys(%HDD)) { if($Dev=~/[dc]\Z/) { next; } my $AtaDevCtl = runCmd("atactl ".basename($Dev)." identify 2>/dev/null"); if($AtaDevCtl) { $AtaDevCtl .= "\n"; $AtaDevCtl .= runCmd("atactl ".basename($Dev)." smart status 2>/dev/null"); $AtaDevCtl .= "\n"; $AtaCtl .= $Dev."\n"; $AtaCtl .= $AtaDevCtl; } } $AtaCtl = encryptSerials($AtaCtl, "Serial #"); writeLog($LOG_DIR."/atactl", $AtaCtl); } my $Diskinfo = ""; if($Opt{"FixProbe"}) { $Diskinfo = readFile($FixProbe_Logs."/diskinfo"); } elsif($Opt{"HWLogs"} and enabledLog("diskinfo") and checkCmd("diskinfo")) { listProbe("logs", "diskinfo"); foreach my $Dev (sort keys(%HDD)) { $Diskinfo .= runCmd("diskinfo -v $Dev 2>/dev/null"); } $Diskinfo=~s/[^\s]+(\s+# Disk ident.+\n)/...$1/g; $Diskinfo=~s/( SN ).+?( MFG )/$1...$2/g; writeLog($LOG_DIR."/diskinfo", $Diskinfo); } my $Disklabel = ""; if($Opt{"FixProbe"}) { $Disklabel = readFile($FixProbe_Logs."/disklabel"); } elsif($Opt{"HWLogs"} and enabledLog("disklabel") and checkCmd("disklabel")) { listProbe("logs", "disklabel"); foreach my $Dev (sort keys(%HDD)) { my $DiskDevLabel = runCmd("disklabel $Dev 2>/dev/null"); if($DiskDevLabel) { $Disklabel .= $DiskDevLabel; $Disklabel .= "\n"; } } $Disklabel=~s/(label:\s+)(.+?)\n/$1...\n/g; $Disklabel = encryptSerials($Disklabel, "duid"); if($Disklabel) { writeLog($LOG_DIR."/disklabel", $Disklabel); } } my $Camcontrol = ""; if($Opt{"FixProbe"}) { $Camcontrol = readFile($FixProbe_Logs."/camcontrol"); } elsif($Opt{"HWLogs"} and enabledLog("camcontrol") and checkCmd("camcontrol")) { listProbe("logs", "camcontrol"); foreach my $Dev (sort keys(%HDD)) { if(my $CCRes = runCmd("camcontrol identify $Dev 2>/dev/null")) { $Camcontrol .= "$Dev\n"; $Camcontrol .= $CCRes; } } $Camcontrol=~s/(serial number\s+)(.+?)\n/$1...\n/g; $Camcontrol=~s/(WWN\s+[a-f\d]{7})[a-f\d]{9}/$1.../g; writeLog($LOG_DIR."/camcontrol", $Camcontrol); } my $MfiUtil = ""; if($Opt{"FixProbe"}) { $MfiUtil = readFile($FixProbe_Logs."/mfiutil"); } elsif($Opt{"HWLogs"} and enabledLog("mfiutil") and checkCmd("mfiutil")) { listProbe("logs", "mfiutil"); $MfiUtil .= runCmd("mfiutil show config 2>/dev/null"); if($MfiUtil) { writeLog($LOG_DIR."/mfiutil", $MfiUtil); } } my $Hwstat = ""; if($Opt{"FixProbe"}) { $Hwstat = readFile($FixProbe_Logs."/hwstat"); } elsif($Opt{"HWLogs"} and enabledLog("hwstat") and checkCmd("hwstat")) { listProbe("logs", "hwstat"); $Hwstat .= runCmd("hwstat 2>/dev/null"); $Hwstat = encryptSerials($Hwstat, "Serial number"); writeLog($LOG_DIR."/hwstat", $Hwstat); } foreach my $Info (split(/\n\n/, $Hwstat)) { if($Info=~/$HID_BATTERY/i) { next; } my %Bat = (); if($Info=~/Model number:\s+(.+)/i) { $Bat{"Device"} = $1; } if($Bat{"Device"}=~/Keyboard|Mouse/i) { next; } if($Info=~/OEM info:\s+(.+)/i) { $Bat{"Vendor"} = $1; } else { if($Bat{"Device"}=~s/\A(DELL)\s+//i) { $Bat{"Vendor"} = $1; } } if($Info=~/Type:\s+(.+)/i) { $Bat{"Technology"} = $1; } if($Info=~/Design voltage\s+(\d+)/i) { $Bat{"Voltage"} = $1/1000; } if($Info=~/Remaining capacity:\s+(\d+)/i) { $Bat{"Capacity"} = $1; } if($Info=~/Design capacity:\s+(\d+)\s+([^\s]+)/i) { if(my $EFullDesign = $1) { my $Unit = $2; if($Unit eq "mWh") { $Bat{"Size"} = ($EFullDesign/1000)." Wh"; } elsif($Unit eq "mAh") { if($Bat{"Voltage"}) { $Bat{"Size"} = ($EFullDesign*$Bat{"Voltage"}/1000)." Wh"; } } # if(not $Bat{"Capacity"} and $Info=~/Last full capacity:\s+(\d+)/i) { # $Bat{"Capacity"} = $1*100/$EFullDesign; # } } } if(not $Bat{"Capacity"}) { next; } if($Info=~/Serial number:\s+(.+)/i) { $Bat{"Serial"} = $1; } registerBattery(\%Bat); } if(not $Opt{"FixProbe"}) { if($Admin and enabledLog("smart-log") and checkCmd("nvme")) { listProbe("logs", "smart-log"); my $NvmeCli = ""; foreach my $Dev (sort keys(%HDD)) { if($Dev=~/nvme/) { my $Output = runCmd("nvme smart-log \"".$Dev."\" 2>/dev/null"); my $OutputAdd = runCmd("nvme smart-log-add \"".$Dev."\" 2>/dev/null"); if($Output or $OutputAdd) { $NvmeCli .= $Dev."\n"; if($Output) { $NvmeCli .= $Output."\n"; } if($OutputAdd) { $NvmeCli .= $OutputAdd."\n"; } $NvmeCli .= "\n"; } } } if($NvmeCli) { writeLog($LOG_DIR."/smart-log", $NvmeCli); } } } my @DrSer = (); foreach my $Dev (keys(%HDD)) { if(not $HDD{$Dev}) { if(index($Dev, "nvme")!=-1) { my %Drv = ( "Type"=>"disk" ); if(defined $HDD_Info{$Dev}) { foreach ("Capacity", "Driver", "Model", "Vendor", "File") { $Drv{$_} = $HDD_Info{$Dev}{$_}; } } if($Drv{"Model"} and my $Vnd = guessDeviceVendor($Drv{"Model"})) { $Drv{"Vendor"} = $Vnd; $Drv{"Model"} = duplVendor($Vnd, $Drv{"Model"}); } if($Drv{"Model"} and $Drv{"Model"} ne "Disk") { $Drv{"Device"} = $Drv{"Model"}; } else { $Drv{"Device"} = "NVMe SSD Drive"; } $Drv{"Device"} .= addCapacity($Drv{"Device"}, $Drv{"Capacity"}); $Drv{"Kind"} = "NVMe"; my $DiskId = undef; if($Drv{"Vendor"}) { $DiskId = devID(nameID($Drv{"Vendor"}), "solid-state-drive", $Drv{"Capacity"}); } else { $DiskId = devID("solid-state-drive", $Drv{"Capacity"}); } $DiskId = $PCI_DISK_BUS.":".fmtID($DiskId); $HW{$DiskId} = \%Drv; countDevice($DiskId, $Drv{"Type"}); } } else { if(my $DSer = $HW{$HDD{$Dev}}->{"Serial"}) { if(not $HW{$HDD{$Dev}}->{"Class"}) { push(@DrSer, $DSer); } } } } if(@DrSer) { $Sys{"Uuid"} = getSysUUID(@DrSer); } foreach my $Dev (keys(%MMC)) { if(not $MMC{$Dev}) { if(index($Dev, "mmcblk")!=-1) { my %Drv = ( "Type"=>"disk" ); if(defined $MMC_Info{$Dev}) { foreach ("Capacity", "Driver", "Vendor", "Device", "Serial") { $Drv{$_} = $MMC_Info{$Dev}{$_}; } } if(defined $VendorByModel{$Drv{"Device"}}) { $Drv{"Vendor"} = $VendorByModel{$Drv{"Device"}}; } $Drv{"Capacity"} = fixCapacity($Drv{"Capacity"}); if($Drv{"Device"}) { $Drv{"Device"} .= " ".addCapacity($Drv{"Device"}, $Drv{"Capacity"}); $Drv{"Kind"} = "MMC"; my $MmcId = "mmc:".fmtID(devID(nameID($Drv{"Vendor"}), devSuffix(\%Drv))); $HW{$MmcId} = \%Drv; countDevice($MmcId, $Drv{"Type"}); $MMC{$Dev} = $MmcId; } } } } if($Opt{"FixProbe"}) { foreach my $RAID_Vendor ("MegaRAID", "HPSA") { my $RAID_Prefix = lc($RAID_Vendor); $Smartctl{$RAID_Vendor} = readFile($FixProbe_Logs."/smartctl_$RAID_Prefix"); my ($CurDev, $CurDid) = (undef, undef); my %DriveDesc = (); foreach my $SL (split(/\n/, $Smartctl{$RAID_Vendor})) { if(index($SL, "/dev/")==0) { if($SL=~/(.+),($RAID_Prefix)_disk_(.+)/) { ($CurDev, $CurDid) = ($1, int($3)); } } elsif($CurDev) { $DriveDesc{$CurDev}{$CurDid} .= $SL."\n"; } } foreach my $Dev (sort keys(%DriveDesc)) { foreach my $Did (sort keys(%{$DriveDesc{$Dev}})) { my $Desc = $DriveDesc{$Dev}{$Did}; if(my $Id = detectDrive($Desc, $Dev, $RAID_Vendor, $Did)) { setDriveStatus($Desc, $Id); } } } } } elsif($Admin and not $Smartctl{"MegaRAID"}) { # try by storcli my $StorcliCmd = undef; foreach my $Cmd ("storcli64", "storcli") { if(checkCmd($Cmd)) { $StorcliCmd = $Cmd; last; } } if($StorcliCmd) { # MegaRAID listProbe("logs", "storcli"); my $Storcli = runCmd($StorcliCmd." /call /vall /eall /sall show 2>&1"); if($Storcli=~/No Controller found/i) { $Storcli = undef; } $Storcli = encryptSerials($Storcli, "SCSI NAA Id"); if($Storcli) { writeLog($LOG_DIR."/storcli", $Storcli); } if(index($Storcli, "unexpected TOKEN_SLASH")!=-1) { $Storcli = undef; } if($Storcli) { my %DInfo = (); my $Ctrl = undef; foreach my $L (split(/\n/, $Storcli)) { if($L=~/Controller = (\d+)/) { $Ctrl = $1; } elsif($Ctrl and $L=~/SCSI NAA Id = (\w+)/) { $DInfo{$Ctrl}{"NAA"} = $1; } elsif($Ctrl and $L=~/\d+:\d+\s+(\d+)/) { $DInfo{$Ctrl}{"DID"}{$1} = 1; } } my %DID = (); foreach my $Controller (sort {int($a)<=>int($b)} keys(%DInfo)) { if(my $NAA = $DInfo{$Controller}{"NAA"}) { if(my $Dev = $DevNameById{"wwn-0x".$NAA}) { foreach my $D (keys(%{$DInfo{$Controller}{"DID"}})) { $DID{$Dev}{$D} = 1; } } } } if($SmartctlCmd) { foreach my $Dev (sort keys(%DID)) { foreach my $Did (sort {int($a)<=>int($b)} keys(%{$DID{$Dev}})) { $Smartctl{"MegaRAID"} .= runSmartctl($SmartctlCmd, undef, $Dev, $Dev, "MegaRAID", "-d megaraid,$Did", $Did); } } } if($Smartctl{"MegaRAID"}) { writeLog($LOG_DIR."/smartctl_megaraid", $Smartctl{"MegaRAID"}); } } } } if(not $Opt{"FixProbe"} and not $Smartctl{"MegaRAID"} and enabledLog("megacli")) { my $MegacliCmd = undef; if(not (checkCmd("mega-version"))) { foreach my $Cmd ("megacli", "MegaCli64", "MegaCli") { if(checkCmd($Cmd)) { $MegacliCmd = $Cmd; last; } } } if($MegacliCmd) { listProbe("logs", "megacli"); my $Megacli = runCmd($MegacliCmd." -PDList -aAll 2>/dev/null"); $Megacli=~s/(Inquiry Data\s*:\s*)[^\s]+/$1.../g; # Hide serial $Megacli = encryptSerials($Megacli, "WWN"); if($Megacli) { writeLog($LOG_DIR."/megacli", $Megacli); } # my %DIDs = (); # while($Megacli=~/Device Id\s*:\s*(\d+)/g) { # $DIDs{$1} = 1; # } } } if(not $Opt{"FixProbe"}) { if(enabledLog("megactl") and checkCmd("megactl")) { listProbe("logs", "megactl"); my $Megactl = runCmd("megactl 2>&1"); if($Megactl) { writeLog($LOG_DIR."/megactl", $Megactl); } } } if(not $Opt{"FixProbe"}) { if(enabledLog("arcconf") and checkCmd("arcconf")) { # Adaptec RAID listProbe("logs", "arcconf"); my @Controllers = (); my $ArcconfList = runCmd("arcconf LIST 2>&1"); while($ArcconfList=~s/Controller\s+(\d+)//) { push(@Controllers, $1); } my $Arcconf = ""; foreach my $Cn (@Controllers) { $Arcconf .= "Controller $Cn:\n"; $Arcconf .= runCmd("arcconf GETCONFIG $Cn PD 2>&1"); $Arcconf .= "\n"; } if($Arcconf) { $Arcconf = encryptSerials($Arcconf, "Serial number"); $Arcconf = encryptSerials($Arcconf, "World-wide name", "arcconf", 1); writeLog($LOG_DIR."/arcconf", $Arcconf); } listProbe("logs", "arcconf_smart"); my $Arcconf_Smart = ""; foreach my $Cn (@Controllers) { $Arcconf_Smart .= "Controller $Cn:\n"; $Arcconf_Smart .= runCmd("arcconf GETSMARTSTATS $Cn TABULAR 2>&1"); $Arcconf_Smart .= "\n"; } if($Arcconf_Smart) { $Arcconf_Smart=~s/\.{4,}/:/g; $Arcconf_Smart = encryptSerials($Arcconf_Smart, "serialNumber"); $Arcconf_Smart = encryptSerials($Arcconf_Smart, "vendorProductID"); writeLog($LOG_DIR."/arcconf_smart", $Arcconf_Smart); } } } if((not $Sys{"System"} or $Sys{"System"}=~/freedesktop/) and $Dmesg) { if($Dmesg=~/Linux version (.+)/) { my $LinVer = $1; foreach my $Lin ("endless", "ubuntu", "debian", "arch", "suse linux", "centos", "artix", "nixos") { if($LinVer=~/$Lin/i) { $Sys{"System"} = $Lin; if($Sys{"System"} eq "suse linux") { $Sys{"System"} = "opensuse"; } $Sys{"System_version"} = undef; last; } } if(index($LinVer, "neverware\@cloudready-builder") != -1) { $Sys{"System"} = "chrome_os"; $Sys{"System_version"} = undef; } elsif(index($LinVer, "Binutils for Uos") != -1) { $Sys{"System"} = "deepin"; $Sys{"System_version"} = undef; } } } if(index($Dmesg, "Secure boot enabled")!=-1) { # or index($Dmesg, "Secure boot could not be determined")!=-1 $Sys{"Secureboot"} = "enabled"; } elsif(defined $Sys{"Secureboot"}) { delete($Sys{"Secureboot"}); } if(-e $FixProbe_Logs."/boot_efi" or index($Dmesg, "] efi:")!=-1) { $Sys{"Boot_mode"} = "EFI"; } else { $Sys{"Boot_mode"} = "BIOS"; } if(not $Sys{"Microarch"} and $Dmesg=~/, ([\w\s\-]+) events, /) { $Sys{"Microarch"} = $1; if($Sys{"Microarch"}=~/disabled/) { $Sys{"Microarch"} = undef; } if($Sys{"Microarch"} eq "Core2") { $Sys{"Microarch"} = "Core"; } elsif($Sys{"Microarch"} eq "Atom") { $Sys{"Microarch"} = undef; } } if($Dmesg=~/microcode:.*?(sig|patch_level)=(0x\w+)/) { $Sys{"Microcode"} = $2; if(not $Sys{"Microarch"} and defined $MicroCodeMicroArch{$Sys{"Microcode"}}) { $Sys{"Microarch"} = $MicroCodeMicroArch{$Sys{"Microcode"}}; } } if($Dmesg=~/Memory usable by graphics device = (\d+)M/) { $Sys{"Video_memory"} = $1/1024.0; } elsif($Dmesg=~/DRM: VRAM: (\d+) MiB/) { $Sys{"Video_memory"} = $1/1024.0; } elsif($Dmesg=~/(\d+)M of VRAM memory ready/) { $Sys{"Video_memory"} = $1/1024.0; } if($Dmesg=~/Memory: \d+k\/(\d+)k available/) { $Sys{"Ram_total"} = $1; } elsif(isBSD() and $Dmesg=~/real (mem|memory)\s*=.+?\(([^()]+?)\s*MB\)/) { $Sys{"Ram_total"} = $2*1024.0; if($Dmesg=~/avail (mem|memory)\s*=.+?\(([^()]+?)\s*MB\)/) { $Sys{"Ram_used"} = $Sys{"Ram_total"} - $2*1024.0; } } if((not $Sys{"Model"} or grep { $Sys{"Model"} eq $_ } ("rpi", "Raspberry Pi")) and $Sys{"Arch"}=~/arm|aarch/i) { if($Dmesg=~/(Machine(| model)|Hardware name): (.+)/) { $Sys{"Model"} = $3; $Sys{"Model"}=~s/, model:.*//; if($Sys{"Model"}=~/(Orange Pi|Banana Pi|Raspberry Pi|Odroid|rockchip|AM335x)/i) { $Sys{"Type"} = "system on chip"; } if($Sys{"Model"}=~/\A(Raspberry Pi) /) { $Sys{"Vendor"} = "Raspberry Pi Foundation"; } elsif($Sys{"Model"}=~/\Arockchip,(.+)\Z/) { $Sys{"Model"} = $1; $Sys{"Vendor"} = "Rockchip"; $Sys{"System"} = "android"; } elsif($Sys{"Model"}=~s/\A(Amlogic|FriendlyElec|Hardkernel|NextThing|NVIDIA|Olimex|Radxa|TI|Xunlong) //) { $Sys{"Vendor"} = $1; $Sys{"Type"} = "system on chip"; } elsif($Sys{"Model"}=~/Pinebook/) { $Sys{"Type"} = "notebook"; $Sys{"Vendor"} = "Pine Microsystems"; } elsif($Sys{"Model"}=~/PinePhone/) { $Sys{"Type"} = "smartphone"; $Sys{"Vendor"} = "Pine Microsystems"; } elsif($Sys{"Model"}=~/Pine64/) { $Sys{"Type"} = "system on chip"; $Sys{"Vendor"} = "Pine Microsystems"; } $Sys{"Model"}=~s/\s+Board\Z//i; if($Sys{"Vendor"} or $Sys{"Model"}) { fixFFByModel($Sys{"Vendor"}, $Sys{"Model"}); } } elsif($Dmesg=~/(Raspberry Pi)/) { $Sys{"Model"} = $1; $Sys{"Type"} = "system on chip"; } ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); } my $XLog = ""; if($Opt{"FixProbe"}) { $XLog = readFile($FixProbe_Logs."/xorg.log"); } else { listProbe("logs", "xorg.log"); $XLog = readFile("/var/log/Xorg.0.log"); if(my $SessUser = getUser()) { # Xorg.0.log in X11/XWayland (Ubuntu >= 18.04) if(my $XLog_U = readFile("/home/".$SessUser."/.local/share/xorg/Xorg.0.log")) { $XLog = $XLog_U; } } else { # Live if(my $XLog_U = readFile("/home/ubuntu/.local/share/xorg/Xorg.0.log")) { $XLog = $XLog_U; } } if($XLog) { $XLog = hideTags($XLog, "Serial#"); $XLog = hidePaths($XLog); $XLog = encryptUUIDs($XLog); if(my $HostName = $ENV{"HOSTNAME"}) { $XLog=~s/ \Q$HostName\E / NODE /g; } $XLog = hideHost($XLog); $XLog = hideByRegexp($XLog, qr/\s?([\w\s]+\’s)/); } if(not $Opt{"Docker"} or $XLog) { writeLog($LOG_DIR."/xorg.log", $XLog); } } my $X11LogMatch = index($XLog, $Sys{"Kernel"})!=-1; if(not $Sys{"Display_server"} and $X11LogMatch) { $Sys{"Display_server"} = "X11"; } my $CmdLine = ""; my ($Nomodeset, $ForceVESA) = (undef, undef); if($XLog) { if($XLog=~/NVIDIA\(\d+\): Memory: (\d+) kBytes/) { $Sys{"Video_memory"} = $1/(1024.0*1024.0); } elsif($XLog=~/Video RAM: (\d+) kByte/) { $Sys{"Video_memory"} = $1/(1024.0*1024.0); } elsif(isBSD() and $XLog=~/VideoRAM: (\d+) KB/) { # SIS $Sys{"Video_memory"} = $1/(1024.0*1024.0); } if($XLog=~/Kernel command line:(.*)/) { $CmdLine = $1; } if(not $Sys{"System"} or $Sys{"System"}=~/freedesktop/ or isBSD()) { my @OSes = ("deepin", "clearlinux", "debian", "opensuse", "alt linux", "ubuntu", "manjaro", "artix", "arch"); if(isBSD()) { @OSes = @KNOWN_BSD; } my $Matched = undef; foreach my $Prefix ("Build Operating System", "Current Operating System", "Kernel command line") { if($XLog=~/$Prefix:(.*)/) { my $Linux = lc($1); $Linux=~s{/arch/}{/}g; foreach my $Lin (@OSes) { if(index($Linux, $Lin) != -1) { $Matched = lc($Lin); last; } } if($Linux=~/alt linux (p\d+)/) { $Matched = "alt-$1"; } } } if($Matched) { if(isBSD()) { $Sys{"System"}=~s/\A(\w+)-/$Matched-/; } else { $Sys{"System"} = $Matched; $Sys{"System_version"} = undef; } } } my @CheckDrivers = @G_DRIVERS; if(not defined $GraphicsCards{"10de"}) { rmArrayVal(\@CheckDrivers, ["nouveau", "nvidia"]); } if(not defined $GraphicsCards{"1002"}) { rmArrayVal(\@CheckDrivers, ["radeon", "amdgpu", "fglrx"]); } if(not defined $GraphicsCards{"8086"}) { rmArrayVal(\@CheckDrivers, \@G_DRIVERS_INTEL); } if($Sys{"Kernel"} and not $X11LogMatch) { # Do not check old X11 log @CheckDrivers = (); } else { $Nomodeset = (index($CmdLine, " nomodeset")!=-1 or index($CmdLine, " nokmsboot")!=-1); $ForceVESA = (index($CmdLine, "xdriver=vesa")!=-1); } foreach my $D (@CheckDrivers) { if($Nomodeset or index($CmdLine, "$D.modeset=0")!=-1) { if($ForceVESA or isIntelDriver($D)) { # can't check setCardStatus($D, "detected"); next; } } if(defined $KernMod{$D} and defined $WorkMod{$D}) { my @Loaded = (); my @Drs = ($D); if(isIntelDriver($D)) { @Drs = ("intel"); } elsif($D eq "nouveau") { # Manjaro 17 @Drs = ("nouveau", "nvidia"); } if(keys(%GraphicsCards_InUse)==1) { # Ubuntu 18 push(@Drs, "modesetting"); } foreach my $Dr (@Drs) { if(index($XLog, "LoadModule: \"$Dr\"")!=-1) { push(@Loaded, $Dr); } } if(@Loaded) { my @Unloaded = (); foreach my $Dr (@Loaded) { if(index($XLog, "UnloadModule: \"$Dr\"")!=-1) { push(@Unloaded, $Dr); } } if(isIntelDriver($D) and defined $GraphicsCards{"1002"} and defined $WorkMod{"fglrx"}) { # fglrx by intel, intel is unloaded setCardStatus($D, "works"); next; } if($#Unloaded==$#Loaded) { setCardStatus($D, "failed"); } else { setCardStatus($D, "works"); } } elsif(grep {$D eq $_} ("nvidia") and defined $GraphicsCards{"8086"}) { # no entries in the Xorg.0.log setCardStatus($D, "works"); } } my @DrLabels = (uc($D)); my @DrIds = ($D); if(isIntelDriver($D)) { @DrLabels = ("intel"); push(@DrIds, "i965"); } elsif($D eq "radeon") { push(@DrIds, ("r600", "r300", "radeonsi")); } elsif($D eq "amdgpu") { push(@DrIds, "radeonsi"); } if(keys(%GraphicsCards_InUse)==1) { push(@DrLabels, "modeset"); } foreach my $DrLabel (@DrLabels) { if(index($XLog, ") ".$DrLabel."(")!=-1) { # (II) RADEON(0) # (II) NOUVEAU(0) # (II) intel(0) # (II) modeset(0) setCardStatus($D, "works"); last; } } foreach my $DrId (@DrIds) { if(index($XLog, "): [DRI2] DRI driver: $DrId")!=-1) { # (II) modeset(G0): [DRI2] DRI driver: nouveau # (II) modeset(0): [DRI2] DRI driver: i965 setCardStatus($D, "works"); last; } } if(keys(%GraphicsCards_InUse)==1 and index($XLog, ") FBDEV(")!=-1) { setCardStatus($D, "failed"); } } if(isBSD()) { if($XLog=~/ modeset\(.+Intel/ or $XLog=~/ intel\(0\): /) { setCardStatusByVendor("8086", "works", "i915"); } elsif($XLog=~/ NVIDIA\(0\): /) { setCardStatusByVendor("10de", "works", "nvidia"); } elsif($XLog=~/ NV\(0\): /) { setCardStatusByVendor("10de", "works", "nv"); } elsif($XLog=~/ RADEON\(0\): Chipset:/) { setCardStatusByVendor("1002", "works", "radeon"); } elsif($XLog=~/ modeset\(0\): Output/ and keys(%GraphicsCards_InUse)==1) { if((keys(%GraphicsCards_InUse))[0]=~/pci:([a-f\d]{4})/) { setCardStatusByVendor($1, "works", undef); } } } } else { # No Xorg log if($Dmesg) { if($Dmesg=~/Command line:(.*)/) { $CmdLine = $1; } $Nomodeset = (index($CmdLine, " nomodeset")!=-1 or index($CmdLine, " nokmsboot")!=-1); $ForceVESA = (index($CmdLine, "xdriver=vesa")!=-1); } foreach my $D (@G_DRIVERS) { if($Nomodeset or index($CmdLine, "$D.modeset=0")!=-1) { if($ForceVESA or isIntelDriver($D)) { # can't check setCardStatus($D, "detected"); next; } } if(defined $WorkMod{$D}) { setCardStatus($D, "works"); } } } if($Nomodeset) { $Sys{"Nomodeset"} = "enabled"; } elsif(defined $Sys{"Nomodeset"}) { delete($Sys{"Nomodeset"}); } if($Sys{"Video_memory"}) { $Sys{"Video_memory"} = roundFloat($Sys{"Video_memory"}, 2); } if(not grep {$HW{$_}{"Type"} eq "monitor"} keys(%HW)) { if(my @LCDs = $XLog=~/\:\s+([^:]+?) \((\w+-?\d+)\)(\: connected| \(boot, connected\)| \(connected\))/g) { # Nvidia foreach my $MPos (0 .. $#LCDs) { if($MPos % 3 != 0) { next; } my ($MName, $MPort, $MConn) = ($LCDs[$MPos], $LCDs[$MPos + 1], $LCDs[$MPos + 2]); my %Mon = (); $Mon{"Device"} = $MName; if($MConn=~/boot/ or index($XLog, "$MPort (boot)")!=-1) { if($XLog=~/Virtual screen size determined to be (\d+) x (\d+)/) { $Mon{"Resolution"} = $1."x".$2; } } my $MID = "eisa:".fmtID(devID(nameID($Mon{"Device"}), $Mon{"Resolution"})); if(defined $HW{$MID}) { last; } if($Mon{"Device"}=~s/\A($ALL_MON_VENDORS)([ \-]|\Z)//) { $Mon{"Vendor"} = $1; } $Mon{"Type"} = "monitor"; $Mon{"Status"} = "works"; if($MPort=~/DFP|LVDS/) { $Mon{"Kind"} = "Digital" } elsif($MPort=~/CRT/) { $Mon{"Kind"} = "Analog" } if($Mon{"Device"}) { $Mon{"Device"} = "LCD Monitor ".$Mon{"Device"}; } else { $Mon{"Device"} = "LCD Monitor"; } if($Mon{"Resolution"}) { $Mon{"Device"} .= " ".$Mon{"Resolution"}; } $HW{$MID} = \%Mon; $HW{$MID}{"Status"} = "works"; } } elsif(my @LCDs = $XLog=~/Output (.+?) using initial mode (\d+x\d+)/g) { foreach my $MPos (0 .. $#LCDs) { if($MPos % 2 != 0) { next; } my ($MPort, $MRes) = ($LCDs[$MPos], $LCDs[$MPos + 1]); my %Mon = (); $Mon{"Type"} = "monitor"; $Mon{"Status"} = "works"; $Mon{"Device"} = "LCD Monitor"; if($MPort=~/DFP|LVDS/) { $Mon{"Kind"} = "Digital" } elsif($MPort=~/CRT/) { $Mon{"Kind"} = "Analog" } $Mon{"Resolution"} = $MRes; my $MID = "eisa:".fmtID(devID(nameID($Mon{"Device"}), $MRes)); $Mon{"Device"} .= " ".$MRes; $HW{$MID} = \%Mon; } } } foreach my $ID (sort keys(%HW)) { if($HW{$ID}{"Type"} eq "monitor") { $Sys{"Monitors"} += 1; } } my $HciConfig = ""; if($Opt{"FixProbe"}) { $HciConfig = readFile($FixProbe_Logs."/hciconfig"); } elsif(enabledLog("hciconfig") and checkCmd("hciconfig")) { listProbe("logs", "hciconfig"); $HciConfig = runCmd("hciconfig -a 2>&1"); $HciConfig = hideMACs($HciConfig); $HciConfig = hideTags($HciConfig, "Name"); if($HciConfig) { writeLog($LOG_DIR."/hciconfig", $HciConfig); } } if($HciConfig) { foreach my $HCI (split(/\n\n/, $HciConfig)) { if(index($HCI, "UP RUNNING ")!=-1) { if($HCI=~/\A[^:]+:?\s/) { foreach my $ID (sort grep {defined $HW{$_}{"Type"} and $HW{$_}{"Type"} eq "bluetooth"} keys(%HW)) { # TODO: identify particular bt devices by lsusb if($HW{$ID}{"Driver"}) { $HW{$ID}{"Status"} = "works"; setAttachedStatus($ID, "works"); } } } } } } my $MmCli = ""; if($Opt{"FixProbe"}) { $MmCli = readFile($FixProbe_Logs."/mmcli"); } elsif(enabledLog("mmcli") and checkCmd("mmcli")) { listProbe("logs", "mmcli"); my $Modems = runCmd("mmcli -L 2>&1"); if($Modems=~/No modems were found/i) { $Modems = ""; } my %MNums = (); while($Modems=~s/Modem\/(\d+)//) { $MNums{$1} = 1; } foreach my $Modem (sort {int($a)<=>int($b)} keys(%MNums)) { my $MInfo = runCmd("mmcli -m $Modem"); $MInfo = hideTags($MInfo, "own|imei|equipment id"); $MmCli .= $MInfo; $MmCli .= "\n"; } if($MmCli) { writeLog($LOG_DIR."/mmcli", $MmCli); } } if($MmCli) { foreach my $MM (split(/\n\n/, $MmCli)) { if($MM=~/model:.*\[(\w+):(\w+)\]/) { if(my $ID = "usb:".lc($1."-".$2)) { if(defined $HW{$ID} and $HW{$ID}{"Driver"}) { $HW{$ID}{"Status"} = "works"; setAttachedStatus($ID, "works"); } } } } } my $OpenscTool = ""; if($Opt{"FixProbe"}) { $OpenscTool = readFile($FixProbe_Logs."/opensc-tool"); } elsif(enabledLog("opensc-tool") and checkCmd("opensc-tool")) { listProbe("logs", "opensc-tool"); $OpenscTool = runCmd("opensc-tool --list-readers"); if($OpenscTool and $OpenscTool!~/No smart card readers/) { $OpenscTool=~s/ \([^\(\)]+\)//g; writeLog($LOG_DIR."/opensc-tool", $OpenscTool); } } if($OpenscTool) { foreach my $SCReader (split(/\n\n/, $OpenscTool)) { if(index($SCReader, "Driver")!=-1) { foreach my $ID (sort grep {defined $HW{$_}{"Type"} and $HW{$_}{"Type"} eq "chipcard"} keys(%HW)) { # TODO: match particular chipcard devices by name if($HW{$ID}{"Driver"}) { $HW{$ID}{"Status"} = "works"; setAttachedStatus($ID, "works"); } } } } } my $Lscpu = ""; if($Opt{"FixProbe"}) { $Lscpu = readFile($FixProbe_Logs."/lscpu"); } elsif(checkCmd("lscpu")) { listProbe("logs", "lscpu"); $Lscpu = runCmd("lscpu 2>&1"); writeLog($LOG_DIR."/lscpu", $Lscpu); } my $CoresPerSocket = undef; if($Lscpu) { my ($Sockets, $Cores, $Threads, $CPUs) = (); my ($CPU_Vendor, $CPU_Name, $CPU_Family, $CPU_ModelNum, $CPU_Stepping) = (); my @CpuVals = (); foreach (split(/\n/, $Lscpu)) { my @CpuAttr = split(":", $_); my $Attr = $CpuAttr[0]; my $Val = $CpuAttr[1]; $Val=~s/\A\s+//; if($Attr eq "Address sizes") { next; } if($Attr eq "Socket(s)") { $Sockets = $Val; } elsif($Attr eq "Core(s) per socket") { $Cores = $Val; $CoresPerSocket = $Val; } elsif($Attr eq "Thread(s) per core") { $Threads = $Val; } elsif($Attr eq "CPU op-mode(s)") { $Sys{"Op_modes"} = $Val; } elsif($Attr eq "Vendor ID" or $Attr eq "Vendor") { $CPU_Vendor = $Val; } elsif($Attr eq "Model name") { $CPU_Name = fmtVal($Val); } elsif($Attr eq "CPU family") { $CPU_Family = $Val; } elsif($Attr eq "Model") { $CPU_ModelNum = $Val; } elsif($Attr eq "Stepping") { $CPU_Stepping = $Val; } elsif($Attr eq "Total CPU(s)") { $CPUs = $Val; } elsif($Attr eq "Architecture") { if(not $Sys{"Arch"}) { $Sys{"Arch"} = $Val; } if(not $Sys{"Type"} and $Val=~/riscv|mips/) { $Sys{"Type"} = "system on chip"; } } push(@CpuVals, $Val); } if($Sockets eq "0") { $Sockets = 1; } if($Sockets and $Cores and $Threads) { $Sys{"Sockets"} = $Sockets; $Sys{"Cores"} = $Cores*$Sockets; $Sys{"Threads"} = $Threads; } elsif(not isBSD()) { if($Lscpu=~/\s((32|64)-bit.*?)\n/) { $Sys{"Op_modes"} = $1; } } if(my $Microarch = detectMicroarch($CPU_Vendor, $CPU_Family, $CPU_ModelNum, $CPU_Name)) { $Sys{"Microarch"} = $Microarch; } if(isBSD()) { if(not $Cores and $CPUs==1) { $Sys{"Cores"} = $CPUs; } if(not $CPU_ID and $CPU_Name) { my %CpuDev = (); if($CPU_Name=~s/\A(ARM) //) { $CPU_Vendor = $1; } elsif($CPU_Name=~/\A(7447A) /) { $CPU_Vendor = "PowerPC"; } $CpuDev{"Vendor"} = fixCpuVendor($CPU_Vendor); $CpuDev{"Device"} = $CPU_Name; $CpuDev{"Device"} = duplVendor($CpuDev{"Vendor"}, $CpuDev{"Device"}); $CpuDev{"Type"} = "cpu"; $CpuDev{"Status"} = "works"; $CPU_ID = "cpu:".fmtID(devID(nameID($CpuDev{"Vendor"}), join(".", ($CPU_Family, $CPU_ModelNum, $CPU_Stepping)), devSuffix(\%CpuDev))); $HW{$CPU_ID} = \%CpuDev; } setDevCount($CPU_ID, "cpu", $Sys{"Threads"}*$Sys{"Cores"}); } } my $CpuInfo = ""; if($Opt{"FixProbe"}) { $CpuInfo = readFile($FixProbe_Logs."/cpuinfo"); } elsif(enabledLog("cpuinfo") and -e "/proc/cpuinfo") { listProbe("logs", "cpuinfo"); $CpuInfo = readFile("/proc/cpuinfo"); $CpuInfo=~s/\n\n(.|\n)+\Z/\n/g; # for one core writeLog($LOG_DIR."/cpuinfo", $CpuInfo); } if(not $Sys{"Cores"}) { if($CpuInfo=~/siblings\s*:\s*(\d+)/) { my $Siblings = $1; if($CpuInfo=~/cpu cores\s*:\s*(\d+)/) { $CoresPerSocket = $1; $Sys{"Threads"} = $Siblings / $CoresPerSocket; if(my $TotalThreads = getDeviceCount($CPU_ID)) { $Sys{"Sockets"} = $TotalThreads/($CoresPerSocket*$Sys{"Threads"}); $Sys{"Cores"} = $Sys{"Sockets"}*$CoresPerSocket; } } } } if($CpuInfo) { my ($CPU_Vendor, $CPU_Family, $CPU_ModelNum) = (); foreach my $L (split(/\n/, $CpuInfo)) { $L=~s/\s*:\s*/:/; my @CpuAttr = split(":", $L); my $Attr = $CpuAttr[0]; my $Val = $CpuAttr[1]; if($Attr eq "vendor_id") { $CPU_Vendor = $Val; } elsif($Attr eq "cpu family") { $CPU_Family = $Val; } elsif($Attr eq "model") { $CPU_ModelNum = $Val; } elsif($Attr eq "cpu model") { if(not $CPU_ID and $Val=~s/\A(MIPS) //) { my %CpuDev = (); $CpuDev{"Vendor"} = fixCpuVendor($1); $CpuDev{"Device"} = $Val; $CpuDev{"Type"} = "cpu"; $CpuDev{"Status"} = "works"; $CPU_ID = "cpu:".fmtID(devID(nameID($CpuDev{"Vendor"}), $CpuDev{"Device"})); $HW{$CPU_ID} = \%CpuDev; } } elsif($Attr eq "machine") { if(not $Sys{"Vendor"} and $Val=~s/\A(TP-LINK) //) { $Sys{"Vendor"} = $1; $Sys{"Model"} = $Val; } } elsif($Attr eq "system type") { if(not $Board_ID and $Val=~s/\A(Qualcomm) //) { my %BoardDev = (); $BoardDev{"Vendor"} = $1; $BoardDev{"Device"} = $Val; $BoardDev{"Type"} = "motherboard"; $Board_ID = "board:".fmtID(devID(nameID($BoardDev{"Vendor"}), $BoardDev{"Device"})); $HW{$Board_ID} = \%BoardDev; } } } if(my $Microarch = detectMicroarch($CPU_Vendor, $CPU_Family, $CPU_ModelNum)) { $Sys{"Microarch"} = $Microarch; } } my $Cpuid = ""; if($Opt{"FixProbe"}) { $Cpuid = readFile($FixProbe_Logs."/cpuid"); } elsif(enabledLog("cpuid") and checkCmd("cpuid")) { listProbe("logs", "cpuid"); if(isBSD()) { $Cpuid = runCmd("cpuid 2>/dev/null"); $Cpuid = encryptSerials($Cpuid, "Processor serial"); if($Cpuid=~/usage: cpuid code/) { $Cpuid = ""; } } else { $Cpuid = runCmd("cpuid -1 2>/dev/null"); $Cpuid = encryptSerials($Cpuid, "serial number"); } writeLog($LOG_DIR."/cpuid", $Cpuid); } if($Cpuid and not $Lscpu and isBSD() and not $CPU_ID) { my %CpuDev = (); if($Cpuid=~/Vendor ID: "(.+?)"/) { $CpuDev{"Vendor"} = $1; } if($Cpuid=~/Extended brand string: "\s*(.+?)\s*"/) { $CpuDev{"Device"} = $1; } if($Cpuid=~/Family (\d+)/) { $CpuDev{"Family"} = $1; } if($Cpuid=~/Model (\d+)/) { $CpuDev{"ModelNum"} = $1; } if($Cpuid=~/Stepping (\d+)/) { $CpuDev{"Stepping"} = $1; } if($CpuDev{"Device"}) { $CPU_ID = registerCPU(\%CpuDev); if($CPU_ID and not getDeviceCount($CPU_ID) and $Cpuid=~/siblings: (\d+)/) { setDevCount($CPU_ID, "cpu", $1); } } if(my $Microarch = detectMicroarch($CpuDev{"Vendor"}, $CpuDev{"Family"}, $CpuDev{"ModelNum"}, $CpuDev{"Device"})) { $Sys{"Microarch"} = $Microarch; } } if(isNetBSD() and not $CPU_ID) { my @Cpus = $Dmesg=~/(cpu\d+ at \w+\d+: .+), id/g; foreach (@Cpus) { if(/cpu(\d+) at \w+\d+: ([^\s]+) (.+)/) { my ($CpuCount, $CpuVendor, $CpuDevice) = ($1, $2, $3); my %CpuDev = (); $CpuDev{"Vendor"} = $CpuVendor; $CpuDev{"Device"} = $CpuDevice; $CPU_ID = registerCPU(\%CpuDev); if($CPU_ID) { setDevCount($CPU_ID, "cpu", $CpuCount+1); } } } } if(isBSD() and not $CPU_ID) { if($Sysctl=~/hw.model\s*[:=]\s*(.+)/) { my $HWModel = $1; $HWModel=~s/\(R\)//g; if($HWModel=~/\A(Genuine Intel|[^\s]+) (.+)/) { my %CpuDev = (); $CpuDev{"Vendor"} = $1; $CpuDev{"Device"} = $2; $CpuDev{"Vendor"} = fmtVal($CpuDev{"Vendor"}); $CpuDev{"Device"} = fmtVal($CpuDev{"Device"}); $CPU_ID = registerCPU(\%CpuDev); if($CPU_ID and $Sysctl=~/hw.ncpu\s*[:=]\s*(\d+)/) { setDevCount($CPU_ID, "cpu", $1); } if(my $Microarch = detectMicroarch($CpuDev{"Vendor"}, undef, undef, $CpuDev{"Device"})) { $Sys{"Microarch"} = $Microarch; } } } } my $Meminfo = ""; if($Opt{"FixProbe"}) { $Meminfo = readFile($FixProbe_Logs."/meminfo"); } else { listProbe("logs", "meminfo"); $Meminfo = readFile("/proc/meminfo"); if($Meminfo) { writeLog($LOG_DIR."/meminfo", $Meminfo); } } if($Meminfo) { if($Meminfo=~/MemTotal:\s+(\d+) kB/) { $Sys{"Ram_total"} = $1; if($Meminfo=~/MemAvailable:\s+(\d+) kB/) { $Sys{"Ram_used"} = $Sys{"Ram_total"} - $1; } registerRAM($Sys{"Ram_total"}); } } my $Df = ""; if($Opt{"FixProbe"}) { $Df = readFile($FixProbe_Logs."/df"); } elsif(not $Opt{"Docker"} and enabledLog("df") and checkCmd("df")) { listProbe("logs", "df"); $Df = runCmd("df -Th 2>/dev/null"); if(not $Df) { # OpenBSD, NetBSD, FreeBSD < 8.0 $Df = runCmd("df -h 2>/dev/null"); } $Df = hidePaths($Df); $Df = hideIPs($Df); $Df = hideUrls($Df); $Df = encryptUUIDs($Df); $Df = hideDf($Df); writeLog($LOG_DIR."/df", $Df); } my ($SpaceTotal, $SpaceUsed) = (0.0, 0.0); my $NewDf = ""; if(index($Df, " Type ")!=-1) { $NewDf = "[^\\s]+\\s+"; } my $BsdDf = ""; if(isBSD()) { $BsdDf = "|[a-z]\\w+\\d|ufsid|gpt|ufs"; } foreach my $DfL (split(/\n/, $Df)) { if($DfL=~/\A\/dev\/([sh]d|nvme|mapper|mmcblk|root$BsdDf).*?\s+$NewDf([\w\.\,]+)\s+([\w\.\,]+)/) { my ($PSize, $PUsed) = ($2, $3); if($PSize) { $SpaceTotal += toGb($PSize); } if($PUsed) { $SpaceUsed += toGb($PUsed); } } } if(isBSD() and $Df=~/ zfs /) { if($Df=~/ zfs\s+([\w\.\,]+)\s+([\w\.\,]+).+?\/\n/) { my ($PSize, $PUsed) = ($1, $2); if($PSize) { $SpaceTotal = toGb($PSize); } if($PUsed) { $SpaceUsed = toGb($PUsed); } } } if($SpaceTotal and $SpaceUsed) { $Sys{"Space_total"} = roundFloat($SpaceTotal, 2); $Sys{"Space_used"} = roundFloat($SpaceUsed, 2); } if($NewDf and $Df=~/^[^\s]+[ \t]+([^\s\/]+)[ \t]+.*[ \t]+\/$/m) { if($1 ne "squashfs") { $Sys{"Filesystem"} = $1; } } $Sys{"Dual_boot"} = 0; $Sys{"Dual_boot_win"} = 0; if($Lsblk) { foreach my $Line (split(/\n/, $Lsblk)) { if($Line=~/\blive-/) { next; } if($Line=~/ (ext[234]|btrfs|xfs) / and index($Line, "/")==-1) { $Sys{"Dual_boot"} = 1; } } if(index($Lsblk, " ntfs ")!=-1) { $Sys{"Dual_boot_win"} = 1; } if(index($Lsblk, " LABEL ")!=-1) { # old format if(index($Lsblk, "/snap/")!=-1) { if($Lsblk=~/^[^\s]+[ \t]+[^\s]+[ \t]+[^\s]+[ \t]+(\w+)[ \t]+.*\/var\/lib\/snapd\/hostfs[ \t]/m) { $Sys{"Filesystem"} = $1; } } else { if($Lsblk=~/^[^\s]+[ \t]+[^\s]+[ \t]+[^\s]+[ \t]+(\w+)[ \t]+.*\/[ \t]/m) { $Sys{"Filesystem"} = $1; } } } else { if(index($Lsblk, "/snap/")!=-1) { if($Lsblk=~/(\w+)\s+[a-f\d\-]+\s+\/var\/lib\/snapd\/hostfs\s/) { $Sys{"Filesystem"} = $1; } } else { if($Lsblk=~/(\w+)\s+[a-f\d\-]+\s+\/\s/) { $Sys{"Filesystem"} = $1; } } } if(isBSD()) { # TODO: detect fs } } my $Findmnt = ""; if($Opt{"FixProbe"}) { $Findmnt = readFile($FixProbe_Logs."/findmnt"); } elsif(not $Opt{"Docker"} and enabledLog("findmnt") and checkCmd("findmnt")) { listProbe("logs", "findmnt"); my $FindmntCmd = "findmnt"; if($Opt{"Flatpak"}) { $FindmntCmd .= " 2>/dev/null"; } else { $FindmntCmd .= " 2>&1"; } $Findmnt = runCmd($FindmntCmd); if($Opt{"Snap"} and $Findmnt=~/Permission denied/) { $Findmnt = ""; } $Findmnt=~s/\[[^\s]+\]/[XXXXX]/g; $Findmnt = hidePaths($Findmnt); $Findmnt = hideIPs($Findmnt); $Findmnt = hideUrls($Findmnt); writeLog($LOG_DIR."/findmnt", $Findmnt); } my $Fstab = ""; if($Opt{"FixProbe"}) { $Fstab = readFile($FixProbe_Logs."/fstab"); $Fstab=~s/#.*\n//g; } elsif(not $Opt{"Docker"} and enabledLog("fstab")) { listProbe("logs", "fstab"); $Fstab = readFile("/etc/fstab"); $Fstab = hidePaths($Fstab); $Fstab = hideIPs($Fstab); $Fstab = hideUrls($Fstab); $Fstab = hidePass($Fstab); $Fstab = encryptUUIDs($Fstab); $Fstab=~s/LABEL=[^\s]+/LABEL=XXXX/g; $Fstab=~s/sshfs#.+/sshfs.../g; $Fstab=~s/#.*\n//g; writeLog($LOG_DIR."/fstab", $Fstab); } my $LocaleConf = ""; if($Opt{"FixProbe"}) { $LocaleConf = readFile($FixProbe_Logs."/locale"); } elsif(enabledLog("locale")) { listProbe("logs", "locale"); if(isBSD()) { if(checkCmd("locale")) { $LocaleConf = runCmd("locale"); } } else { $LocaleConf = readFile("/etc/locale.conf"); } if($LocaleConf) { writeLog($LOG_DIR."/locale", $LocaleConf); } } if(not $Sys{"Lang"}) { if($LocaleConf=~/LANG="(.+)"/) { $Sys{"Lang"} = $1; } elsif($LocaleConf=~/LANG=(.+)/) { $Sys{"Lang"} = $1; } } my $Mount = ""; if($Opt{"FixProbe"}) { $Mount = readFile($FixProbe_Logs."/mount"); } elsif(not $Opt{"Docker"} and enabledLog("mount") and checkCmd("mount")) { listProbe("logs", "mount"); $Mount = runCmd("mount -v 2>&1 | column -t"); if($Opt{"Snap"} and $Mount=~/Permission denied/) { $Mount = ""; } $Mount = hidePaths($Mount); $Mount = hideIPs($Mount); $Mount = hideUrls($Mount); writeLog($LOG_DIR."/mount", $Mount); } if(not $Sys{"Filesystem"}) { if($Fstab=~/\s+\/\s+([^\s]+)\s+/) { if($1 ne "auto") { $Sys{"Filesystem"} = $1; } } } if(not $Sys{"Filesystem"}) { if($Mount=~/\s+on\s+(\/|\/usr)\s+type\s+([^\s]+)/) { $Sys{"Filesystem"} = $2; } } if(not $Sys{"Filesystem"}) { my @Filesystems = ("btrfs", "jfs", "reiserfs", "xfs", "zfs", "aufs", "ext[234]", "overlay", "hammer2", "ufs", "ffs"); LOOP: foreach my $Log ($Df, $Lsblk, $Findmnt) { foreach my $Fs (@Filesystems) { if($Log=~/\s+($Fs)\s+/) { $Sys{"Filesystem"} = $1; last LOOP; } } } } if(isBSD()) { if(not $Sys{"Filesystem"}) { if($Sysctl=~/vfs\.mounts\.ffs has [1-9]/) { $Sys{"Filesystem"} = "ffs"; } } if(not $Sys{"Filesystem"}) { if($Disklabel=~/ 4\.2BSD /) { $Sys{"Filesystem"} = "ufs"; } } if(not $Sys{"Filesystem"}) { if($Dmesg=~/(root file system type:|mount root from)\s+(\w+)/) { $Sys{"Filesystem"} = $2; if($Sys{"Filesystem"} eq "ffs") { $Sys{"Filesystem"} = "ufs"; } } } } my $XInput = ""; if($Opt{"FixProbe"}) { $XInput = readFile($FixProbe_Logs."/xinput"); } elsif(enabledLog("xinput") and checkCmd("xinput")) { listProbe("logs", "xinput"); $XInput = runCmd("xinput list --long 2>&1"); writeLog($LOG_DIR."/xinput", clearLog_X11($XInput)); } if($XInput=~/xwayland/i) { $Sys{"Display_server"} = "Wayland"; } my $BootLog = ""; if($Opt{"FixProbe"}) { $BootLog = readFile($FixProbe_Logs."/boot.log"); } elsif(enabledLog("boot.log") and -f "/var/log/boot.log" and -s "/var/log/boot.log" < $MAX_LOG_SIZE*50) { listProbe("logs", "boot.log"); $BootLog = clearLog(readFile("/var/log/boot.log")); $BootLog=~s&(Mounted|Mounting)\s+/.+&$1 XXXXX&g; $BootLog=~s&(Setting hostname\s+).+:&$1XXXXX:&g; $BootLog=~s&(File System Check on) .+&$1 XXX&g; $BootLog=~s& [^\s]+ (NET\[)& XXX $1&g; $BootLog=~s&(failed to look up) (.+?) (on)&$1 XXX $3&g; $BootLog = hideLVM($BootLog); $BootLog = encryptUUIDs($BootLog); $BootLog = hideDevDiskUUIDs($BootLog); $BootLog = hideIPs($BootLog); writeLog($LOG_DIR."/boot.log", $BootLog); } if(not $Sys{"System"} or $Sys{"System"}=~/freedesktop/) { if($BootLog=~/Endless OS/) { $Sys{"System"} = "endless"; $Sys{"System_version"} = undef; } } my $Sctl = ""; if($Opt{"FixProbe"}) { $Sctl = readFile($FixProbe_Logs."/systemctl"); } elsif(not $Opt{"Docker"} and enabledLog("systemctl") and checkCmd("systemctl")) { listProbe("logs", "systemctl"); if($Sctl = runCmd("systemctl 2>/dev/null")) { $Sctl = hideByRegexp($Sctl, qr/\/home\/([^\s]+)/); $Sctl = hideByRegexp($Sctl, qr/\/media\/([^\s]+)/); $Sctl = hideByRegexp($Sctl, qr/home-([^\s]+)/); $Sctl = hideByRegexp($Sctl, qr/media-([^\s]+)/); $Sctl = hideByRegexp($Sctl, qr/ cluster ([^\s]+)/); $Sctl = hidePaths($Sctl); $Sctl=~s/(User Slice of|Session \d+ of user|Synchronization for).+/$1 XXXXX/g; if(my $SessUser = getUser()) { $Sctl=~s/( of user)\s+\Q$SessUser\E/$1 USER/g; $Sctl=~s/\@$SessUser\./\@USER\./g; } $Sctl = decorateSystemd($Sctl); $Sctl = encryptUUIDs($Sctl); $Sctl = hideDevDiskUUIDs($Sctl); writeLog($LOG_DIR."/systemctl", $Sctl); } } if((not $Sys{"Display_manager"} or $Sys{"Display_manager"} eq "TDM") and $Sctl) { foreach my $DM (@ALL_DISPLAY_MANAGERS) { if(index($Sctl, $DM.".service")!=-1 and $Sctl=~/\b$DM\.service\s+loaded.+\s+running/) { $Sys{"Display_manager"} = fixDisplayManager($DM); last; } } } my $Gpart = ""; if($Opt{"FixProbe"}) { $Gpart = readFile($FixProbe_Logs."/gpart"); } elsif(enabledLog("gpart") and checkCmd("gpart")) { listProbe("logs", "gpart"); $Gpart = runCmd("gpart show 2>/dev/null"); $Gpart = hidePaths($Gpart); writeLog($LOG_DIR."/gpart", $Gpart); } if(isBSD() and $Gpart) { if($Gpart=~/ efi /) { $Sys{"Boot_mode"} = "EFI"; } else { $Sys{"Boot_mode"} = "BIOS"; } if($Gpart=~/ GPT /) { $Sys{"Part_scheme"} = "GPT"; } elsif($Gpart=~/ MBR /) { $Sys{"Part_scheme"} = "MBR"; } elsif($Gpart=~/ BSD /) { $Sys{"Part_scheme"} = "BSD"; } } if(enabledLog("gpart_list") and checkCmd("gpart")) { listProbe("logs", "gpart_list"); my $GpartList = runCmd("gpart list -a 2>/dev/null"); $GpartList = hidePaths($GpartList); $GpartList = encryptUUIDs($GpartList); if($GpartList) { writeLog($LOG_DIR."/gpart_list", $GpartList); } } my $Fdisk = ""; if($Opt{"FixProbe"}) { $Fdisk = readFile($FixProbe_Logs."/fdisk"); } elsif(enabledLog("fdisk") and checkCmd("fdisk") and (isOpenBSD() or isNetBSD())) { listProbe("logs", "fdisk"); foreach my $Dev (sort keys(%HDD)) { if($Dev=~/[dc]\Z/) { next; } if(my $FdiskDev = runCmd("fdisk -v ".basename($Dev)." 2>/dev/null")) { $Fdisk .= "$Dev\n".$FdiskDev."\n\n"; } } if($Fdisk) { $Fdisk = encryptUUIDs($Fdisk); writeLog($LOG_DIR."/fdisk", $Fdisk); } } if(isBSD() and $Fdisk) { if($Fdisk=~/ EFI /) { $Sys{"Boot_mode"} = "EFI"; } else { $Sys{"Boot_mode"} = "BIOS"; } foreach (split(/\n\n/, $Fdisk)) { if(/Not Found/) { next; } if(/GPT:| GPT /) { $Sys{"Part_scheme"} = "GPT"; last; } elsif(/MBR:| MBR /) { $Sys{"Part_scheme"} = "MBR"; last; } } } if($Fdisk=~/Disk\s?label type: gpt/) { $Sys{"Part_scheme"} = "GPT"; } elsif($Fdisk=~/Disk\s?label type: dos/) { $Sys{"Part_scheme"} = "MBR"; } my $X86info = ""; if($Opt{"FixProbe"}) { $X86info = readFile($FixProbe_Logs."/x86info"); } elsif(enabledLog("x86info") and checkCmd("x86info")) { listProbe("logs", "x86info"); $X86info = runCmd("x86info -a 2>/dev/null"); if($X86info) { writeLog($LOG_DIR."/x86info", $X86info); } } my $Getprop = ""; if($Opt{"FixProbe"}) { $Getprop = readFile($FixProbe_Logs."/getprop"); } elsif(enabledLog("getprop") and checkCmd("getprop")) { # Android (Termux) listProbe("logs", "getprop"); $Getprop = runCmd("getprop"); if($Getprop) { writeLog($LOG_DIR."/getprop", $Getprop); } } my $Ofwdump = ""; if($Opt{"FixProbe"}) { $Ofwdump = readFile($FixProbe_Logs."/ofwdump"); } elsif(defined $Sys{"Freebsd_release"} and enabledLog("ofwdump") and checkCmd("ofwdump")) { listProbe("logs", "ofwdump"); $Ofwdump = runCmd("ofwdump -a"); writeLog($LOG_DIR."/ofwdump", $Ofwdump); } # TODO: add new fixes here print "Ok\n"; } sub identifyVideoDriver_BSD($$) { my ($Driver, $File) = @_; if($File!~/vgapci/ or not defined $DrmAttached{$File}) { return $Driver; } my %VgaDr = (); $VgaDr{$Driver} = 1; foreach my $DrmFile (sort keys(%{$DrmAttached{$File}})) { if($DrmFile=~/drm/) { foreach my $DrmDrv (sort keys(%{$DrmAttached{$DrmFile}})) { $VgaDr{$DrmDrv} = 1; } } else { $DrmFile=~s/\d+\Z//; $VgaDr{$DrmFile} = 1; } } if(defined $VgaDr{"i915"}) { $Driver = "i915"; } elsif(defined $VgaDr{"radeon"}) { $Driver = "radeon"; } elsif(defined $VgaDr{"amdgpu"}) { $Driver = "amdgpu"; } elsif(defined $VgaDr{"nvidia"}) { $Driver = "nvidia"; } elsif(defined $VgaDr{"agp"}) { $Driver = "agp"; } else { $Driver = join(", ", keys(%VgaDr)); } return $Driver; } sub getSysUUID(@) { return strToUUID(clientHash("_".join("_", sort @_))); } sub detectMicroarch(@) { my $V = shift(@_); my $F = shift(@_); my $M = shift(@_); my $N = undef; if(@_) { $N = shift(@_); } $V = fixCpuVendor($V); if($V and $F) { if(defined $FamilyMicroArch{$V}{$F}{$M}) { return $FamilyMicroArch{$V}{$F}{$M}; } elsif(defined $FamilyMicroArch{$V}{$F}{"*"}) { return $FamilyMicroArch{$V}{$F}{"*"}; } } if($V and $N) { if(defined $ModelMicroArch{$V}{$N}) { return $ModelMicroArch{$V}{$N}; } } return undef; } sub getLongPCI($) { my $ID = $_[0]; if(not defined $LongID{$ID}) { return undef; } my @L_IDs = keys(%{$LongID{$ID}}); if($#L_IDs==0) { return $L_IDs[0]; } return undef; } sub runSmartctl(@) { my $SmartctlCmd = shift(@_); my $Id = shift(@_); my $Dev = shift(@_); my $OrigDev = shift(@_); my ($Raid, $AddOpt, $RNum) = (); if(@_) { $Raid = shift(@_); } if(@_) { $AddOpt = shift(@_); } if(@_) { $RNum = shift(@_); } my $Cmd = $SmartctlCmd." -x \"".$Dev."\""; if(isBSD()) { if(not defined $Sys{"Freebsd_release"} or $Sys{"Freebsd_release"} < 7.0) { $Cmd = $SmartctlCmd." -a \"".$Dev."\""; } } $Cmd .= " -T permissive"; if($AddOpt) { $Cmd .= " ".$AddOpt; } my $Output = undef; if($Sys{"System"}=~/dragonfly/) { $Output = runCmd($Cmd." -d sat 2>/dev/null"); } else { $Output = runCmd($Cmd." 2>/dev/null"); } if($Output=~/Unknown USB bridge|Device Identity failed|Unable to detect device type|failed: Device busy|To monitor NVMe disks use|No such device or address/) { return ""; } if(not $Output or $Output=~/Operation not permitted|Permission denied/) { if($Opt{"Snap"} and not $SnapNoBlockDevices) { print STDERR "\nWARNING: Make sure 'block-devices' interface is connected to verify SMART attributes of your drives:\n\n"; print STDERR " sudo snap connect hw-probe:block-devices :block-devices\n"; $SnapNoBlockDevices = 1; } return ""; } if(isBSD() and $Output=~/No such file|Device not configured|Operation not supported/) { return ""; } if($Raid) { if(index($Output, "failed: cannot open")!=-1 or index($Output, "INQUIRY failed")!=-1 or index($Output, "Input/output error")!=-1) { # empty N slot return ""; } } if($Id and index($Id, "usb:")==0 and $Output=~/Unsupported USB|Unknown USB/i) { # device doesn't provide SMART return ""; } if($Id and index($Id, "nvme:")==0 and $Output=~/Unable to detect device type/i) { # old version of smartctl return ""; } if($Id and index($Id, "scsi:")==0 and $Output=~/Unsupported|Unknown|Unable/i) { # unsupported scsi drive return ""; } $Output = encryptSerials($Output, "Serial Number"); $Output = encryptSerials($Output, "Serial number"); $Output = hideWWNs($Output); if(not $Id) { $Id = detectDrive($Output, $OrigDev, $Raid, $RNum); } if($Id) { setDriveStatus($Output, $Id); } if($Raid and $Raid eq "MegaRAID") { $Output = $OrigDev.",megaraid_disk_".fNum($RNum)."\n".$Output."\n"; } elsif($Raid and $Raid eq "HPSA") { $Output = $OrigDev.",hpsa_disk_".fNum($RNum)."\n".$Output."\n"; } else { $Output = $OrigDev."\n".$Output."\n"; } return $Output; } sub registerCPU($) { my $Device = $_[0]; $Device->{"Vendor"} = fixCpuVendor($Device->{"Vendor"}); $Device->{"Device"} = fmtVal($Device->{"Device"}); $Device->{"Device"} = duplVendor($Device->{"Vendor"}, $Device->{"Device"}); $Device->{"Type"} = "cpu"; $Device->{"Status"} = "works"; $CPU_ID = "cpu:".fmtID(devID(nameID($Device->{"Vendor"}), join(".", ($Device->{"Family"}, $Device->{"ModelNum"}, $Device->{"Stepping"})), devSuffix($Device))); $HW{$CPU_ID} = $Device; return $CPU_ID; } sub registerRAM($) { my $TotalKb = $_[0]; if(grep {$_=~/\Amem:/} keys(%HW)) { return undef; } my %Device = (); $Device{"FF"} = "DIMM"; if($Sys{"Type"}=~/$MOBILE_TYPE/) { $Device{"FF"} = "SODIMM"; } $Device{"Type"} = "memory"; $Device{"Status"} = "works"; $Device{"Size"} = sprintf("%.0f", $TotalKb/1048576); if(grep {$_ eq $Device{"Size"}} (63, 47, 31, 23, 15, 11, 7)) { $Device{"Size"} += 1; } elsif(grep {$_ eq $Device{"Size"}} (62, 46, 30, 22, 14)) { $Device{"Size"} += 2; } $Device{"Size"} .= "GB"; if($Device{"Size"} eq "0GB") { $Device{"Size"} = sprintf("%.2f", $TotalKb/1048576)."GB"; } $Device{"Device"} = "RAM Module(s) ".$Device{"Size"}." ".$Device{"FF"}; my $RAM_ID = "mem:ram-modules-".lc($Device{"Size"})."-".lc($Device{"FF"}); $HW{$RAM_ID} = \%Device; return $RAM_ID; } sub registerCdrom($$) { my ($Descr, $DevFile) = @_; my %CDDev = (); $CDDev{"Device"} = $Descr; $CDDev{"Vendor"} = guessDeviceVendor($CDDev{"Device"}); $CDDev{"Device"} = duplVendor($CDDev{"Vendor"}, $CDDev{"Device"}); $CDDev{"Type"} = "cdrom"; $CDDev{"Status"} = "detected"; $CDDev{"File"} = $DevFile; my @Dr = (); if($DevFile=~/\A(cd|acd)\d\Z/) { @Dr = ($1); } if(my $ADr = $DevAttached{$DevFile}) { $ADr=~s/\d+\Z//; push(@Dr, $ADr); } if(@Dr) { $CDDev{"Driver"} = join(", ", @Dr); } $CDROM_ID = "scsi:".fmtID(devID(nameID($CDDev{"Vendor"}), $CDDev{"Device"})); $HW{$CDROM_ID} = \%CDDev; } sub registerBattery($) { my $Device = $_[0]; $Device->{"Type"} = "battery"; if(not $Sys{"Type"} or $Sys{"Type"}=~/$DESKTOP_TYPE|$SERVER_TYPE|other/) { if(not grep {$Sys{"Type"} eq $_} ("mini pc", "all in one") and $Device->{"Device"}!~/CRB/) { $Sys{"Type"} = "notebook"; } } if(defined $BatType{$Device->{"Technology"}}) { $Device->{"Technology"} = $BatType{$Device->{"Technology"}}; } if($Device->{"Technology"} eq "Unknown") { $Device->{"Technology"} = undef; } if(not $Device->{"Device"}) { $Device->{"Device"} = "Battery"; } if($Device->{"Size"}=~/(.+)( Wh)/) { $Device->{"Size"} = sprintf("%.1f", $1).$2; } if($Device->{"Size"} eq "0.0 Wh") { $Device->{"Size"} = undef; } my $ID = undef; if($Device->{"Device"}=~/\A(Battery|Primary|Dell|Bat)\d*\Z/i or not $Device->{"Vendor"}) { $Device->{"Device"} = $1; if($Device->{"Size"}=~/(.+)( Wh)/) { $Device->{"Size"} = sprintf("%.0f", $1).$2; } $ID = devID(nameID($Device->{"Vendor"}), lc($Device->{"Device"}), $Device->{"Technology"}, $Device->{"Size"}); if($Device->{"Serial"} and $Device->{"Serial"} ne " ") { $ID = devID($ID, "serial", $Device->{"Serial"}); } } else { $ID = devID(nameID($Device->{"Vendor"}), devSuffix($Device)); $Device->{"Device"} = "Battery ".$Device->{"Device"}; } $ID = fmtID($ID); if($Device->{"Technology"}) { $Device->{"Device"} .= " ".$Device->{"Technology"}; } if($Device->{"Size"}) { $Device->{"Device"} .= " ".$Device->{"Size"}; } if($Device->{"Capacity"}=~/\A(\d+)/) { if($1>$MIN_BAT_CAPACITY) { $Device->{"Status"} = "works"; } else { $Device->{"Status"} = "malfunc"; } } if($ID) { $HW{"bat:".$ID} = $Device; } } sub rmArrayVal($$) { my ($Arr, $Vals) = @_; foreach my $Val (@{$Vals}) { @{$Arr} = grep {$_ ne $Val} @{$Arr}; } } sub fixCapacity($) { my $Capacity = $_[0]; if($Capacity=~/\A([\d\.]+)GB\Z/) { my $Gb = $1; if($Gb=~/\./) { $Gb = roundToNearest($Gb); } if($Gb>24 and $Gb % 16 != 0) { my $Nearest = int(($Gb + 15)/16)*16; if($Nearest-$Gb<=5) { $Gb = $Nearest; } } elsif($Gb=~/\A1[345]\Z/) { $Gb = 16; } elsif($Gb==23) { $Gb = 24; } return $Gb."GB"; } return $Capacity; } sub isIntelDriver($) { return grep {$_[0] eq $_} @G_DRIVERS_INTEL; } sub setDriveStatus($$) { my ($Desc, $Id) = @_; my $Status = undef; if($Desc=~/result:\s*(PASSED|FAILED)/i) { my $Res = $1; if($Res eq "PASSED") { $Status = "works"; } elsif($Res eq "FAILED") { $Status = "failed"; } } elsif($Desc=~/SMART Health Status:\s*(.+)/i) { my $Res = $1; if($Res eq "OK") { $Status = "works"; } elsif($Res=~/FAIL/) { $Status = "failed"; } } if($Status) { $HW{$Id}{"Status"} = $Status; } if($USE_IA) { LHW::IA::parseSMART($Desc, $HW{$Id}); } setAttachedStatus($Id, "works"); # got SMART } sub setAttachedStatus(@) { my $Id = shift(@_); my $Status = shift(@_); if(isBSD()) { my %DevID = (); foreach (keys(%HW)) { if(defined $HW{$_} and defined $HW{$_}{"File"}) { $DevID{$HW{$_}{"File"}} = $_; } } if(my $File = $HW{$Id}{"File"}) { foreach my $Parent (keys(%{$DevAttachedRecursive{$File}})) { if(defined $DevID{$Parent}) { $HW{$DevID{$Parent}}{"Status"} = $Status; } } } return; } my $Recur = {}; if(@_) { $Recur = shift(@_); } if(defined $Recur->{$Id}) { return; } $Recur->{$Id} = 1; if(my $DevNum = $DeviceNumByID{$Id}) { if(my $AttachedTo = $DeviceAttached{$DevNum}) { if(my $AttachedId = $DeviceIDByNum{$AttachedTo}) { if(my $L_ID = getLongPCI($AttachedId)) { $AttachedId = "pci:".$L_ID; } $HW{$AttachedId}{"Status"} = $Status; if($Status eq "works") { setAttachedStatus($AttachedId, $Status, $Recur); } } } } } sub shortModel($) { my $M = $_[0]; $M=~s/\AMotherboard\s+//gi; $M=~s/\s+\Z//g; $M=~s/\s*\(.+\)//g; $M=~s/\s+Rev\s+.+//ig; $M=~s/\s+REV\:[^\s]+//ig; # REV:0A $M=~s/(\s+|\/)[x\d]+\.[x\d]+//i; $M=~s/\s*[\.\*]\Z//; $M=~s/\s*\d\*.*//; # Motherboard C31 1*V1.* $M=~s/\s+(Unknow|INVALID|Default string)\Z//; $M=~s/\s+Board\Z//g; # $M=~s/\s+R\d+\.\d+\Z//ig; # R2.0 return $M; } sub shortOS($) { my $Name = $_[0]; $Name=~s/\s+(gnu\/linux)\Z//i; $Name=~s/\s+(linux|project|amd64|x86_64)\s+/ /i; $Name=~s/\s*(linux|project|amd64|x86_64)\Z//i; $Name=~s/\s+/\-/g; $Name=~s/[\.\-]\Z//g; return $Name; } sub registerBoard($) { my $Device = $_[0]; $Device->{"Vendor"}=~s{\Ahttp://www\.}{}i; # http://www.abit.com.tw as vendor if(index($Device->{"Vendor"}, "abit.com.tw")!=-1) { $Device->{"Vendor"} = "ABIT"; } cleanValues($Device); if(emptyProduct($Device->{"Version"})) { delete($Device->{"Version"}); } if($Device->{"Device"}=~/\bName\d*\b/i) { $Device->{"Device"} = "Board"; } if(my $Ver = $Device->{"Version"}) { $Device->{"Device"} .= " ".$Ver; } $Device->{"Device"} = duplVendor($Device->{"Vendor"}, $Device->{"Device"}); $Device->{"Type"} = "motherboard"; $Device->{"Status"} = "works"; if(not $Device->{"Vendor"} and not $Device->{"Device"}) { return undef; } if($Device->{"Device"}) { if(not $Device->{"Vendor"}) { $Device->{"Vendor"} = $VendorByModel{shortModel($Device->{"Device"})}; } if(not $Device->{"Vendor"}) { if($Device->{"Device"}=~/\AMS\-\d+\Z/) { $Device->{"Vendor"} = "MSI"; } elsif($Device->{"Device"}=~/\ASiS\-\d+/) { $Device->{"Vendor"} = "SiS Technology"; } elsif($Device->{"Device"}=~/\A(4CoreDual|4Core1600|775XFire|ALiveNF|ConRoe[A-Z\d])/) { # ConRoe1333, ConRoeXFire $Device->{"Vendor"} = "ASRock"; } } } else { $Device->{"Device"} = "Board"; } my $ID = devID(nameID($Device->{"Vendor"}), devSuffix($Device)); $ID = fmtID($ID); if($Device->{"Device"} ne "Board") { $Device->{"Device"} = "Motherboard ".$Device->{"Device"}; } my $MID = "board:".$ID; $HW{$MID} = $Device; return $MID; } sub registerBIOS($) { my $Device = $_[0]; cleanValues($Device); my @Name = (); if($Device->{"Version"}) { push(@Name, $Device->{"Version"}); } if(my $BiosDate = $Device->{"Release Date"}) { push(@Name, $Device->{"Release Date"}); if($BiosDate=~/\b(19\d\d|20\d\d)\b/) { $Sys{"Year"} = $1; } elsif($BiosDate=~/\b\d\d\/\d\d\/([01]\d)\b/) { $Sys{"Year"} = "20".$1; } else { delete($Sys{"Year"}); } if($Sys{"Year"} and ($Sys{"Year"} > getYear(time) + 1 or $Sys{"Year"} < 1993)) { delete($Sys{"Year"}); } } $Device->{"Device"} = join(" ", @Name); if(not $Device->{"Vendor"} or not $Device->{"Device"}) { return undef; } $Device->{"Type"} = "bios"; $Device->{"Status"} = "works"; my $ID = devID(nameID($Device->{"Vendor"}), devSuffix($Device)); $ID = fmtID($ID); $ID = shortID("bios", $ID); my $BusID = "bios:".$ID; $Device->{"Device"} = "BIOS ".$Device->{"Device"}; if($ID) { $HW{"bios:".$ID} = $Device; return $BusID; } return undef; } sub detectMonitor($) { my $Info = $_[0]; my ($V, $D) = (); my %Device = (); if($Info=~/Digital display/) { $Device{"Kind"} = "Digital"; } elsif($Info=~/Analog display/) { $Device{"Kind"} = "Analog"; } if($Info=~/Made in:? (.+)/) { $Device{"Made"} = $1; } if($Info=~/Manufacturer:\s*(.+?)\s+Model\s+(.+?)\s+Serial/) { ($V, $D) = ($1, $2); } elsif($Info=~/EISA ID:\s*(\w{3})(\w+)/) { ($V, $D) = (uc($1), uc($2)); } if(not $V and not $D) { # edid-decode 2020 if($Info=~/Manufacturer:\s*(.+)/) { $V = $1; } if($Info=~/Model:\s*(.+)/) { $D = $1; } if($Info=~/Product Serial Number:\s*(.+)/) { $Device{"Serial"} = $1; $Device{"Serial"}=~s/\A\'(.+)\'\Z/$1/; } } if($D=~/\A\d+\Z/ and $Info!~/\(valid\)/) { # new format by edid-decode c498d2224d (2019-11-30) # revert to HEX $D = sprintf('%x', $D); } if(length($D)<4) { foreach (1 .. 4 - length($D)) { $D = "0".$D; } } if(not $V or not $D) { return; } if($V eq "\@\@\@") { return; } $Device{"Model"} = uc($V.$D); if($Info=~/(Monitor name|Display Product Name):[ ]*(.*?)(\n|\Z)/) { $Device{"Device"} = $2; $Device{"Name"} = $2; } elsif($Info=~s/(ASCII string|Alphanumeric Data String):\s*(.*?)(\n|\Z)//) { $Device{"Name"} = $2; } $Device{"Device"}=~s/\A\'(.+)\'\Z/$1/; $Device{"Name"}=~s/\A\'(.+)\'\Z/$1/; foreach my $Attr ("Maximum image size", "Screen size", "Detailed mode", "DTD 1", "DTD 2") { if($Info=~/$Attr:(.+?)\n/i) { my $MonSize = $1; if($MonSize=~/(\d+)\s*mm\s*x\s*(\d+)\s*mm/) { $Device{"Size"} = $1."x".$2."mm"; } elsif($MonSize=~/([\d\.]+)\s*cm\s*x\s*([\d\.]+)\s*cm/) { $Device{"Size"} = ($1*10)."x".($2*10)."mm"; } if($Device{"Size"}) { last; } } } if(grep {$Device{"Size"} eq $_} ("1600x900mm", "160x90mm", "16x9mm", "0x0mm")) { $Device{"Size"} = undef; } $Info=~s/(CTA extension block|CTA-861 Extension Block|CEA extension block).+//s; my %Resolutions = (); while($Info=~s/(\d+)x(\d+)\@\d+//) { $Resolutions{$1} = $2; } while($Info=~s/\n\s+(\d+)\s+.+?\s+hborder//) { my $W = $1; if($Info=~s/\n\s+(\d+)\s+.+?\s+vborder//) { my $H = $1; if(not defined $Resolutions{$W} or $H>$Resolutions{$W}) { $Resolutions{$W} = $H; } } } if(not keys(%Resolutions)) { # edid-decode 2020 while($Info=~s/ (\d+)x(\d+) //) { $Resolutions{$1} = $2; } } if(my @Res = sort {int($b)<=>int($a)} keys(%Resolutions)) { $Device{"Resolution"} = $Res[0]."x".$Resolutions{$Res[0]}; } if(not $Device{"Resolution"}) { # monitor-parse-edid if($Info=~s/"(\d+x\d+)"//) { $Device{"Resolution"} = $1; } } if($V) { if(my $Vendor = getPnpVendor($V)) { $Device{"Vendor"} = $Vendor; $Device{"Device"}=~s/\A\Q$Vendor\E(\s+|\-)//ig; } elsif(not $Device{"Vendor"}) { $Device{"Vendor"} = $V; } } my $MName = $Device{"Device"}; if(not $MName or $Device{"Vendor"}=~/\Q$MName\E/i) { $Device{"Device"} = "LCD Monitor"; } my $ID = undef; if($Device{"Vendor"}) { if($Device{"Vendor"} ne $V) { $ID = devID(nameID($Device{"Vendor"})); } } $ID = devID($ID, $V.$D); $ID = fmtID($ID); if(my $OldID = $Monitor_ID{uc($V.$D)}) { my $OldID_F = "eisa:".$OldID; my $Name = $Device{"Device"}; if($Name ne "LCD Monitor") { if($HW{$OldID_F}{"Vendor"}!~/\Q$Name\E/i) { $HW{$OldID_F}{"Device"}=~s/LCD Monitor/$Name/; } } $HW{$OldID_F}{"Status"} = "works"; # got EDID if(my $Res = $Device{"Resolution"}) { $HW{$OldID_F}{"Device"}=~s/ \d+x\d+ / $Res /; } return; } if($D) { $Device{"Device"} .= " ".uc($V.$D); } if($Device{"Resolution"}) { $Device{"Device"} .= " ".$Device{"Resolution"}; } if($Device{"Size"}) { $Device{"Device"} .= " ".$Device{"Size"}; } if(my $Inches = computeInch($Device{"Size"})) { $Device{"Inches"} = sprintf("%.1f", $Inches); $Device{"Device"} .= " ".$Device{"Inches"}."-inch"; if(my $Density = computeDensity($Device{"Resolution"}, $Inches)) { $Device{"Density"} = roundFloat($Density, 1); } if(my $Ratio = computeRatio($Device{"Size"})) { $Device{"Ratio"} = $Ratio; } if(my $Area = computeArea($Device{"Size"})) { $Device{"Area"} = $Area; } if($Device{"Size"}=~/\A(\d+)/) { $Device{"Width"} = $1; } } if(not $Device{"Ratio"}) { if(my $RatioByRes = computeRatio($Device{"Resolution"})) { $Device{"Ratio"} = $RatioByRes; } } $Device{"Type"} = "monitor"; if($Opt{"IdentifyMonitor"}) { $Device{"Vendor"} = nameID($Device{"Vendor"}); if(not defined $MonVendor{$V}) { $Device{"Unknown"} = 1; } } if($ID) { if(not defined $HW{"eisa:".$ID}) { $HW{"eisa:".$ID} = \%Device; $HW{"eisa:".$ID}{"Status"} = "works"; # got EDID } } } sub detectDrive(@) { my $Desc = shift(@_); my $Dev = shift(@_); my $Raid = undef; my $RNum = undef; if(@_) { $Raid = shift(@_); } if(@_) { $RNum = shift(@_); } my $Device = { "Type"=>"disk" }; if($Raid) { $Device->{"RAID"} = $Raid; if($Raid eq "MegaRAID") { $Device->{"MegaRAID_Disk"} = fNum($RNum); } elsif($Raid eq "HPSA") { $Device->{"HPSA_Disk"} = fNum($RNum); } $Device->{"File"} = $Dev; $Dev .= ",".fNum($RNum); } my $Bus = "ide"; # SATA, PATA, M.2, mSATA, etc. if($Dev=~/\A(nvme|nvd|nda)/) { $Bus = $PCI_DISK_BUS; $Device->{"Kind"} = "NVMe"; } if(not $Opt{"IdentifyDrive"} and not $Raid and defined $HDD_Info{$Dev}) { foreach ("Capacity", "Driver", "File") { $Device->{$_} = $HDD_Info{$Dev}{$_}; } } if($Desc=~/Serial [Nn]umber:\s*(.+?)(\Z|\n)/) { $Device->{"Serial"} = $1; } if($Desc=~/Device Model:\s*(.+?)(\Z|\n)/) { # ATA $Device->{"Device"} = $1; } elsif($Desc=~/Model Number:\s*(.+?)(\Z|\n)/) { # NVMe $Device->{"Device"} = $1; } elsif($Desc=~/Product:\s*(.+?)(\Z|\n)/) { # SAS $Device->{"Device"} = $1; } if($Desc=~/Model Family:\s*(.+?)(\Z|\n)/) { $Device->{"Family"} = $1; } if($Desc=~/Firmware Version:\s*(.+?)(\Z|\n)/) { $Device->{"Firmware"} = $1; } if($Desc=~/Form Factor:\s*(.+?)(\Z|\n)/) { # ATA $Device->{"FF"} = $1; } if($Desc=~/LU WWN Device Id:\s*\w\s(\w{6})\s(\w+|\.\.\.)(\Z|\n)/) { $Device->{"IEEE_OUI"} = $1; } elsif($Desc=~/IEEE OUI Identifier:\s*0x(\w+)/) { $Device->{"IEEE_OUI"} = $1; } elsif($Desc=~/IEEE EUI-64:\s*(\w{6})\s(\w+|\.\.\.)(\Z|\n)/) { $Device->{"IEEE_OUI"} = $1; } if(not $Device->{"Kind"}) { if($Desc=~/NVM Commands|NVMe Log/ or $Device->{"Device"}=~/\bNVMe\b/i) { $Device->{"Kind"} = "NVMe"; $Bus = $PCI_DISK_BUS; } elsif($Desc=~/Rotation Rate:.*Solid State Device/ or $Device->{"Device"}=~/\bSSD/ or $Device->{"Family"}=~/\bSSD/) { $Device->{"Kind"} = "SSD"; } else { $Device->{"Kind"} = "HDD"; } } if($Desc=~/User Capacity:.*\[(.+?)\]/) { # ATA $Device->{"Capacity"} = $1; } elsif($Desc=~/Size\/Capacity:.*\[(.+?)\]/) { # NVMe $Device->{"Capacity"} = $1; } elsif($Desc=~/User Capacity:\s*([\d\,]+)\s+bytes/) { # NVMe $Device->{"Capacity"} = $1; $Device->{"Capacity"}=~s/,//g; $Device->{"Capacity"} = bytesToHuman($Device->{"Capacity"}); } if($Desc=~/Vendor:\s*(.+?)(\Z|\n)/) { $Device->{"Vendor"} = $1; } $Device->{"Capacity"}=~s/,/./g; $Device->{"Capacity"}=~s/\.0+ //g; $Device->{"Capacity"}=~s/\s+//g; $Device->{"Device"}=~s/\//-/g; $Device->{"Device"}=~s/"/-inch/g; $Device->{"Device"}=~s/\ASSD\s+//g; $Device->{"Device"}=~s/\A(m\.2\s)([^\s]+\s)/$2$1/g; $Device->{"Device"}=~s/\Am\.2\s+//g; $Device->{"Device"}=~s/\s{2,}/ /g; $Device->{"Device"}=~s/\.\Z//g; fixDrive_Pre($Device, $Bus); if(not $Device->{"Vendor"}) { # NVMe if($Desc=~/PCI Vendor ID:\s*0x(\w+)/) { $Device->{"Vendor"} = nameID(getPciVendor($1)); } elsif($Desc=~/PCI Vendor\/Subsystem ID:\s*0x(\w+)/) { $Device->{"Vendor"} = nameID(getPciVendor($1)); } if($Device->{"Vendor"} and my $Vnd = guessDeviceVendor($Device->{"Vendor"})) { $Device->{"Vendor"} = $Vnd; $Device->{"Device"} = duplVendor($Vnd, $Device->{"Device"}); } } if(not $Opt{"IdentifyDrive"}) { if(not $Device->{"Vendor"} or not $Device->{"Device"}) { return; } } $Device->{"Device"} = duplVendor($Device->{"Vendor"}, $Device->{"Device"}); $Device->{"Device"}=~s/[\[\]]/ /g; $Device->{"Device"}=~s/\A //g; $Device->{"Device"}=~s/ \Z//g; fixDrive($Device); # HUA7210SASUN1.0T XXXXXXXXXX # SD88SA024SA0 SUN24G XXXXXXXXXX # ST31000NSSUN1.0T XXXXXXXXXX $Device->{"Device"}=~s/(\s+\d\d\w{8,})\Z//; $Device->{"Model"} = $Device->{"Device"}; if(isBSD()) { my @Dr = (); if($Dev=~/\/(\w+?)\d+[cd]?\Z/) { push(@Dr, $1); } my $ShortDev = $Dev; $ShortDev=~s/(\d)[cd]\Z/$1/; if(my $ADr = $DevAttached{basename($ShortDev)}) { $ADr=~s/\d+\Z//; push(@Dr, $ADr); } if(@Dr) { $Device->{"Driver"} = join(", ", @Dr); } $Device->{"File"} = basename($ShortDev); } my $ID = devID(nameID($Device->{"Vendor"})); $ID = devID($ID, devSuffix($Device)); my $HWId = $Bus.":".fmtID($ID); if(defined $HW{$HWId}) { if($Device->{"Capacity"}=~/KB/ and $HW{$HWId}->{"Capacity"}!~/KB/) { $Device->{"Capacity"} = $HW{$HWId}->{"Capacity"}; } } $Device->{"Device"} .= addCapacity($Device->{"Device"}, $Device->{"Capacity"}); foreach (keys(%{$Device})) { if(not $Device->{$_}) { delete($Device->{$_}); } } $HW{$HWId} = $Device; $HDD{$Dev} = $HWId; $DeviceNumByID{$HWId} = $DriveNumByFile{$Dev}; countDevice($HWId, $Device->{"Type"}); return $HWId; } sub fixDrive_Pre($$) { my ($Device, $Bus) = @_; if($Bus eq $PCI_DISK_BUS) { $Device->{"Kind"} = "NVMe"; } if($Device->{"Vendor"} and nonVendor($Device->{"Vendor"})) { $Device->{"Device"} = $Device->{"Vendor"}." ".$Device->{"Device"}; $Device->{"Vendor"} = undef; } if(not $Device->{"Vendor"} and not $Device->{"Family"} and $Device->{"Device"}) { if($Device->{"Device"}=~/\ASATA (32GB |)SSD\Z/) { $Device->{"Vendor"} = $DEFAULT_VENDOR; } } my %FixName = ( "ASUS-PHISON SSD" => "ASUS PHISON SSD", "kingpower1108 SSD" => "KingPower 1108 SSD" ); if(defined $FixName{$Device->{"Device"}}) { $Device->{"Device"} = $FixName{$Device->{"Device"}}; } if(not $Device->{"Vendor"} and $Device->{"Device"}) { if(my $Vnd = guessDeviceVendor($Device->{"Device"})) { $Device->{"Vendor"} = $Vnd; $Device->{"Device"} = duplVendor($Vnd, $Device->{"Device"}); } my $FamilyVnd = undef; if($Device->{"Family"}=~/\A([^ ]+)\s+/) { $FamilyVnd = $1; } if($FamilyVnd) { if($Device->{"Device"}=~s/\A\Q$FamilyVnd\E([\s_\-]+|\Z)//i) { $Device->{"Vendor"} = $FamilyVnd; } } if(not $Device->{"Vendor"}) { if(my $VndDr = guessDriveVendor($Device->{"Device"})) { $Device->{"Vendor"} = $VndDr; $Device->{"Device"} = duplVendor($Device->{"Vendor"}, $Device->{"Device"}); } } if(not $Device->{"Vendor"} and $FamilyVnd) { $Device->{"Vendor"} = $FamilyVnd; } } if(not $Device->{"Vendor"} or grep {$Device->{"Vendor"} eq $_} ("Generic", "ZALMAN")) { if(my $VndS = guessSerialVendor($Device->{"Serial"})) { # Obsolete - we don't collect serials $Device->{"Vendor"} = $VndS; } elsif(my $VndF = guessFirmwareVendor($Device->{"Firmware"})) { $Device->{"Vendor"} = $VndF; } } } sub guessDriveKind($$) { my ($Vendor, $Name) = @_; my $Model = $Vendor." ".$Name; if($Name=~/SSD|Solid State/) { return "SSD"; } if($Model=~/\A(ADATA|AMD|Apacer|Corsair|Crucial|Goodram|Intel|Kingston|LITEON|Micron|Mushkin|OCZ|Patriot|Plextor|PNY|SanDisk|SK hynix|Smartbuy|SPCC|Team|Transcend|HGST HUSM|HP VK0|Samsung (MM|MZ|PM|SG|SM)|Seagate ST(120H|400F|480F|800F)|Teclast|Toshiba (A100|KSG|Q\d|THNS|T[LR]\d|V[TX]\d)|TSA \d|WDC WD[BS])/i) { # TODO: list mechanical drive vendors instead? return "SSD"; } if($Name=~/\b(SSHD|HDD)\b/) { return "HDD"; } if($Model=~/\A(Fujitsu M|HGST H[A-Z]{2}\d|Hitachi|HP [FGMV]B\d|IBM\/Hitachi|IBM DTLA|Maxtor|Quantum|Samsung (H[DEMNS]|MP|SP|SV)|Seagate (ST\d|STM\d|Expansion|BUP )|Toshiba (DT|HD|M)|WDC WD\d)/i) { return "HDD"; } return undef; # HDD? } sub nonVendor($) { return (length($_[0])<2 or $_[0]=~/\A\d+GB\Z/ or grep { lc($_[0]) eq lc($_) } ("1TB", "M.2", "mSATA", "N600", "PATA", "PCIe", "SSD", "SATAII", "SATAIII", "SATA", "SATA2", "SATA3", "Series", "SB", "SC2")); } sub fixDrive($) { my $Device = $_[0]; if($Device->{"Vendor"}=~/\A(WD|ST)\d+/) { # model name instead of vendor name $Device->{"Device"} = $Device->{"Vendor"}." ".$Device->{"Device"}; if(defined $DiskVendor{$1}) { $Device->{"Vendor"} = $DiskVendor{$1}; } } elsif($Device->{"Vendor"}=~s/\AWDC (WD\d+)\Z/WDC/) { # model name instead of vendor name $Device->{"Vendor"} = "WDC"; $Device->{"Device"} = $1." ".$Device->{"Device"}; } elsif(defined $DiskVendor{$Device->{"Vendor"}} and $Device->{"Vendor"} ne $DiskVendor{$Device->{"Vendor"}}) { $Device->{"Device"} = $Device->{"Vendor"}." ".$Device->{"Device"}; $Device->{"Vendor"} = $DiskVendor{$Device->{"Vendor"}}; $Device->{"Device"} = duplVendor($Device->{"Vendor"}, $Device->{"Device"}); } if(not $Device->{"Kind"} or $Device->{"Kind"} eq "HDD") { # kind of several models is not detected properly by smartmontools # or smartmontools output is not collected if(my $FixedKind = guessDriveKind($Device->{"Vendor"}, $Device->{"Device"})) { $Device->{"Kind"} = $FixedKind; } } if(not $Device->{"Device"}) { # no model name $Device->{"Device"} = $Device->{"Capacity"}; $Device->{"Device"}=~s/\.\d+//g; if($Device->{"Kind"} eq "SSD") { $Device->{"Device"} = "SSD ".$Device->{"Device"}; } } if($Device->{"Kind"} eq "SSD") { if($Device->{"Device"} eq "DISK") { $Device->{"Device"} = "SSD DISK"; } elsif($Device->{"Device"}=~/\A(\d{3})\Z/) { $Device->{"Device"} = "SSD $1"; } elsif($Device->{"Device"}=~/\A\d+\s*(GB|TB|G|T)\Z/) { $Device->{"Device"} = "SSD ".$Device->{"Device"}; } } $Device->{"Capacity"} = fixCapacity($Device->{"Capacity"}); if($Device->{"Kind"} eq "SSD" or $Device->{"Kind"} eq "NVMe") { if(grep {$Device->{"Device"} eq $_} ("SSD", "SATA SSD", "SATA-III SSD", "Solid State Disk", "SSD Sata III", "DISK", "SSD DISK") or (grep {uc($Device->{"Vendor"}) eq $_} @DRIVE_ADD_SIZE and $Device->{"Device"}!~/\A(THNS)/)) { # Modify device ID to distinguish SSDs if($Device->{"Capacity"}=~/\A([\d\.]+)([GT]B)\Z/) { my ($C, $Suffix) = ($1, $2); my $Cap = undef; if($Suffix eq "TB") { $C = int($C); $Cap = $Device->{"Capacity"}; } elsif($Suffix eq "GB") { if($C>=7) { if($C % 2 != 0) { $C += 1; } if(!isPowerOfTwo($C)) { if(isPowerOfTwo($C + 2)) { $C = $C + 2; } elsif(isPowerOfTwo($C + 4)) { $C = $C + 4; } } } $Cap = $C."GB"; } if($Device->{"Device"}!~/[^1-9]+$C([^\d]+|\Z)/) { $Device->{"Device"} .= addCapacity($Device->{"Device"}, $Cap); } } } } elsif($Device->{"Device"} eq "ZALMAN") { $Device->{"Device"} .= addCapacity($Device->{"Device"}, $Device->{"Capacity"}); } if(not $Device->{"Vendor"}) { if(grep {$Device->{"Device"} eq $_} ("T60", "T120", "V-16", "SSD-512G", "BR 64GB") or $Device->{"Device"}=~/\A\d+(G|GB|T|TB) SSD\Z/ or $Device->{"Device"}=~/\ASSD\s*\d+(G|GB|T|TB)\Z/ or $Device->{"Device"}=~/\ASATA3\s+\d+(G|GB|T|TB)\s+SSD\Z/) { # SSD32G, SSD60G # 64GB SSD # SATA3 128GB SSD $Device->{"Vendor"} = $DEFAULT_VENDOR; } } if(not $Device->{"Vendor"} or $Device->{"Vendor"} eq $DEFAULT_VENDOR) { if(my $Oui = $Device->{"IEEE_OUI"}) { if(defined $IeeeOui{$Oui}) { if($Opt{"IdentifyDrive"}) { $Device->{"Err"} = "identifying by IEEE OUI"; } else { printMsg("ERROR", "identifying unknown vendor by IEEE OUI for drive ".$Device->{"Device"}); } $Device->{"Vendor"} = $IeeeOui{$Oui}; } } } } sub isPowerOfTwo($) { return not $_[0] & $_[0]-1; } sub isUnknownRam($) { my $Vendor = $_[0]; if($Vendor=~/(Mfg |Manufacturer|A1_AssetTagNum)/) { return 1; } if($Vendor=~/\A(0x|)(0+|F+)\Z/) { return 1; } if(grep {$Vendor eq $_} ("8313", "7F7F", "ADCD", "M0", "M1", "K", "K1", "U", "6", "G u", "Unknown")) { return 1; } return 0; } sub isJedecRam($) { my $Vendor = $_[0]; if($Vendor=~/\A[A-F0-9]{16}\Z/ or $Vendor=~/\A[A-F0-9]{4}\Z/) { return 1; } return 0; } sub guessRamVendor($) { my $Name = $_[0]; if($Name=~/\A99[A-Z\d]{5}\-\d{3}\.[A-Z\d]{4,}\Z/) { return "Kingston"; } if($Name=~/\A($ALL_MEM_VENDORS)/i) { return $1; } foreach my $Len (reverse(2 .. 8)) { my $Prefix = substr($Name, 0, $Len); if(defined $RamVendor{$Prefix}) { return $RamVendor{$Prefix}; } } return undef; } sub guessDriveVendor($) { my ($Name, $NameOrig) = ($_[0], $_[0]); if(defined $DiskModelVendor{$Name}) { return $DiskModelVendor{$Name}; } if($Name=~/\A(.+?)[-_ ]\d+GB?\Z/) { if(defined $DiskModelVendor{$1}) { return $DiskModelVendor{$1}; } } foreach my $Len (reverse(3 .. length($Name))) { if($Name=~/\A([A-Z\d\-\_]{$Len})([A-Z\d\-\s]+|\Z)/ and defined $DiskVendor{$1}) { return $DiskVendor{$1}; } } if($Name=~/\A([A-Z]{2})[A-Z\d\-]+/ and defined $DiskVendor{$1}) { return $DiskVendor{$1}; } foreach my $Len (3, 2) { if($Name=~/\A[A-Z\d]{2,}\-([A-Z]{$Len})[A-Z\d]+/ and defined $DiskVendor{$1}) { # C400-MTFDDAT064MAM # M4-CT256M4SSD2 return $DiskVendor{$1}; } } foreach my $P (sort {$b cmp $a} keys(%DiskVendor)) { if(length($P)>=4) { if(index($Name, $P)==0) { return $DiskVendor{$P}; } } } if($Name=~/\A(MT|MSH|NT|P3|P3D|P4|T|PA25)\-(60|64|120|128|240|256|480|512|960|1TB|2TB)\Z/ or grep { $Name eq $_ } ("V-32", "NT-256", "NT-512", "Q-360", "Q-720")) { # MT-64 MSH-256 P3-128 P3D-240 P3-2TB T-60 V-32 PA25-128 NT-64 return "KingSpec"; } if($NameOrig=~s/\A(SSD|NVMe) //i) { return guessDriveVendor($NameOrig); } return; } sub guessSerialVendor($) { my $Serial = $_[0]; if(not $Serial) { return; } if($Serial=~/\A([A-Z]+)\-/) { if(defined $SerialVendor{$1}) { return $SerialVendor{$1}; } } elsif($Serial=~/\A([A-Z]{3})/) { if(defined $SerialVendor{$1}) { return $SerialVendor{$1}; } } return; } sub guessFirmwareVendor($) { my $Firmware = $_[0]; if(not $Firmware) { return; } if(defined $FirmwareVendor{$Firmware}) { return $FirmwareVendor{$Firmware}; } if($Firmware=~/\A(\w{4})/) { if(defined $FirmwareVendor{$1}) { return $FirmwareVendor{$1}; } } return; } sub guessDeviceVendor($) { my $Device = $_[0]; if($Device=~/(\A|\s)($ALL_DRIVE_VENDORS|$ALL_VENDORS|$ALL_CDROM_VENDORS)([\s_\-\[]|\Z)/i) { return $2; } if($Device=~/\A($ALL_DRIVE_VENDORS_SHORT)\s/) { return $1; } return; } sub computeInch($) { my $Size = $_[0]; my ($W, $H) = (); if($Size=~/(\A|\s)(\d+)x(\d+)mm(\s|\Z)/) { ($W, $H) = ($2, $3); } elsif($Size=~/(\A|\s)([\d\.]+)x([\d\.]+)cm(\s|\Z)/) { ($W, $H) = (10*$2, 10*$3); } if($W and $H) { return sqrt($W*$W + $H*$H)/25.4; } return; } sub computeDensity($$) { my ($Resolution, $Inches) = @_; if($Inches and $Resolution=~/(\d+)x(\d+)/) { my ($W, $H) = ($1, $2); return sprintf("%.1f", sqrt($W*$W + $H*$H)/$Inches); } return; } sub computeRatio($) { my $Size = $_[0]; my %Ratio = ( "1.1" => "11/10", "1.2" => "6/5", "1.25" => "5/4", "1.26" => "5/4", "1.27" => "5/4", "1.28" => "5/4", "1.3" => "4/3", "1.4" => "4/3", "1.5" => "3/2", "1.6" => "16/10", "1.7" => "16/9", "1.8" => "16/9", "1.9" => "16/9", "2.3" => "21/9", "2.4" => "21/9", "3.5" => "32/9", "3.6" => "32/9" ); if($Size=~/(\d+)x(\d+)/) { my $ResP = $1/$2; my $Res = sprintf("%.2f", $ResP); my $ResP1 = sprintf("%.1f", $ResP); if(defined $Ratio{$Res}) { $Res = $Ratio{$Res}; } elsif(defined $Ratio{$ResP1}) { $Res = $Ratio{$ResP1}; } return $Res; } return; } sub computeArea($) { my $Size = $_[0]; if($Size=~/(\d+)x(\d+)/) { return sprintf("%.0f", $1*$2/(25.4*25.4)); } return; } sub getXRes($) { if($_[0]=~/\A(\d+)/) { return $1; } return; } sub duplVendor($$) { my ($Vendor, $Device) = @_; if($Vendor) { # do not duplicate vendor name if(not $Device=~s/\A\Q$Vendor\E([\s\-\_\[\.]+|\Z)//gi and not $Device=~s/\s+\Q$Vendor\E\s+/ /gi and not $Device=~s/\s+\Q$Vendor\E\Z//gi) { if(my $ShortVendor = nameID($Vendor)) { if($ShortVendor ne $Vendor) { $Device=~s/\A\Q$ShortVendor\E[\s\-\_\[\.]+//gi; $Device=~s/\s+\Q$ShortVendor\E\s+/ /gi; $Device=~s/\s+\Q$ShortVendor\E\Z//gi; } } } } return $Device; } sub roundToNearest($) { my $Num = $_[0]; my $Delta = $Num - int($Num); if($Delta*10>5) { return int($Num)+1; } return int($Num); } sub cleanValues($) { my $Hash = $_[0]; foreach my $Key (keys(%{$Hash})) { if(my $Val = $Hash->{$Key}) { if(emptyVal($Val)) { delete($Hash->{$Key}); } } } } sub emptyVal($) { my $Val = $_[0]; $Val=~s/\A\s+//; $Val=~s/\s+\Z//; if($Val=~/\A[\[\(]*(not specified|not available|out of spec|not defined|No Device Manufacturer|No Device Part Number|invalid|error|unkn|unknown|undefined|unknow|uknown|empty|n\/a|none|default string|vendor|device|unknown vendor|default|MB serial number|PCA serial|customer|model|_|unde|null|no string|reserved|Unknown \(0\)|\?|unknown unknown|generic|[\.\}\*\_]+)[\)\]]*\Z/i or $Val=~/(\A|\b|\d)(to be filled|unclassified device|not defined|bad index|does not exist|unkn|uknown|default)(\b|\Z)/i) { return 1; } return 0; } sub devSuffix($) { my $Device = $_[0]; my $Suffix = $Device->{"Device"}; if($Device->{"Type"} eq "cpu") { if($Device->{"Vendor"} eq "Intel") { # short suffix my @Parts = (); if($Device->{"Device"}=~/(\A| )CPU /) { if($Device->{"Device"}=~/\A(.+?)\s+CPU/) { push(@Parts, $1); } if($Device->{"Device"}=~/(\A| )CPU\s+(.+?)\s*\@/) { push(@Parts, $2); } } elsif($Device->{"Device"}=~/ processor /) { if($Device->{"Device"}=~/\A(.+?)\s+processor/i) { push(@Parts, $1); } } elsif($Device->{"Device"}=~/\A(686-class)\Z/) { push(@Parts, $1); } $Suffix = join("-", @Parts); } elsif($Device->{"Vendor"} eq "AMD") { $Suffix=~s/X2 Dual Core Processor/X2/; } elsif($Device->{"Vendor"} eq "ARM") { if($Device->{"Device"}=~/(.+)\s+Processor/) { $Suffix = $1; } } } elsif($Device->{"Type"} eq "memory") { if(my $FF = $Device->{"FF"}) { $Suffix .= "-".$FF; } } if($Device->{"Type"} eq "memory" or $Device->{"Type"} eq "disk" or $Device->{"Type"} eq "battery") { if(my $Ser = $Device->{"Serial"}) { $Suffix .= "-serial-".$Ser; } } return $Suffix; } sub shortID($$) { my ($Bus, $ID) = @_; my $Len = length($ID); my $BusLen = length($Bus); my $MaxLen = 100; my $ShaLen = 8; if($Len + $BusLen + 1 > $MaxLen) { return substr($ID, 0, $MaxLen - $BusLen - 1 - $ShaLen - 1)."-".getSha512L($ID, $ShaLen); } return $ID; } sub fmtID($) { my $ID = $_[0]; $ID=~s/[\W]+\Z//g; $ID=~s/[\W\_]+/-/g; $ID=~s/\A[\-]+//g; $ID=~s/[\-]+\Z//g; return $ID; } sub nameID(@) { my $Name = shift(@_); my $Type = undef; if(@_) { $Type = shift(@_); } if(not $Type or $Type ne "memory") { $Name=~s/\s*\([^()]*\)//g; $Name=~s/\s*\[[^\[\]]*\]//g; } while ($Name=~s/(\s*\,\s*|\s+)(Inc|Ltda|Ltd|Co |Co|GmbH|Corp|Tech\.|Pte|LLC|Sdn|Bhd|BV|AG|RSS|PLC|s\.r\.l\.|srl|S\.P\.A|B\.V|S\.A|s r\. o|s\.r\.o|z\.s\.p\.o|Ind|e\.V|a\.s|Co\.Ltd|Int\'l|Intl|I\.T\.G|IND\.CO\.|zrt\.|S\.A\. de C\.V\.|GmbH \& Co\. KG|S\.L\.|d\.o\.o\.)(\.|\.*\Z)//gi) {} $Name=~s/,?\s+[a-z]{2,4}\.//gi; $Name=~s/,(.+)\Z//gi; $Name=~s/(_S\.p\.A\.)\Z//gi; while ($Name=~s/\s+(Corporation|Computer|Computers|International|Electric|Company|Electronics|Electronic|Elektronik|Technologies|Technology|Technolog)\Z//ig) {} $Name=~s/[\.\,]/ /g; $Name=~s/\s*\Z//g; $Name=~s/\A\s*//g; return $Name; } sub fixVendorModel($$) { my ($V, $M) = @_; $V=~s/\s+\Z//g; $V=~s/\AMotherboard by\s+//gi; if($M eq "ABIT") { return ($M, $V); } if(not $V) { if($M=~s/\A(Bananapi|BQ|Google Inc\.|Google|Khadas|LGE|MEDION|Motorola|Nokia|OrangePi|Purism|Qualcomm Technologies, Inc\.|Rockchip|Samsung|Shenzhen Amediatech Technology Co\., Ltd|SolidRun|Tanix|Ugoos|Xiaomi)(\s+|\Z)//i) { $V = $1; } elsif($M=~/\A(TravelMate)/) { $V = "Acer"; } elsif($M=~/\A(LapBook)/) { $V = "Chuwi"; } elsif($M=~/\A(Spring Peak)/) { $V = "Gigabyte Technology"; } elsif($M=~/\A(EZbook)/) { $V = "Jumper"; } elsif($M=~/\A(ODROIDC)/) { $V = "Hardkernel"; } elsif($M=~/\A(Helios64)/) { $V = "Kobol"; } elsif($M=~/\A(PineTab)/) { $V = "Pine Microsystems"; } elsif($M=~/\A(Raspberry Pi)/) { $V = "Raspberry Pi Foundation"; } elsif($M=~/\A(Satellite)/) { $V = "Toshiba"; } elsif($M=~/\A(ZBOX\-)/) { $V = "ZOTAC"; } } $M = duplVendor($V, $M); return ($V, $M); } sub fixModel($$$) { my ($Vendor, $Model, $Version) = @_; if(not $Model) { if($Version and $Version=~/\ALenovo (.+)/) { $Model = $1; } else { return undef; } } $Model=~s/\A\-//; $Model=~s/\A[\.]+\s*//; $Model=~s/\A-?\[(.+)\]\-?\Z/$1/; # IBM $Model=~s/\A\Q$Vendor\E\s+//i; $Model=~s/\s+Board\Z//i; $Model=~s/\AMotherboard\s+//gi; $Model=~s/\A'//; $Model=~s/['\s\.]+\Z//; $Model=~s/'n'/ and /; if($Model eq $Vendor) { return ""; } if($Vendor eq "Hewlett-Packard") { $Model=~s/\AHP\s+//gi; $Model=~s/\s+Notebook PC\s*\Z//gi; $Model=~s/PAVILION/Pavilion/gi; $Model=~s/Envy/ENVY/gi; } elsif(uc($Vendor) eq "TOSHIBA") { $Model=~s/SATELLITE/Satellite/g; } elsif($Vendor=~/Fujitsu/) { $Model=~s/Amilo/AMILO/g; $Model=~s/LifeBook/LIFEBOOK/g; $Model=~s/Stylistic/STYLISTIC/g; } elsif($Vendor=~/\ADell(\s|\Z)/i) { $Model=~s/\A(MM061|MXC061|MP061|ME051)\Z/Inspiron $1/g; $Model=~s/\A(MXC062)\Z/XPS $1/g; $Model=~s/\ADell System //g; # Dell System Vostro 3450 by Dell Inc. $Model=~s/\ADell //g; # Dell 500 by Dell Inc. } elsif($Vendor eq "Micro-Star International") { $Model=~s/\AMSI\s+//gi; $Model=~s/\ANotebook\s+//gi; } elsif(uc($Vendor) eq "LENOVO") { if($Model=~/\A\s*INVALID\s*\Z/) { $Model = ""; } if($Version=~/\A\s*INVALID\s*\Z/) { $Version = ""; } if($Version=~/[A-Z]/i) { $Version=~s/\ALenovo-?\s*//i; if($Version and $Version!~/Rev \d/i) { while($Model=~s/\A\Q$Version\E\s+//i) {} if($Model!~/\Q$Version\E/i) { $Model = $Version." ".$Model; } } } $Model=~s/Ideapad/IdeaPad/gi; $Model=~s/ideacentre/IdeaCentre/gi; $Model=~s/\AProduct\s+Lenovo\s+//i; $Model=~s/\AProduct\s+//i; $Model=~s/\ALenovo\s+//i; } elsif($Version=~/ThinkPad/i and $Model!~/ThinkPad/i) { $Model = $Version." ".$Model; } if(emptyProduct($Model)) { $Model = undef; } return $Model; } sub listDir($) { my $Dir = $_[0]; if(not $Dir) { return (); } opendir(my $DH, $Dir); if(not $DH) { return (); } my @Contents = grep { $_ ne "." && $_ ne ".." } readdir($DH); closedir($DH); @Contents = sort @Contents; return @Contents; } sub probeSys() { if(checkCmd("uname")) { $Sys{"Arch"} = runCmd("uname -m"); $Sys{"Kernel"} = runCmd("uname -r"); } else { require POSIX; $Sys{"Arch"} = (POSIX::uname())[4]; $Sys{"Kernel"} = (POSIX::uname())[2]; } my ($Distr, $DistrVersion, $Rel) = probeDistr(); setDistr($Distr, $DistrVersion, $Rel, undef); if(not $Sys{"System"}) { printMsg("ERROR", "failed to detect Linux/BSD distribution"); if($Opt{"Snap"}) { warnSnapInterfaces(); exitStatus(1); } } if($Sys{"Arch"}=~/unknown/i) { require Config; $Sys{"Arch"} = $Config::Config{"archname"}; $Sys{"Arch"}=~s/\-linux.*//; } if(isBSD()) { if($Sys{"Arch"} eq "x86_64") { $Sys{"Arch"} = "amd64"; } } if($Opt{"PC_Name"}) { $Sys{"Name"} = $Opt{"PC_Name"}; } $Sys{"Probe_ver"} = $TOOL_VERSION; $Sys{"DE"} = $ENV{"XDG_CURRENT_DESKTOP"}; if(not $Sys{"DE"}) { $Sys{"DE"} = $ENV{"DESKTOP_SESSION"}; } if(my $KDE_Ver = $ENV{"KDE_SESSION_VERSION"} and $Sys{"DE"} eq "KDE") { $Sys{"DE"} .= $KDE_Ver; } if(not $Sys{"Display_manager"}) { my $DmFile = "/etc/X11/default-display-manager"; if(-f $DmFile) { if(readFile($DmFile)=~/bin\/(.+)/) { $Sys{"Display_manager"} = $1; } } } if(not $Sys{"Display_manager"}) { my $DmFile = "/etc/systemd/system/display-manager.service"; if(-f $DmFile) { if(readFile($DmFile)=~/ExecStart=([^\s]+)/) { $Sys{"Display_manager"} = basename($1); } } } if(not $Sys{"Display_manager"}) { my $DmFile = "/etc/sysconfig/displaymanager"; if(-f $DmFile) { if(readFile($DmFile)=~/DISPLAYMANAGER="(.+)"/) { $Sys{"Display_manager"} = $1; } } } my $DeFile = "/etc/sysconfig/desktop"; if(-f $DeFile) { my $DeContent = readFile($DeFile); if(not $Sys{"DE"}) { if($DeContent=~/DESKTOP="(.+)"/) { $Sys{"DE"} = $1; } } if(not $Sys{"Display_manager"}) { if($DeContent=~/DISPLAYMANAGER="(.+)"/) { $Sys{"Display_manager"} = $1; } } } foreach my $DM (@ALL_DISPLAY_MANAGERS) { if($DM eq "slim") { $DM .= ".lock"; } if(-e "/run/$DM" or -e "/run/$DM.pid") { $DM=~s/\..+//g; $Sys{"Display_manager"} = $DM; last; } } $Sys{"Display_manager"} = fixDisplayManager($Sys{"Display_manager"}); if($Sys{"DE"}) { $Sys{"Current_desktop"} = $Sys{"DE"}; } elsif(isBSD()) { my $WMs = runCmd("ps x | grep wm | grep -v grep"); if($WMs=~/\s(\w+wm)/) { $Sys{"Current_wm"} = $1; } if(runCmd("ps x | grep start-hello | grep -v grep")) { $Sys{"DE"} = "helloDesktop"; $Sys{"Current_desktop"} = $Sys{"DE"}; } } $Sys{"Display_server"} = ucfirst($ENV{"XDG_SESSION_TYPE"}); if(defined $ENV{"WAYLAND_DISPLAY"}) { $Sys{"Display_server"} = "Wayland"; } $Sys{"Lang"} = $ENV{"LANG"}; foreach (keys(%Sys)) { chomp($Sys{$_}); } } sub fixDisplayManager($) { my $DM = uc($_[0]); if(my $Fixed = $DisplayManager_Fix{$DM}) { return $Fixed; } return $DM; } sub probeDmi() { if(not enabledLog("dmi_id")) { return; } listProbe("logs", "dmi_id"); my $Dmi = ""; foreach my $File ("sys_vendor", "product_name", "product_version", "chassis_type", "board_vendor", "board_name", "board_version", "board_serial", "bios_vendor", "bios_version", "bios_date") { my $Value = readFile("/sys/class/dmi/id/".$File); if(not $Value) { next; } $Value=~s/\s+\Z//g; $Value = fmtVal($Value); if($File eq "sys_vendor") { if(not emptyProduct($Value)) { $Sys{"Vendor"} = $Value; } } elsif($File eq "product_name") { if(not emptyProduct($Value)) { $Sys{"Model"} = $Value; } } elsif($File eq "product_version") { if(not emptyProduct($Value)) { $Sys{"Version"} = $Value; } } elsif($File eq "chassis_type") { if(my $CType = getChassisType($ChassisType{$Value})) { $Sys{"Type"} = $CType; } } elsif($File eq "board_serial") { $Value = clientHash($Value); } if($Value ne "" and $Value=~/[A-Z0-9]/i) { $Dmi .= $File.": ".$Value."\n"; } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/dmi_id", $Dmi); } ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); $Sys{"Model"} = fixModel($Sys{"Vendor"}, $Sys{"Model"}, $Sys{"Version"}); } sub emptyProduct($) { my $Val = $_[0]; if(not $Val or $Val=~/\b(System manufacturer|System Product|Board Vendor|Board Manufacturer|Mainboard|System Manufacter|stem manufacturer|System Manufact|SYSTEM_MANUFACTURER|Name|Version|to be filled|empty|Not Specified|Default[ _]string|board version|Unknow|n\/a|Not)\b/i or $Val=~/\A([_0O\-\.\s]+|[X]+|NA|N\/A|\-O|1234567890|0123456789|[\.\,]+|Board|\$|\$\(PROJECT_SKU_NAME\)|SYSTEM_PRODUCT_NAME|Unknown Product|Product)\Z/i or emptyVal($Val)) { return 1; } if(nonASCII($Val)) { return 1; } return 0; } sub getChassisType($) { my $CType = lc($_[0]); $CType=~s/ chassis//i; if($CType!~/unknown|other/) { return $CType; } return; } sub fixProduct() { foreach my $Attr ("Vendor", "Version", "Model") { if(emptyProduct($Sys{$Attr})) { $Sys{$Attr} = ""; } } } sub fixFFBySubj($$$) { my ($Val1, $Val2, $Strings) = @_; my %TypeMap = ( "desktop" => $DESKTOP_TYPE."|".$SERVER_TYPE, "server" => $SERVER_TYPE, "notebook" => $MOBILE_TYPE ); LOOP: foreach my $FF ("desktop", "server", "notebook", "tablet", "convertible", "all in one", "stick pc", "firewall", "mini pc", "nettop") { if(not defined $Strings->{$FF} or $Sys{"Type"} eq $FF) { next; } if(my $FFPattern = $TypeMap{$FF}) { if($Sys{"Type"}=~/$FFPattern/) { next; } } if($FF eq "mini pc" and $Sys{"Type"} eq "stick pc") { next; } if(defined $Val2) { foreach my $V1Pattern (keys(%{$Strings->{$FF}})) { my $V2Pattern = join("|", @{$Strings->{$FF}{$V1Pattern}}); if($V1Pattern ne "undef" and $V2Pattern) { if($Val1=~/$V1Pattern/i and $Val2=~/$V2Pattern/) { $Sys{"Type"} = $FF; last LOOP; } } elsif($V1Pattern ne "undef") { if($Val1=~/$V1Pattern/i) { $Sys{"Type"} = $FF; last LOOP; } } elsif($V2Pattern) { if($Val2=~/$V2Pattern/) { $Sys{"Type"} = $FF; last LOOP; } } } } else { if(my $V1Pattern = join("|", @{$Strings->{$FF}})) { if($Val1=~/\b($V1Pattern)\b/) { $Sys{"Type"} = $FF; last LOOP; } } } } } sub fixFFByCPU($) { my $CPU = $_[0]; my %UnknownCPU = ( "desktop" => [ "Celeron CPU E\\d+", "Pentium (CPU G\\d+|D CPU|Dual-Core CPU E\\d+) ", "Core 2 CPU \\d+ ", "Core 2 Duo CPU E\\d+", "Core i\\d CPU \\d+ ", "Core i\\d-\\d+ CPU", "CPU Q(9400|8200)", "Athlon 64 X2 Dual Core Processor \\d+", "Athlon X4 \\d+", "Athlon 64 Processor \\d+\+", "Phenom II X[24] B?\\d+", "FX-\\d+ Six-Core", "A10\-\\d+K", "Xeon CPU (\\d+|[EXW]\\d{4}|(E5|D)-\\d{4}) ", "Core i7-\\d+ CPU", "FX-\\d+ Eight-Core", "Atom CPU C3508", "Ryzen 5 1600", "PPC970FX" ], "server" => [ "Opteron X3216", "Xeon Silver", "Xeon Gold", "Xeon Platinum", "POWER" ], "notebook" => [ "Athlon Neo X2 .* L3", "Core i3 CPU M 380" ] ); if($CPU!~/Mobile/) { fixFFBySubj($CPU, undef, \%UnknownCPU); } if(not $Sys{"Type"} or $Sys{"Type"} eq "desktop") { if($Sys{"Arch"}=~/aarch64/) { $Sys{"Type"} = "system on chip"; } } if(not $Sys{"Type"}) { if($CPU=~/Dual CPU T3400/) { $Sys{"Type"} = "notebook"; } if($Sys{"Arch"}=~/riscv64|mips/) { $Sys{"Type"} = "system on chip"; } } } sub fixFFByCDRom($) { my $CDRom = $_[0]; my %UnknownCDRom = ( "desktop" => [ "DVR-118L", "DDU1615", "SH-222AB", "DVR-111D", "GSA-H10N", "CRX230EE", "iHAS122", "DVR-112D", "GH22LP20", "AD-7200A", "TS-H553A", "AD-7173A", "DVDRAM_GSA-H60N", "GH24NSB0", "SH-222BB", "SOHR-5238S", "ND-3540A" ] ); fixFFBySubj($CDRom, undef, \%UnknownCDRom); } sub fixFFByBoard($) { my $Board = $_[0]; my %UnknownBoard = ( "desktop" => [ "DG965OT", "D510MO", "GA-K8NMF-9", "DG965RY", "DG33BU", "D946GZIS", "N3150ND3V", "D865GSA", "DP55WG", "H61MXT1", "D875PBZ", "F2A55", "Z68XP-UD3", "Z77A-GD65", "M4A79T", "775Dual-880Pro", "P4Dual-915GL", "P4i65GV", "D5400XS", "D201GLY", "MicroServer", "IPPSB-DB", "MS-AA53", "C2016-BSWI-D2", "N3160TN", "D915PBL", "EIRD-SAM", "D865PERL", "D410PT", "D525MW", "D945GCNL", "BSWI-D2", "B202", "D865GBF", "G1-CPU-IMP", "Aptio CRB", "A1SRi", "D865GVHZ", "IC17X", "N3050ND3H", "Bettong CRB", "E3C246D2I", "Pine Trail - M CRB", "Ultra 24", "DP55WB", "J3160NH", "CM-iAM" ], "server" => [ "X10DRT-P", "X10DRW-i", "X10SDV-TP8F", "X10DRH-iT", "CS24-SC", "K1SPE-IN001" ], "notebook" => [ "W7430", "Poyang", "PSMBOU", "Lhotse-II", "Nettiling", "EI Capitan", "JV11-ML", "M7x0S Bottom", "M720SR", "26446AG", "S5610", "SANTA ROSA CRB", "PowerBook\\d+", "VivoBook", "VPCF12C5E" ], "tablet" => [ "SurfTab" ], "all in one" => [ "AFLMB4-945GSE", "EZ1600" ] ); fixFFBySubj($Board, undef, \%UnknownBoard); } sub fixFFByModel($$) { # can't distinguish all-in-ones vs notebooks (very similar hardware: same cdroms, mobile graphics cards, etc.) # so need to check by exact model name my ($V, $M) = @_; my %UnknownModel = ( "notebook" => { undef => [ "AO531h", "AOA110", "Aspire (7720|5670|\\d+Z)", "EasyNote", "Extensa \\d+", "MacBook", "RoverBook", "A410-K\\.BE47P1", "0PJTXT", "R490-KR6WK" ], "Alienware" => [ "m15" ], "Clevo" => [ "M740TU", "D40EV", "M720R" ], "eMachines" => [ "Rhine V1.40" ], "Fujitsu" => [ "ESPRIMO Mobile" ], "Google" => [ "Auron_Yuna", "Pantheon", "Parrot" ], "IBM" => [ "26474MG", "2658MNG" ], "Intel" => [ "Corbett Park CRB" ], "MAXDATA" => [ "o.max_5xs" ], "Medion" => [ "E16402" ], "NCS" => [ "ONE1" ], "NOTEBOOK" => [ undef ], "Samsung" => [ "R50\\/R51" ], "Toshiba" => [ "Satellite" ], "TPVAOC" => [ "AA183M" ] }, "desktop" => { undef => [ "MacPro", "D865GLC", "ESPRIMO P", "Aspire easyStore", "X11SSL-F", "TERRA_PC", "VXC Class" ] }, "firewall" => { undef => [ "Firewall" ], "Silver Peak Systems|Sophos" => [ undef ] }, "server" => { undef => [ "X10DRG-O\\+-CPU", "ML10Gen", "Super Server", "H12SSW-NT", "X11SCE-F" ], "Neousys" => [ "Nuvo" ] }, "stick pc" => { "MEEGOPAD" => [ undef ], undef => [ "IdeaCentre Stick" ] }, "nettop" => { undef => [ "MS-B120", "IdeaCentre Q150" ] }, "mini pc" => { undef => [ "Apple Mac mini", "Macmini", "ESPRIMO Q510", "MMLP5AP-SI", "Mini PC", "TL-WR842N", "Thin Client", "Thin Mini", "VMac mini", "Aptio CRB", "Propc Nano", "XS35V5", "M6JR120", "D425KT", "TV Box", "ZBOX\\-" ], "Beelink" => [ "\\ASII" ], "Chuwi" => [ "Box" ], "Clientron" => [ "C800" ], "Compulab" => [ "Intense", "fitlet", "Airtop", "fit-PC", "1160405" ], "congatec" => [ "conga" ], "Flytech" => [ "C56" ], "Gigabyte" => [ "GB-BSi3-6100" ], "GMKtec" => [ "NucBox7" ], "Intel" => [ "NUC\\d" ], "Kontron" => [ "SMX945" ], "LTD Delovoy Office" => [ "IRU 316" ], "Orbsmart" => [ "\\AAW" ], "Radiant" => [ "P845" ], "Supermicro" => [ "X11SSE-F", "A1SAi" ], "THD" => [ "RX2" ], "Ugoos" => [ "UT2" ], "ZOTAC" => [ "\\AZBOX" ] }, "all in one" => { undef => [ " AiO PC", "AFLMB-9652", "\\A(MS-6657|EZ1601|AIO)\\Z" ], "Apple" => [ "\\AiMac" ], "BANGHO" => [ "B801" ], "Lenovo" => [ "\\A(S310|IdeaCentre B|ThinkCentre M90z) " ], "Hewlett" => [ " Aio\\Z" ], "MiTAC" => [ "\\AAIO " ], "MICRO-STAR" => [ "MS-AE3111", "MS-AA1511" ], "Samsung" => [ "SDNE-R78BA2-20" ], }, "tablet" => { undef => [ "Hi10 .+ tablet", "Visconte4U", "TERRA_PAD" ], "ONDA" => [ "Tablet|V919" ], "Microsoft" => [ "Surface" ], "Hampoo" => [ "\\A(E4D6|D4D6|I1D6|P02BD6)" ], "Acer" => [ "ICONIA Tab" ], "TMAX" => [ "TM" ], "Wacom" => [ "Citiq" ] } ); if(not defined $M) { $M = "Ignore"; } fixFFBySubj($V, $M, \%UnknownModel); if($Sys{"Type"}=~/$MOBILE_TYPE/ and $Sys{"Type"} ne "convertible") { if($M=~/convertible/i) { $Sys{"Type"} = "convertible"; } } } sub fixFFByMonitor($) { my $Mon = $_[0]; my %UnknownMonitor = ( "all in one" => [ "AIO PC" ], "notebook" => [ "LGD02E9", "SEC3445", "LPLA500", "CMO1680", "LGD018B", "APP9C20", "AUO11ED" ], "desktop" => [ "VA1913w-2", "NS-39D310NA19", "TD2421" ] ); fixFFBySubj($Mon, undef, \%UnknownMonitor); } sub fixFFByGPU($) { my $GPU = $_[0]; if($Sys{"Type"}!~/$DESKTOP_TYPE|$SERVER_TYPE/) { if($GPU=~/\A((NV|G)\d+ \[GeForce|RV\d+ \[Radeon |GeForce4 MX|RV635 PRO|Radeon HD 3870)/) { $Sys{"Type"} = "desktop"; } } } sub fixFFByTouchpad($) { my $Id = $_[0]; if($Sys{"Type"}!~/$MOBILE_TYPE|phone/) { if($Id=~/ps\/2/ and $HW{$Id}{"Device"}!~/Im.*PS\/2/) { $Sys{"Type"} = "notebook"; } } } sub fixFFByDisk($) { my $Disk = $_[0]; if($Sys{"Type"}!~/$DESKTOP_TYPE|$SERVER_TYPE/) { if($Disk=~/HD321KJ|ST3500413AS|ST2000DM001|WD205BA|WD20EZRZ|HD103UJ|WD30EFRX/) { $Sys{"Type"} = "desktop"; } } } sub fixChassis() { my (%Bios, %Board) = (); foreach my $L (split(/\n/, readFile($FixProbe_Logs."/dmi_id"))) { if($L=~/\A(\w+?):\s+(.+?)\Z/) { my ($File, $Value) = ($1, $2); $Value = fmtVal($Value); if($File eq "chassis_type") { if(my $CType = getChassisType($ChassisType{$Value})) { $Sys{"Type"} = $CType; } } elsif($File eq "sys_vendor") { if(not emptyProduct($Value)) { $Sys{"Vendor"} = $Value; } } elsif($File eq "product_name") { if(not emptyProduct($Value)) { $Sys{"Model"} = $Value; } } elsif($File eq "product_version") { if(not emptyProduct($Value)) { $Sys{"Version"} = $Value; } } elsif($File eq "bios_vendor") { $Bios{"Vendor"} = fmtVal($Value); } elsif($File eq "bios_version") { $Bios{"Version"} = $Value; } elsif($File eq "bios_date") { if(not emptyProduct($Value)) { $Bios{"Release Date"} = $Value; } } elsif($File eq "board_vendor") { $Board{"Vendor"} = fmtVal($Value); } elsif($File eq "board_serial") { $Board{"Serial"} = fmtVal($Value); } elsif($File eq "board_name") { $Board{"Device"} = fmtVal($Value); } elsif($File eq "board_version") { $Board{"Version"} = $Value; } } } ($Sys{"Vendor"}, $Sys{"Model"}) = fixVendorModel($Sys{"Vendor"}, $Sys{"Model"}); $Sys{"Model"} = fixModel($Sys{"Vendor"}, $Sys{"Model"}, $Sys{"Version"}); if(not $Bios_ID) { $Bios_ID = registerBIOS(\%Bios); } if(not $Board_ID) { $Board_ID = registerBoard(\%Board); } if(not $Sys{"Type"} or grep {$Sys{"Type"} eq $_} ("soc", "system on chip", "notebook", "hand held")) { if($Sys{"Kernel"}=~/\-(sunxi|sunxi64|raspi2)\Z/i or $Sys{"Vendor"}=~/raspberry/i) { $Sys{"Type"} = "system on chip"; } if($Sys{"Kernel"}=~/\-sunxi/) { $Sys{"Vendor"} = "sunxi"; } elsif($Sys{"Kernel"}=~/\-(tegra)\Z/i) { $Sys{"Type"} = "system on chip"; $Sys{"Vendor"} = "NVIDIA"; $Sys{"Model"} = "Tegra"; } elsif($Sys{"Kernel"}=~/\-(rockchip-.*)\Z/i) { $Sys{"Type"} = "system on chip"; $Sys{"Vendor"} = "Rockchip"; } elsif($Sys{"Kernel"}=~/_(byt\-g9ff829d)\Z/i) { $Sys{"Type"} = "tablet"; $Sys{"Vendor"} = "Lenovo"; $Sys{"Model"} = "YOGA"; $Sys{"System"} = "android"; } elsif($Sys{"Kernel"}=~/PhoenixOS/i) { $Sys{"System"} = "phoenixos"; } elsif($Sys{"Kernel"}=~/-Microsoft\Z/i) { $Sys{"Type"} = "desktop"; $Sys{"Vendor"} = "Microsoft"; $Sys{"Model"} = "Windows Subsystem for Linux"; } } if(not $Sys{"Model"} and not $Sys{"Vendor"}) { if($Sys{"Kernel"}=~/\-(gf5d7b8b)\Z/i) { $Sys{"Type"} = "tablet"; $Sys{"Vendor"} = "Google"; $Sys{"Model"} = "Nexus 7"; $Sys{"System"} = "android"; } elsif($Sys{"Kernel"}=~/\-(LuisKERNEL|Dark-Ages)\-|\-perf\+|SM-N9500|lineageos|FKernel-v|lineage|g8ca5a01/i) { $Sys{"Type"} = "smartphone"; $Sys{"System"} = "android"; } elsif($Sys{"Kernel"}=~/\-qcom\-msm8916/i) { $Sys{"Type"} = "smartphone"; } elsif($Sys{"Kernel"}=~/microsoft-standard-WSL2/i) { $Sys{"Type"} = "virtual machine"; $Sys{"Vendor"} = "Microsoft"; $Sys{"Model"} = "Windows Subsystem for Linux "; } } } sub ipAddr2ifConfig($) { my $IPaddr = $_[0]; my $IFConfig = ""; foreach my $Line (split(/\n/, $IPaddr)) { if($Line=~s/\A\d+:\s+//) { $IFConfig .= "\n".$Line."\n"; } else { $IFConfig .= $Line."\n"; } } return $IFConfig; } sub probeHWaddr() { my $IFConfig = undef; if($Opt{"FixProbe"}) { $IFConfig = fixHWaddr(); } else { my $ByIPaddr = 0; if(checkCmd("ifconfig")) { listProbe("logs", "ifconfig"); $IFConfig = runCmd("ifconfig -a 2>&1"); $IFConfig = hideIPs($IFConfig); $IFConfig = encryptMACs($IFConfig); $IFConfig=~s/(inet6 |inet |netmask |broadcast )[^\s]+/$1\XXX/g; $IFConfig=~s/(ssid )(.+?)( channel)/$1...$3/g; if(isBSD()) { $IFConfig=~s/(nwid|join|authname|wgpubkey|wgpeer) .+/$1 .../g; $IFConfig=~s/(groups|description|groups): .+/$1: .../g; } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/ifconfig", $IFConfig); } } elsif(checkCmd("ip")) { listProbe("logs", "ip_addr"); if(my $IPaddr = runCmd("ip addr 2>&1")) { $IPaddr = hideIPs($IPaddr); $IPaddr = encryptMACs($IPaddr); $IPaddr=~s/(inet6 |inet |brd )[^\s]+/$1\XXX/g; $IFConfig = ipAddr2ifConfig($IPaddr); $ByIPaddr = 1; if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/ip_addr", $IPaddr); } } } elsif(checkModule("IO/Socket.pm") and checkModule("IO/Interface.pm")) { require IO::Socket; require IO::Interface; my $Socket = IO::Socket::INET->new(Proto => "udp"); my @Ifs = (); my %Addrs = (); my $Macs = ""; foreach my $If ($Socket->if_list) { if(my $Mac = $Socket->if_hwaddr($If)) { $Mac = lc($Mac); $Mac=~s/:/-/g; $Mac = lc(clientHash(lc($Mac))); push(@Ifs, $If); # save order $Addrs{$If} = $Mac; $Macs .= "$If:$Mac\n"; } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/macs", $Macs); } $Sys{"HWaddr"} = selectHWAddr(\@Ifs, \%Addrs); } else { printMsg("ERROR", "can't find 'ifconfig' or 'ip'"); exitStatus(1); } if($IFConfig) { if(isBSD()) { $IFConfig=~s/(\n\w)/\n$1/g; } $Sys{"HWaddr"} = detectHWaddr($IFConfig, $ByIPaddr); if($Opt{"HWLogs"} and enabledLog("ethtool_p")) { my $EthtoolP = ""; foreach my $E (sort keys(%PermanentAddr)) { $EthtoolP .= $E."=>".$PermanentAddr{$E}."\n"; } if($EthtoolP) { writeLog($LOG_DIR."/ethtool_p", $EthtoolP); } } } if(not $Sys{"HWaddr"}) { printMsg("ERROR", "failed to detect hwid"); if($Opt{"Snap"}) { warnSnapInterfaces(); } exitStatus(1); } } if($IFConfig) { foreach my $I (split(/\n\n/, $IFConfig)) { if($I=~/\A([^:\s]+):?\s/) { my $F = $1; if(($I=~/packets\s*:?\s*(\d+)/ and $1) or ($I=~/valid_lft\s+(\d+)/ and $1)) { $UsedNetworkDev{$F} = 1; } if(isBSD()) { if($I=~/status: (associated|active)/) { $UsedNetworkDev{$F} = 1; } } } } } } sub fixHWaddr() { my $IFConfig = readFile($FixProbe_Logs."/ifconfig"); my $ByIPaddr = 0; if(isBSD()) { $IFConfig=~s/(\n\w)/\n$1/g; } if(not $IFConfig) { if(my $IPaddr = readFile($FixProbe_Logs."/ip_addr")) { $IFConfig = ipAddr2ifConfig($IPaddr); $ByIPaddr = 1; } } if($IFConfig) { # fix HWaddr my $EthtoolP = readFile($FixProbe_Logs."/ethtool_p"); if($EthtoolP) { foreach my $E (split(/\n/, $EthtoolP)) { if($E=~/(.+)\=\>(.+)/) { $PermanentAddr{$1} = $2; } } } my $UAddr = $Sys{"HWaddr"}; $UAddr=~s/\-/:/g; if($EthtoolP or $IFConfig=~/\Q$UAddr\E/i or grep { uc($Sys{"HWaddr"}) eq $_ } @WrongAddr) { if(my $NewAddr = detectHWaddr($IFConfig, $ByIPaddr)) { $Sys{"HWaddr"} = $NewAddr; } } } return $IFConfig; } sub warnSnapInterfaces() { print STDERR "\nERROR: Make sure required Snap interfaces are connected:\n\n"; print STDERR " for i in hardware-observe system-observe block-devices log-observe upower-observe physical-memory-observe network-observe raw-usb mount-observe opengl;do sudo snap connect hw-probe:\$i :\$i; done\n"; # auto-connected: # # hardware-observe # mount-observe # network-observe # system-observe # upower-observe # log-observe # opengl } sub countStr($$) { my ($Str, $Target) = @_; my $Count = 0; while($Str=~s/$Target//) { $Count += 1; } return $Count; } sub detectHWaddr(@) { my $IFConfig = shift(@_); my $ByIPaddr = 0; if(@_) { $ByIPaddr = shift(@_); } my @Devs = (); my %Addrs = (); my %Blocks = (); foreach my $Block (split(/[\n]\s*[\n]+/, $IFConfig)) { my $Addr = undef; if($Block=~/\A(docker|vboxnet|vmnet|tun\d|cali|vxlan\.calico|dummy)/) { next; } if($Block=~/permaddr\s+([^\s]+)/) { $Addr = lc($1); } elsif($Block=~/ether\s+([^\s]+)/) { # new $Addr = lc($1); } elsif($Block=~/HWaddr\s+([^\s]+)/) { # old $Addr = lc($1); } elsif($Block=~/lladdr\s+([^\s]+)/) { # OpenBSD $Addr = lc($1); } elsif($Block=~/address:\s+([^\s]+)/) { # NetBSD $Addr = lc($1); } if(not $Addr) { next; } if(index($Addr, ":")!=-1) { # Support for old probes $Addr=~s/:/-/g; } my $NetDev = undef; if($Block=~/\A([^:]+?):?\s/) { $NetDev = $1; } else { next; } push(@Devs, $NetDev); # save order $Addrs{$NetDev} = $Addr; $Blocks{$NetDev} = $Block; } return selectHWAddr(\@Devs, \%Addrs, $ByIPaddr, \%Blocks); } sub getIF($$) { my ($Name, $Part) = @_; if($Name=~/\A([a-z]+)(\d+)/i) { if($Part eq "name") { return $1; } elsif($Part eq "num") { return int($2); } } return undef; } sub sortNaturally(@) { return sort {getIF($a, "name") cmp getIF($b, "name")} sort {getIF($a, "num") <=> getIF($b, "num")} @_; } sub selectHWAddr(@) { my $Devs = shift(@_); my $Addrs = shift(@_); my $ByIPaddr = 0; if(@_) { $ByIPaddr = shift(@_); } my $Blocks = {}; if(@_) { $Blocks = shift(@_); } # TODO: sort all i-faces on next re-gen of the DB if($ByIPaddr) { @{$Devs} = sortNaturally(@{$Devs}); } my (@Eth, @Wlan, @Other, @Wrong, @Virtual, @Extra) = (); my %MacDev = (); foreach my $NetDev (@{$Devs}) { my $Addr = $Addrs->{$NetDev}; $MacDev{$Addr} = $NetDev; if(not $Opt{"FixProbe"}) { if(my $RealMac = getRealHWaddr($NetDev)) { $PermanentAddr{$NetDev} = clientHash($RealMac); } } if(defined $PermanentAddr{$NetDev}) { $Addr = lc($PermanentAddr{$NetDev}); $Addr=~s/:/-/g; # support for old probes } if(grep { uc($Addr) eq $_ or clientHash($Addr) eq $_ } @WrongAddr) { push(@Wrong, $Addr); next; } if(defined $ExtraConnection{$NetDev}) { # external network devices push(@Extra, $Addr); } elsif($NetDev=~/\Aenp\d+s\d+.*u\d+\Z/i) { # enp0s20f0u3, enp0s29u1u5, enp0s20u1, etc. push(@Other, $Addr); } elsif(index($Addr, "-")!=-1 and (countStr($Addr, "00")>=5 or countStr($Addr, "88")>=5 or countStr($Addr, "ff")>=5)) { # 00-dd-00-00-00-00, 88-88-88-88-87-88, ... # Support for old probes push(@Other, $Addr); } elsif(isBSD() and $Blocks->{$NetDev}=~/media:.*Wireless|NetWiFi/) { $WLanInterface{$NetDev} = 1; push(@Wlan, $Addr); } elsif(isBSD() and $Blocks->{$NetDev}=~/media:.*Ethernet/) { $EthernetInterface{$NetDev} = 1; push(@Eth, $Addr); } elsif($NetDev=~/\Ae/) { $EthernetInterface{$NetDev} = 1; push(@Eth, $Addr); } elsif($NetDev=~/\Aw/) { $WLanInterface{$NetDev} = 1; push(@Wlan, $Addr); } elsif($NetDev=~/\Avir/) { push(@Virtual, $Addr); } else { push(@Other, $Addr); } } my $Sel = undef; if(@Eth) { $Sel = $Eth[0]; } elsif(@Wlan) { $Sel = $Wlan[0]; } elsif(@Other) { $Sel = $Other[0]; } elsif(@Extra) { @Extra = sort {$MacDev{$b}=~/\Aw/ cmp $MacDev{$a}=~/\Aw/} @Extra; $Sel = $Extra[0]; } elsif(@Wrong) { $Sel = $Wrong[0]; } elsif(@Virtual) { $Sel = $Virtual[0]; } if(not $Sel) { return clientHash("DEFAULT"); } return $Sel; } sub getRealHWaddr($) { my $Dev = $_[0]; if(checkCmd("ethtool")) { my $Info = runCmd("ethtool -P $Dev 2>/dev/null"); if($Info=~/(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/) { my $Mac = lc($1); $Mac=~s/:/-/g; if($Mac ne "00-00-00-00-00-00" and $Mac ne "ff-ff-ff-ff-ff-ff") { return $Mac; } } } return; } sub readFileHex($) { my $Path = $_[0]; local $/ = undef; open(FILE, "<", $Path); binmode FILE; my $Data = ; close FILE; return unpack('H*', $Data); } sub readFile($) { my $Path = $_[0]; open(FILE, "<", $Path); local $/ = undef; my $Content = ; close(FILE); return $Content; } sub readLine($) { my $Path = $_[0]; open(FILE, "<", $Path); my $Line = ; close(FILE); return $Line; } sub fixDistr($$$$) { my ($Distr, $DistrVersion, $Rel, $Build) = @_; if(not $Distr) { if(-f "$FixProbe_Logs/issue") { my $Issue = readLine("$FixProbe_Logs/issue"); if($Issue=~/ROSA Enterprise Linux Server release ([\d\.]+)/i) { $Distr = "rels"; $DistrVersion = $1; } } } if(not $Distr) { if(-f "$FixProbe_Logs/rpms") { my $RpmsLine = readLine("$FixProbe_Logs/rpms"); if($RpmsLine=~/\.([a-z]\w+)\.\w+\Z/i) { if(defined $DistSuffix{$1} and $DistSuffix{$1}=~/\A(.+?)\-(\d.*)\Z/) { $Distr = $1; $DistrVersion = $2; } } if(not $Distr) { my $Rpms = readFile("$FixProbe_Logs/rpms"); foreach my $Pkg (sort keys(%DistPackage)) { if($Rpms=~/\Q$Pkg\E/) { if($DistPackage{$Pkg}=~/\A(.+?)\-(\d.*)\Z/) { $Distr = $1; $DistrVersion = $2; } last; } } } } elsif(-f "$FixProbe_Logs/debs") { my $Debs = readFile("$FixProbe_Logs/debs"); if(index($Debs, "termux-")!=-1) { $Distr = "android"; $DistrVersion = undef; $Rel = undef; $Sys{"Type"} = "smartphone"; } } } # Support for MX if($Distr=~/\A(debian|ubuntu)/) { my $Debs = readFile("$FixProbe_Logs/debs"); if($Debs=~/(mx-system|ddm-mx|q4os-desktop|kubuntu-desktop|xubuntu-desktop|lubuntu-desktop|ubuntu-budgie-desktop|ubuntu-mate-desktop|ubuntu-unity-desktop|ubuntudde-desktop|ubuntustudio-desktop|ubuntukylin-desktop) ([\d\.]+)/) { $Distr = $1; my $PkgVersion = $2; if($Distr=~/(mx|q4os)/) { $Distr = $1; $DistrVersion = $PkgVersion; $Rel = undef; $DistrVersion=~s/\A(\d+)\..+/$1/; } elsif($Distr=~/(kubuntu|xubuntu|lubuntu|ubuntu-budgie|ubuntu-mate|ubuntu-unity|ubuntudde|ubuntustudio|ubuntukylin)/) { $Distr = $1; } } } if(not $Distr or $Distr=~/freedesktop/) { if($Sys{"Kernel"}=~/\drosa\b/) { $Distr = "rosa"; if($Sys{"Kernel"}=~/\A3\.0\./) { $DistrVersion = "2012lts"; } elsif($Sys{"Kernel"}=~/\A3\.(8|10)\./) { $DistrVersion = "2012.1"; } elsif($Sys{"Kernel"}=~/\A3\.(14|17|18|19)\./) { $DistrVersion = "2014.1"; } elsif($Sys{"Kernel"}=~/\A4\./) { $DistrVersion = "2014.1"; } elsif($Sys{"Kernel"}=~/\A5\./) { $DistrVersion = "2016.1"; } else { printMsg("ERROR", "failed to fix 'system' attribute (kernel is '".$Sys{"Kernel"}."')"); } } } if(not $Rel) { if($Distr eq "rosa") { if($DistrVersion eq "2012.1") { if($Sys{"Kernel"}=~/\A3\.10\.(3\d|4\d)\-/) { $Rel = "rosafresh-r3"; } elsif($Sys{"Kernel"}=~/\A3\.10\.19\-/) { $Rel = "rosafresh-r2"; } elsif($Sys{"Kernel"}=~/\A3\.8\.12\-/) { $Rel = "rosafresh-r1"; } } } } if($Distr eq "debian" and (not $DistrVersion or $DistrVersion!~/\A(\d|Testing|Unstable|Sid)/i)) { if($Sys{"Kernel"}=~/\A5.1/) { $DistrVersion = "11"; } elsif($Sys{"Kernel"}=~/\A(4\.19|4\.20|5\.[023456789])/) { $DistrVersion = "10"; } elsif($Sys{"Kernel"}=~/\A(4\.9|4\.1[012345678])/) { $DistrVersion = "9"; } elsif($Sys{"Kernel"}=~/\A3\.16/) { $DistrVersion = "8"; } elsif($Sys{"Kernel"}=~/\A3\.2/) { $DistrVersion = "7"; } } if($Distr eq "gentoo" and not $DistrVersion) { if($Sys{"Kernel"}=~/\A5\.(1[01234]|[56789])/) { $DistrVersion = "2.7"; } elsif($Sys{"Kernel"}=~/\A(5\.[01234]|4\.1[89]|4\.20)/) { $DistrVersion = "2.6"; } elsif($Sys{"Kernel"}=~/\A(4\.14)/) { $DistrVersion = "2.4.1"; } elsif($Sys{"Kernel"}=~/\A4\.[589]/) { $DistrVersion = "2.3"; } elsif($Sys{"Kernel"}=~/\A4\.4/) { $DistrVersion = "2.2"; } } if(not $Distr or $Distr=~/freedesktop/) { if($Sys{"Kernel"}=~/\.fc(\d\d)\./) { $Distr = "fedora"; $DistrVersion = $1; } elsif($Sys{"Kernel"}=~/\.el(\d+)[_\d]*(\.|\Z)/) { $Distr = "centos"; $DistrVersion = $1; } elsif($Sys{"Kernel"}=~/\d-generic\Z/) { $Distr = "ubuntu"; } elsif($Sys{"Kernel"}=~/-arch[12]-/) { $Distr = "arch"; } elsif($Sys{"Kernel"}=~/\-ck\d\Z/) { $Distr = "manualinux"; $DistrVersion = undef; } elsif($Sys{"Kernel"}=~/\-(gentoo)\-/) { $Distr = $1; $DistrVersion = undef; } } elsif($Sys{"Kernel"}=~/-siduction-/) { $Distr = "siduction"; } if(isBSD($Distr)) { if(my $Pkgs = readFile("$FixProbe_Logs/pkglist")) { # if($Pkgs=~/($KNOWN_BSD_ALL) /i) { # $Distr = lc($1); # } if($Pkgs=~/ghostbsd-pkg-conf (\d[\.\d]+)/i) { $Distr = "ghostbsd"; if(not $DistrVersion or $DistrVersion eq $Sys{"Freebsd_version"}) { $DistrVersion = $1; } } elsif($Pkgs=~/GhostBSD_PKG os\/kernel (\d[\.\d]+)/i) { $Distr = "ghostbsd"; if(not $DistrVersion or $DistrVersion eq $Sys{"Freebsd_version"}) { $DistrVersion = $1; } } elsif($Pkgs=~/TING opnsense/i) { $Distr = "ting"; } elsif($Pkgs=~/\/opnsense(|\-devel) (\d[\.\d]*\d)/i) { $Distr = "opnsense"; $DistrVersion = $2; } elsif(my @AllVers = ($Pkgs=~m/helloSystem\s+(.+)/gi)) { foreach my $ReleaseString (@AllVers) { $Distr = "hellosystem"; my $ReleaseString = $1; if($ReleaseString=~/(\d[\d\.]*)_(.+)/) { $DistrVersion = $1; $Build = $2; } else { $DistrVersion = $ReleaseString; } } } } if(readFile("$FixProbe_Logs/df")=~/ \/($KNOWN_BSD_ALL)\n/i) { $Distr = lc($1); } } setDistr($Distr, $DistrVersion, $Rel, $Build); } sub setDistr($$$$) { my ($Distr, $DistrVersion, $Rel, $Build) = @_; if($DistrVersion) { $Distr = $Distr."-".$DistrVersion; } if($Distr) { $Sys{"System"} = $Distr; } if($DistrVersion or $DistrVersion eq "0") { $Sys{"System_version"} = $DistrVersion; } if($Rel) { $Sys{"Systemrel"} = $Rel; } if($Build) { $Sys{"Systembuild"} = $Build; } } sub getPrettyName($) { if($_[0]=~/\bPRETTY_NAME=[ \t]*[\"\']*([^"'\n]+)/) { return $1; } return undef; } sub probeDistr() { my ($Name, $Release, $Descr) = (); my $FreeBSDVer = ""; my $OPNsenseVer = ""; my $OSname = ""; if($Opt{"FixProbe"}) { $FreeBSDVer = readFile($FixProbe_Logs."/freebsd-version"); $OPNsenseVer = readFile($FixProbe_Logs."/opnsense-version"); $OSname = readFile($FixProbe_Logs."/osname"); } else { if(isBSD($^O)) { if(checkCmd("freebsd-version")) { listProbe("logs", "freebsd-version"); $FreeBSDVer = runCmd("freebsd-version"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/freebsd-version", $FreeBSDVer); } } if(checkCmd("opnsense-version")) { listProbe("logs", "opnsense-version"); $OPNsenseVer = runCmd("opnsense-version"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/opnsense-version", $OPNsenseVer); } } $OSname = $^O; writeLog($LOG_DIR."/osname", $OSname); } } if($OSname) { $Name = $OSname; } if($FreeBSDVer) { $Release = $FreeBSDVer; $Sys{"Freebsd_version"} = $FreeBSDVer; } if(isBSD($Name)) { if(not $Release) { $Release = $Sys{"Kernel"}; } if($Release=~/(.+)-RELEASE-(.+)/) { $Release = $1."-".$2; } elsif($Release=~/(.+)-RELEASE\Z/) { $Release = $1; } my $OS_Release = undef; if($Opt{"FixProbe"}) { $OS_Release = readFile($FixProbe_Logs."/os-release"); } else { if($OS_Release = readFile("/etc/os-release")) { # For future (e.g. on GhostBSD) listProbe("logs", "os-release"); writeLog($LOG_DIR."/os-release", $OS_Release); } } if($OSname=~/freebsd/) { if(not $Sys{"Freebsd_version"}) { $Sys{"Freebsd_version"} = $Release; } # FuryBSD my $OptLocal = ""; if($Opt{"FixProbe"}) { $OptLocal = readFile($FixProbe_Logs."/opt-local-bin"); } else { $OptLocal = join("\n", listDir("/opt/local/bin")); if($OptLocal) { writeLog($LOG_DIR."/opt-local-bin", $OptLocal); } } if($OptLocal=~/($KNOWN_BSD_ALL)/i) { $Name = lc($1); } # GhostBSD my $GhostBSDConf = ""; if($Opt{"FixProbe"}) { $GhostBSDConf = readFile($FixProbe_Logs."/GhostBSD.conf"); } else { if(-e "/etc/pkg/GhostBSD.conf") { $GhostBSDConf = "..."; } if($GhostBSDConf) { writeLog($LOG_DIR."/GhostBSD.conf", $GhostBSDConf); } } if($GhostBSDConf) { $Name = "ghostbsd"; } my $GhostBSDRc = ""; if($Opt{"FixProbe"}) { $GhostBSDRc = readFile($FixProbe_Logs."/rc.conf.ghostbsd"); } else { if(-e "/etc/rc.conf.ghostbsd") { $GhostBSDRc = "..."; } if($GhostBSDRc) { writeLog($LOG_DIR."/rc.conf.ghostbsd", $GhostBSDRc); } } if($GhostBSDRc) { $Name = "ghostbsd"; } my $GhostBSDVer = ""; if($Opt{"FixProbe"}) { $GhostBSDVer = readFile($FixProbe_Logs."/ghostbsd-version"); if(not $GhostBSDVer) { $GhostBSDVer = readFile($FixProbe_Logs."/ports_ver"); } } elsif($Name eq "ghostbsd") { if(checkCmd("ghostbsd-version")) { $GhostBSDVer = runCmd("ghostbsd-version"); writeLog($LOG_DIR."/ghostbsd-version", $GhostBSDVer); } elsif(checkCmd("pkg")) { $GhostBSDVer = runCmd("pkg rquery \'\%v\' ports"); chomp($GhostBSDVer); writeLog($LOG_DIR."/ghostbsd-version", $GhostBSDVer); } } if($GhostBSDVer) { $Release = $GhostBSDVer; } # OPNsense my $OPNsenseConf = ""; if($Opt{"FixProbe"}) { $OPNsenseConf = readFile($FixProbe_Logs."/OPNsense.conf"); } else { if(-e "/usr/local/etc/pkg/repos/OPNsense.conf") { $OPNsenseConf = "..."; } if($OPNsenseConf) { writeLog($LOG_DIR."/OPNsense.conf", $OPNsenseConf); } } if($OPNsenseConf) { $Name = "opnsense"; } if($OPNsenseVer=~/opnsense\s+(\d[\d\.]+)/i) { $Name = "opnsense"; $Release = $1; } # NomadBSD my $RootVersion = ""; if($Opt{"FixProbe"}) { $RootVersion = readFile($FixProbe_Logs."/VERSION"); } else { $RootVersion = readFile("/VERSION"); if($RootVersion) { writeLog($LOG_DIR."/VERSION", $RootVersion); } } if($RootVersion) { $Name = "nomadbsd"; chomp($RootVersion); $Release = $RootVersion; } # ClonOS my $EtcIssue = ""; if($Opt{"FixProbe"}) { $EtcIssue = readFile($FixProbe_Logs."/issue"); } else { $EtcIssue = readFile("/etc/issue"); $EtcIssue = hideIPs($EtcIssue); if($EtcIssue) { writeLog($LOG_DIR."/issue", $EtcIssue); } } if($EtcIssue=~/(ClonOS) ([^\s]+)/) { $Name = lc($1); $Release = $2; } # helloSystem my $StartHello = ""; if($Opt{"FixProbe"}) { $StartHello = readFile($FixProbe_Logs."/start-hello"); } else { $StartHello = checkCmd("start-hello"); if($StartHello) { writeLog($LOG_DIR."/start-hello", $StartHello); } } if($StartHello) { $Name = "hellosystem"; } } if(defined $Sys{"Freebsd_version"}) { $Sys{"Freebsd_release"} = $Sys{"Freebsd_version"}; $Sys{"Freebsd_release"}=~s/-.+\Z//; } if($OSname=~/netbsd/) { # OS108 my $SlimTheme = ""; if($Opt{"FixProbe"}) { $SlimTheme = readFile($FixProbe_Logs."/slim.theme"); } else { my $SlimFile = "/usr/pkg/share/slim/themes/default/slim.theme"; if(-e $SlimFile) { $SlimTheme = runCmd("head $SlimFile -n 1"); } if($SlimTheme) { writeLog($LOG_DIR."/slim.theme", $SlimTheme); } } if($SlimTheme=~/OS108/) { $Name = "os108"; } } # TrueOS and others my $Uname = ""; if($Opt{"FixProbe"}) { $Uname = readFile($FixProbe_Logs."/uname"); } elsif(checkCmd("uname")) { $Uname = runCmd("uname -v"); $Uname = hideEmail($Uname); $Uname = hidePaths($Uname); if($Uname) { writeLog($LOG_DIR."/uname", $Uname); } } if($Uname=~/($KNOWN_BSD_ALL)/i) { $Name = lc($1); } if($Uname=~/\b(ting)-(\d[\d\.]*\d)/i) { $Name = lc($1); $Release = lc($2); } if($Uname=~/RELENG_(\d+)_(\d+)_(\d+)/i) { $Release = $1.".".$2.".".$3; } if(my $PrettyName = getPrettyName($OS_Release)) { if($PrettyName=~/(MyBee)/) { $Name = lc($1); } } if($Name!~/dragonfly/) { # There is os-release on DragonFly if($Name and $Release) { return ($Name, $Release, "", ""); } return ($Name, "", "", ""); } } my $LSB_Rel = ""; if($Opt{"FixProbe"}) { $LSB_Rel = readFile($FixProbe_Logs."/lsb_release"); } elsif(not isBSD($Name) and not $Opt{"Docker"} and not $Opt{"Snap"} and not $Opt{"Flatpak"} and checkCmd("lsb_release")) { listProbe("logs", "lsb_release"); $LSB_Rel = runCmd("lsb_release -i -d -r -c 2>/dev/null"); if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/lsb_release", $LSB_Rel); } } my $OS_Rel = ""; if($Opt{"FixProbe"}) { $OS_Rel = readFile($FixProbe_Logs."/os-release"); } else { listProbe("logs", "os-release"); $OS_Rel = readFile("/etc/os-release"); if($Opt{"Snap"}) { # Snap strict confinement my $OSRelHostFs = "/var/lib/snapd/hostfs/etc/os-release"; if(-e $OSRelHostFs) { $OS_Rel = readFile($OSRelHostFs); } } elsif($Opt{"Flatpak"}) { foreach my $OSRelHost ("/run/host/etc/os-release", "/run/host/usr/lib/os-release", "/var/run/host/etc/os-release", "/var/run/host/usr/lib/os-release") { if(-e $OSRelHost) { $OS_Rel = readFile($OSRelHost); last; } } } if($Opt{"HWLogs"}) { writeLog($LOG_DIR."/os-release", $OS_Rel); } } my $Sys_Rel = ""; if($Opt{"FixProbe"}) { $Sys_Rel = readFile($FixProbe_Logs."/system-release"); } else { listProbe("logs", "system-release"); $Sys_Rel = readFile("/etc/system-release"); if($Opt{"HWLogs"} and $Sys_Rel) { writeLog($LOG_DIR."/system-release", $Sys_Rel); } } my $LSB_Rel_F = ""; if($Opt{"FixProbe"}) { $LSB_Rel_F = readFile($FixProbe_Logs."/lsb-release"); } else { listProbe("logs", "lsb-release"); $LSB_Rel_F = readFile("/etc/lsb-release"); if($Opt{"HWLogs"} and $LSB_Rel_F) { writeLog($LOG_DIR."/lsb-release", $LSB_Rel_F); } } if($LSB_Rel) { # Desktop if($LSB_Rel=~/ID:\s*(.*)/) { $Name = $1; } if(lc($Name) eq "n/a") { $Name = ""; } if($LSB_Rel=~/Release:\s*(.*)/) { $Release = lc($1); $Release=~s/\A\Q$Name\E\-//gi; } if($Release eq "n/a") { $Release = ""; } if($Release and $Name) { $Release=~s/\A$Name[\s\-]+//i; } if($LSB_Rel=~/Description:\s*(.*)/) { $Descr = $1; } if($Name=~/\ARedHatEnterprise/i) { $Name = "rhel"; } elsif($Name=~/\A(ROSAEnterpriseServer|ROSACobalt)/i) { return ("rels", $Release, "", ""); } elsif($Name=~/\AROSAEnterpriseDesktop/i) { return ("red", $Release, "", ""); } elsif($Name=~/\ARosa\.DX/i) { if($Descr=~/(Chrome|Nickel|Cobalt)/i) { return ("rosa-dx-".lc($1), $Release, "", ""); } } elsif($Descr=~/\AROSA SX/i) { if($Descr=~/(CHROME|NICKEL|COBALT)/i) { return ("rosa-sx-".lc($1), $Release, "", ""); } } elsif($Descr=~/\AROSA (Chrome|Nickel|Cobalt) ([\d\.]+)/i) { return ("rosa-".lc($1), lc($2), "", ""); } elsif($Descr=~/\AROSA (Chrome|Nickel|Cobalt)\Z/i) { return ("rosa-".lc($1), $Release, "", ""); } elsif($Name=~/\AROSA/i) { my $Rel = ""; if($Descr=~/ R([\d\-\.]+)/i) { $Rel = "rosafresh-r".$1; } elsif($Descr=~/ Enterprise Desktop X([\d\-\.]+)/i) { $Rel = "red-x".$1; } elsif($OS_Rel=~/Fresh R(\d+) /) { $Rel = "rosafresh-r".$1; } return ("rosa", $Release, $Rel, ""); } elsif($Name=~/\AOpenMandriva/i) { return ("openmandriva", $Release, "", ""); } elsif($Name=~/\AopenSUSE (Tumbleweed|MicroOS)/i or $Descr=~/\AopenSUSE (Tumbleweed|MicroOS)/i) { return ("opensuse", lc($1)."-XXXXXXXX", "", ""); } elsif($Name eq "Pop") { return ("pop!_os", $Release, "", ""); } elsif(lc($Name) eq "neon") { return ("kde-neon", $Release, "", ""); } elsif($Descr=~/\A(BigLinux|Devuan|ECP VeiL|KDE neon|LMDE|Maui|Neptune|openSUSE Leap|Parrot|Pop\!_OS|RED OS)/i) { $Name = $1; } elsif($Descr=~/\A(antiX|elementary OS)[-\s]([\d\.]+)/i) { $Name = $1; $Release = $2; } elsif($Descr=~/\A(SUSE Linux Enterprise Desktop)/i) { $Name = "sled"; } elsif($Descr=~/\A(SUSE Linux Enterprise Server)/i) { $Name = "sles"; } elsif($Name=~/\ACentOSStream/i) { $Name = "centos"; $Release = "stream"; } } if($LSB_Rel_F) { if($LSB_Rel_F=~/DISTRIB_ID[:=][ \t]*(.*)/) { $Name = $1; $Name=~s/\A"(.+)"\Z/$1/; $Name=~s/\s+\Z/$1/g; } if($LSB_Rel_F=~/DISTRIB_RELEASE[:=][ \t]*(.*)/) { $Release = lc($1); $Release=~s/\A"(.+)"\Z/$1/; $Release=~s/\A\Q$Name\E[\-\s]+//gi; } if($LSB_Rel_F=~/DISTRIB_DESCRIPTION[:=][ \t]*(.*)/) { $Descr = $1; } if($Descr=~/Easy Buster/) { $Name = "EasyOS"; } elsif($Descr=~/(BigLinux|Bluestar Linux|CryptoDATA|KDE neon|LMDE|Neptune|Netrunner|Pop\!_OS|PuppyRus-A)/) { $Name = $1; } elsif($Descr=~/(Linux Lite) ([\d\.]+)/) { $Name = $1; $Release = $2; } } if(grep { $Release eq $_ } ("amd64", "x86_64")) { $Release = undef; } if((not $Name or not $Release or $Name=~/\A(arcolinux|ubuntu|alt)/i) and $OS_Rel) { if($OS_Rel=~/\bID=[ \t]*[\"\']*([^"'\n]+)/) { $Name = $1; $Name=~s/\s+\Z//; } elsif($OS_Rel=~/\bID_LIKE=[ \t]*[\"\']*([^"'\n]+)/) { $Name = $1; $Name=~s/\s+\Z//; } if($OS_Rel=~/\bNAME=[ \t]*[\"\']*([^"'\n]+)/) { my $RealName = $1; if($RealName=~/(Acronis Cyber Infrastructure|KDE neon|MakuluLinux|Peppermint|Pop\!_OS|Virtuozzo Infrastructure)/) { $Name = $1; } } if(my $PrettyName = getPrettyName($OS_Rel)) { if($PrettyName=~/(CryptoDATA|Debian|Docker Desktop|Devuan|ECP VeiL|Feren OS|GNOME OS|Itd Os|KDE Neon|LMDE|MakuluLinux|Makululinux|MocaccinoOS|Neptune|OpenVZ|Parrot|pearOS|Pop\!_OS|Porteus|SkiffOS|Sn3rpOs|SuperX|Virtuozzo Hybrid Infrastructure|Windowsfx|XCP-ng|Zorin)/) { $Name = $1; } elsif($PrettyName=~/(Linux Lite) ([\d\.]+)/) { $Name = $1; $Release = $2; } } if($OS_Rel=~/\bVERSION_ID=[ \t]*[\"\']*([^"'\n]+)/ and $Name ne "Linux Lite") { $Release = lc($1); $Release=~s/\A\Q$Name\E[_\- ]//i; if($Release eq "i") { $Release = undef; } } if(not $Release and $OS_Rel=~/\bVERSION_CODENAME=[ \t]*([^"'\n]+)/) { my $VerCode = $1; if($VerCode eq "bookworm") { $Release = "12"; } } if($Name=~/Acronis|Ultimate|Virtuozzo/i) { if($OS_Rel=~/\bVERSION=[ \t]*[\"\']*([^"'\n]+)/) { $Release = lc($1); } } if($Release eq "n/a") { $Release = ""; } } if($Name=~/\A(CentOS|ClearOS|RHEL|Debian|MX)/i) { $Release=~s/\A(\d+)\..+/$1/; } elsif($Name=~/\Aopensuse-(tumbleweed|microos)/) { $Release="XXXXXXXX"; } #elsif($Name=~/\A(Endless)/i) { # $Release=~s/\A(\d+\.\d+)\..+/$1/; #} if((not $Name or not $Release) and $Sys_Rel) { if($Sys_Rel=~/\A(.+?)\s+release\s+([\d\.]+)/ or $Sys_Rel=~/\A(.+?)\s+([\d\.]+)/) { $Name = $1; $Release = lc($2); } } if($Name=~/funtoo/) { $Release=~s/\A(intel64-skylake-|generic_64-)//; } elsif($Name eq "blackpantheros") { $Name = "blackpanther-os"; } elsif($Name eq "elementary OS") { $Name = "elementary"; } elsif($Name=~/kali/) { $Release=~s/\Akali-//; } $Release=~s/\s+\(.+?\)\Z//; $Name = shortOS($Name); $Name = lc($Name); if(grep {$Name eq $_} ("arch", "endeavouros", "garuda", "kaos", "void", "xero", "xerolinux-kde", "xerolinux-xfce") and not $Release) { $Release = "rolling"; } if($Name and $Release) { return ($Name, $Release, "", ""); } return ($Name, "", "", ""); } sub devID(@) { my @ID = grep { $_ } @_; return lc(join("-", @ID)); } sub fNum($) { my $N = $_[0]; if(length($N)==1) { $N = "0".$N; } return $N; } sub devSort($$) { return ($_[0]=~/\A(pci|usb)/ cmp $_[1]=~/\A(pci|usb)/); } sub writeDevsDump() { foreach my $ID (keys(%HW)) { $HW{$ID}{"Bus"} = getDeviceBus($ID); if((my $Count = getDeviceCount($ID))>1) { $HW{$ID}{"Count"} = $Count; } if(not $HW{$ID}{"Status"}) { $HW{$ID}{"Status"} = "detected"; } } my $HWDump = JSON::XS->new->pretty->indent->space_after->canonical->encode(\%HW); if($Opt{"FixProbe"}) { writeFile($Opt{"FixProbe"}."/devices.json", $HWDump); if(-e $Opt{"FixProbe"}."/devices") { unlink($Opt{"FixProbe"}."/devices"); } } else { writeFile($DATA_DIR."/devices.json", $HWDump); } } sub writeDevs() { my $HWData = ""; foreach my $ID (sort {devSort($b, $a)} sort keys(%HW)) { foreach (keys(%{$HW{$ID}})) { $HW{$ID}{$_}=~s/;/ /g; } my @D = (); push(@D, $ID); push(@D, $HW{$ID}{"Class"}); if(not $HW{$ID}{"Status"}) { $HW{$ID}{"Status"} = "detected"; } push(@D, $HW{$ID}{"Status"}); # test result push(@D, $HW{$ID}{"Type"}); push(@D, $HW{$ID}{"Driver"}); push(@D, $HW{$ID}{"Vendor"}); push(@D, $HW{$ID}{"Device"}); if($HW{$ID}{"SVendor"} or $HW{$ID}{"SDevice"}) { push(@D, $HW{$ID}{"SVendor"}); if(defined $HW{$ID}{"SDevice"} and $HW{$ID}{"SDevice"} ne "Device") { push(@D, $HW{$ID}{"SDevice"}); } } my $HWLine = join(";", @D)."\n"; $HWData .= $HWLine; if((my $Count = getDeviceCount($ID))>1) { foreach (2 .. $Count) { $HWData .= $HWLine; } } } if($Opt{"FixProbe"}) { writeFile($Opt{"FixProbe"}."/devices", $HWData); if(-e $Opt{"FixProbe"}."/devices.json") { unlink($Opt{"FixProbe"}."/devices.json"); } } else { writeFile($DATA_DIR."/devices", $HWData); } } sub writeHost() { my $Host = ""; foreach my $K (sort keys(%Sys)) { if($K eq "Name" or $K eq "Version") { next; } if($Sys{$K} ne "") { $Host .= lc($K).":".$Sys{$K}."\n"; } } if($Sys{"Name"}) { $Host .= "id:".$Sys{"Name"}."\n"; } # Host Info if($Opt{"FixProbe"}) { writeFile($Opt{"FixProbe"}."/host", $Host); } else { writeFile($DATA_DIR."/host", $Host); } } sub nonASCII($) { return $_[0]=~/[^\x00-\x7f]/; } sub readHost($) { my $Path = $_[0]; my $Content = readFile($Path."/host"); my %Map = ( "id"=>"Name", "hwaddr"=>"HWaddr", "de"=>"DE", "nics"=>"NICs" ); foreach my $Line (split(/\n/, $Content)) { if($Line=~/\A(\w+)\:(.*)\Z/) { my ($K, $V) = ($1, $2); if(defined $Map{$K}) { $K = $Map{$K}; } else { $K = ucfirst($K); } $Sys{$K} = $V; } } if(-s $FixProbe_Logs."/dmi_id" or -s $FixProbe_Logs."/dmidecode" or ($Sys{"Arch"}=~/arm|aarch/i and -s $FixProbe_Logs."/dmesg") or -s $FixProbe_Logs."/sysctl") { foreach ("Vendor", "Model", "Subvendor", "Submodel", "Type") { delete($Sys{$_}); } } $Sys{"NICs"} = 0; $Sys{"Monitors"} = 0; $Sys{"Sockets"} = undef; $Sys{"Cores"} = undef; $Sys{"Threads"} = undef; $Sys{"Op_modes"} = undef; $Sys{"Ram_used"} = undef; $Sys{"Ram_total"} = undef; $Sys{"Filesystem"} = undef; $Sys{"System"} = undef; $Sys{"Systemrel"} = undef; $Sys{"Microarch"} = undef; } sub getUser() { foreach my $Var ("SUDO_USER", "USERNAME", "USER") { if(defined $ENV{$Var} and $ENV{$Var} ne "root") { return $ENV{$Var}; } } return; } sub writeLogs() { print "Reading logs ... "; if($Opt{"ListProbes"}) { print "\n"; } # level=minimal if(enabledLog("bsdhwmon") and checkCmd("bsdhwmon")) { listProbe("logs", "bsdhwmon"); my $Bsdhwmon = runCmd("bsdhwmon 2>/dev/null"); if($Bsdhwmon=~/Your motherboard does not/) { $Bsdhwmon = ""; } writeLog($LOG_DIR."/bsdhwmon", $Bsdhwmon); } if($Admin) { if(not $Opt{"Docker"} and enabledLog("dmesg.1") and checkCmd("journalctl")) { listProbe("logs", "dmesg.1"); my $Dmesg_Old = runCmd("journalctl -a -k -b -1 -o short-monotonic 2>/dev/null | grep -v systemd"); $Dmesg_Old=~s/\]\s+.*?\s+kernel:/]/g; $Dmesg_Old = hideDmesg($Dmesg_Old); writeLog($LOG_DIR."/dmesg.1", $Dmesg_Old); } } if(enabledLog("xorg.log.1")) { listProbe("logs", "xorg.log.1"); my $XLog_Old = readFile("/var/log/Xorg.0.log.old"); if(my $SUser = getUser()) { # Old Xorg log in XWayland (Ubuntu 18.04) if(my $XLog_Old_U = readFile("/home/".$SUser."/.local/share/xorg/Xorg.0.log.old")) { $XLog_Old = $XLog_Old_U; } } else { # Live if(my $XLog_Old_U = readFile("/home/ubuntu/.local/share/xorg/Xorg.0.log.old")) { $XLog_Old = $XLog_Old_U; } } $XLog_Old = hideTags($XLog_Old, "Serial#"); $XLog_Old = hidePaths($XLog_Old); $XLog_Old = encryptUUIDs($XLog_Old); if(my $HostName = $ENV{"HOSTNAME"}) { $XLog_Old=~s/ \Q$HostName\E / NODE /g; } $XLog_Old = hideHost($XLog_Old); $XLog_Old = hideByRegexp($XLog_Old, qr/\s?([\w\s]+\’s)/); writeLog($LOG_DIR."/xorg.log.1", $XLog_Old); } if(enabledLog("mcelog") and checkCmd("mcelog")) { listProbe("logs", "mcelog"); my $Mcelog = runCmd("mcelog --client 2>&1"); if($Mcelog=~/No such file or directory/) { $Mcelog = ""; } writeLog($LOG_DIR."/mcelog", $Mcelog); } if(enabledLog("xorg.conf")) { listProbe("logs", "xorg.conf"); my $XorgConf = readFile("/etc/X11/xorg.conf"); if(not $XorgConf) { $XorgConf = readFile("/usr/share/X11/xorg.conf"); } if(not $XorgConf and isBSD()) { $XorgConf = readFile("/usr/local/etc/X11/xorg.conf"); } if(not $Opt{"Docker"} or $XorgConf) { writeLog($LOG_DIR."/xorg.conf", $XorgConf); } } if(enabledLog("grub") and -e "/etc/default/grub") { listProbe("logs", "grub"); my $Grub = readFile("/etc/default/grub"); $Grub = hidePaths($Grub); $Grub = encryptUUIDs($Grub); $Grub = hideMACs($Grub); writeLog($LOG_DIR."/grub", $Grub); } if(not $Opt{"Docker"}) { if(enabledLog("grub.cfg") and -f "/boot/grub2/grub.cfg") { listProbe("logs", "grub.cfg"); my $GrubCfg = readFile("/boot/grub2/grub.cfg"); $GrubCfg = hidePaths($GrubCfg); $GrubCfg = encryptUUIDs($GrubCfg); $GrubCfg = hideMACs($GrubCfg); $GrubCfg=~s/.*password.+/###/g; writeLog($LOG_DIR."/grub.cfg", $GrubCfg); } } if(checkCmd("xrandr")) { if(enabledLog("xrandr")) { listProbe("logs", "xrandr"); my $XRandr = runCmd("xrandr --verbose 2>&1"); $XRandr = clearLog_X11($XRandr); if(not $XRandr and defined $ENV{"XDG_SESSION_TYPE"}) { printMsg("WARNING", "X11-related logs are not collected (try to run 'sudo -E hw-probe ...')"); } writeLog($LOG_DIR."/xrandr", $XRandr); } if(enabledLog("xrandr_providers")) { listProbe("logs", "xrandr_providers"); my $XRandrProviders = runCmd("xrandr --listproviders 2>&1"); writeLog($LOG_DIR."/xrandr_providers", clearLog_X11($XRandrProviders)); } } if(enabledLog("glxinfo") and checkCmd("glxinfo")) { listProbe("logs", "glxinfo"); my $Glxinfo = runCmd("glxinfo 2>&1"); $Glxinfo = clearLog_X11($Glxinfo); $Glxinfo=~s/(GLX Visuals)(.|\n)+?\Z/$1\n...\n/g; writeLog($LOG_DIR."/glxinfo", $Glxinfo); } if(enabledLog("biosdecode") and checkCmd("biosdecode")) { listProbe("logs", "biosdecode"); my $BiosDecode = ""; if($Admin) { $BiosDecode = runCmd("biosdecode 2>/dev/null"); if(length($BiosDecode)<20) { $BiosDecode = ""; } } writeLog($LOG_DIR."/biosdecode", $BiosDecode); } if(enabledLog("boltctl") and checkCmd("boltctl")) { listProbe("logs", "boltctl"); if(my $BoltCtl = runCmd("boltctl list --all")) { writeLog($LOG_DIR."/boltctl", $BoltCtl); } } # level=default if(not $Opt{"AppImage"} and enabledLog("cpupower") and checkCmd("cpupower")) { # TODO: Why doesn't work in AppImage? listProbe("logs", "cpupower"); my $CPUpower = ""; $CPUpower .= "frequency-info\n--------------\n"; $CPUpower .= runCmd("cpupower frequency-info 2>&1"); $CPUpower .= "\n"; $CPUpower .= "idle-info\n---------\n"; $CPUpower .= runCmd("cpupower idle-info 2>&1"); if($CPUpower=~/cpupower not found/) { $CPUpower = undef; } if($CPUpower) { writeLog($LOG_DIR."/cpupower", $CPUpower); } } if(enabledLog("dkms_status") and checkCmd("dkms")) { listProbe("logs", "dkms_status"); my $DkmsStatus = ""; if($Admin) { $DkmsStatus = runCmd("dkms status 2>&1"); } writeLog($LOG_DIR."/dkms_status", $DkmsStatus); } if(enabledLog("xdpyinfo") and checkCmd("xdpyinfo")) { listProbe("logs", "xdpyinfo"); if(my $Xdpyinfo = runCmd("xdpyinfo 2>&1")) { $Xdpyinfo=~s/(visual:(.|\n)+?)\Z/...\n/g; writeLog($LOG_DIR."/xdpyinfo", clearLog_X11($Xdpyinfo)); } } if(enabledLog("rpms") and checkCmd("rpm")) { listProbe("logs", "rpms"); my $Rpms = runCmd("rpm -qa 2>/dev/null | sort"); if($Rpms) { writeLog($LOG_DIR."/rpms", $Rpms); } } if(enabledLog("dpkg") and checkCmd("dpkg")) { listProbe("logs", "debs"); my $Dpkgs = runCmd("dpkg -l 2>/dev/null | awk '/^[hi]i/{print \$2,\$3,\$4}'"); if($Dpkgs) { writeLog($LOG_DIR."/debs", $Dpkgs); } } if(enabledLog("apk") and $Sys{"System"}=~/alpine/i and checkCmd("apk")) { listProbe("logs", "apk"); my $Apk = runCmd("apk info 2>/dev/null"); if($Apk) { writeLog($LOG_DIR."/apk", $Apk); } } if(enabledLog("pkglist")) { if(checkCmd("pacman")) { # Arch / Manjaro listProbe("logs", "pkglist"); my $Pkglist = runCmd("pacman -Q 2>/dev/null"); if($Pkglist) { writeLog($LOG_DIR."/pkglist", $Pkglist); } } if($Sys{"System"}=~/solus/i and checkCmd("eopkg")) { listProbe("logs", "pkglist"); my $Pkglist = runCmd("eopkg list-installed -l 2>/dev/null | grep Name:"); if($Pkglist) { $Pkglist=~s/Name\: //g; $Pkglist=~s/, version: / /g; $Pkglist=~s/, release: / r/g; writeLog($LOG_DIR."/pkglist", $Pkglist); } } if($Sys{"System"}=~/clear-linux/i and checkCmd("swupd")) { listProbe("logs", "pkglist"); my $BundleList = runCmd("swupd bundle-list"); if($BundleList) { writeLog($LOG_DIR."/bundle-list", $BundleList); } } if($Sys{"System"}=~/slackware/i and -d "/var/log/packages") { listProbe("logs", "pkglist"); my $LogPkgs = `ls -1 /var/log/packages | sort`; if($LogPkgs) { writeLog($LOG_DIR."/pkglist", $LogPkgs); } } if($Sys{"System"}=~/gentoo/i and -d "/var/db/pkg") { listProbe("logs", "pkglist"); my $DbPkgs = `ls /var/db/pkg/*`; if($DbPkgs) { writeLog($LOG_DIR."/pkglist", $DbPkgs); } } if(isOpenBSD() and checkCmd("pkg_info")) { listProbe("logs", "pkg_info"); my $PkgInfo = runCmd("pkg_info -qP"); if($PkgInfo) { writeLog($LOG_DIR."/pkglist", $PkgInfo); } } elsif(isNetBSD() and checkCmd("pkgin")) { listProbe("logs", "pkgin"); my $PkgList = runCmd("pkgin list | sort"); $PkgList=~s/\s+.+?\n/\n/g; if($PkgList) { writeLog($LOG_DIR."/pkglist", $PkgList); } } elsif($Sys{"System"}=~/midnightbsd/ and checkCmd("mport")) { listProbe("logs", "mport"); my $Pkginfo = runCmd("mport list"); $Pkginfo=~s/\s+.+?\n/\n/g; if($Pkginfo) { writeLog($LOG_DIR."/pkglist", $Pkginfo); } } elsif(defined $Sys{"Freebsd_release"} and $Sys{"Freebsd_release"} < 10.0) { listProbe("logs", "pkg_info"); my $PkgInfo = runCmd("pkg_info"); $PkgInfo=~s/\s+.+?\n/\n/g; if($PkgInfo) { writeLog($LOG_DIR."/pkglist", $PkgInfo); } } elsif(isBSD() and checkCmd("pkg")) { # any modern FreeBSD-based system listProbe("logs", "pkg"); my $PkgQuery = runCmd("pkg query --all '\%R \%o \%v' | sort"); if($PkgQuery) { writeLog($LOG_DIR."/pkglist", $PkgQuery); } } } if(not $Opt{"Docker"}) { if(enabledLog("rfkill") and checkCmd("rfkill")) { listProbe("logs", "rfkill"); my $Rfkill = runCmd("rfkill list 2>&1"); if($Opt{"Snap"} and $Rfkill=~/Permission denied|Bad file descriptor/) { $Rfkill = ""; } writeLog($LOG_DIR."/rfkill", $Rfkill); } } if(enabledLog("iw_list") and checkCmd("iw")) { listProbe("logs", "iw_list"); my $IwList = runCmd("iw list 2>&1"); if($Opt{"Snap"} and $IwList=~/Permission denied/) { $IwList = ""; } if($IwList) { writeLog($LOG_DIR."/iw_list", $IwList); } } if(enabledLog("iwconfig") and checkCmd("iwconfig")) { listProbe("logs", "iwconfig"); my $IwConfig = runCmd("iwconfig 2>&1"); $IwConfig = hideMACs($IwConfig); $IwConfig = hideTags($IwConfig, "ESSID"); writeLog($LOG_DIR."/iwconfig", $IwConfig); } if(enabledLog("nm-tool") and checkCmd("nm-tool")) { listProbe("logs", "nm-tool"); my $NmTool = runCmd("nm-tool 2>&1"); if($NmTool) { writeLog($LOG_DIR."/nm-tool", $NmTool); } } if(enabledLog("nmcli") and checkCmd("nmcli")) { listProbe("logs", "nmcli"); my $NmCli = runCmd("nmcli c 2>&1"); $NmCli=~s/.+\s+([^\s]+\s+[^\s]+\s+[^\s]+\s*\n)/XXX $1/g; $NmCli=~s/\AXXX /NAME/g; $NmCli = encryptUUIDs($NmCli); if($NmCli) { writeLog($LOG_DIR."/nmcli", $NmCli); } } if($Admin and enabledLog("fdisk") and checkCmd("fdisk") and not isBSD()) { listProbe("logs", "fdisk"); my $Fdisk = runCmd("fdisk -l 2>&1"); if($Opt{"Snap"} and $Fdisk=~/Permission denied/) { $Fdisk = ""; } $Fdisk = hidePaths($Fdisk); $Fdisk = hideTags($Fdisk, "Disk identifier"); if($Fdisk) { writeLog($LOG_DIR."/fdisk", $Fdisk); } } if(enabledLog("inxi") and my $InxiCmd = checkCmd("inxi")) { listProbe("logs", "inxi"); my $Inxi = undef; if(readLine($InxiCmd)=~/perl/) { # The new Perl inxi $Inxi = runCmd("inxi -Fxxxzmd --no-host 2>&1"); } else { # Old inxi $Inxi = runCmd("inxi -Fxzd -c 0 -! 31 2>&1"); } $Inxi=~s/\s+\w+\:\s*//g; writeLog($LOG_DIR."/inxi", $Inxi); } my $I2cdetect = ""; if(enabledLog("i2cdetect") and checkCmd("i2cdetect")) { listProbe("logs", "i2cdetect"); $I2cdetect = runCmd("i2cdetect -l 2>&1"); writeLog($LOG_DIR."/i2cdetect", $I2cdetect); } if($Admin and (enabledLog("ddcutil") or enabledLog("ddc")) and $Sys{"Type"}!~/$MOBILE_TYPE/ and $Sys{"Model"}!~/VirtualBox|QEMU|VMWare|Virtual Machine|Parallels Virtual/) { my $DDCUtilCmd = undef; if($Opt{"Snap"} or $Opt{"AppImage"} or $Opt{"Flatpak"}) { $DDCUtilCmd = findCmd("ddcutil"); } elsif(checkCmd("ddcutil")) { $DDCUtilCmd = "ddcutil"; } if($DDCUtilCmd) { listProbe("logs", "ddcutil"); my $DDCUtil = ""; my @Range = (0 .. 31); #if($I2cdetect) #{ # @Range = (); # foreach my $L (split(/\n/, $I2cdetect)) # { # if($L=~/i2c-(\d+).+(NVIDIA|nvkm|i915|Radeon|AMDGPU|DPD)/) # { # push(@Range, $1); # } # } #} foreach my $N (@Range) { my $DDCCmd = "$DDCUtilCmd probe --bus $N"; # if($Opt{"AppImage"} or $Opt{"Docker"}) { # $DDCCmd .= " --f1"; # } my $DDCProbe = runCmd($DDCCmd." --sleep-multiplier 0.5 | grep -v 'DDC Null Response'"); if($DDCProbe=~/Maximum retries exceeded/) { $DDCProbe = runCmd($DDCCmd); } if($DDCProbe) { if($DDCProbe!~/No monitor detected|communication failed/) { $DDCUtil .= "# ddcutil probe --bus $N\n"; $DDCUtil .= $DDCProbe."\n"; } } } if($Opt{"HWLogs"} and $DDCUtil) { $DDCUtil = encryptSerials($DDCUtil, "sn"); $DDCUtil=~s/(binary serial number ).+?(\n)/$1...$2/g; writeLog($LOG_DIR."/ddcutil", $DDCUtil); } } } if(-e "/sys/firmware/efi") # defined $KernMod{"efivarfs"} { # installed in EFI mode if(enabledLog("efivar") and checkCmd("efivar")) { listProbe("logs", "efivar"); my $Efivar = runCmd("efivar -l 2>&1"); if($Efivar=~/error listing variables/i) { $Efivar = ""; } writeLog($LOG_DIR."/efivar", $Efivar); } if($Admin and $Sys{"Arch"}=~/x86_64|amd64/) { if(enabledLog("efibootmgr") and checkCmd("efibootmgr")) { listProbe("logs", "efibootmgr"); my $Efibootmgr = runCmd("efibootmgr -v 2>&1"); if($Opt{"Snap"} and $Efibootmgr=~/Permission denied/) { $Efibootmgr = ""; } $Efibootmgr = encryptUUIDs($Efibootmgr); $Efibootmgr = hideByRegexp($Efibootmgr, qr/MAC\((.+?)\)/); $Efibootmgr = hideByRegexp($Efibootmgr, qr/0x([a-f\d]{8})/); $Efibootmgr = hideByRegexp($Efibootmgr, qr/U\.U\.I\.D\.=([a-fA-F\d\.]{17}-[a-fA-F\d\.]{9}-[a-fA-F\d\.]{9}-[a-fA-F\d\.]{9}-[a-fA-F\d\.]{25})/); writeLog($LOG_DIR."/efibootmgr", $Efibootmgr); } } if(enabledLog("boot_efi") and -d "/boot/efi" and not $Opt{"Snap"}) { listProbe("logs", "boot_efi"); my $BootEfi = runCmd("find /boot/efi 2>/dev/null | sort"); writeLog($LOG_DIR."/boot_efi", $BootEfi); } } my $Switch = "/sys/kernel/debug/vgaswitcheroo/switch"; if(enabledLog("vgaswitcheroo") and -e $Switch) { listProbe("logs", "vgaswitcheroo"); my $SInfo = readFile($Switch); writeLog($LOG_DIR."/vgaswitcheroo", $SInfo); } if(enabledLog("input_devices")) { listProbe("logs", "input_devices"); my $InputDevices = readFile("/proc/bus/input/devices"); if($InputDevices) { writeLog($LOG_DIR."/input_devices", $InputDevices); } } if(enabledLog("iostat") and checkCmd("iostat")) { listProbe("logs", "iostat"); my $Iostat = runCmd("iostat 2>&1"); $Iostat=~s/\(.+\)/(...)/; writeLog($LOG_DIR."/iostat", $Iostat); } if(enabledLog("acpi") and checkCmd("acpi")) { listProbe("logs", "acpi"); my $Acpi = runCmd("acpi -V 2>/dev/null"); writeLog($LOG_DIR."/acpi", $Acpi); } if(defined $KernMod{"fglrx"} and $KernMod{"fglrx"}!=0) { if(enabledLog("fglrxinfo") and checkCmd("fglrxinfo")) { listProbe("logs", "fglrxinfo"); my $Fglrxinfo = runCmd("fglrxinfo -t 2>&1"); writeLog($LOG_DIR."/fglrxinfo", $Fglrxinfo); } if(enabledLog("amdconfig") and checkCmd("amdconfig")) { listProbe("logs", "amdconfig"); my $AMDconfig = runCmd("amdconfig --list-adapters 2>&1"); writeLog($LOG_DIR."/amdconfig", $AMDconfig); } } elsif(defined $KernMod{"nvidia"} and $KernMod{"nvidia"}!=0) { if(enabledLog("nvidia-smi")) { foreach ("64", "") { my $NvidiaSmi_Path = "/usr/lib".$_."/nvidia/bin/nvidia-smi"; if(-e $NvidiaSmi_Path) { listProbe("logs", "nvidia-smi"); my $NvidiaSmi = runCmd("$NvidiaSmi_Path -q 2>&1"); writeLog($LOG_DIR."/nvidia-smi", $NvidiaSmi); last; } } } } if(enabledLog("vulkaninfo") and checkCmd("vulkaninfo")) { listProbe("logs", "vulkaninfo"); my $Vulkaninfo = runCmd("vulkaninfo 2>&1"); if($Vulkaninfo!~/Cannot create/i) { writeLog($LOG_DIR."/vulkaninfo", $Vulkaninfo); } } if(enabledLog("vdpauinfo") and checkCmd("vdpauinfo")) { listProbe("logs", "vdpauinfo"); my $Vdpauinfo = runCmd("vdpauinfo 2>&1"); if($Vdpauinfo=~/Failed to open/i) { $Vdpauinfo = undef; } if($Vdpauinfo) { writeLog($LOG_DIR."/vdpauinfo", clearLog_X11($Vdpauinfo)); } } if(enabledLog("vainfo") and checkCmd("vainfo")) { listProbe("logs", "vainfo"); my $Vainfo = ""; my $DriDir = "/dev/dri"; foreach my $DriDev (listDir($DriDir)) { if($DriDev!~/render/) { next; } my $VaInfoCmd = "vainfo --display drm --device $DriDir/$DriDev"; my $VaInfoRes = qx($VaInfoCmd 2>&1); if($VaInfoRes=~/failed with error/i) { next; } $Vainfo .= "# $VaInfoCmd\n"; $Vainfo .= "$VaInfoRes\n"; } if(not $Vainfo) { $Vainfo = runCmd("vainfo 2>&1"); if($Vainfo=~/failed with error/i) { $Vainfo = undef; } } if($Vainfo) { writeLog($LOG_DIR."/vainfo", clearLog_X11($Vainfo)); } } if(enabledLog("scsi")) { listProbe("logs", "scsi"); my $Scsi = readFile("/proc/scsi/scsi"); if($Scsi) { # list all devices in RAID writeLog($LOG_DIR."/scsi", $Scsi); } } if(enabledLog("ioports")) { listProbe("logs", "ioports"); my $IOports = readFile("/proc/ioports"); if($IOports) { writeLog($LOG_DIR."/ioports", $IOports); } } if(enabledLog("interrupts")) { listProbe("logs", "interrupts"); my $Interrupts = readFile("/proc/interrupts"); if($Interrupts) { writeLog($LOG_DIR."/interrupts", $Interrupts); } } my $Aplay = undef; if(enabledLog("aplay") and checkCmd("aplay")) { listProbe("logs", "aplay"); $Aplay = runCmd("aplay -l 2>&1"); if(length($Aplay)<80 and $Aplay=~/no soundcards found|not found/i) { $Aplay = ""; } writeLog($LOG_DIR."/aplay", $Aplay); } if(enabledLog("arecord") and checkCmd("arecord")) { listProbe("logs", "arecord"); my $Arecord = runCmd("arecord -l 2>&1"); if(length($Arecord)<80 and $Arecord=~/no soundcards found|not found/i) { $Arecord = ""; } writeLog($LOG_DIR."/arecord", $Arecord); } if(enabledLog("amixer") and checkCmd("amixer")) { listProbe("logs", "amixer"); if(not defined $Aplay) { $Aplay = runCmd("aplay -l"); } my %CardNums = (); while($Aplay=~/card\s+(\d+)/g) { $CardNums{$1} = 1; } my $Amixer = ""; foreach my $ACard (sort {int($a)<=>int($b)} keys(%CardNums)) { $Amixer .= runCmd("amixer -c$ACard info 2>&1"); $Amixer .= runCmd("amixer -c$ACard 2>&1"); $Amixer .= "\n"; } writeLog($LOG_DIR."/amixer", $Amixer); } if(enabledLog("alsactl") and checkCmd("alsactl")) { listProbe("logs", "alsactl"); system("alsactl store -f $TMP_DIR/alsactl 2>/dev/null"); if(-f "$TMP_DIR/alsactl") { move("$TMP_DIR/alsactl", $LOG_DIR."/alsactl"); } } if(enabledLog("systemd-analyze") and checkCmd("systemd-analyze")) { listProbe("logs", "systemd-analyze"); if(my $SystemdAnalyze = runCmd("systemd-analyze 2>/dev/null")) { $SystemdAnalyze .= "\n"; $SystemdAnalyze .= runCmd("systemd-analyze blame 2>/dev/null"); if($SystemdAnalyze) { $SystemdAnalyze = decorateSystemd($SystemdAnalyze); $SystemdAnalyze = hideDevDiskUUIDs($SystemdAnalyze); $SystemdAnalyze = hideByRegexp($SystemdAnalyze, qr/shaman\@(.+?)\.service/); writeLog($LOG_DIR."/systemd-analyze", $SystemdAnalyze); } } } if(enabledLog("gpu-manager.log") and -f "/var/log/gpu-manager.log") { # Ubuntu listProbe("logs", "gpu-manager.log"); if(my $GpuManager = readFile("/var/log/gpu-manager.log")) { writeLog($LOG_DIR."/gpu-manager.log", $GpuManager); } } if(not $Opt{"Docker"} and enabledLog("modprobe.d")) { listProbe("logs", "modprobe.d"); my @Modprobe = listDir("/etc/modprobe.d/"); my $Mprobe = ""; foreach my $Mp (@Modprobe) { if($Mp eq "00_modprobe.conf" or $Mp eq "01_mandriva.conf") { next; } $Mprobe .= $Mp."\n"; foreach (1 .. length($Mp)) { $Mprobe .= "-"; } $Mprobe .= "\n"; my $Content = readFile("/etc/modprobe.d/".$Mp); $Content=~s{http(s|)://[^ ]+}{}g; $Mprobe .= $Content; $Mprobe .= "\n\n"; } $Mprobe=~s/#.*\n//g; writeLog($LOG_DIR."/modprobe.d", $Mprobe); } if(enabledLog("xorg.conf")) { listProbe("logs", "xorg.conf.d"); my $XConfig = ""; my @XDirs = ("/etc/X11/xorg.conf.d", "/usr/share/X11/xorg.conf.d"); if(isBSD()) { @XDirs = ("/usr/local/etc/X11/xorg.conf.d/", @XDirs); } foreach my $XDir (@XDirs) { if(not -d $XDir) { next; } my @XorgConfD = listDir($XDir); foreach my $Xc (@XorgConfD) { if($Xc!~/\.conf\Z/) { next; } $XConfig .= $Xc."\n"; foreach (1 .. length($Xc)) { $XConfig .= "-"; } $XConfig .= "\n"; $XConfig .= readFile($XDir."/".$Xc); $XConfig .= "\n\n"; } } $XConfig=~s/#.*\n//g; if(not $Opt{"Docker"} or $XConfig) { writeLog($LOG_DIR."/xorg.conf.d", $XConfig); } } if(enabledLog("rc.conf")) { listProbe("logs", "rc.conf"); my $RcConf = readFile("/etc/rc.conf"); $RcConf = hidePaths($RcConf); $RcConf = encryptUUIDs($RcConf); $RcConf = hideMACs($RcConf); $RcConf = hideIPs($RcConf); $RcConf=~s/((hostname|user|host|port|vm_list|autossh_rules|syslogd_flags)\s*=).+/$1.../g; $RcConf=~s/(openvpn)\w*\s*=.+/$1.../g; $RcConf=~s/[ ]*#.*//g; $RcConf=~s/[\n]{2,}/\n/g; writeLog($LOG_DIR."/rc.conf", $RcConf); } if(enabledLog("loader.conf")) { listProbe("logs", "loader.conf"); my $BootLoader = readFile("/boot/loader.conf"); $BootLoader = hidePaths($BootLoader); $BootLoader=~s/[ ]*#.*//g; $BootLoader=~s/[\n]{2,}/\n/g; writeLog($LOG_DIR."/loader.conf", $BootLoader); } if(enabledLog("gpart") and checkCmd("gpart")) { listProbe("logs", "gpart"); my $Gpart = runCmd("gpart show 2>/dev/null"); $Gpart = hidePaths($Gpart); writeLog($LOG_DIR."/gpart", $Gpart); } if(enabledLog("gpart_list") and checkCmd("gpart")) { listProbe("logs", "gpart_list"); my $GpartList = runCmd("gpart list -a 2>/dev/null"); $GpartList = hidePaths($GpartList); $GpartList = encryptUUIDs($GpartList); if($GpartList) { writeLog($LOG_DIR."/gpart_list", $GpartList); } } if(enabledLog("vmstat") and checkCmd("vmstat")) { listProbe("logs", "vmstat"); my $Vmstat = runCmd("vmstat 2>/dev/null"); writeLog($LOG_DIR."/vmstat", $Vmstat); } if(enabledLog("top_head") and checkCmd("top")) { listProbe("logs", "top_head"); my $TopHead = runCmd("top -b -d1 | head -n 4 2>/dev/null"); $TopHead=~s/(last pid:\s+)\d+/$1.../; $TopHead=~s/\s+[^\s]+\s+\d\d:\d\d:\d\d//; writeLog($LOG_DIR."/top_head", $TopHead); } if($Opt{"Scanners"}) { if(enabledLog("sane-find-scanner") and checkCmd("sane-find-scanner")) { listProbe("logs", "sane-find-scanner"); my $FindScanner = runCmd("sane-find-scanner -q 2>/dev/null"); writeLog($LOG_DIR."/sane-find-scanner", $FindScanner); } if(enabledLog("scanimage") and checkCmd("scanimage")) { listProbe("logs", "scanimage"); my $Scanimage = runCmd("scanimage -L 2>/dev/null | grep -v v4l"); if($Scanimage=~/No scanners were identified/i) { $Scanimage = ""; } writeLog($LOG_DIR."/scanimage", $Scanimage); } } if(enabledLog("drm_info") and checkCmd("drm_info")) { listProbe("logs", "drm_info"); my $DrmInfo = runCmd("drm_info 2>/dev/null"); writeLog($LOG_DIR."/drm_info", $DrmInfo); } # level=maximal if(enabledLog("firmware") and -d "/lib/firmware") { listProbe("logs", "firmware"); my $Firmware = undef; if(isBSD()) { $Firmware = runCmd("find /boot/firmware -type f | sort"); } else { $Firmware = runCmd("find /lib/firmware -type f | sort"); } $Firmware=~s{/lib/firmware/}{}g; writeLog($LOG_DIR."/firmware", $Firmware); } if(enabledLog("top") and checkCmd("top")) { listProbe("logs", "top"); my $TopInfo = runCmd("top -n 1 -b 2>&1"); if(my $SessUser = getUser()) { $TopInfo=~s/ \Q$SessUser\E / USER /g; } writeLog($LOG_DIR."/top", $TopInfo); } if(enabledLog("pstree") and checkCmd("pstree")) { listProbe("logs", "pstree"); my $Pstree = runCmd("pstree 2>&1"); writeLog($LOG_DIR."/pstree", $Pstree); } if(enabledLog("numactl") and checkCmd("numactl")) { listProbe("logs", "numactl"); my $Numactl = runCmd("numactl -H"); if($Numactl) { writeLog($LOG_DIR."/numactl", $Numactl); } } if(enabledLog("slabtop") and checkCmd("slabtop")) { listProbe("logs", "slabtop"); my $Slabtop = runCmd("slabtop -o"); writeLog($LOG_DIR."/slabtop", $Slabtop); } # scan for available WiFi networks if(enabledLog("iw_scan") and checkCmd("iw")) { listProbe("logs", "iw_scan"); my $IwScan = ""; if($Admin) { foreach my $I (sort keys(%WLanInterface)) { $IwScan .= $I."\n"; foreach (1 .. length($I)) { $IwScan .= "-"; } $IwScan .= "\n"; $IwScan .= runCmd("iw dev $I scan 2>&1"); $IwScan .= "\n"; } } $IwScan = hideTags($IwScan, "SSID|UUID|Serial Number"); $IwScan = hideMACs($IwScan); writeLog($LOG_DIR."/iw_scan", $IwScan); } # scan for available bluetooth connections if(enabledLog("hcitool_scan") and checkCmd("hcitool")) { listProbe("logs", "hcitool_scan"); my $HciScan = runCmd("hcitool scan --class 2>&1"); if($HciScan=~/No such device/i) { $HciScan = ""; } $HciScan = hideMACs($HciScan); if($HciScan) { writeLog($LOG_DIR."/hcitool_scan", $HciScan); } } if(enabledLog("route") and checkCmd("route")) { listProbe("logs", "route"); my $Route = runCmd("route 2>&1"); $Route = hideIPs($Route); writeLog($LOG_DIR."/route", $Route); } if(enabledLog("xvinfo") and checkCmd("xvinfo")) { listProbe("logs", "xvinfo"); my $XVInfo = runCmd("xvinfo 2>&1"); $XVInfo = encryptUUIDs($XVInfo); writeLog($LOG_DIR."/xvinfo", clearLog_X11($XVInfo)); } if(enabledLog("lsinitrd") and checkCmd("lsinitrd")) { listProbe("logs", "lsinitrd"); my $Lsinitrd = runCmd("lsinitrd 2>&1"); $Lsinitrd=~s/.*?(\w+\s+\d+\s+\d\d\d\d\s+)/$1/g; writeLog($LOG_DIR."/lsinitrd", $Lsinitrd); } if(enabledLog("update-alternatives") and checkCmd("update-alternatives")) { listProbe("logs", "update-alternatives"); my $Alternatives = runCmd("update-alternatives --list 2>/dev/null"); writeLog($LOG_DIR."/update-alternatives", $Alternatives); } if($Opt{"Printers"}) { if($Admin) { my $MAX_P_LEN = 1000; my $ELog = "/var/log/cups/error_log"; if(enabledLog("cups_error_log") and -e $ELog) { listProbe("logs", "cups_error_log"); my $CupsError = readFile($ELog); if(length($CupsError)>$MAX_P_LEN) { $CupsError = "...\n\n".substr($CupsError, -$MAX_P_LEN); } writeLog($LOG_DIR."/cups_error_log", $CupsError); } my $ALog = "/var/log/cups/access_log"; if(enabledLog("cups_access_log") and -e $ALog) { listProbe("logs", "cups_access_log"); my $CupsAccess = readFile($ALog); if(length($CupsAccess)>$MAX_P_LEN) { $CupsAccess = "...\n\n".substr($CupsAccess, -$MAX_P_LEN); } writeLog($LOG_DIR."/cups_access_log", $CupsAccess); } } } # Disabled as it can hang the system # my $SuperIO = ""; # if($Admin) { # $SuperIO = runCmd("superiotool -d 2>/dev/null"); # } # writeLog($LOG_DIR."/superiotool", $SuperIO); if($Opt{"DumpACPI"}) { listProbe("logs", "acpidump"); if(isBSD()) { my $AcpiDump_Decoded = ""; if($Admin and checkCmd("acpidump")) { $AcpiDump_Decoded = runCmd("acpidump -dt 2>/dev/null"); } writeLog($LOG_DIR."/acpidump_decoded", $AcpiDump_Decoded); } else { my $AcpiDump = ""; # To decode acpidump: # 1. acpixtract -a acpidump # 2. iasl -d ECDT.dat if($Admin) { if(checkCmd("acpidump")) { $AcpiDump = runCmd("acpidump 2>/dev/null"); } } writeLog($LOG_DIR."/acpidump", $AcpiDump); if($Opt{"DecodeACPI"}) { if(-s "$LOG_DIR/acpidump") { decodeACPI("$LOG_DIR/acpidump", "$LOG_DIR/acpidump_decoded"); } } } } print "Ok\n"; } sub checkCmd(@) { my $Cmd = shift(@_); my $Verify = undef; if(@_) { $Verify = shift(@_); } if(index($Cmd, "/")!=-1 and -x $Cmd) { # relative or absolute path return $Cmd; } my @Paths = split(/:/, $ENV{"PATH"}); if(isNetBSD()) { push(@Paths, "/usr/sbin", "/sbin", "/usr/pkg/sbin"); } if(not $Verify) { @Paths = sort {length($a)<=>length($b)} @Paths; } foreach my $Dir (@Paths) { $Dir=~s{/+\Z}{}g; if(-x "$Dir/$Cmd") { if($Verify) { if(not `$Dir/$Cmd --version 2>/dev/null`) { next; } } return "$Dir/$Cmd"; } } return; } sub findCmd($) { my $Cmd = $_[0]; if(my $Path = checkCmd($Cmd, 1)) { return $Path; } return $Cmd; } sub decodeACPI($$) { my ($Dump, $Output) = @_; $Dump = abs_path($Dump); if(not checkCmd("acpixtract") or not checkCmd("iasl")) { return 0; } my $TmpDir = $TMP_DIR."/acpi"; mkpath($TmpDir); chdir($TmpDir); # list data my $DSL = runCmd("acpixtract -l \"$Dump\" 2>&1"); $DSL .= "\n"; $DSL=~s{\Q$Dump\E}{acpidump}; # extract *.dat system("acpixtract -a \"$Dump\" >/dev/null 2>&1"); # decode *.dat my @Files = listDir("."); foreach my $File (@Files) { if($File=~/\A(.+)\.dat\Z/) { my $Name = $1; if($Name=~/dsdt/i) { # next; } runCmd("iasl -d \"$File\" 2>&1"); my $DslFile = $Name.".dsl"; if(-f $DslFile) { $DSL .= uc($Name)."\n"; foreach (1 .. length($Name)) { $DSL .= "-"; } $DSL .= "\n"; my $Data = readFile($DslFile); $Data=~s{\A\s*/\*.*?\*/\s*}{}sg; $DSL .= $Data; $DSL .= "\n"; $DSL .= "\n"; } unlink($File); unlink($DslFile); } } chdir($ORIG_DIR); writeFile($Output, $DSL); rmtree($TmpDir); return 1; } sub clearLog_X11($) { if(length($_[0])<$EMPTY_LOG_SIZE and $_[0]=~/No protocol specified|Can't open display|unable to open display|Unable to connect to|cannot connect to|couldn't open display|cannot open shared object file/i) { return ""; } return $_[0]; } sub clearLog($) { my $Log = $_[0]; my $Sc = chr(27); $Log=~s/$Sc\[.*?m//g; $Log=~s/$Sc%G//g; return $Log; } sub showInfo() { my $ShowDir = $DATA_DIR; if($Opt{"Source"}) { if(-f $Opt{"Source"}) { if(isPkg($Opt{"Source"})) { my $Pkg = abs_path($Opt{"Source"}); chdir($TMP_DIR); system("tar", "-m", "-xf", $Pkg); chdir($ORIG_DIR); if($?) { printMsg("ERROR", "failed to extract package (".$?.")"); exitStatus(1); } if(my @Dirs = listDir($TMP_DIR)) { $ShowDir = $TMP_DIR."/".$Dirs[0]; } else { printMsg("ERROR", "failed to extract package"); exitStatus(1); } } else { printMsg("ERROR", "not a package"); exitStatus(1); } } elsif(-d $Opt{"Source"}) { $ShowDir = $Opt{"Source"}; } else { printMsg("ERROR", "can't access '".$Opt{"Source"}."'"); exitStatus(1); } } else { if(not -d $DATA_DIR) { printMsg("ERROR", "'".$DATA_DIR."' is not found, please make probe first"); exitStatus(1); } } my %Tbl; my %STbl; my $Devs = {}; if(-e $ShowDir."/devices") { foreach my $L (split(/\s*\n\s*/, readFile($ShowDir."/devices"))) { my @Info = split(/;/, $L); my %Dev = ( "ID" => $Info[0], "Class" => $Info[1], "Status" => $Info[2], "Type" => $Info[3], "Driver" => $Info[4], "Vendor" => $Info[5], "Device" => $Info[6], "SVendor" => $Info[7], "SDevice" => $Info[8] ); my $ID = $Dev{"ID"}; $Dev{"Bus"} = getDeviceBus($ID); if(not defined $Devs->{$ID}) { $Devs->{$ID} = \%Dev; } if(not defined $Devs->{$ID}{"Count"}) { $Devs->{$ID}{"Count"} = 0; } $Devs->{$ID}{"Count"} += 1; } } elsif(-e $ShowDir."/devices.json" and $USE_JSON_XS) { require Encode; $Devs = JSON::XS::decode_json(Encode::encode_utf8(readFile($ShowDir."/devices.json"))); } foreach my $ID (keys(%{$Devs})) { $Devs->{$ID}{"ID"} = $ID; if(not defined $TypeOrder{$Devs->{$ID}->{"Type"}}) { $TypeOrder{$Devs->{$ID}->{"Type"}} = "Z"; } if(not defined $BusOrder{$Devs->{$ID}->{"Bus"}}) { $TypeOrder{$Devs->{$ID}->{"Bus"}} = "Z"; } } my @AllCols = ("Bus", "ID", "Class", "Vendor", "Device", "Type", "Driver", "Status"); my @ShortCols = ("Bus", "ID", "Vendor", "Device", "Type"); if(isBSD($^O)) { @ShortCols = ("Bus", "Vendor", "Device", "Type"); } foreach my $ID (sort {$BusOrder{$Devs->{$a}{"Bus"}} cmp $BusOrder{$Devs->{$b}{"Bus"}}} sort {$TypeOrder{$Devs->{$a}{"Type"}} cmp $TypeOrder{$Devs->{$b}{"Type"}}} sort {$Devs->{$a}{"Bus"} cmp $Devs->{$b}{"Bus"}} sort {$Devs->{$a}{"Type"} cmp $Devs->{$b}{"Type"}} sort keys(%{$Devs})) { my $Dev = $Devs->{$ID}; foreach my $Attr (@AllCols) { if(not defined $Dev->{$Attr}) { $Dev->{$Attr} = undef; } } foreach my $Attr (keys(%{$Dev})) { if(not defined $Tbl{$Attr}) { $Tbl{$Attr} = []; } my $Val = $Dev->{$Attr}; if($Attr eq "ID") { if(index($Val, "-serial-")!=-1) { $Val=~s/\-serial\-(.+?)\Z//; } } if($Opt{"Compact"}) { if($Attr eq "ID") { $Val=~s/\A(\w+:)//g; $Val = shortStr($Val, 19); } elsif($Attr eq "Vendor") { $Val = shortStr($Val, 16); } elsif($Attr eq "Device") { $Val = shortStr($Val, 35); } elsif($Attr eq "Type") { $Val = shortStr($Val, 12); } elsif($Attr eq "Driver") { $Val = shortStr($Val, 10); } } push(@{$Tbl{$Attr}}, $Val); if($Dev->{"Count"}>1) { foreach (2 .. $Dev->{"Count"}) { push(@{$Tbl{$Attr}}, $Val); } } } } foreach my $L (split(/\s*\n\s*/, readFile($ShowDir."/host"))) { if($L=~/(\w+):(.*)/) { my ($Attr, $Val) = ($1, $2); if($Opt{"Compact"}) { if($Attr eq "id") { $Val = shortStr($Val, 25); } } $STbl{$Attr} = $Val; } } print "\n"; if($Opt{"Show"} or $Opt{"ShowHost"}) { print "Host Info\n"; print "=========\n\n"; foreach my $Attr ("system", "arch", "kernel", "vendor", "model", "year", "type", "hwaddr", "id") { if($STbl{$Attr}) { print ucfirst($Attr).": "; print " " x (length("kernel")-length($Attr)); print $STbl{$Attr}."\n"; } } print "\n\n"; } if($Opt{"ShowDevices"}) { my $Rows = $#{$Tbl{"ID"}}; my $DevsTitle = "Devices (".($Rows + 1).")"; print $DevsTitle."\n"; print "=" x (length($DevsTitle)); print "\n\n"; if(defined $Opt{"Verbose"}) { showTable(\%Tbl, $Rows, @AllCols); } else { showTable(\%Tbl, $Rows, @ShortCols); } print "\n"; } } sub getDeviceBus($) { if($_[0]=~/\A([^:]+)\:/) { my $Bus = uc($1); if($Bus=~/BAT|BIOS|BOARD|CPU|MEM|FLOPPY/) { $Bus = "SYS"; } return $Bus; } return undef; } sub shortStr($$) { my ($Str, $Len) = @_; if(length($Str)>$Len) { return substr($Str, 0, $Len-3)."..."; } return $Str; } sub showTable(@) { my ($Tbl, $Num, @Columns) = @_; my %Max; foreach my $Col (sort keys(%{$Tbl})) { if(not defined $Max{$Col}) { $Max{$Col} = 0; } foreach my $El (@{$Tbl->{$Col}}) { if(length($El) > $Max{$Col}) { $Max{$Col} = length($El); } } } my $Br = ""; my $Hd = ""; foreach my $Col (@Columns) { my $Hd_T = $Col; if(not $Hd) { $Hd_T = "| ".$Hd_T; } $Hd .= "| ".$Col; $Hd .= mulCh(" ", $Max{$Col} - length($Col) + 1); $Br .= "+"; $Br .= mulCh("-", $Max{$Col} + 2); } $Br .= "+"; $Hd .= "|"; print $Br."\n"; print $Hd."\n"; print $Br."\n"; foreach my $Row (0 .. $Num) { foreach my $Col (@Columns) { my $El = $Tbl->{$Col}[$Row]; print "| ".$El; print alignStr($El, $Max{$Col} + 1); } print "|\n"; } print $Br."\n"; } sub mulCh($$) { my $Str = ""; foreach (1 .. $_[1]) { $Str .= $_[0]; } return $Str; } sub alignStr($$) { my $Align = ""; foreach (1 .. ($_[1] - length($_[0]))) { $Align .= " "; } return $Align; } sub checkGraphics() { print "Check graphics ... "; my $Glxgears = getGears(); listProbe("tests", "glxgears"); my $Out_I = runCmd("vblank_mode=0 $Glxgears"); $Out_I=~s/(\d+ frames)/\n$1/; $Out_I=~s/GL_EXTENSIONS =.*?\n//; writeLog($TEST_DIR."/glxgears", clearLog_X11($Out_I)); my $Out_D = undef; if(grep {defined $WorkMod{$_}} @G_DRIVERS_INTEL and $Sys{"Type"}=~/$MOBILE_TYPE/) { # Hybrid graphics if(defined $KernMod{"nvidia"}) { # check NVidia Optimus with proprietary driver if(checkCmd("optirun")) { listProbe("tests", "glxgears (Nvidia)"); $Out_D = runCmd("optirun $Glxgears"); } } elsif(defined $KernMod{"nouveau"}) { # check NVidia Optimus with free driver listProbe("tests", "glxgears (Nouveau)"); system("xrandr --setprovideroffloadsink 1 0"); # nouveau Intel if($?) { printMsg("ERROR", "failed to run glxgears test on discrete card"); } else { $Out_D = runCmd("DRI_PRIME=1 vblank_mode=0 $Glxgears"); } } elsif(defined $KernMod{"radeon"} or defined $KernMod{"amdgpu"}) { # check Radeon Hybrid graphics with free driver listProbe("tests", "glxgears (Radeon)"); $Out_D = runCmd("DRI_PRIME=1 vblank_mode=0 $Glxgears"); } } if($Out_D) { $Out_D=~s/(\d+ frames)/\n$1/; $Out_D=~s/GL_EXTENSIONS =.*?\n//; writeLog($TEST_DIR."/glxgears_discrete", clearLog_X11($Out_D)); } checkGraphicsCardOutput($Out_I, $Out_D); print "Ok\n"; } sub checkGraphicsCardOutput($$) { my ($Int, $Discrete) = @_; my $Success = "frames in"; if(grep {defined $WorkMod{$_}} @G_DRIVERS_INTEL) { if(defined $WorkMod{"nvidia"}) { if($Discrete=~/$Success/) { setCardStatus("nvidia", "works"); } } elsif(defined $WorkMod{"nouveau"}) { if($Discrete=~/$Success/ and $Discrete=~/GL_VENDOR.+nouveau/i) { setCardStatus("nouveau", "works"); } } elsif(defined $WorkMod{"radeon"} or defined $WorkMod{"amdgpu"}) { if($Discrete=~/$Success/) { setCardStatus("radeon", "works"); } } if($Int=~/$Success/ and $Int=~/GL_VENDOR.+Intel/i) { foreach (@G_DRIVERS_INTEL) { setCardStatus($_, "works"); } } } elsif(defined $WorkMod{"nouveau"} or defined $WorkMod{"nvidia"}) { if($Int=~/$Success/) { setCardStatus("nouveau", "works"); } } elsif(defined $WorkMod{"radeon"} or defined $WorkMod{"amdgpu"} or defined $WorkMod{"fglrx"}) { if($Int=~/$Success/) { setCardStatus("radeon", "works"); } } } sub setCardStatus($$) { my ($Dr, $Status) = @_; my $V = $DriverVendor{$Dr}; if(defined $GraphicsCards{$V}) { foreach my $ID (sort keys(%{$GraphicsCards{$V}})) { if($GraphicsCards{$V}{$ID} eq $Dr or $Status eq "detected") { $HW{$ID}{"Status"} = $Status; if($Status eq "works") { setAttachedStatus($ID, $Status); } } } } } sub setCardStatusByVendor($$$) { my ($V, $Status, $Driver) = @_; if(defined $GraphicsCards{$V}) { foreach my $ID (sort keys(%{$GraphicsCards{$V}})) { $HW{$ID}{"Status"} = $Status; if($Status eq "works") { setAttachedStatus($ID, $Status); } if($Driver and $HW{$ID}{"Driver"}=~/vgapci/) { $HW{$ID}{"Driver"} = $Driver; } } } } sub checkHW() { # TODO: test operability, set status to "works", "malfunc" or "failed" if($Opt{"CheckGraphics"} and checkCmd("glxgears")) { if(defined $ENV{"WAYLAND_DISPLAY"} or $ENV{"XDG_SESSION_TYPE"} eq "wayland" or defined $ENV{"DISPLAY"}) { checkGraphics(); } } if($Opt{"CheckMemory"} and checkCmd("memtester")) { print "Check memory ... "; my $Memtester = runCmd("memtester 8 1"); $Memtester=~s/\A(.|\n)*(Loop)/$2/g; while($Memtester=~s/[^\cH]\cH//g) {} writeLog($TEST_DIR."/memtester", $Memtester); print "Ok\n"; } if($Opt{"CheckHdd"}) { my $CheckHddCmd = undef; if(isBSD()) { if(enabledLog("diskinfo") and checkCmd("diskinfo")) { $CheckHddCmd = "diskinfo -c"; } } elsif(checkCmd("hdparm")) { $CheckHddCmd = "hdparm -t"; } if($CheckHddCmd) { print "Check HDDs ... "; my $HDD_Read = ""; my $HDD_Num = 0; foreach my $Dr (sort keys(%HDD)) { my $DrFile = basename($Dr); my $Title = ""; if(my $DevId = $HDD{$Dr}) { my $HddInfo = $HW{$DevId}; $Title = $HddInfo->{"Vendor"}." ".$HddInfo->{"Device"}; } elsif(defined $HDD_Info{$DrFile}) { $Title = $HDD_Info{$DrFile}{"Title"}; } my $Cmd = $CheckHddCmd." ".$Dr; my $Out = runCmd($Cmd); $Out=~s/\A\n\Q$Dr\E\:?\n//; $Out=~s/.+#.+\n//g; $HDD_Read .= "Drive $Title\n"; $HDD_Read .= "$Cmd\n"; $HDD_Read .= $Out."\n"; if(defined $Opt{"LimitCheckHdd"} and $HDD_Num++>=$Opt{"LimitCheckHdd"}) { last; } } if($HDD_Read) { writeLog($TEST_DIR."/hdd_read", $HDD_Read); } print "Ok\n"; } } my $Md5 = "md5sum"; if(isBSD()) { $Md5 = "md5"; } if(my @CPUs = grep { /\Acpu:/ } keys(%HW)) { my $CPU_Info = $HW{$CPUs[0]}; my $CPU_Title = $CPU_Info->{"Vendor"}." ".$CPU_Info->{"Device"}; if($Opt{"CheckCpu"} and checkCmd("dd") and checkCmd($Md5)) { print "Check CPU ... "; runCmd("dd if=/dev/zero bs=1M count=512 2>$TMP_DIR/cpu_perf | $Md5"); my $CPUPerf = $CPU_Title."\n"; $CPUPerf .= "dd if=/dev/zero bs=1M count=512 | $Md5\n"; $CPUPerf .= readFile("$TMP_DIR/cpu_perf"); writeLog($TEST_DIR."/cpu_perf", $CPUPerf); print "Ok\n"; if($CPUPerf=~/copied,.+, (.+?)\Z/) { $HW{$CPUs[0]}{"Rate"} = $1; } } if($Opt{"Check7z"} and checkCmd("7z")) { # 7z from p7zip-plugins on Fedora print "Run 7z benchmark ... "; my $SevenZBench = runCmd("7z b | grep -v Copyright"); writeLog($TEST_DIR."/7z_b", $SevenZBench); print "Ok\n"; } } } sub getGears() { return "glxgears -info 2>/dev/null & sleep 17 ; killall glxgears 2>/dev/null"; } sub listProbe($$) { if($Opt{"ListProbes"}) { print $_[0]."/".$_[1]."\n"; } } my %EnabledLog = ( "minimal" => [ "acpidump", "acpidump_decoded", "arcconf", "biosdecode", "boltctl", "cpuinfo", "dev", "df", "dmesg", "dmesg.1", "dmidecode", "dmi_id", "edac-util", "edid", "edid-decode", "ethtool_p", "glxinfo", "grub", "hciconfig", "hdparm", "hwinfo", "ifconfig", "ip_addr", "lsb_release", "lsb-release", "lsblk", "lscpu", "lsmod", "lspci", "lspci_all", "lsusb", "mcelog", "megacli", "megactl", "mmcli", "opensc-tool", "os-release", "power_supply", "sensors", "smartctl", "smartctl_megaraid", "system-release", "upower", "usb-devices", "xorg.log", "xorg.log.1", "xrandr", "xrandr_providers" ], "default" => [ "acpi", "amdconfig", "amixer", "apk", "aplay", "arecord", "boot.log", "boot_efi", "cpuid", "cpupower", "dkms_status", "dpkg", "drm_info", "efibootmgr", "efivar", "fdisk", "fglrxinfo", "getprop", "gpu-manager.log", "grub.cfg", "hp-probe", "i2cdetect", "input_devices", "interrupts", "inxi", "ioports", "iostat", "iw_list", "iwconfig", "lspnp", "modprobe.d", "nm-tool", "nmcli", "nvidia-smi", "pkglist", "rfkill", "rpms", "sane-find-scanner", "scanimage", "scsi", "smart-log", "systemd-analyze", "uptime", "vainfo", "vdpauinfo", "vgaswitcheroo", "vulkaninfo", "xdpyinfo", "xinput", "xorg.conf" ], "maximal" => [ "alsactl", "cups_access_log", "cups_error_log", "ddcutil", "ddc", "findmnt", "firmware", "fstab", "hcitool_scan", "hddtemp", "iw_scan", "modinfo", "mount", "vmstat", "neofetch", "numactl", "route", "slabtop", "systemctl", "udev-db", "update-alternatives", "xvinfo" ], "optional" => [ "avahi", "lsinitrd", "pstree", "top" ] ); my %EnabledLog_BSD = ( "minimal" => [ "apm", "arcconf", "atactl", "biosdecode", "curl", "dev", "devinfo", "df", "dmesg", "dmidecode", "fdisk", "geom", "glxinfo", "gpart", "gpart_list", "hwstat", "ifconfig", "kldstat", "locale", "lscpu", "mcelog", "megacli", "mfiutil", "modstat", "pciconf", "pcictl", "pcictl_n", "pcidump", "pkglist", "shasum", "smartctl", "smartctl_megaraid", "sndstat", "sysctl", "usbconfig", "usbctl", "usbdevs", "xorg.log", "xrandr" ], "default" => [ "acpidump", "acpidump_decoded", "amixer", "aplay", "arecord", "camcontrol", "config", "cpuid", "diskinfo", "disklabel", "drm_info", "efibootmgr", "efivar", "iostat", "kldstat_v", "lsblk", "lspci", "lspci_all", "lsusb", "neofetch", "ofwdump", "sane-find-scanner", "scanimage", "smart-log", "top_head", "uptime", "vmstat", "x86info", "xinput", "xorg.conf", "xorg.log.1", "xrandr_providers" ], "maximal" => [ "alsactl", "firmware", "fstab", "loader.conf", "mount", "rc.conf", "sysinfo" ], "optional" => [ "pstree" ] ); sub completeEnabledLogs() { foreach my $LL ("minimal", "default") { foreach my $L (@{$EnabledLog{$LL}}) { push(@{$EnabledLog{"maximal"}}, $L); } } foreach my $L (@{$EnabledLog{"minimal"}}) { push(@{$EnabledLog{"default"}}, $L); } } sub enabledLog($) { my $Name = $_[0]; if(defined $Opt{"Disable"}) { if(grep { $_ eq $Name } split(/,/, $Opt{"Disable"})) { return 0; } } if(defined $Opt{"Enable"}) { if(grep { $_ eq $Name } split(/,/, $Opt{"Enable"})) { return 1; } } if(grep { $_ eq $Name } @{$EnabledLog{$Opt{"LogLevel"}}}) { return 1; } if(defined $Sys{"System"} and $Sys{"System"}=~/ROSA/i) { if($Opt{"LogLevel"} eq "default") { if($Name eq "fstab") { return 1; } } } return 0; } sub getMaxLogSize($) { my $Log = $_[0]; my $MaxSize = 2*$MAX_LOG_SIZE; if(grep {$Log eq $_} @LARGE_LOGS) { $MaxSize = $MAX_LOG_SIZE; } if($Log eq "boot.log") { $MaxSize = $MAX_LOG_SIZE/8; } return $MaxSize; } sub writeLog($$) { my ($Path, $Content) = @_; my $Log = basename($Path); if(not grep {$Log eq $_} @ProtectedLogs) { my $MaxSize = getMaxLogSize($Log); if(length($Content)>$MaxSize) { if($Log eq "boot.log") { # Save end of log $Content = substr($Content, -$MaxSize+4); $Content=~s/\A.*?\n//; $Content = "...\n".$Content; } else { $Content = substr($Content, 0, $MaxSize-3)."..."; } } } writeFile($Path, $Content); } sub appendFile($$) { my ($Path, $Content) = @_; if(my $Dir = dirname($Path)) { mkpath($Dir); } open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n"); print FILE $Content; close(FILE); } sub writeFile($$) { my ($Path, $Content) = @_; return if(not $Path); if(my $Dir = dirname($Path)) { mkpath($Dir); } open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); print FILE $Content; close(FILE); } sub readPciIds_A($$) { my ($Path, $Info) = @_; readPciIds($Path, $Info); if(-e "$Path.add") { readPciIds("$Path.add", $Info); } } sub readUsbIds_A($$) { my ($Path, $Info) = @_; readUsbIds($Path, $Info); if(-e "$Path.add") { readUsbIds("$Path.add", $Info); } } sub readPciIds($$) { my $List = readFile($_[0]); my $Info = $_[1]; my ($V, $D, $SV, $SD, $C, $SC, $SSC) = (); foreach (split(/\n/, $List)) { if(/\A(\t*)([a-f\d]{4}) /) { my $L = length($1); if($L==0) { $V = $2; if(/[a-f\d]{4}\s+(.*?)\Z/) { $Info->{"V"}{$V} = $1; } } elsif($L==1) { $D = $2; if(/\t[a-f\d]{4}\s+(.*?)\Z/) { $Info->{"I"}{$V}{$D} = $1; } } elsif($L==2) { if(/\t([a-f\d]{4}) ([a-f\d]{4})\s+(.*?)\Z/) { $SV = $1; $SD = $2; $Info->{"D"}{$V}{$D}{$SV}{$SD} = $3; } } } elsif(/\AC ([a-f\d]{2}) (.+)/) { $C = $1; $Info->{"C"}{$C} = $2; $Info->{"C"}{$C."00"} = $2; } elsif(/\A(\t+)([a-f\d]{2}) (.+)/) { my $L = length($1); if($L==1) { $SC = $2; $Info->{"C"}{$C.$SC} = $3; } elsif($L==2) { $SSC = $2; $Info->{"C"}{$C.$SC.$SSC} = $3; } } } } sub readUsbIds($$) { my $List = readFile($_[0]); my $Info = $_[1]; my ($V, $D) = (); foreach (split(/\n/, $List)) { if(/\A(\t*)([a-f\d]{4}) /) { my $L = length($1); if($L==0) { $V = $2; if(/[a-f\d]{4}\s+(.*?)\Z/) { $UsbVendor{$V} = $1; } } elsif($L==1) { $D = $2; if(/\t[a-f\d]{4}\s+(.*?)\Z/) { $Info->{$V}{$D} = $1; } } } } } sub readSdioIds_Sys() { readSdioIds("/usr/share/hwdata/sdio.ids", \%SdioInfo, \%SdioVendor); } sub readSdioIds($$$) { my ($Path, $Info, $Vnds) = @_; if(not -e $Path) { return; } my $List = readFile($Path); my ($V, $D); foreach (split(/\n/, $List)) { if(/\A(\t*)(\w{4}) /) { my $L = length($1); if($L==0) { $V = $2; if(/\A\w{4}\s+(.*?)\Z/) { $Vnds->{$V} = $1; } } elsif($L==1) { $D = $2; if(/\t\w{4}\s+(.*?)\Z/) { $Info->{$V}{$D} = $1; } } } } } sub getTimeStamp($) { my $Date = localtime($_[0]); if($Date=~/\w+\s+\w+\s+\d+\s+(\d+:\d+):\d+\s+\d+/) { return $1; } return $Date; } sub getYear($) { my $Date = localtime($_[0]); if($Date=~/ (\d+)\Z/) { return $1; } return undef; } sub setPublic(@) { my $Path = shift(@_); my $R = undef; if(@_) { $R = shift(@_); } if(not checkCmd("chmod")) { return; } my @Chmod = ("chmod", "777"); if($R) { push(@Chmod, $R); } push(@Chmod, $Path); system(@Chmod); if(not $Opt{"Snap"} and not $Opt{"Flatpak"}) { if(my $SessUser = getUser()) { my @Chown = ("chown", $SessUser.":".$SessUser); if($R) { push(@Chown, $R); } push(@Chown, $Path); system(@Chown); } } } sub fixLsUsb($) { my $Content = $_[0]; my @Content_New = (); foreach my $Block (split(/\n\n/, $Content)) { if($Block=~/: ID (\w{4}):(\w{4})/) { my ($V, $D) = ($1, $2); if(defined $UsbVendor{$V}) { my $Vendor = $UsbVendor{$V}; if(defined $UsbInfo{$V}{$D}) { my $Product = $UsbInfo{$V}{$D}; $Block=~s{(:\s+ID\s+$V:$D)[ ]+\n}{$1 $Vendor $Product\n}; $Block=~s{(idVendor\s+0x$V)[ ]+\n}{$1 $Vendor\n}; $Block=~s{(idProduct\s+0x$D)[ ]+\n}{$1 $Product\n}; } } } push(@Content_New, $Block); } return join("\n\n", @Content_New); } sub fixLsPci_All($) { my $Content = $_[0]; my @Content_New = (); foreach my $Block (split(/\n\n/, $Content)) { if($Block=~/ Class \[([a-f\d]+)\]: Device \[(\w{4}):(\w{4})\]/) { my ($C, $V, $D) = ($1, $2, $3); if(defined $PciInfo{"V"}{$V}) { my $Vendor = $PciInfo{"V"}{$V}; if(defined $PciInfo{"I"}{$V}{$D}) { my $Product = $PciInfo{"I"}{$V}{$D}; $Block=~s{(\]:) Device (\[$V:$D\])}{$1 $Vendor $Product $2}; if($Block=~/Subsystem: Device \[(\w{4}):(\w{4})\]/) { my ($SV, $SD) = ($1, $2); if(my $SubVendor = $PciInfo{"V"}{$SV}) { my $Subsystem = "Device"; if(defined $PciInfo{"D"}{$V}{$D}{$SV}{$SD}) { $Subsystem = $PciInfo{"D"}{$V}{$D}{$SV}{$SD}; } elsif($V eq $SV and $D eq $SD) { $Subsystem = $Product; } $Block=~s{(Subsystem:) Device (\[$SV:$SD\])}{$1 $SubVendor $Subsystem $2}; } } } } if(defined $PciInfo{"C"}{$C}) { my $Class = $PciInfo{"C"}{$C}; $Block=~s{ Class (\[$C\]:)}{ $Class $1}; } } push(@Content_New, $Block); } return join("\n\n", @Content_New); } sub fixLsPci($) { my $Content = $_[0]; my @Content_New = (); my %Dev = (); foreach my $Block (split(/\n\n/, $Content)) { foreach my $Attr ("Class", "Vendor", "Device", "SVendor", "SDevice") { if($Block=~/$Attr:.*\[([a-f\d]{4})\]/) { $Dev{$Attr} = $1; } } my $C = $Dev{"Class"}; if(defined $PciInfo{"C"}{$C}) { my $Class = $PciInfo{"C"}{$C}; $Block=~s{(Class:\s+)Class(\s+\[$C\])}{$1$Class$2}; } my ($V, $D) = ($Dev{"Vendor"}, $Dev{"Device"}); if(defined $PciInfo{"V"}{$V}) { my $Vendor = $PciInfo{"V"}{$V}; $Block=~s{(Vendor:\s+)Vendor(\s+\[$V\])}{$1$Vendor$2}; if(defined $PciInfo{"I"}{$V}{$D}) { my $Product = $PciInfo{"I"}{$V}{$D}; $Block=~s{(Device:\s+)Device(\s+\[$D\])}{$1$Product$2}; my ($SV, $SD) = ($Dev{"SVendor"}, $Dev{"SDevice"}); if(my $SubVendor = $PciInfo{"V"}{$SV}) { $Block=~s{(SVendor:\s+)Unknown vendor(\s+\[$SV\])}{$1$SubVendor$2}; my $Subsystem = undef; if(defined $PciInfo{"D"}{$V}{$D}{$SV}{$SD}) { $Subsystem = $PciInfo{"D"}{$V}{$D}{$SV}{$SD}; } elsif($V eq $SV and $D eq $SD) { $Subsystem = $Product; } if($Subsystem) { $Block=~s{(SDevice:\s+)Device(\s+\[$SD\])}{$1$Subsystem$2}; } } } } push(@Content_New, $Block); } return join("\n\n", @Content_New); } sub fixLogs($) { my $Dir = $_[0]; if(-f "$Dir/hwinfo" and -s "$Dir/hwinfo" < 2*$EMPTY_LOG_SIZE) { # Support for HW Probe 1.4 if(readFile("$Dir/hwinfo")=~/unrecognized arguments|error while loading shared libraries/) { # hwinfo: error: unrecognized arguments: --all # hwinfo: error while loading shared libraries: libhd.so.21: cannot open shared object file: No such file or directory writeFile("$Dir/hwinfo", ""); } } foreach my $L ("iostat", "systemd-analyze", "systemctl", "disklabel") { # Support for HW Probe 1.3-1.4 # iostat: command not found if(-f "$Dir/$L" and -s "$Dir/$L" < 50) { unlink($Dir."/$L"); } } foreach my $L ("glxinfo", "xdpyinfo", "xinput", "vdpauinfo", "xrandr") { if(-e "$Dir/$L" and -s "$Dir/$L" < $EMPTY_LOG_SIZE) { if(not clearLog_X11(readFile("$Dir/$L"))) { writeFile("$Dir/$L", ""); } } } if(-f "$Dir/vulkaninfo") { # Support for HW Probe 1.3 if(readFile("$Dir/vulkaninfo")=~/Cannot create/i) { unlink("$Dir/vulkaninfo"); } } if(-f "$Dir/vainfo" and -s "$Dir/vainfo" < $EMPTY_LOG_SIZE) { # Support for HW Probe 1.4 # error: failed to initialize display if(readFile("$Dir/vainfo")=~/failed to initialize/) { writeFile("$Dir/vainfo", ""); } } if(-f "$Dir/cpupower" and -s "$Dir/cpupower" < $EMPTY_LOG_SIZE) { # Support for HW Probe 1.3 if(readFile("$Dir/cpupower")=~/cpupower not found/) { unlink("$Dir/cpupower"); } } if(-f "$Dir/rfkill" and -s "$Dir/rfkill") { # Support for HW Probe 1.4 # Can't open RFKILL control device: No such file or directory if(readFile("$Dir/rfkill")=~/No such file or directory|Bad file descriptor/) { writeFile("$Dir/rfkill", ""); } } foreach my $L ("aplay", "arecord") { if(-e "$Dir/$L" and -s "$Dir/$L" < 50) { if(readFile("$Dir/$L")=~/command not found/) { writeFile("$Dir/$L", ""); } } } foreach my $L ("lsusb", "usb-devices", "lspci", "lspci_all", "dmidecode", "dmesg", "megacli") { if(-f "$Dir/$L" and -s "$Dir/$L" < 2*$EMPTY_LOG_SIZE) { # Support for HW Probe 1.4 # sh: XXX: command not found # pcilib: Cannot open /proc/bus/pci # lspci: Cannot find any working access method. # lsusb: error while loading shared libraries: libusb-1.0.so.0: cannot open shared object file: No such file or directory # ERROR: ld.so: object '/usr/lib/arm-linux-gnueabihf/libarmmem.so' from /etc/ld.so.preload cannot be preloaded # /dev/mem: Permission denied # dmidecode: command not found # No SMBIOS nor DMI entry point found # dmesg: read kernel buffer failed: Operation not permitted writeFile("$Dir/$L", ""); } } foreach my $L ("pstree", "findmnt", "fdisk", "df") { # Support for HW Probe 1.4 if(-f "$Dir/$L" and -s "$Dir/$L" < $EMPTY_LOG_SIZE) { # sh: XXX: command not found # lsblk: Permission denied writeFile("$Dir/$L", ""); } } foreach my $L ("efibootmgr", "lsblk") { # Support for HW Probe 1.4 if(-f "$Dir/$L" and -s "$Dir/$L" < $EMPTY_LOG_SIZE/2) { writeFile("$Dir/$L", ""); } } foreach my $L ("lsusb", "usb-devices", "lspci", "lspci_all", "dmidecode", "hwinfo") { # Support for old probes if(not -e "$Dir/$L") { next; } my $Content = readFile("$Dir/$L"); if($L eq "lsusb") { if(index($Content, "Resource temporarily unavailable")!=-1) { $Content=~s/can't get device qualifier: Resource temporarily unavailable\n//g; $Content=~s/cannot read device status, Resource temporarily unavailable \(11\)\n//g; $Content=~s/cannot read port 1 status, Resource temporarily unavailable \(11\)\n//g; $Content=~s/can't get debug descriptor: Resource temporarily unavailable\n//g; $Content=~s/can't get hub descriptor, LIBUSB_ERROR_(IO|PIPE) \(Resource temporarily unavailable\)\n//g; writeFile("$Dir/$L", $Content); } if(index($Content, "some information will be missing")!=-1) { $Content=~s/Couldn't open device, some information will be missing\n//g; $Content=~s/Couldn't get configuration descriptor 0, some information will be missing\n//g; writeFile("$Dir/$L", $Content); } if($Opt{"UsbIDs"}) { if(index($Content, "HW_PROBE_USB_")!=-1 or $Content=~/: ID [a-f\d]{4}:[a-f\d]{4} \n/) { $Content=~s{lsusb: cannot open "/tmp/HW_PROBE_USB_", Permission denied\n\n}{}g; if($Opt{"UsbIDs"}) { $Content = fixLsUsb($Content); } writeFile("$Dir/$L", $Content); } } } elsif($L eq "lspci" or $L eq "lspci_all") { if(index($Content, "lspci: Unable to load libkmod resources: error")!=-1) { $Content=~s/lspci: Unable to load libkmod resources: error -\d+\n//g; writeFile("$Dir/$L", $Content); } if(index($Content, "pcilib: sysfs_read_vpd: read failed: Input/output error")!=-1) { $Content=~s/pcilib: sysfs_read_vpd: read failed: Input\/output error\n//g; writeFile("$Dir/$L", $Content); } if($Opt{"PciIDs"}) { if($L eq "lspci" and index($Content, "Class:\tClass [")!=-1) { writeFile("$Dir/$L", fixLsPci($Content)); } if($L eq "lspci_all" and index($Content, "]: Device [")!=-1) { writeFile("$Dir/$L", fixLsPci_All($Content)); } } } elsif($L eq "dmidecode") { if(index($Content, "Table is unreachable, sorry")!=-1) { $Content=~s{/dev/mem: Bad address\nTable is unreachable, sorry.\n}{}g; $Content=~s{/dev/mem: lseek: Value too large for defined data type\nTable is unreachable, sorry.\n}{}g; writeFile("$Dir/$L", $Content); } } elsif($L eq "hwinfo") { if(index($Content, "sh: /dev/null: Permission denied")!=-1) { $Content=~s{sh: /dev/null: Permission denied\n}{}g; writeFile("$Dir/$L", $Content); } } if(index($Content, "ERROR: ld.so:")!=-1) { # ERROR: ld.so: object '/usr/lib/arm-linux-gnueabihf/libarmmem.so' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored. # ERROR: ld.so: object 'libesets_pac.so' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored. $Content=~s/ERROR: ld\.so:.+?: ignored\.\n//g; writeFile("$Dir/$L", $Content); } } if(-e "$Dir/inxi") { # Support for HW Probe 1.4 if(readFile("$Dir/inxi")=~/Unsupported option|Use of uninitialized value/) { unlink("$Dir/inxi"); } } if(-e "$Dir/storcli") { # Support for HW Probe 1.4 if(index(readFile("$Dir/storcli"), "unexpected TOKEN_SLASH")!=-1) { writeFile("$Dir/storcli", ""); } } foreach my $L (@LARGE_LOGS) { if(-s "$FixProbe_Logs/$L" > getMaxLogSize($L)) { if(my $Content = readFile("$FixProbe_Logs/$L")) { writeLog("$FixProbe_Logs/$L", $Content); } } } if(-e "$Dir/modinfo") { # Support for HW Probe 1.4 if(my $Content = readFile("$Dir/modinfo")) { if(index($Content, "signature: ")!=-1) { if($Content=~s/:*\n\s+[A-F\d]{2}\:.+//g) { writeFile("$Dir/modinfo", $Content); } } } } if($Sys{"Probe_ver"} eq "1.4" or not $Sys{"Probe_ver"}) { # HW Probe <= 1.4 if(-e "$FixProbe_Logs/boot.log" and my $Content = readFile("$FixProbe_Logs/boot.log")) { writeLog("$FixProbe_Logs/boot.log", clearLog($Content)); } if(-e "$FixProbe_Logs/rpms" and my $Content = readFile("$FixProbe_Logs/rpms")) { my @Rpms = sort { "\L$a" cmp "\L$b" } split(/\n/, $Content); writeLog("$FixProbe_Logs/rpms", join("\n", @Rpms)); } if(-f "$Dir/mcelog") { if(readFile("$Dir/mcelog")=~/No such file or directory/) { writeFile("$Dir/mcelog", ""); } } } } sub createIDsLink($) { my $Type = $_[0]; my $Type_U = uc($Type); my $Link = "/tmp/HW_PROBE_".$Type_U."_"; if($Opt{"Flatpak"}) { $Link = "/var/tmp/P_".$Type_U; } if(-e $Link) { return undef; } if($Opt{"Snap"}) { if(my $SNAP_Dir = $ENV{"SNAP"}) { symlink("$SNAP_Dir/usr/share/$Type.ids", $Link); return $Link; } } elsif($Opt{"Flatpak"}) { symlink("/app/share/$Type.ids", $Link); return $Link; } return undef; } sub makeProbe() { probeSys(); probeDmi(); probeHWaddr(); probeHW(); if(keys(%ExtraConnection)) { fixHWaddr(); } if($Opt{"Logs"}) { writeLogs(); } if($Opt{"Check"}) { checkHW(); } if($USE_JSON_XS) { writeDevsDump(); } else { writeDevs(); } writeHost(); } sub fixByPkgs($) { my $Subj = $_[0]; if(isBSD()) { push(@DE_Package, ["awesome", "Awesome"]); push(@DE_Package, ["blackbox", "Blackbox"]); push(@DE_Package, ["evilwm", "evilwm"]); push(@DE_Package, ["fluxbox", "Fluxbox"]); push(@DE_Package, ["fvwm2", "fvwm2"]); push(@DE_Package, ["i3", "i3"]); push(@DE_Package, ["icewm", "IceWM"]); push(@DE_Package, ["jwm", "JWM"]); push(@DE_Package, ["menumaker", "MenuMaker"]); push(@DE_Package, ["openbox", "Openbox"]); push(@DE_Package, ["pekwm", "PekWM"]); push(@DE_Package, ["ratpoison", "Ratpoison"]); push(@DE_Package, ["sawfish", "Sawfish"]); push(@DE_Package, ["spectrwm", "spectrwm"]); push(@DE_Package, ["windowmaker", "Window Maker"]); push(@DE_Package, ["wm2", "wm2"]); push(@DE_Package, ["xmonad", "xmonad"]); } foreach my $PkgsFile ("rpms", "debs", "pkglist") { my $PkgsPath = "$FixProbe_Logs/$PkgsFile"; if(-e $PkgsPath) { my $Pkgs = readFile($PkgsPath); my @CheckPkgs = (); if($Subj eq "DE") { @CheckPkgs = @DE_Package; } elsif($Subj eq "DisplayServer") { @CheckPkgs = @DisplayServer_Package; } elsif($Subj eq "DisplayManager") { @CheckPkgs = @DisplayManager_Package; } foreach my $CPkg (@CheckPkgs) { my $P = $CPkg->[0]; if(index($Pkgs, $P)==-1) { next; } if($Pkgs=~/(\A|\s)\Q$P\E\b/) { return $CPkg->[1]; } if(isBSD()) { if($Pkgs=~/ .+\/\Q$P\E\b/) { return $CPkg->[1]; } } } } } return undef; } sub initDataDir($) { my $Dir = $_[0]; return ($Dir, $Dir."/logs", $Dir."/tests"); } sub isBSD(@_) { my $OS = undef; if(@_) { $OS = shift(@_); } elsif(not $Opt{"FixProbe"}) { if($^O=~/bsd|dragonfly/) { return 1; } } else { $OS = $Sys{"System"}; } return ($OS=~/bsd|dragonfly|\bting\b/ or $OS=~/$KNOWN_BSD_ALL/); } sub isNetBSD(@_) { my $OS = $Sys{"System"}; if(@_) { $OS = shift(@_); } return $OS=~/netbsd|os108/; } sub isOpenBSD(@_) { my $OS = $Sys{"System"}; if(@_) { $OS = shift(@_); } return $OS=~/openbsd|fuguita|libertybsd/; } sub scenario() { if($Opt{"Help"}) { print $HelpMessage; exitStatus(0); } if($Opt{"DumpVersion"}) { print $TOOL_VERSION."\n"; exitStatus(0); } if($Opt{"ShowVersion"}) { print $ShortUsage; exitStatus(0); } if(checkModule("Digest/SHA.pm")) { $USE_DIGEST = 1; require Digest::SHA; } elsif(not $Opt{"FixProbe"}) { if(checkCmd("sha512sum")) { $USE_DIGEST_ALT = "sha512sum"; } elsif(checkCmd("sha512")) { $USE_DIGEST_ALT = "sha512"; } elsif(checkCmd("openssl") and runCmd("openssl version")!~/0\.9\.[1-7]/) { $USE_DIGEST_ALT = "openssl dgst -sha512"; } elsif(checkCmd("shasum") and runCmd("shasum --version 2>/dev/null")) { $USE_DIGEST_ALT = "shasum -a 512"; } else { printMsg("ERROR", "can't find any utility to compute SHA512"); exitStatus(1); } } if($Opt{"IdentifyDrive"} or $Opt{"IdentifyMonitor"}) { if($Opt{"AppImage"}) { printMsg("ERROR", "this function is not supported by AppImage"); exitStatus(1); } else { if(checkModule("Data/Dumper.pm")) { $USE_DUMPER = 1; require Data::Dumper; $Data::Dumper::Sortkeys = 1; } } } if(checkModule("JSON/XS.pm")) { $USE_JSON_XS = 1; require JSON::XS; } if(my $IA = checkModule("LHW/IntelligentAnalysis.pm", 1)) { $USE_IA = 1; require $IA; } if($Opt{"DecodeACPI"}) { $Opt{"DumpACPI"} = 1; } if(not $Opt{"Compact"}) { $Opt{"Compact"} = 1; } if($Opt{"Show"}) { $Opt{"ShowDevices"} = 1; } if($Opt{"Maximal"}) { $Opt{"LogLevel"} = "maximal"; } if($Opt{"Minimal"}) { $Opt{"LogLevel"} = "minimal"; } if($Opt{"LogLevel"}) { if($Opt{"LogLevel"}=~/\A(min|mini|minimum)\Z/i) { $Opt{"LogLevel"} = "minimal"; } elsif($Opt{"LogLevel"}=~/\A(max|maxi|maximum)\Z/i) { $Opt{"LogLevel"} = "maximal"; } if($Opt{"LogLevel"}!~/\A(minimal|default|maximal)\Z/i) { printMsg("ERROR", "unknown log level '".$Opt{"LogLevel"}."'"); exitStatus(1); } $Opt{"LogLevel"} = lc($Opt{"LogLevel"}); $Opt{"Logs"} = 1; } else { $Opt{"LogLevel"} = "default"; if(not $Opt{"All"} and not $Opt{"Logs"} and $Opt{"Probe"}) { $Opt{"Logs"} = 1; $Opt{"LogLevel"} = "minimal"; } } if(isBSD($^O)) { %EnabledLog = %EnabledLog_BSD; $URL = $URL_BSD; my @Exclude = (); if($^O=~/openbsd|dragonfly/ or isNetBSD($^O)) { push(@Exclude, "loader", "gpart", "gpart_list", "diskinfo", "camcontrol"); } if($^O=~/freebsd|dragonfly/) { push(@Exclude, "disklabel"); } if(@Exclude) { my $Ex = join("|", @Exclude); foreach my $K (keys(%EnabledLog)) { @{$EnabledLog{$K}} = grep {$_!~/$Ex/} @{$EnabledLog{$K}}; } } } completeEnabledLogs(); foreach my $LogStatus ("Enable", "Disable") { if(not defined $Opt{$LogStatus}) { next; } foreach my $L (split(/,/, $Opt{$LogStatus})) { if(not grep { $_ eq $L } @{$EnabledLog{"minimal"}} and not grep { $_ eq $L } @{$EnabledLog{"default"}} and not grep { $_ eq $L } @{$EnabledLog{"maximal"}} and not grep { $_ eq $L } @{$EnabledLog{"optional"}}) { printMsg("ERROR", "logging of \'$L\' cannot be enabled or disabled"); exitStatus(1); } } } if($Opt{"HWInfoPath"}) { if(not -f $Opt{"HWInfoPath"}) { printMsg("ERROR", "can't access file '".$Opt{"HWInfoPath"}."'"); exitStatus(1); } } if($Opt{"IdentifyDrive"} or $Opt{"IdentifyMonitor"}) { if(not $USE_DUMPER) { printMsg("ERROR", "requires perl-Data-Dumper module"); exitStatus(1); } } if($Opt{"IdentifyDrive"}) { if(not -f $Opt{"IdentifyDrive"}) { printMsg("ERROR", "can't access file '".$Opt{"IdentifyDrive"}."'"); exitStatus(1); } if(not -T $Opt{"IdentifyDrive"}) { printMsg("ERROR", "not text file '".$Opt{"IdentifyDrive"}."'"); exitStatus(1); } my $SmartOut = readFile($Opt{"IdentifyDrive"}); my $MultiSmart = (index($SmartOut, "/dev") == 0); foreach my $DriveDesc (split("\n\n\n", $SmartOut)) { if($MultiSmart and index($DriveDesc, "/dev") != 0) { next; } my $DriveDev = "ID"; if($DriveDesc=~/\A(.+)\n/) { $DriveDev = $1; } detectDrive($DriveDesc, $DriveDev); } print Data::Dumper::Dumper(\%HW); exitStatus(0); } if($Opt{"ShowDmesg"}) { if(not -f $Opt{"ShowDmesg"}) { printMsg("ERROR", "can't access file '".$Opt{"ShowDmesg"}."'"); exitStatus(1); } print hideDmesg(readFile($Opt{"ShowDmesg"}), 1); exitStatus(0); } if($Opt{"IdentifyMonitor"}) { if(not -f $Opt{"IdentifyMonitor"}) { printMsg("ERROR", "can't access file '".$Opt{"IdentifyMonitor"}."'"); exitStatus(1); } detectMonitor(readFile($Opt{"IdentifyMonitor"})); print Data::Dumper::Dumper(\%HW); exitStatus(0); } if($Opt{"DecodeACPI_From"} and $Opt{"DecodeACPI_To"}) { if(not -f $Opt{"DecodeACPI_From"}) { printMsg("ERROR", "can't access file '".$Opt{"DecodeACPI_From"}."'"); exitStatus(1); } decodeACPI($Opt{"DecodeACPI_From"}, $Opt{"DecodeACPI_To"}); exitStatus(0); } if($Opt{"InstallDeps"}) { $Opt{"All"} = 1; } if($Opt{"Save"}) { $Opt{"All"} = 1; } if($Opt{"Logs"}) { $Opt{"Probe"} = 1; } if($Opt{"All"}) { $Opt{"Probe"} = 1; $Opt{"Logs"} = 1; } if($Opt{"Check"}) { $Opt{"Probe"} = 1; $Opt{"Logs"} = 1; } if($Opt{"Probe"}) { $Opt{"HWLogs"} = 1; } if($Opt{"Probe"} or $Opt{"StartMonitoring"} or $Opt{"StopMonitoring"} or $Opt{"ShowLog"}) { if(not $Admin) { if($Opt{"Snap"}) { printMsg("WARNING", "run as root for better results ('sudo -E hw-probe ...' or with 'su')"); } elsif($Opt{"Flatpak"}) { printMsg("WARNING", "run as root for better results"); } else { printMsg("ERROR", "you should run as root ('sudo -E hw-probe ...' or with 'su')"); exitStatus(1); } } } if($Opt{"Probe"} and not $Opt{"FixProbe"}) { if(-d $DATA_DIR) { if(not -w $DATA_DIR) { printMsg("ERROR", "can't write to '".$DATA_DIR."', please run as root"); exitStatus(1); } rmtree($DATA_DIR); } } if($Opt{"ShowLog"}) { printMsg("INFO", readFile($PROBE_LOG)); } if($Opt{"FixProbe"}) { $Opt{"Probe"} = 0; $Opt{"HWLogs"} = 0; $Opt{"Logs"} = 0; } if($Opt{"Probe"} and ($Opt{"Upload"} or $Opt{"Save"})) { ($DATA_DIR, $LOG_DIR, $TEST_DIR) = initDataDir($TMP_PROBE); } if($Opt{"Check"} or $Opt{"CheckExtended"}) { $Opt{"CheckGraphics"} = 1; $Opt{"CheckMemory"} = 1; $Opt{"CheckHdd"} = 1; $Opt{"CheckCpu"} = 1; } if($Opt{"CheckExtended"}) { $Opt{"Check7z"} = 1; } if($Opt{"CheckGraphics"} or $Opt{"CheckMemory"} or $Opt{"CheckHdd"} or $Opt{"CheckCpu"} or $Opt{"Check7z"}) { $Opt{"Check"} = 1; $Opt{"Logs"} = 1; } if(my $PciIDs = $Opt{"PciIDs"}) { if(not -e $PciIDs) { printMsg("ERROR", "can't access '".$PciIDs."'"); exitStatus(1); } readPciIds_A($PciIDs, \%PciInfo); } if(my $UsbIDs = $Opt{"UsbIDs"}) { if(not -e $UsbIDs) { printMsg("ERROR", "can't access '".$UsbIDs."'"); exitStatus(1); } readUsbIds_A($UsbIDs, \%UsbInfo); } if(my $SdioIDs = $Opt{"SdioIDs"}) { if(not -e $SdioIDs) { printMsg("ERROR", "can't access '".$SdioIDs."'"); exitStatus(1); } readSdioIds($SdioIDs, \%SdioInfo, \%SdioVendor); if(-e "$SdioIDs.add") { readSdioIds("$SdioIDs.add", \%AddSdioInfo, \%AddSdioVendor); } } if($Opt{"PnpIDs"}) { if(not -e $Opt{"PnpIDs"}) { printMsg("ERROR", "can't access '".$Opt{"PnpIDs"}."'"); exitStatus(1); } } if($Opt{"FixProbe"}) { if(not -e $Opt{"FixProbe"}) { printMsg("ERROR", "can't access '".$Opt{"FixProbe"}."'"); exitStatus(1); } if(-f $Opt{"FixProbe"} and isPkg($Opt{"FixProbe"})) { # package my $PName = basename($Opt{"FixProbe"}); $FixProbe_Pkg = abs_path($Opt{"FixProbe"}); $Opt{"FixProbe"} = $FixProbe_Pkg; my $TmpDir = $TMP_DIR; if(-s $Opt{"FixProbe"} > 1048576) { $TmpDir = $TMP_LOCAL; mkpath($TmpDir); } copy($Opt{"FixProbe"}, $TmpDir."/".$PName); chdir($TmpDir); system("tar", "-m", "-xf", $PName); chdir($ORIG_DIR); $Opt{"FixProbe"} = $TmpDir."/hw.info"; } else { printMsg("ERROR", "unsupported probe format '".$Opt{"FixProbe"}."'"); exitStatus(1); } $Opt{"FixProbe"}=~s/[\/]+\Z//g; $FixProbe_Logs = $Opt{"FixProbe"}."/logs"; $FixProbe_Tests = $Opt{"FixProbe"}."/tests"; if(-d $Opt{"FixProbe"}) { if(not listDir($FixProbe_Logs)) { printMsg("ERROR", "can't find logs in '".$Opt{"FixProbe"}."'"); exitStatus(1); } } else { printMsg("ERROR", "can't access '".$Opt{"FixProbe"}."'"); exitStatus(1); } if(-f "$FixProbe_Logs/media_urls") { # support for old probes foreach my $File ("journalctl", "journalctl.1", "lib_modules", "ld.so.cache", "sys_module", "media_active", "media_urls", "lspcidrake", "lpstat", "lpinfo", "systemctl_status", "ps", "cups_access_log", "cups_error_log", "sane-find-scanner", "scanimage", "codec", "sys_class", "lsinitrd", "xmodmap", "avahi", "dmesg.old", "asound_modules", "syslog", "lib", "parted", "gdisk") { unlink($FixProbe_Logs."/".$File); } my $Udevadm = $FixProbe_Logs."/udevadm"; if(-f $Udevadm) { if(readFile($Udevadm)!~/sdio/i) { unlink($Udevadm); } } } if(my $RmLog = $Opt{"RmLog"}) { if(-f "$FixProbe_Logs/$RmLog" and not grep {$RmLog eq $_} @ProtectedLogs and not grep {$RmLog eq $_} @ProtectFromRm) { writeFile("$FixProbe_Logs/$RmLog", ""); } } if($Opt{"RmObsolete"}) { # Leave only hardware-specific logs, do not save logs and configs necessary for investigating problems foreach my $L ("boot.log", "dmesg.1", "findmnt", "fstab", "grub.cfg", "mount", "pstree", "systemctl", "top", "xorg.log.1", "xorg.conf.d", "xorg.conf", "modprobe.d", "interrupts", "gl_conf-alternatives", "alsactl") # NOTE: systemctl is needed to detect Display_manager { if(-e "$FixProbe_Logs/$L") { if($L eq "dmesg.1" and -s "$FixProbe_Logs/dmesg" <= 1024) { next; } printMsg("INFO", "removing $L"); unlink("$FixProbe_Logs/$L"); } } if(my $Xdpy = readFile("$FixProbe_Logs/xdpyinfo")) { if($Xdpy=~s/(visual:(.|\n)+?)\Z/...\n/g) { writeFile("$FixProbe_Logs/xdpyinfo", $Xdpy); } } if(my $Glx = readFile("$FixProbe_Logs/glxinfo")) { if($Glx=~s/(GLX Visuals)(.|\n)+?\Z/$1\n...\n/g) { writeFile("$FixProbe_Logs/glxinfo", $Glx); } } if(my $Sysctl = readFile("$FixProbe_Logs/sysctl")) { if($Sysctl=~s/(debug.tslog_user:)(.|\n)+/$1 ...\n/g) { writeFile("$FixProbe_Logs/sysctl", $Sysctl); } } } if(my $TrLog = $Opt{"TruncateLog"}) { if(-f "$FixProbe_Logs/$TrLog" and not grep {$TrLog eq $_} @ProtectedLogs) { if(my $Content = readFile("$FixProbe_Logs/$TrLog")) { writeLog("$FixProbe_Logs/$TrLog", $Content); } } } $Opt{"Logs"} = 0; } if($Opt{"Save"}) { if(not -d $Opt{"Save"}) { printMsg("ERROR", "please create directory first"); exitStatus(1); } } if($Opt{"Upload"}) { if(not checkCmd("curl")) { if(not $Opt{"Snap"} and not $Opt{"Flatpak"}) { printMsg("WARNING", "'curl' package is not installed"); } } } if($Opt{"Probe"} or $Opt{"Check"}) { makeProbe(); if(not $Opt{"Upload"} and not $Opt{"Save"} and not $Opt{"Show"} and not $Opt{"ShowDevices"} and not $Opt{"ShowHost"} and not $Opt{"Docker"}) { print "Local probe path: $DATA_DIR\n"; } } elsif($Opt{"FixProbe"}) { readHost($Opt{"FixProbe"}); # instead of probeSys fixLogs($FixProbe_Logs); my ($Distr, $DistrVersion, $Rel, $Build) = probeDistr(); fixDistr($Distr, $DistrVersion, $Rel, $Build); if(isBSD()) { if($Sys{"Arch"} eq "x86_64") { $Sys{"Arch"} = "amd64"; } } fixProduct(); fixChassis(); probeHWaddr(); probeHW(); if($Sys{"System"} eq "debian") { fixDistr($Sys{"System"}, $Sys{"System_version"}, undef, undef); } if(not $Sys{"System"}) { $Sys{"System"} = "lfs"; } if(keys(%ExtraConnection)) { fixHWaddr(); } checkGraphicsCardOutput(readFile($FixProbe_Tests."/glxgears"), readFile($FixProbe_Tests."/glxgears_discrete")); if($Opt{"PC_Name"}) { $Sys{"Name"} = $Opt{"PC_Name"}; # fix PC name } # 1.6: added Current_desktop to identify probe of XDG_* # We identify early 1.6 pre-releases (version is not bumped yet) by presence of Uuid property in Sys if(not $Sys{"DE"} or $Sys{"DE"} eq "KDE" or (not $Sys{"Current_desktop"} and $Sys{"Probe_ver"} ne "1.5")) { if(my $FixDE = fixByPkgs("DE")) { if($Sys{"DE"} ne "KDE" or $FixDE=~/KDE/) { $Sys{"DE"} = $FixDE; } } } if($Sys{"System"}=~/\Aubuntu(-\d|\Z)/) { if($Sys{"DE"}=~/KDE/) { $Sys{"System"}=~s/\Aubuntu/kubuntu/; } elsif($Sys{"DE"}=~/LXDE|LXQt/) { $Sys{"System"}=~s/\Aubuntu/lubuntu/; } elsif($Sys{"DE"}=~/XFCE/) { $Sys{"System"}=~s/\Aubuntu/xubuntu/; } elsif($Sys{"DE"}=~/MATE/) { $Sys{"System"}=~s/\Aubuntu/ubuntu-mate/; } elsif($Sys{"DE"}=~/Unity/) { $Sys{"System"}=~s/\Aubuntu/ubuntu-unity/; } elsif($Sys{"DE"}=~/Budgie/) { $Sys{"System"}=~s/\Aubuntu/ubuntu-budgie/; } elsif($Sys{"DE"}=~/Deepin/) { $Sys{"System"}=~s/\Aubuntu/ubuntudde/; } elsif($Sys{"DE"}=~/UKUI/) { $Sys{"System"}=~s/\Aubuntu/ubuntukylin/; } } if(not $Sys{"Display_server"}) { if(my $FixDisplayServer = fixByPkgs("DisplayServer")) { $Sys{"Display_server"} = $FixDisplayServer; } } if(not $Sys{"Display_manager"}) { if(my $FixDM = fixByPkgs("DisplayManager")) { $Sys{"Display_manager"} = fixDisplayManager($FixDM); } } if(isBSD()) { if($Sys{"System"}=~/hellosystem/i and (not $Sys{"DE"} or $Sys{"DE"}=~/openbox/i)) { $Sys{"DE"} = "helloDesktop"; } if(not $Sys{"DE"}) { if($Sys{"Current_wm"}) { $Sys{"DE"} = $Sys{"Current_wm"}; } elsif($Sys{"Wm"}) { $Sys{"DE"} = $Sys{"Wm"}; } elsif(-s $FixProbe_Logs."/xorg.log") { if(isOpenBSD()) { $Sys{"DE"} = "fvwm"; } } } } if($Opt{"DecodeACPI"}) { if(-s "$FixProbe_Logs/acpidump") { decodeACPI("$FixProbe_Logs/acpidump", "$FixProbe_Logs/acpidump_decoded"); } } if($USE_JSON_XS) { writeDevsDump(); } else { writeDevs(); } writeHost(); if($FixProbe_Pkg) { # package my $PName = basename($FixProbe_Pkg); chdir(dirname($Opt{"FixProbe"})); my $Compress = ""; if($PName=~/gz\Z/) { $Compress .= "tar -czf ".$PName." hw.info"; } else { # XZ if($Opt{"LowCompress"}) { # low CPU/RAM, high SPACE $Compress .= "XZ_OPT=-0 "; # high CPU, low RAM/SPACE # $Compress .= "XZ_OPT='--memlimit=15MiB' "; } elsif($Opt{"HighCompress"}) { # high CPU/RAM, low SPACE $Compress .= "XZ_OPT=-9 "; } else { # default is -9 } $Compress .= "tar -cJf ".$PName." hw.info"; } qx/$Compress/; if($?) { printMsg("ERROR", "can't create a package"); chdir($ORIG_DIR); exitStatus(1); } move($PName, $FixProbe_Pkg); if($!=~/Permission denied/) { printMsg("ERROR", "failed to access $FixProbe_Pkg"); } chdir($ORIG_DIR); rmtree($Opt{"FixProbe"}); # remove temp directory } } if($Admin and ($Opt{"Flatpak"} or $Opt{"OutputDir"})) { # Allow to mix root and non-root runs setPublic($PROBE_DIR, "-R"); } if($Opt{"Show"} or $Opt{"ShowDevices"} or $Opt{"ShowHost"}) { showInfo(); } if($Opt{"Upload"}) { uploadData(); cleanData(); } elsif($Opt{"Save"}) { saveProbe($Opt{"Save"}); cleanData(); } if($Opt{"GenerateGroup"}) { if(not $Opt{"Email"}) { printMsg("ERROR", "please specify -email option (your Email for notifications)"); exitStatus(1); } generateGroup(); } if($Opt{"Email"} and $Opt{"Email"}!~/\A[^\@]+\@[^\@]+\.\w{2,}\Z/) { printMsg("ERROR", "invalid Email address"); exitStatus(1); } if($Opt{"RemindGroup"}) { probeHWaddr(); if(not $Sys{"HWaddr"}) { printMsg("ERROR", "failed to detect hwid"); exitStatus(1); } remindGroup(); } if($Opt{"StartMonitoring"} or $Opt{"StopMonitoring"}) { if(not $Opt{"Group"}) { printMsg("ERROR", "please specify -i option (inventory id)"); exitStatus(1); } if(not $Admin and ($Opt{"Flatpak"} or $Opt{"Snap"})) { printMsg("WARNING", "not all hardware monitoring features are available when using Flatpak or Snap as a non-root user"); } if($Opt{"StartMonitoring"}) { $Opt{"Probe"} = 1; $Opt{"Logs"} = 1; $Opt{"HWLogs"} = 1; $Opt{"CheckGraphics"} = 1; $Opt{"CheckMemory"} = 1; $Opt{"CheckHdd"} = 1; $Opt{"CheckCpu"} = 1; makeProbe(); $Opt{"Monitoring"} = 1; uploadData(); } elsif($Opt{"StopMonitoring"}) { $Opt{"Probe"} = 1; probeHWaddr(); } setupMonitoring(); } if($BY_DESKTOP) { # Wait for user to save the probe ID sleep(60); } exitStatus(0); } scenario();