#!/bin/sh PROGNAME="dump-server-state" REPOSITORY="https://gitea.evolix.org/evolix/dump-server-state" VERSION="22.03.8" readonly VERSION dump_dir= rc=0 # base functions show_version() { cat <, Jérémy Lecour , Éric Morino , Brice Waegeneire and others. ${REPOSITORY} ${PROGNAME} comes with ABSOLUTELY NO WARRANTY.This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public License v3.0 for details. END } show_help() { cat < "${dump_dir}/apt-config.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* apt-config OK" else debug "* apt-config ERROR" debug "${last_result}" rc=10 fi else debug "* apt-config is not found" fi } do_dpkg_full() { debug "## DPkg full state" dir_state_status="/var/lib/dpkg/status" apt_config_bin=$(command -v apt-config) if [ -n "${apt_config_bin}" ]; then eval "$(${apt_config_bin} shell dir_state_status Dir::State::status)" fi dpkg_dir=$(dirname "${dir_state_status}") last_result=$(mkdir -p "${dump_dir}${dpkg_dir}" && chmod -R 755 "${dump_dir}${dpkg_dir}") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* mkdir/chmod OK" else debug "* mkdir/chmod ERROR" debug "${last_result}" rc=10 fi rsync_bin=$(command -v rsync) if [ -n "${rsync_bin}" ]; then last_result=$(${rsync_bin} -ah --itemize-changes --exclude='*-old' "${dpkg_dir}/" "${dump_dir}${dpkg_dir}/") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* rsync OK" else debug "* rsync ERROR :" debug "${last_result}" rc=10 fi else debug "* rsync not found" last_result=$(cp -r "${dpkg_dir}/*" "${dump_dir}${dpkg_dir}/" && rm -rf "${dump_dir}${dpkg_dir}/*-old") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* cp OK" else debug "* cp ERROR :" debug "${last_result}" rc=10 fi fi } do_dpkg_status() { debug "## DPkg status" dir_state_status="/var/lib/dpkg/status" apt_config_bin=$(command -v apt-config) if [ -n "${apt_config_bin}" ]; then eval "$(${apt_config_bin} shell dir_state_status Dir::State::status)" fi last_result=$(cp "${dir_state_status}" "${dump_dir}/dpkg-status.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* cp OK" else debug "* cp ERROR :" debug "${last_result}" rc=10 fi } do_packages() { debug "## List of installed package" dpkg_bin=$(command -v dpkg) if [ -n "${dpkg_bin}" ]; then last_result=$(${dpkg_bin} --get-selections "*" > "${dump_dir}/current_packages.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* dpkg OK" else debug "* dpkg ERROR :" debug "${last_result}" rc=10 fi else debug "* dpkg not found" fi } do_uname() { debug "## uname" last_result=$(uname -a > "${dump_dir}/uname.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* uname OK" else debug "* uname ERROR" debug "${last_result}" rc=10 fi } do_uptime() { debug "## uptime" last_result=$(uptime > "${dump_dir}/uptime.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* uptime OK" else debug "* uptime ERROR" debug "${last_result}" rc=10 fi } do_processes() { debug "## Process list" last_result=$(ps fauxw > "${dump_dir}/ps.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* ps OK" else debug "* ps ERROR" debug "${last_result}" rc=10 fi pstree_bin=$(command -v pstree) if [ -n "${pstree_bin}" ]; then last_result=$(${pstree_bin} -pan > "${dump_dir}/pstree.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* pstree OK" else debug "* pstree ERROR" debug "${last_result}" rc=10 fi fi } do_netstat() { debug "## Network status" ss_bin=$(command -v ss) if [ -n "${ss_bin}" ]; then last_result=$(${ss_bin} -tanpul > "${dump_dir}/netstat-ss.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* ss OK" else debug "* ss ERROR" debug "${last_result}" rc=10 fi else debug "* ss not found" fi netstat_bin=$(command -v netstat) if [ -n "${netstat_bin}" ]; then last_result=$(netstat -laputen > "${dump_dir}/netstat-legacy.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* netstat OK" else debug "* netstat ERROR" debug "${last_result}" rc=10 fi else debug "* netstat not found" fi } do_netcfg() { debug "## Network configuration" ip_bin=$(command -v ip) if [ -n "${ip_bin}" ]; then last_result=$(${ip_bin} address show > "${dump_dir}/ip-address.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* ip address OK" else debug "* ip address ERROR" debug "${last_result}" rc=10 fi last_result=$(${ip_bin} route show > "${dump_dir}/ip-route.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* ip route OK" else debug "* ip route ERROR" debug "${last_result}" rc=10 fi else debug "* ip not found" ifconfig_bin=$(command -v ifconfig) if [ -n "${ifconfig_bin}" ]; then last_result=$(${ifconfig_bin} > "${dump_dir}/ifconfig.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* ifconfig OK" else debug "* ifconfig ERROR" debug "${last_result}" rc=10 fi else debug "* ifconfig not found" fi fi } do_iptables() { debug "## iptables" iptables_bin=$(command -v iptables) nft_bin=$(command -v nft) if [ -n "${nft_bin}" ]; then debug "* nft found, skip iptables" else if [ -n "${iptables_bin}" ]; then last_result=$({ ${iptables_bin} -L -n -v; ${iptables_bin} -t filter -L -n -v; } >> "${dump_dir}/iptables-v.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* iptables -v OK" else debug "* iptables -v ERROR" debug "${last_result}" rc=10 fi last_result=$({ ${iptables_bin} -L -n; ${iptables_bin} -t filter -L -n; } >> "${dump_dir}/iptables.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* iptables OK" else debug "* iptables ERROR" debug "${last_result}" rc=10 fi else debug "* iptables not found" fi iptables_save_bin=$(command -v iptables-save) if [ -n "${iptables_save_bin}" ]; then last_result=$(${iptables_save_bin} > "${dump_dir}/iptables-save.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* iptables-save OK" else debug "* iptables-save ERROR" debug "${last_result}" rc=10 fi else debug "* iptables-save not found" fi fi } do_sysctl() { debug "## sysctl values" sysctl_bin=$(command -v sysctl) if [ -n "${sysctl_bin}" ]; then last_result=$(${sysctl_bin} -a --ignore 2>/dev/null | sort -h > "${dump_dir}/sysctl.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* sysctl OK" else debug "* sysctl ERROR" debug "${last_result}" rc=10 fi else debug "* sysctl not found" fi } do_virsh() { debug "## virsh list" virsh_bin=$(command -v virsh) if [ -n "${virsh_bin}" ]; then last_result=$(${virsh_bin} list --all > "${dump_dir}/virsh-list.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* virsh list OK" else debug "* virsh list ERROR" debug "${last_result}" rc=10 fi else debug "* virsh not found" fi } do_lxc() { debug "## lxc list" lxc_ls_bin=$(command -v lxc-ls) if [ -n "${lxc_ls_bin}" ]; then last_result=$(${lxc_ls_bin} --fancy > "${dump_dir}/lxc-list.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* lxc list OK" else debug "* lxc list ERROR" debug "${last_result}" rc=10 fi else debug "* lxc-ls not found" fi } do_disks() { debug "## Disks" lsblk_bin=$(command -v lsblk) awk_bin=$(command -v awk) if [ -n "${lsblk_bin}" ] && [ -n "${awk_bin}" ]; then disks=$(${lsblk_bin} -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | ${awk_bin} '{print $1}') for disk in ${disks}; do dd_bin=$(command -v dd) if [ -n "${dd_bin}" ]; then last_result=$(${dd_bin} if="/dev/${disk}" of="${dump_dir}/MBR-${disk}" bs=512 count=1 2>&1) last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* dd ${disk} OK" else debug "* dd ${disk} ERROR" debug "${last_result}" rc=10 fi else debug "* dd not found" fi fdisk_bin=$(command -v fdisk) if [ -n "${fdisk_bin}" ]; then last_result=$(${fdisk_bin} -l "/dev/${disk}" > "${dump_dir}/partitions-${disk}" 2>&1) last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* fdisk ${disk} OK" else debug "* fdisk ${disk} ERROR" debug "${last_result}" rc=10 fi else debug "* fdisk not found" fi done cat "${dump_dir}"/partitions-* > "${dump_dir}/partitions" else if [ -n "${lsblk_bin}" ]; then debug "* lsblk not found" fi if [ -n "${awk_bin}" ]; then debug "* awk not found" fi fi } do_mount() { debug "## Mount points" findmnt_bin=$(command -v findmnt) if [ -n "${findmnt_bin}" ]; then last_result=$(${findmnt_bin} > "${dump_dir}/mount.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* mount points OK" else debug "* mount points ERROR" debug "${last_result}" rc=10 fi else debug "* findmnt not found" mount_bin=$(command -v mount) if [ -n "${mount_bin}" ]; then last_result=$(${mount_bin} > "${dump_dir}/mount.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* mount points OK" else debug "* mount points ERROR" debug "${last_result}" rc=10 fi else debug "* mount not found" fi fi } do_df() { debug "## df" df_bin=$(command -v df) if [ -n "${df_bin}" ]; then last_result=$(${df_bin} --portability > "${dump_dir}/df.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* df OK" else debug "* df ERROR" debug "${last_result}" rc=10 fi else debug "* df not found" fi } do_dmesg() { debug "## dmesg" dmesg_bin=$(command -v dmesg) if [ -n "${dmesg_bin}" ]; then last_result=$(${dmesg_bin} > "${dump_dir}/dmesg.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* dmesg OK" else debug "* dmesg ERROR" debug "${last_result}" rc=10 fi else debug "* dmesg not found" fi } do_mysql_processes() { debug "## MySQL processes" mysqladmin_bin=$(command -v mysqladmin) if [ -n "${mysqladmin_bin}" ]; then # Look for local MySQL or MariaDB process if pgrep mysqld > /dev/null || pgrep mariadbd > /dev/null; then last_result=$(${mysqladmin_bin} --verbose processlist > "${dump_dir}/mysql-processlist.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* mysqladmin OK" else debug "* mysqladmin ERROR" debug "${last_result}" rc=10 fi else debug "* no mysqld or mariadbd process is running" fi else debug "* mysqladmin not found" fi } do_systemctl() { debug "## Systemd services" systemctl_bin=$(command -v systemctl) if [ -n "${systemctl_bin}" ]; then last_result=$(${systemctl_bin} --no-legend --state=failed --type=service > "${dump_dir}/systemctl-failed-services.txt") last_rc=$? if [ ${last_rc} -eq 0 ]; then debug "* failed services OK" else debug "* failed services ERROR" debug "${last_result}" rc=10 fi else debug "* systemctl not found" fi } main() { if [ -z "${dump_dir}" ]; then echo "ERROR: You must provide the --dump-dir argument" >&2 exit 1 fi if [ -d "${dump_dir}" ]; then if [ "${FORCE}" != "1" ]; then echo "ERROR: The dump directory ${dump_dir} already exists. Delete it first." >&2 exit 2 fi else create_dump_dir fi if [ "${DO_ETC}" -eq 1 ]; then do_etc fi if [ "${DO_DPKG_FULL}" -eq 1 ]; then do_dpkg_full fi if [ "${DO_DPKG_STATUS}" -eq 1 ]; then do_dpkg_status fi if [ "${DO_APT_STATES}" -eq 1 ]; then do_apt_states fi if [ "${DO_APT_CONFIG}" -eq 1 ]; then do_apt_config fi if [ "${DO_PACKAGES}" -eq 1 ]; then do_packages fi if [ "${DO_PROCESSES}" -eq 1 ]; then do_processes fi if [ "${DO_UPTIME}" -eq 1 ]; then do_uptime fi if [ "${DO_UNAME}" -eq 1 ]; then do_uname fi if [ "${DO_NETSTAT}" -eq 1 ]; then do_netstat fi if [ "${DO_NETCFG}" -eq 1 ]; then do_netcfg fi if [ "${DO_IPTABLES}" -eq 1 ]; then do_iptables fi if [ "${DO_SYSCTL}" -eq 1 ]; then do_sysctl fi if [ "${DO_VIRSH}" -eq 1 ]; then do_virsh fi if [ "${DO_LXC}" -eq 1 ]; then do_lxc fi if [ "${DO_DISKS}" -eq 1 ]; then do_disks fi if [ "${DO_MOUNT}" -eq 1 ]; then do_mount fi if [ "${DO_DF}" -eq 1 ]; then do_df fi if [ "${DO_DMESG}" -eq 1 ]; then do_dmesg fi if [ "${DO_MYSQL_PROCESSES}" -eq 1 ]; then do_mysql_processes fi if [ "${DO_SYSTEMCTL}" -eq 1 ]; then do_systemctl fi debug "=> Your dump is available at ${dump_dir}" exit ${rc} } # parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do case $1 in -h|-\?|--help) show_help exit 0 ;; -V|--version) show_version exit 0 ;; -v|--verbose) VERBOSE=1 ;; -f|--force) FORCE=1 ;; -d|--dump-dir) # with value separated by space if [ -n "$2" ]; then dump_dir=$2 shift else printf 'ERROR: "-d|--dump-dir" requires a non-empty option argument.\n' >&2 exit 1 fi ;; --dump-dir=?*) # with value speparated by = dump_dir=${1#*=} ;; --dump-dir=) # without value printf 'ERROR: "--dump-dir" requires a non-empty option argument.\n' >&2 exit 1 ;; --backup-dir) printf 'WARNING: "--backup-dir" is deprecated in favor of "--dump-dir".\n' if [ -n "${dump_dir}" ]; then debug "Dump directory is already set, let's ignore this one." else debug "Dump directory is not set already, let's stay backward compatible." # with value separated by space if [ -n "$2" ]; then dump_dir=$2 shift else printf 'ERROR: "--backup-dir" requires a non-empty option argument.\n' >&2 exit 1 fi fi ;; --backup-dir=?*) # with value speparated by = printf 'WARNING: "--backup-dir" is deprecated in favor of "--dump-dir".\n' if [ -n "${dump_dir}" ]; then debug "Dump directory is already set, let's ignore this one." else debug "Dump directory is not set already, let's stay backward compatible." dump_dir=${1#*=} fi ;; --backup-dir=) # without value printf 'WARNING: "--backup-dir" is deprecated in favor of "--dump-dir".\n' if [ -n "${dump_dir}" ]; then debug "Dump directory is already set, let's ignore this one." else printf 'ERROR: "--backup-dir" requires a non-empty option argument.\n' >&2 exit 1 fi ;; --etc) DO_ETC=1 ;; --no-etc) DO_ETC=0 ;; --dpkg-full) DO_DPKG_FULL=1 ;; --no-dpkg-full) DO_DPKG_FULL=0 ;; --dpkg-status) DO_DPKG_STATUS=1 ;; --no-dpkg-status) DO_DPKG_STATUS=0 ;; --apt-states) DO_APT_STATES=1 ;; --no-apt-states) DO_APT_STATES=0 ;; --apt-config) DO_APT_CONFIG=1 ;; --no-apt-config) DO_APT_CONFIG=0 ;; --packages) DO_PACKAGES=1 ;; --no-packages) DO_PACKAGES=0 ;; --processes) DO_PROCESSES=1 ;; --no-processes) DO_PROCESSES=0 ;; --uptime) DO_UPTIME=1 ;; --no-uptime) DO_UPTIME=0 ;; --uname) DO_UNAME=1 ;; --no-uname) DO_UNAME=0 ;; --netstat) DO_NETSTAT=1 ;; --no-netstat) DO_NETSTAT=0 ;; --netcfg) DO_NETCFG=1 ;; --no-netcfg) DO_NETCFG=0 ;; --iptables) DO_IPTABLES=1 ;; --no-iptables) DO_IPTABLES=0 ;; --sysctl) DO_SYSCTL=1 ;; --no-sysctl) DO_SYSCTL=0 ;; --virsh) DO_VIRSH=1 ;; --no-virsh) DO_VIRSH=0 ;; --lxc) DO_LXC=1 ;; --no-lxc) DO_LXC=0 ;; --disks) DO_DISKS=1 ;; --no-disks) DO_DISKS=0 ;; --mount) DO_MOUNT=1 ;; --no-mount) DO_MOUNT=0 ;; --df) DO_DF=1 ;; --no-df) DO_DF=0 ;; --dmesg) DO_DMESG=1 ;; --no-dmesg) DO_DMESG=0 ;; --mysql-processes) DO_MYSQL_PROCESSES=1 ;; --no-mysql-processes) DO_MYSQL_PROCESSES=0 ;; --systemctl) DO_SYSTEMCTL=1 ;; --no-systemctl) DO_SYSTEMCTL=0 ;; --) # End of all options. shift break ;; -?*) # ignore unknown options printf 'WARN: Unknown option : %s\n' "$1" >&2 exit 1 ;; *) # Default case: If no more options then break out of the loop. break ;; esac shift done # Default values : "${VERBOSE:=0}" : "${FORCE:=0}" : "${DO_ETC:=0}" : "${DO_DPKG_FULL:=0}" : "${DO_DPKG_STATUS:=1}" : "${DO_APT_STATES:=1}" : "${DO_APT_CONFIG:=1}" : "${DO_PACKAGES:=1}" : "${DO_PROCESSES:=1}" : "${DO_UNAME:=1}" : "${DO_UPTIME:=1}" : "${DO_NETSTAT:=1}" : "${DO_NETCFG:=1}" : "${DO_IPTABLES:=1}" : "${DO_SYSCTL:=1}" : "${DO_VIRSH:=1}" : "${DO_LXC:=1}" : "${DO_DISKS:=1}" : "${DO_MOUNT:=1}" : "${DO_DF:=1}" : "${DO_DMESG:=1}" : "${DO_MYSQL_PROCESSES:=1}" : "${DO_SYSTEMCTL:=1}" export LC_ALL=C set -u main