#!/usr/bin/env perl # Copyright (c) 2025, R. W. Rodolico # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # linux_identity # Linux identity section: UUID/serial source and placeholder rules (shared in # library.pm), virtualization inferred from virt-what, DMI and /proc/xen. # # Revision History: # 2026-10-18 Claude v1.0.0 new sensor, split from unix_identity (bug 210) package linux_identity; use strict; use warnings; use version; our $VERSION = version->declare('v1.0.0'); use FindBin; use File::Spec; use lib File::Spec->catdir($FindBin::Bin), File::Spec->catdir("$FindBin::Bin/.."); use library qw(:all); ####################################################### # # inferVirtualization( $virtWhat, $maker, $product, $procXen, $xenCaps ) # # Linux virtualization word (Spec 1.3): virt-what output when it has # any, else inferred from the DMI manufacturer/product and /proc/xen; # 'none' when physical. A Xen dom0 (virt-what xen-dom0, or control_d in # /proc/xen/capabilities) runs on the hardware, so it is 'none'. # # Parameters: # $virtWhat - virt-what output (may be empty or several lines) # $maker - system manufacturer # $product - system product name # $procXen - true when /proc/xen exists # $xenCaps - contents of /proc/xen/capabilities # # Returns: # scalar virtualization word # # Side Effects: # none # ####################################################### sub inferVirtualization { my ($virtWhat, $maker, $product, $procXen, $xenCaps) = @_; my @facts = grep { $_ ne '' } map { trim($_) } split(/\n/, defined($virtWhat) ? $virtWhat : ''); return 'none' if grep { $_ eq 'xen-dom0' } @facts; return join(',', @facts) if @facts; my $id = (defined($maker) ? $maker : '') . ' ' . (defined($product) ? $product : ''); return 'qemu' if $id =~ /QEMU/i; return 'kvm' if $id =~ /\bKVM\b/i; return 'vmware' if $id =~ /VMware/i; return 'virtualbox' if $id =~ /VirtualBox|innotek/i; return 'hyperv' if $id =~ /Microsoft Corporation/i && $id =~ /Virtual Machine/i; return 'xen' if $id =~ /\bXen\b/i; return 'none' if defined($xenCaps) && $xenCaps =~ /control_d/; return 'xen' if $procXen; return 'none'; } ####################################################### # # main() # # Probes identity fields and emits the identity section lines. # # Parameters: # none # # Returns: # integer exit status (1 wrong OS) # # Side Effects: # runs dmidecode, cat and virt-what, writes STDOUT/STDERR # ####################################################### sub main { if (!checkOS({ 'linux' => undef })) { print STDERR "unsupported os\n"; return 1; } my %p; $p{'rawUuid'} = trim(runCommand('dmidecode', '-s', 'system-uuid')); $p{'rawUuid'} = trim(runCommand('cat', '/sys/class/dmi/id/product_uuid')) if $p{'rawUuid'} eq '' && -r '/sys/class/dmi/id/product_uuid'; $p{'rawSerial'} = trim(runCommand('dmidecode', '-s', 'system-serial-number')); $p{'rawSerial'} = trim(runCommand('cat', '/sys/class/dmi/id/product_serial')) if $p{'rawSerial'} eq '' && -r '/sys/class/dmi/id/product_serial'; $p{'boardSerial'} = trim(runCommand('dmidecode', '-s', 'baseboard-serial-number')); $p{'maker'} = trim(runCommand('dmidecode', '-s', 'system-manufacturer')); $p{'product'} = trim(runCommand('dmidecode', '-s', 'system-product-name')); $p{'machineId'} = -r '/etc/machine-id' ? trim(runCommand('cat', '/etc/machine-id')) : ''; $p{'machineId'} = trim(runCommand('cat', '/var/lib/dbus/machine-id')) if $p{'machineId'} eq '' && -r '/var/lib/dbus/machine-id'; my $virtWhat = validCommandOnSystem({'virt-what' => undef}) ? runCommand('virt-what') : ''; my $xenCaps = -r '/proc/xen/capabilities' ? runCommand('cat', '/proc/xen/capabilities') : ''; $p{'virt'} = inferVirtualization($virtWhat, $p{'maker'}, $p{'product'}, -d '/proc/xen', $xenCaps); my $out = identityReport(\%p); print join("\n", @{$out}), "\n" if @{$out}; return 0; } exit main() unless caller; 1;