#!/bin/bash # Use bash since ash can only do math on 32bit numbers, which is not sufficient for ID44 calculations # also for negative length to ${x:0:-1} # This script gathers information about the hardware and configuration and reports it back to the server . /opt/openslx/config export LANG=C.UTF-8 export LC_ALL=C.UTF-8 declare -rg REPORTFILE="/run/hwreport" declare -rg jsonfile="/run/hwreport.json" # new json-based reporting, run first in background and wait at the end of script # This is only supported by Sat3.11, released 12-2023, so keep creating the old format # too below this call, until we don't support 3.10 anymore. if ! python3 /opt/openslx/hardware-stats/collect_hw_info_json.py -p > "$jsonfile"; then echo -n "" > "$jsonfile" fi & tmpmask="/tmp/hwreport-$$." mktemp() { local FILE= if [ -x /bin/mktemp ]; then FILE=$( /bin/mktemp "${tmpmask}XXXXXXXX" ) fi if [ -z "$FILE" ] && [ -x /opt/openslx/bin/mktemp ]; then FILE=$( /opt/openslx/bin/mktemp "${tmpmask}XXXXXXXX" ) fi if [ -z "$FILE" ]; then FILE="${tmpmask}$RANDOM$RANDOM$$" fi echo "$FILE" } # Run on exit cleanup_exit() { wait rm -f -- "$tmpmask"* # no space! exit 0 } slxfdisk() { local binary RET for binary in "/opt/openslx/sbin/fdisk -u" "busybox fdisk -u" "fdisk"; do $binary "$@" RET=$? [ "$RET" = "127" ] && continue # command not found [ "$RET" = "1" ] && [ "${binary%% *}" = "busybox" ] && continue # maybe applet not found return $RET done return 127 } ################################################################################ # 1) Get MAC Address used for booting # MAC="${SLX_PXE_MAC}" if [ -z "$MAC" ]; then # get MAC from sysfs MAC="$(cat /sys/class/net/${SLX_PXE_NETIF:-br0}/address)" fi if [ -z "$MAC" ]; then BOOTIF="$(grep -Po '(?<=BOOTIF=)[0-9a-f\-:]+' /proc/cmdline)" [ "${#BOOTIF}" -eq "20" ] && MAC="${BOOTIF:3}" fi if [ -z "$MAC" ]; then echo "Getting MAC from /proc/cmdline failed, using 'ip a'..." _mac="$(ip a | grep -A 1 ': br0' | grep -o 'ether ..:..:..:..:..:..' | cut -d' ' -f2)" [ "$_mac" -eq 17 ] && MAC="$_mac" fi if [ -z "$MAC" ]; then MAC="88-77-66-55-44-33" fi # always uppercase and dash-separated MAC="${MAC^^}" MAC="${MAC//:/-}" echo "Determined MAC=$MAC" ################################################################################ # 2) Number of real and virtual CPU cores # Virtual, cheap way VCORES=$(grep '^processor\s' /proc/cpuinfo | sort -u | wc -l) # Real cores CPUCORES=$(cat /sys/devices/system/cpu/cpu*/topology/thread_siblings_list | sort -u | wc -l) CPUSOCKETS=$(grep '^physical id' /proc/cpuinfo | sort -u | wc -l) # Fallback 1... if [ -z "$CPUCORES" ] || [ "$CPUCORES" = "0" ]; then for c in $(dmidecode -t 4 | grep 'Core Enabled' | awk -F ':' '{print $2}'); do CPUCORES=$(( $CPUCORES + $c )) done fi # Fallback 2... if [ -z "$CPUCORES" ] || [ "$CPUCORES" = "0" ]; then CPUCORES=$(grep -E -e '^core id\s*:' -e '^physical id\s*:' /proc/cpuinfo | xargs -l2 echo | sort -u | wc -l) fi echo "$CPUCORES real cores, $VCORES with HT" ################################################################################ # 3) CPU model name # CPUMODEL=$(grep -m1 '^model name\s*:' /proc/cpuinfo | sed 's/^model name\s*:\s*//;s/\s\s*/ /g;s/^ //;s/ $//') echo "$CPUMODEL" ################################################################################ # 4) RAM # RAM=$(grep -m1 '^MemTotal:' /proc/meminfo | awk '{print $2}') RAM=$(( $RAM / 1024 )) if [ -z "$RAM" ] || [ "$RAM" -lt 500 ]; then # Fallback to dmidecode RAM=0 for c in $(dmidecode -t 17 | grep -o 'Size:.*MB$' | awk '{print $2}'); do RAM=$(( $RAM + $c )) done fi echo "$RAM MB RAM" ################################################################################ # 5) 64bit virtualization support # VT="UNSUPPORTED" CPU_VENDOR=$( grep -m1 '^vendor_id\s*:' /proc/cpuinfo | awk '{print $3}' ) VIRTTYPE=$(grep -m1 '^flags\s*:' /proc/cpuinfo | grep -wo -e svm -e vmx) [ -n "$CPU_VENDOR" ] && modprobe msr echo "CPUID: $CPU_VENDOR" if [ "$CPU_VENDOR" = "GenuineIntel" ]; then BIT1="$( rdmsr --bitfield 0:0 0x3a 2>/dev/null || echo "fail" )" BIT2="$( rdmsr --bitfield 2:2 0x3a 2>/dev/null || echo "fail" )" if [ "$BIT1" = "fail" ] || [ "$BIT2" = "fail" ]; then VT="UNKNOWN" elif [ "$BIT1" = "1" ] && [ "$BIT2" = "0" ]; then VT="DISABLED" elif [ "$VIRTTYPE" = "vmx" ]; then VT="ENABLED" fi elif [ "$CPU_VENDOR" = "AuthenticAMD" ]; then BIT="$( rdmsr --bitfield 4:4 0xc0010114 2>/dev/null || echo "fail" )" if [ "$BIT" = "fail" ]; then VT="UNKNOWN" elif [ "$BIT" = "1" ]; then VT="DISABLED" elif [ "$VIRTTYPE" = "svm" ]; then VT="ENABLED" fi fi echo "KVM support is $VT" ################################################################################ # 6) ID44 partition size # ID44=0 if ! slx-tools fs_path_isvolatile "/tmp/virt" ; then ID44_SPACE=($(slx-tools fs_path_space "/tmp/virt")) [ -n "${ID44_SPACE[1]}" ] && ID44="$(( ${ID44_SPACE[1]} / 1024 ))" if [ "$ID44" -eq 0 ]; then # Fallback to old way to detect it # Try df first, make sure device starts with /dev read -r df_dev df_size df_used df_avail df_usepercent df_mountpoint df_crap < <(df -P /tmp/virt | grep ' /tmp/virt$') if [ -n "${df_size}" ] && [ "${df_dev:0:5}" = "/dev/" ]; then # df reports kbytes, convert to MB ID44=$(( df_size / 1024 )) fi if [ "$ID44" = 0 ]; then # slxfdisk fallback for c in $(slxfdisk -l | grep -E '[0-9]+[\-\+MG]?\s+44\s+' | awk '{print $1}'); do val=$(blockdev --getsize64 "$c") [ -z "$val" ] && continue [ "$val" -gt "$ID44" ] && ID44=$val done # blockdev reports bytes, convert to MB ID44=$(( $ID44 / 1058576 )) # we'd rather underreport fi fi fi echo "Scratch space: $ID44 MB" ################################################################################ # 7) check smart values # FDISK=$(mktemp) declare -a DISKS shopt -s extglob for disk in /dev/disk/by-path/!(*-part*|*-usb-*); do [ -L "$disk" ] || continue disk="$( readlink -f "$disk" )" DISKS+=("$disk") slxfdisk -l "$disk" | sed -r 's/\s+[0-9]+,[0-9]+,[0-9]+/ /g;/^\// s/\s[0-9]+\.?[0-9]+[MGT]\s/ /;s/^Units: /Units =foo= /' | awk '{if ($1 !~ /^[0-9]+$/ || $5 ~ /^[0-9a-f]{4}/) { print $0 } else { $5 = "0700 " $5; print $0 }}' # Work around busybox YET AGAIN changing something in an incompatible way done > "$FDISK" shopt -u extglob [ -z "$SLX_SMARTCTL_MIN_REALLOC" ] && SLX_SMARTCTL_MIN_REALLOC=0 BADSECTORS=0 if which smartctl; then ALLSMART=$(mktemp) FILE=$(mktemp) [ -z "$FILE" ] && FILE="/tmp/smartctl.$$.$RANDOM.$RANDOM" for dev in "${DISKS[@]}"; do smartctl -i -H -A -f "brief" "${dev%n[0-9]}" > "$FILE" # Exit code might be nonzero to indicate fail status, or because some (but not all) ATA commands # failed. So instead, see if we at least got basic device information and go on if so. grep -q -E -e '^Device Model:' -e '^\s*5\s.+\s.+\s.+\s[0-9]' -e '^Model Number:' "$FILE" || continue echo "NEXTHDD=$dev" >> "$ALLSMART" cat "$FILE" >> "$ALLSMART" # parse OVERALL=$(grep -o "test result: .*$" "$FILE" | cut -c 14-) [ "x$OVERALL" = "xPASSED" ] && OVERALL="" REALLOC=$(grep "^ *5 " "$FILE" | awk '{print $8}') PENDING=$(grep "^ *197 " "$FILE" | awk '{print $8}') SPINRETRY_VAL=$(grep "^ *10 " "$FILE" | awk '{print $4}') SPINRETRY_THR=$(grep "^ *10 " "$FILE" | awk '{print $6}') [ -n "$OVERALL" ] && BADSECTORS=$(( $BADSECTORS + 100 )) if [ -n "$REALLOC" ] && [ "$REALLOC" -gt "0" ]; then BADSECTORS=$(( $BADSECTORS + $REALLOC )) fi if [ -n "$PENDING" ] && [ "$PENDING" -gt "5" ]; then BADSECTORS=$(( $BADSECTORS + $PENDING )) fi if [ -n "$SPINRETRY_VAL" ] && [ "$SPINRETRY_VAL" -le "$SPINRETRY_THR" ]; then BADSECTORS=$(( $BADSECTORS + 100 )) fi done rm -f -- "$FILE" fi echo "SMART: $OVERALL - $REALLOC reallocated, $PENDING pending" ################################################################################ # 8) Read system model and manufacturer # dmidec() { local RETVAL=$(dmidecode "$@" 2>/dev/null | grep -v '^#' | grep -v '^Invalid' | sed 's/\s\s*/ /g;s/^ //;s/ $//') case "$RETVAL" in ""|*"Product Name"*|*"be filled"*|"unknown"|*"efault string"*|*"efault String"*|*"product name"*|*"anufacturer"*|*"ystem model"*) RETVAL="Unknown" ;; esac echo "$RETVAL" } bashesc () { sed s/\'/\'\"\'\"\'/g <<< $* } HW_MODEL=$(dmidec -q -s system-product-name) HW_MANUF=$(dmidec -q -s system-manufacturer) # Try fallback to baseboard if [ "$HW_MODEL" = "Unknown" ]; then HW_MODEL=$(dmidec -q -s baseboard-product-name) HW_MANUF=$(dmidec -q -s baseboard-manufacturer) fi HW_MANUF=$(bashesc "$HW_MANUF") HW_MODEL=$(bashesc "$HW_MODEL") ################################################################################ # Save raw data to report file # cat > "$REPORTFILE" <<-EOF ############################### CPU ##################################### Sockets: $CPUSOCKETS Real cores: $CPUCORES Virtual cores: $VCORES ######################## Partition tables ############################### EOF cat "$FDISK" >> "$REPORTFILE" cat >> "$REPORTFILE" <<-EOF ############################ PCI ID ##################################### EOF lspci -n -m >> "$REPORTFILE" cat >> "$REPORTFILE" <<-EOF ########################## dmidecode #################################### EOF dmidecode >> "$REPORTFILE" if [ -n "$ALLSMART" ] && [ -s "$ALLSMART" ]; then cat >> "$REPORTFILE" <<-EOF ########################### smartctl #################################### EOF cat "$ALLSMART" >> "$REPORTFILE" fi cat >> "$REPORTFILE" <<-EOF ######################### EOF echo "Created report file" [ -n "$ALLSMART" ] && rm -f -- "$ALLSMART" ################################################################################ # Save information to local file for later use # cat > "/run/hwinfo" <