#!/usr/bin/env bash set -e all_components=( "dmi" "dmesg" "acpidump" "acpi" "platform" "serial" "sam" "kconfig" "lspci" "lsusb" ) # commands-line interface print_help () { pname="$(basename "${0}")" echo "${pname} - Linux Surface diagnostics tool" echo "" echo "Collects diagnostics information for Microsoft Surface devices running Linux." echo "" echo "USAGE:" echo " ${pname} [FLAGS...] COMPONENTS..." echo "" echo "FLAGS:" echo " -h, --help Prints this help message" echo " -o, --output FILE The gzip archive to write to [default: diagnostics.tar.gz]" echo "" echo "COMPONENTS:" echo " Space-separated list of components or 'full' for all components." echo "" echo " Available components are:" for c in "${all_components[@]}"; do echo " ${c}" done echo "" echo "Examples:" echo " ${pname} --help Print this help message" echo " ${pname} full Collect full suite of diagnostics" echo " ${pname} dmi acpidump Collect only DMI information and ACPI dump" } components=() archive="diagnostics.tar.gz" while [[ ${#} -gt 0 ]]; do key="${1}" case "${key}" in -h|--help) print_help exit ;; -o|--output) if [[ ${#} -lt 2 ]]; then echo "Error: Missing argument for option '${1}'" echo " Run with '--help' for more information." exit 1 fi archive="${2}" if [[ "${archive}" != *".tar.gz" ]]; then echo "Error: Invalid file ending for output archive, should be '.tar.gz'." exit 1 fi shift 2 ;; *) components+=("${1}") shift ;; esac done # ensure some component is selected if [[ ${#components[@]} -eq 0 ]]; then echo "Error: No component selected" echo " Run with '--help' for more information." exit 1 fi # handle special component names if [[ " ${components[*]} " =~ " full " ]]; then components=("${all_components[@]}") fi # validate component list for c in "${components[@]}"; do if [[ ! " ${all_components[*]} " =~ " ${c} " ]]; then echo "Error: Unknown component '${c}'" echo " Run with '--help' for more information." exit 1 fi done; # set up temporary directory tmpdir="" on_exit () { # remove temporary directory if set if [[ -n "${tmpdir}" ]]; then rm -rf "${tmpdir}" fi } trap on_exit EXIT tmpdir="$(mktemp -d -t "surface-diagnostics.XXXXXX")" # collect information timestamp=$(date --iso-8601=seconds) collect_sysfs () { subsystem="${1}" if [[ -d "/sys/bus/${subsystem}/" ]]; then tree -l -L 3 "/sys/bus/${subsystem}/drivers/" > "${tmpdir}/sys-bus-${subsystem}-drivers.txt" tree -l -L 3 "/sys/bus/${subsystem}/devices/" > "${tmpdir}/sys-bus-${subsystem}-devices.txt" else echo "" > "${tmpdir}/sys-bus-${subsystem}-drivers.txt" echo "" > "${tmpdir}/sys-bus-${subsystem}-devices.txt" fi } # collect timestamp and uname echo " ==> collecting information..." echo "${timestamp}" > "${tmpdir}/timestamp" echo " - uname" uname -a > "${tmpdir}/uname" # collect DMI if specified if [[ " ${components[*]} " =~ " dmi " ]]; then echo " - DMI system information" sudo dmidecode -t system > "${tmpdir}/dmi-system.txt" fi # collect dmesg log if specified if [[ " ${components[*]} " =~ " dmesg " ]]; then echo " - dmesg log" sudo dmesg > "${tmpdir}/dmesg.log" fi # collect acpidump if specified if [[ " ${components[*]} " =~ " acpidump " ]]; then echo " - ACPI dump" sudo acpidump | perl -pe 'BEGIN{undef $/;} s|MSDM.*?\n\n||sgm' > "${tmpdir}/acpidump.txt" fi # collect acpi sysfs entries and device paths if specified if [[ " ${components[*]} " =~ " acpi " ]]; then echo " - ACPI device information and paths (sysfs)" collect_sysfs "acpi" grep ".*" "/sys/bus/acpi/devices/"*"/path" > "${tmpdir}/sys-bus-acpi-devices.paths.txt" fi # collect platform sysfs entries if [[ " ${components[*]} " =~ " platform " ]]; then echo " - platform device information (sysfs)" collect_sysfs "platform" fi # collect serial sysfs entries if [[ " ${components[*]} " =~ " serial " ]]; then echo " - serial/UART device information (sysfs)" collect_sysfs "serial" fi # collect SAM information if [[ " ${components[*]} " =~ " sam " ]]; then echo " - surface aggregator module information (sysfs)" collect_sysfs "surface_aggregator" # get SAM firmware version out="${tmpdir}/sam-firmware-version" if [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/" ]]; then echo "" > "${out}" elif [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/physical_node" ]]; then echo "" > "${out}" elif [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/physical_node/sam" ]]; then echo "" > "${out}" else cat "/sys/bus/acpi/devices/MSHW0084:00/physical_node/sam/firmware_version" > "${out}" fi fi # collect kernel config if [[ " ${components[*]} " =~ " kconfig " ]]; then echo " - kernel configuration" # attempt to load module for accessing current kernel config if modinfo configs > /dev/null 2>&1; then sudo modprobe configs fi # try to get current config from /proc, if available if [[ -f "/proc/config.gz" ]]; then zcat "/proc/config.gz" > "${tmpdir}/kernel-$(uname -r).conf" fi # try to get current config from /boot, if available if [[ -f "/boot/config" ]]; then cp "/boot/config" "${tmpdir}/kernel-$(uname -r).conf" fi # try to get any config from /boot find "/boot" -name 'config-*' -print0 | while IFS= read -r -d '' file; do name="$(basename "${file}")" cp "${file}" "${tmpdir}/kernel-${name#"config-"}.conf" done; fi # collect lspci if [[ " ${components[*]} " =~ " lspci " ]]; then echo " - lspci" sudo lspci -nn -vvv > "${tmpdir}/lspci.txt" sudo lspci -nn -v -t > "${tmpdir}/lspci-tree.txt" fi # collect lsusb if [[ " ${components[*]} " =~ " lsusb " ]]; then echo " - lsusb" sudo lsusb -v > "${tmpdir}/lsusb.txt" sudo lsusb -v -t > "${tmpdir}/lsusb-tree.txt" fi # bundle to archive echo " ==> generating archive..." tar -czf "./${archive}" -C "${tmpdir}" . echo " ==> done" echo "" echo "Please post the generated '${archive}' file."