#!/usr/bin/env bash # The following can be update (use a complex delimiter) DUMP_DELIMITER="#!#!#!#! SYSDUMP DELIMITER #!#!#!" ensure() { command -v "$1" >/dev/null 2>&1 || { echo >&2 "$1 not found, abort..."; exit 1; } } safecat() { if [ -f "$1" ] && [ -r "$1" ] then cat "$1" else echo "File \"$1\" not found or not readable" >> "${LOG_FILE}" fi } safecatroot() { if [ $(id -u) -eq 0 ] then safecat $@ else echo "File \"$1\" requires root permissions to be read" >> "${LOG_FILE}" fi } safecmd() { if command -v "$1" >/dev/null 2>&1 then $@ else echo "Command $@ not found" >> "${LOG_FILE}" fi } safecmdroot() { if [ $(id -u) -eq 0 ] then safecmd $@ else echo "Root permissions required for $@" >> "${LOG_FILE}" fi } safegetentry() { [[ ${DUMP_ENTRIES} =~ $1 ]] && cat "${DUMP_FILE}"|jq -r ".[\"${1}\"]"|base64 -d } dump() { [ -z "$2" ] && return entry=$1 shift value=$($@ 2>&1|base64 -w 0) [ "$USE_COMMA" -eq 1 ] && echo "\"${entry}\": \"${value}\"," [ "$USE_COMMA" -eq 0 ] && echo "\"${entry}\": \"${value}\"" } dumpdisk() { [ -z "$2" ] && return entry=$1 shift command -v "tar" >/dev/null 2>&1 || { echo "Cannot dump \"$1\" (tar not found)" >> "${LOG_FILE}"; return; } value=$(tar cf - "$1" 2>/dev/null|base64 -w 0) [ "$USE_COMMA" -eq 1 ] && echo "\"${entry}\": \"${value}\"," [ "$USE_COMMA" -eq 0 ] && echo "\"${entry}\": \"${value}\"" } cleandump() { newdump=$(mktemp) cat "$1" | awk 'BEGIN{STARTED=0};/'"${DUMP_DELIMITER}"'/{if(STARTED){STARTED=0}else{STARTED=1}};!/'"${DUMP_DELIMITER}"'/{if(STARTED){print $0}}' > "$newdump" mv "$newdump" "$1" } sysdump() { LOG_FILE=$(mktemp 2>/dev/null || echo "./sysdump.X9_8965252JHFHJGFJHGJHG") echo "Log file is ${LOG_FILE}" > "${LOG_FILE}" echo "Local UTC date is $(date -u)" >> "${LOG_FILE}" echo "Bash version is ${BASH_VERSION}" >> "${LOG_FILE}" echo "Current working directory is ${PWD}" >> "${LOG_FILE}" echo "Current user is $(whoami)" >> "${LOG_FILE}" # Populate users path USER_DIRS=() if [ $(id -u) -eq 0 ] then for dir in /home/* do [[ ${dir} != *"lost+found"* ]] && USER_DIRS+=($dir) done USER_DIRS+=(/root) else USER_DIRS+=($HOME) fi echo "Considered home directories in this dump are:" >> "${LOG_FILE}" for hp in "${USER_DIRS[@]}"; do echo " - ${hp}" >> "${LOG_FILE}"; done # Start dump echo "${DUMP_DELIMITER}" echo "{" USE_COMMA=1 # safecat dump "/etc/nftables.conf" safecat /etc/nftables.conf dump "/etc/group" safecat /etc/group for hp in "${USER_DIRS[@]}" do dump "${hp}/.bashrc" safecat ${hp}/.bashrc dump "${hp}/.bash_profile" safecat ${hp}/.bash_profile dump "${hp}/.bash_history" safecat ${hp}/.bash_history done dump "/etc/fstab" safecat /etc/fstab dump "/etc/ssh/sshd_config" safecat /etc/ssh/sshd_config dump "/proc/cpuinfo" safecat /proc/cpuinfo dump "/etc/os-release" safecat /etc/os-release dump "/proc/zoneinfo" safecat /proc/zoneinfo dump "/proc/meminfo" safecat /proc/meminfo dump "/proc/cmdline" safecat /proc/cmdline dump "/proc/version" safecat /proc/version dump "/etc/resolv.conf" safecat /etc/resolv.conf dump "/etc/sysctl.conf" safecat /etc/sysctl.conf dump "/etc/apt/sources.list" safecat /etc/apt/sources.list dump "/etc/hosts" safecat /etc/hosts dump "/etc/bash.bashrc" safecat /etc/bash.bashrc dump "/etc/timezone" safecat /etc/timezone dump "kernel_config" safecat /boot/config-$(uname -r) # safecatroot dump "/etc/shadow" safecatroot /etc/shadow dump "/etc/sudoers" safecatroot /etc/sudoers dump "/boot/grub/grub.cfg" safecatroot /boot/grub/grub.cfg # safecmd dump "date" date +%s dump "hostname" safecmd hostname dump "systemd-detect-virt" safecmd systemd-detect-virt dump "id" safecmd id dump "env" safecmd env dump "top" safecmd top -b -n 1 dump "locale" safecmd locale dump "systemctl" safecmd systemctl --no-pager dump "free" safecmd free -h dump "df" safecmd df -h dump "boot_folder" safecmd ls -R /boot/ dump "lib_folder" safecmd ls -R /lib/ dump "lib64_folder" safecmd ls -R /lib64/ for hp in "${USER_DIRS[@]}"; do dump "home_${hp}" safecmd ls -al ${hp}; done for hp in "${USER_DIRS[@]}"; do user=$(basename "${hp}"); dump "crontab_${user}" safecmd crontab -u "${user}" -l; done dump "root_folder" safecmd ls -al / dump "uid" safecmd id -u dump "gid" safecmd id -g dump "gids" safecmd id -G dump "ipaddr" safecmd ip addr dump "uname" safecmd uname -a dump "lsb_release" safecmd lsb_release dump "uptime" safecmd uptime dump "mount" safecmd mount dump "lscpu" safecmd lscpu dump "lsblk" safecmd lsblk dump "lsusb" safecmd lsusb dump "lsmod" safecmd lsmod dump "lspci" safecmd lspci dump "lsirq" safecmd lsirq dump "lsfd" safecmd lsfd dump "lshw" safecmd lshw dump "glxinfo" safecmd glxinfo -B dump "compgen" safecmd compgen -c dump "openssl" safecmd openssl help dump "users" safecmd users dump "declare" safecmd declare dump "ping" safecmd ping -c 2 -W 2 4.2.2.2 # dump versions for cmd in bash gcc ld python3 cmake make tar zip gzip bzip2 xz cpio wget rsync curl node pip apt cat systemctl gpg R ruby awk grep sshfs docker java git do dump "cmd_${cmd}_version" safecmd $cmd --version done dump "cmd_ssh_version" safecmd sshd -V dump "cmd_tmux_version" safecmd tmux -V dump "cmd_nginx_version" safecmd nginx -v dump "cmd_go_version" safecmd go version # safecmdroot dump "dmidecode" safecmdroot dmidecode dump "iptables" safecmdroot iptables -L dump "fdisk" safecmdroot fdisk -l dump "dmesg" safecmdroot dmesg # dumpdisk for hp in "${USER_DIRS[@]}" do [ -d "${hp}/.ssh" ] && dumpdisk "tar_${hp}/.ssh" "${hp}/.ssh" [ -d "${hp}/.gnupg" ] && dumpdisk "tar_${hp}/.gnupg" "${hp}/.gnupg" done USE_COMMA=0 dump "dump_log" cat "${LOG_FILE}" echo "}" echo "${DUMP_DELIMITER}" rm "${LOG_FILE}" } # Parse arguments POSITIONAL_ARGS=() ACTION="dump" while [[ $# -gt 0 ]]; do case $1 in -l|--list-entries) ACTION="list" shift ;; -s|--summarize) ACTION="summarize" shift # past value ;; -p|--parse) ACTION="parse" shift # past value ;; -h|--help) echo "Usage: $0 [OPTION] [DUMP_FILE] [ENTRIES]" echo " -l, --list-entries: Show available entries from a dump file" echo " -p, --parse: Parse the content of a dump file" echo " Example 1: $0 -p dump.json" echo " Example 2: $0 -p dump.json uname uptime" echo " -s, --summarize: Summarize a dump file" echo " -h, --help: Show this help" exit 0 ;; -*|--*) echo "Unknown option $1" exit 1 ;; *) POSITIONAL_ARGS+=("$1") # save positional arg shift # past argument ;; esac done set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters # Check requirements ensure base64 [ "$ACTION" == "dump" ] && [ $# -ne 0 ] && { echo "I do not understand the following: $@"; exit 1; } [ "$ACTION" != "dump" ] && [ $# -eq 0 ] && { echo "Missing dump file path"; exit 1; } [ "$ACTION" != "dump" ] && [ ! -f "$1" ] && { echo "File \"$1\" not found"; exit 1; } # Do dump [ "$ACTION" == "dump" ] && { sysdump; exit 0; } # Setting up dump file ensure awk ensure jq [ $(grep -c "${DUMP_DELIMITER}" "$1") -ne 0 ] && cleandump "$1" # Setup safegetentry DUMP_FILE="$1" DUMP_ENTRIES=$(cat "${DUMP_FILE}"|jq -r "keys[]";) # List entries [ "$ACTION" == "list" ] && { ensure jq; echo "$DUMP_ENTRIES"; exit 0; } # Parse dump file if [ "$ACTION" == "parse" ] then if [ $# -gt 1 ] then shift for entry in $@ do echo "====================> $entry" >&2 safegetentry ${entry} done exit 0 fi while IFS= read -r entry; do echo "====================> $entry" [[ "$entry" =~ ^"tar_" ]] && echo "Skipping, its a tar archive" && continue safegetentry ${entry} done <<< "${DUMP_ENTRIES}" exit 0 fi # Summarize dump file if [ "$ACTION" == "summarize" ] then OS_RELEASE=$(safegetentry /etc/os-release) CPU_INFO=$(safegetentry /proc/cpuinfo) MEM_INFO=$(safegetentry /proc/meminfo) PING_SUCCESS_COUNT=$(safegetentry ping|grep "packet loss"|cut -d, -f 2|awk '{print $1+0}') IPADDR=$(safegetentry ipaddr) INFO_DISKS=$(safegetentry df) KERNEL_CONFIG=$(safegetentry kernel_config) LSHW=$(safegetentry lshw) # Extract infos INFO_OS_NAME=$(echo "$OS_RELEASE"|grep "^NAME=" | cut -d'"' -f 2) INFO_OS_VERSION=$(echo "$OS_RELEASE"|grep "^VERSION=" | cut -d'"' -f 2) INFO_HOSTNAME=$(safegetentry hostname) INFO_CPU_MODEL=$(echo "$CPU_INFO"|grep "model name" | cut -d':' -f 2|uniq|awk '{$1=$1};1') INFO_CPU_CORE_N_PHY=$(echo "$CPU_INFO"|grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') INFO_CPU_CORE_N_VIRT=$(echo "$CPU_INFO"|grep -c ^processor /proc/cpuinfo) [ "$INFO_CPU_CORE_N_PHY" -eq "$INFO_CPU_CORE_N_VIRT" ] && INFO_CPU_HYPERTHREADING="off" || INFO_CPU_HYPERTHREADING="on" [ $PING_SUCCESS_COUNT -gt 0 ] && INFO_OTHER_NETWORK="Available" || INFO_OTHER_NETWORK="Unreachable" INFO_INET4=$(echo "$IPADDR"|awk '/inet /{printf $2", "}'| sed 's/..$//') INFO_INET6=$(echo "$IPADDR"|awk '/inet6 /{printf $2", "}'| sed 's/..$//') INFO_MEM_TOTAL=$(echo "$MEM_INFO"|awk '/MemTotal/{print $2/1000" MB"}') INFO_MEM_FREE=$(echo "$MEM_INFO"|awk '/MemFree/{print $2/1000" MB"}') INFO_MEM_SWAP_TOTAL=$(echo "$MEM_INFO"|awk '/SwapTotal/{print $2/1000" MB"}') INFO_MEM_SWAP_FREE=$(echo "$MEM_INFO"|awk '/SwapFree/{print $2/1000" MB"}') INFO_UPTIME=$(safegetentry uptime|cut -d, -f 1|awk '{$1=$1};1') INFO_DUMP_DATE=$(safegetentry date) && INFO_DUMP_DATE=$(date -d "@${INFO_DUMP_DATE}") INFO_DUMP_USER=$(safegetentry env|grep "^USER="|cut -d= -f2) INFO_TIMEZONE=$(safegetentry /etc/timezone) INFO_KERNEL=$(safegetentry /proc/version|awk '/ version /{print $1" v"$3}') INFO_IS_VIRT=$(safegetentry systemd-detect-virt|awk 'BEGIN{state="No"};!/none/{state="Yes ("$0")"};END{print(state)}') INFO_KERNEL_RAPL=$(echo "$KERNEL_CONFIG"|awk 'BEGIN{DETECTED=0};/^CONFIG_INTEL_RAPL=(y|m)/{DETECTED=1};END{if(DETECTED){print("Supported")}else{print("NA")}}') INFO_KERNEL_KVM=$(echo "$KERNEL_CONFIG"|awk 'BEGIN{DETECTED=0};/^CONFIG_KVM=(y|m)/{DETECTED=1};END{if(DETECTED){print("Supported")}else{print("NA")}}') INFO_KERNEL_I2C=$(echo "$KERNEL_CONFIG"|awk 'BEGIN{DETECTED=0};/^CONFIG_I2C=(y|m)/{DETECTED=1};END{if(DETECTED){print("Supported")}else{print("NA")}}') INFO_KERNEL_SPI=$(echo "$KERNEL_CONFIG"|awk 'BEGIN{DETECTED=0};/^CONFIG_SPI=(y|m)/{DETECTED=1};END{if(DETECTED){print("Supported")}else{print("NA")}}') INFO_HW_TYPE=$(echo "$LSHW"|grep -m 1 description| awk '{$1="";print($0)}'|awk '{$1=$1};1') INFO_HW_VENDOR=$(echo "$LSHW"|grep -m 1 vendor| awk '{$1="";print($0)}'|awk '{$1=$1};1') INFO_HW_VERSION=$(echo "$LSHW"|grep -m 1 version| awk '{$1="";print($0)}'|awk '{$1=$1};1') INFO_HW_DISPLAY=$(echo "$LSHW"|grep "\-display" -A20|grep -m 1 product| awk '{$1="";print($0)}'|awk '{$1=$1};1') # Print Information echo "====> System <====" echo "OS Name: ${INFO_OS_NAME}" echo "OS Version: ${INFO_OS_VERSION}" echo "Kernel: ${INFO_KERNEL}" echo "Hostname: ${INFO_HOSTNAME}" echo "Uptime: ${INFO_UPTIME}" echo "Virtualization: ${INFO_IS_VIRT}" echo echo "====> CPU <====" echo "Model: ${INFO_CPU_MODEL}" echo "Physical Core Count: ${INFO_CPU_CORE_N_PHY}" echo "Logical Core Count: ${INFO_CPU_CORE_N_VIRT}" echo "HyperThreading State: ${INFO_CPU_HYPERTHREADING}" echo echo "====> RAM <====" echo "Total: ${INFO_MEM_TOTAL}" echo "Free: ${INFO_MEM_FREE}" echo "Swap Total: ${INFO_MEM_SWAP_TOTAL}" echo "Swap Free: ${INFO_MEM_SWAP_FREE}" echo echo "====> Disk <====" [ -z "$INFO_DISKS" ] && echo "ERROR: No disks informations (require df)" [ ! -z "$INFO_DISKS" ] && echo "${INFO_DISKS}" echo echo "====> Kernel Config <====" [ -z "$KERNEL_CONFIG" ] && echo "ERROR: Kernel config not found" if [ ! -z "$KERNEL_CONFIG" ] then echo "Intel RAPL: ${INFO_KERNEL_RAPL}" echo "KVM: ${INFO_KERNEL_KVM}" echo "I2C: ${INFO_KERNEL_I2C}" echo "SPI: ${INFO_KERNEL_SPI}" fi echo echo "====> Applications <====" echo "ssh: $(safegetentry cmd_ssh_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "rsync: $(safegetentry cmd_rsync_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "gcc: $(safegetentry cmd_gcc_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "python3: $(safegetentry cmd_python3_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "wget: $(safegetentry cmd_wget_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "curl: $(safegetentry cmd_curl_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "tmux: $(safegetentry cmd_tmux_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo "git: $(safegetentry cmd_git_version|wc -c|awk '{if($0>0){print("Available")}else{print("NA")}}')" echo echo "====> Hardware <====" [ -z "$LSHW" ] && echo "ERROR: Require lshw and root permissions" if [ ! -z "$LSHW" ] then echo "Type: ${INFO_HW_TYPE}" echo "Vendor: ${INFO_HW_VENDOR}" echo "Version: ${INFO_HW_VERSION}" echo "Display: ${INFO_HW_DISPLAY}" fi echo echo "====> Other informations <====" echo "Dump User: ${INFO_DUMP_USER}" echo "Dump Date: ${INFO_DUMP_DATE}" echo "Timezone: ${INFO_TIMEZONE}" echo "Internet: ${INFO_OTHER_NETWORK}" echo "IPv4: ${INFO_INET4}" echo "IPv6: ${INFO_INET6}" fi