#!/bin/bash
#
# SpinetiX system report
#
# Copyright (c) 2016 SpinetiX S.A. All rights reserved.
#
# Considerably inspired by the sysreport tool from Red Hat Inc.
#
# WARNING: this utility will output to stdout a tar.gz file with the
# report, so be careful about output anything to stdout, stderr is OK.
#
# Command line switches:
#
# -x : enables an extended report is generated (including lenghty tests)
# -r : used when running in the recovery system (skips tests for
#      subsystems not present in the recovery console)
# -R <path> : path to mounted root fs of main system (used when
#      run from the recovery console)
#
# When running chroot'ed the extended mode is automatically enabled
# (this occurs when running within the normal system but from a device
# booted into the recovery console).
#
# Extended and recovery modes are not exclusive fo each other.
#

# Explicitly redirect stdin to be /dev/null so that no command may block on
# stdin or interfere with the parent's stdin.
exec < /dev/null

umask 0077
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
DATE=$(/bin/date -u +%Y%m%d%H%M%S | /usr/bin/tr -d ' ')

# Actual FFSTYPE set by inithwinfo()
FFSTYPE=auto

# The GUIDs of UEFI global variables and UEFI security database variables
UEFI_GLBL_GUID=8be4df61-93ca-11d2-aa0d-00e098032b8c
UEFI_SECDB_GUID=d719b2cb-3d3a-4596-a3bc-dad00e67656f

if [ $UID != 0 ]; then
  echo "ERROR: you must be root to use this utility" >&2
  exit 1
fi

# Limit the maximum running CPU time to 5 minutes (gzip may take long)
# to avoid problems with runaway sub-processes
ulimit -S -t 300 # soft, sends SIGXCPU
ulimit -H -t 320 # hard, sends SIGKILL

# Parse command line switches
DO_EXTENDED=
DO_RECOVERY=
ROOTPREFIX=
while getopts ":xrR:" opt ; do
    case "$opt" in
        x)
            DO_EXTENDED="yes"
            ;;
        r)
            DO_RECOVERY="yes"
            ;;
        R)
            ROOTPREFIX="$OPTARG"
            ;;
        \?)
            echo "WARNING: ignoring invalid option -$OPTARG" >&2
            ;;
        :)
            echo "WARNING: option -$OPTARG requires an argument" >&2
            ;;
    esac
done

# Test if running inside chroot or not
if [ "$(stat -c %d:%i / 2>/dev/null)" != "$(stat -c %d:%i /proc/1/root/. 2>/dev/null)" ]; then
    IN_CHROOT="yes"
else
    IN_CHROOT=''
fi
# Automatically enable extended in chroot
[ "$IN_CHROOT" = "yes" ] && DO_EXTENDED="yes"

# Be sure TMPDIR is on a large storage; when running within recovery
# both /tmp and /var/tmp are a tmpfs; in the normal system /var/tmp
# has large storage.
TMPDIR=/var/tmp
export TMPDIR

# The standard NV index were the RSA 2048 EK certificate is stored
EK_RSA_CERT_NV_INDEX=0x1C00002

# The standard NV index were the ECC NIST P-256 EK certificate is stored
EK_ECC_CERT_NV_INDEX=0x1C0000A

# The persistent handle to store the RSA 2048 EK, it is the one used by Windows 10
EK_RSA_PHANDLE=0x81010001

#
# FUNCTIONS
#

is_mounted() {
    local dev mpt fstype opts dump pass junk

    while read dev mpt fstype opts dump pass junk; do
        [ "$mpt" = "$1" ] && return 0 # found
    done < /proc/mounts

    return 1 # not found
}

inithwinfo() {
    HWTYPE=
    MODELEEPROMNAME=
    MTDTYPE=
    FFSTYPE=
    KDEVPARTNAME=
    RECOVERYIMGBDEV1=
    RECOVERYIMGBDEV2=
    RECOVERYDATABDEV1=
    RECOVERYDATABDEV2=
    RECOVERYIMGTYPE=
    RECOVERYDATATYPE=
    HASUBOOT=
    HASGRUB=
    local rootname rootdev partsep

    if [ "$HOSTTYPE" = "arm" ]; then
        while read tag sep val; do
            if [ "$tag" = "Hardware" ]; then
                HWTYPE="$val"
                break;
            fi
        done < /proc/cpuinfo
    elif [ -d /sys/class/dmi/id ]; then
        if [ "$(< /sys/class/dmi/id/sys_vendor)" = "SpinetiX" ]; then
            HWTYPE="$(< /sys/class/dmi/id/product_family)"
            [ -n "$HWTYPE" -a "$HWTYPE" != "SYSTEM_FAMILY" ] || HWTYPE="$(< /sys/class/dmi/id/product_name)"
        else
            HWTYPE="generic-pc"
        fi
    fi

    case "$HWTYPE" in
        Sakura)
            MTDTYPE=nand
            FFSTYPE=jffs2
            RECOVERYDATATYPE=jffs2
            HASUBOOT=1
            ;;
        Bonsai)
            MTDTYPE=nor
            FFSTYPE=cramfs
            RECOVERYDATATYPE=cramfs
            HASUBOOT=1
            ;;
        ikebana)
            MODELEEPROMNAME=single
            MTDTYPE=nor
            RECOVERYIMGBDEV1=/dev/mmcblk0gp0
            RECOVERYIMGBDEV2=/dev/mmcblk0gp1
            RECOVERYDATABDEV1=/dev/mmcblk0boot0
            RECOVERYDATABDEV2=/dev/mmcblk0boot1
            RECOVERYIMGTYPE=mkimage
            RECOVERYDATATYPE=cramfs
            HASUBOOT=1
            ;;
        Fukiran)
            RECOVERYIMGBDEV1=/dev/mmcblk0p4
            RECOVERYIMGBDEV2=/dev/mmcblk0p5
            RECOVERYDATABDEV1=/dev/mmcblk0boot0
            RECOVERYDATABDEV2=/dev/mmcblk0boot1
            RECOVERYIMGTYPE=squashfs
            RECOVERYDATATYPE=squashfs
            HASGRUB=1
            ;;
        generic-pc|iBX410|iBX410W|iBX440)
            # failsafe / recovery images are in partitions 4 and 5 of the disk where the root fs is
            # failsafe / recovery data are in partitions 6 and 7 of the disk where the root fs is
            rootname="$(lsblk --raw --noheadings -o TYPE,PARTUUID,PKNAME | awk '{ if ($1 == "part" && $2 == "e178fa35-fbc0-4f66-9da5-cc7ad00e527e") { print $3; exit } }')"
            rootdev="$(lsblk --raw --noheadings -o TYPE,KNAME,PATH | awk -v pkname="$rootname" '{ if ($1 == "disk" && $2 == pkname) { print $3; exit } }')"
            if [ -n "$rootdev" ]; then
                # Only device names ending in a digit have "p" to separate the partition number
                [ "${rootdev%[0-9]}" != "$rootdev" ] && partsep=p || partsep=
                RECOVERYIMGBDEV1="$rootdev$partsep"4
                RECOVERYIMGBDEV2="$rootdev$partsep"5
                RECOVERYDATABDEV1="$rootdev$partsep"6
                RECOVERYDATABDEV2="$rootdev$partsep"7
            fi
            RECOVERYIMGTYPE=squashfs
            RECOVERYDATATYPE=squashfs
            HASGRUB=1
            ;;
        *)
            echo "WARNING: unknown hardware type '$HWTYPE'" >&2
            ;;
    esac
}

function reassign_stdout()
{
    exec 3>&1
    exec > $1
}

function restore_stdout()
{
    exec 1>&3 3>&-
}

function catiffile()
{
    if [ -f "$1" ]; then
        cat "$1" 2>&1
    else
        echo "ERROR: file '$1' not found or not a regular file"
    fi
}

function catifpath()
{
    if [ -e "$1" ]; then
        cat "$1" 2>&1
    else
        echo "ERROR: path '$1' not found"
    fi
}

function catifexec()
{
    "$@" 2>&1
    RET=$?
    if [ $RET -ne 0 ]; then
        echo "ERROR: command '$1' failed (exit code: $RET)"
    fi
    return $RET
}

# same as catifexec but filtering out terminal escape sequences
function catifexec_term_filter()
{
    # the sed below filters out all ASCII terminal CSI sequences (color, font, etc.)
    "$@" 2>&1 | sed 's/\x1b\[[\x30-\x3F]*[\x20-\x2F]*[\x40-\x7E]//g'
    RET=${PIPESTATUS[0]}
    if [ $RET -ne 0 ]; then
        echo "ERROR: command '$1' failed (exit code: $RET)"
    fi
    return $RET
}

# runs a command in a given directory
# arg1: directory
# arg2: command
# arg3...argN: command arguments
function run_in_dir()
{
    local dir="$1"
    shift
    (cd "$dir" && "$@")
}

function section_start()
{
    echo
    echo "=== $@"
}

function subsection_start()
{
    echo
    echo "--- $@"
}

function mkimagedev()
{
    local tmp
    local ret
    tmp="$(mktemp -t mtd.XXXXXXXX)"
    if [ $? -ne 0 ]; then
        return 1
    fi
    if [ "$MTDTYPE" = "nand" ]; then
        nanddump -b -o "$1" > "$tmp"
    elif [ "$MTDTYPE" = "nor" ]; then
        cp "$1" "$tmp"
    fi
    if [ $? -ne 0 ]; then
        rm -f "$tmp"
        return 1
    fi
    mkimage -l "$tmp"
    ret=$?
    rm -f "$tmp"
    return $ret
}

# arg1: block device
# arg2: maximum size in KiB (optional)
# if maximum size is not provided a reasonable one is used
function mkimageblkdev()
{
    local tmp tmplog
    local ret
    local maxkb="$2"
    tmp="$(mktemp -t blk.XXXXXXXX)" && tmplog="$(mktemp -t dd.XXXXXXXX)"
    if [ $? -ne 0 ]; then
        [ -n "$tmp" ] && rm -f "$tmp"
        [ -n "$tmplog" ] && rm -f "$tmplog"
        return 1
    fi
    # we impose a count just in case the partition is
    # oversized, we use 16k block size for performance and
    # future compatibility with large logical sector devices
    [ -n "$maxkb" ] || maxkb="$((64*1024))"
    # NOTE: dd always outputs some status to stderr, so redirect it to
    # a temporary file and output its stderr only if dd fails.
    dd bs=16k if="$1" of="$tmp" count="$(( (maxkb + 15) / 16 ))" \
        status=noxfer 2>"$tmplog"
    if [ $? -ne 0 ]; then
        cat "$tmplog"
        rm -f "$tmp" "$tmplog"
        return 1
    fi
    mkimage -l "$tmp"
    ret=$?
    rm -f "$tmp" "$tmplog"
    return $ret
}

# dumps the recovery image section when stored on a block device
# arg1: block device
# arg2: recovery image index
function dump_bdev_recovery_section()
{
    local bdev="$1"
    local idx="$2"
    if [ -n "$bdev" ]; then
        case "$RECOVERYIMGTYPE" in
            mkimage)
                section_start "U-Boot recovery image $idx"
                catifexec mkimageblkdev "$bdev"
               ;;
            squashfs)
                section_start "squashfs recovery image $idx"
                catifexec unsquashfs -l "$bdev"
                ;;
            *)
                echo "WARNING: unknown recovery image type '$RECOVERYIMGTYPE'" >&2
        esac
    fi
}

#
# MAIN
#

# When running in chroot, meaning from recovery console, the bind
# mounts in /etc/fstab might be missing if running from an old
# recovery console, so we mount any missing bind mounts
if [ "$IN_CHROOT" = "yes" ]; then
    while read dev mpt fstype opts dump pass junk; do

        [ "${dev:0:1}" = "#" ] && continue # comment line 
        [ "$opts" = "bind" ] || continue # only bind mounts
        [ "${dev:0:5}" = "/dev/" ] && continue # a true device
        [ "${dev:0:1}" != "/" ] && continue # not a path
        [ -d "$dev" ] || continue # the source of the bind does not exist

        is_mounted "$mpt" || mount "$mpt"

    done < /etc/fstab > /dev/null 2>&1

    # create the important entries in /var/volatile
    [ ! -e /var/volatile/log ] && mkdir /var/volatile/log
    [ ! -e /var/volatile/tmp ] && mkdir /var/volatile/tmp && chmod 1777 /var/volatile/tmp
    [ ! -e /tmp ] && ln -snf var/volatile/tmp /tmp
fi

ROOT=$(mktemp -d /tmp/spxreport-XXXXXXXX)
if [ $? -ne 0 ]; then
    echo "ERROR: could not create temp dir" >&2
    exit 1
fi

trap "rm -rf $ROOT" EXIT

inithwinfo

if [ -d /sys/firmware/efi/efivars ]; then
    if ! is_mounted /sys/firmware/efi/efivars; then
        mount -t efivarfs none /sys/firmware/efi/efivars
    fi
fi

# === date ===

echo "$DATE" > $ROOT/date

# === system data ===

# From here stdout redirected to file
reassign_stdout $ROOT/sys-data

section_start "OS release"
catiffile /etc/os-release

section_start "SpinetiX firmware release"
catiffile /etc/spinetix-release

if [ -z "$DO_RECOVERY" ]; then
    section_start "Updater status"
    catiffile /var/lib/updater/status
fi

section_start "Filesystem version"
catiffile /etc/version

section_start "Date"
catifexec date +"%a %F %T [%Z / UTC%z]"

if [ -z "$DO_RECOVERY" ]; then
    section_start "Timezone file"
    catifexec readlink -q -f /etc/localtime
fi

section_start "Uptime"
catifexec uptime

section_start "Power-on time counters"
for f in /sys/bus/i2c/drivers/{spx-eeprom,Bonsai-EEPROM}/*/poweron_hours; do
    if [ -f "$f" -a -r "$f" ]; then
        s="${f#/sys/bus/i2c/drivers/}"
        subsection_start "${s#*/}"
        catiffile "$f"
    fi
done

if [ -n "$MODELEEPROMNAME" ]; then
    section_start "Model name and signature"
    for d in /sys/bus/i2c/drivers/{spx-eeprom,Bonsai-EEPROM}/*; do
        if ! [ -f "$d"/subname ]; then
            continue
        fi
        if [ "$(cat "$d"/subname 2>/dev/null)" != "$MODELEEPROMNAME" ]; then
            continue
        fi
        s="$(dd if="$d"/data bs=1 count=4 skip=17 2>/dev/null)"
        case "$s" in
            ikes)
                m=DiVA
                ;;
            ikem)
                m=HMP300
                ;;
            ikex|ikeb)
                m=HMP350
                ;;
            *)
                m=unknown
                ;;
        esac
        echo "$m - $s"
    done
fi

if [ -d /sys/class/spx-board-info/id ]; then
    section_start "Board info"
    for f in /sys/class/spx-board-info/id/*; do
        if [ -f "$f" -a -r "$f" ]; then
            s="${f##*/}"
            [ "$s" != "uevent" ] || continue
            subsection_start "$s"
            catiffile "$f"
        fi
    done
fi

if [ -z "$DO_RECOVERY" ]; then
    section_start "Branding"
    catifexec readlink /usr/share/resources/default
fi

section_start "CPU and serial"
catiffile /proc/cpuinfo

section_start "Memory"
catiffile /proc/meminfo

if [ -d /sys/class/dmi/id ]; then
    section_start "DMI data"
    for f in /sys/class/dmi/id/*; do
        if [ -f "$f" -a -r "$f" ]; then
            s="${f##*/}"
            [ "$s" != "uevent" ] || continue
            subsection_start "$s"
            catiffile "$f"
        fi
    done
fi

section_start "Running processes and threads"
catifexec ps -e H \
    -o euser,pid,ppid,tid,ni,cls,rtprio,priority,tty,%cpu,%mem,rss,vsz,start_time,time,state,wchan,cmd

section_start "Process tree"
catifexec pstree -A -l -p

section_start "Memory statistics"
catifexec memstat -w

section_start "Hostname"
catifexec hostname

section_start "Network links"
catifexec ip link

section_start "Network addresses"
catifexec ip addr

section_start "Network routes"
catifexec ip -4 route
catifexec ip -6 route

section_start "Network link stats"
catifexec netstat -i -e

section_start "Network protocol stats"
catifexec netstat -s

section_start "Listening network endpoints"
catifexec netstat -n -l -e -p

section_start "Client network endpoints"
catifexec netstat -n -e -p -o -u -t

if [ "$HWTYPE" = "Bonsai" -o "$HWTYPE" = Sakura ]; then
    section_start "EMAC link"
    catiffile /proc/net/emac_link

    section_start "EMAC stats"
    catiffile /proc/net/emac_rfc2665_stats
fi

section_start "Multicast groups"
catifexec ip maddr

section_start "IGMP state"
catiffile /proc/net/igmp

section_start "IGMP multicast filter"
catiffile /proc/net/mcfilter

section_start "IGMPv6 state"
catiffile /proc/net/igmp6

section_start "IGMPv6 multicast filter"
catiffile /proc/net/mcfilter6

# No NTP in recovery console
if [ -z "$DO_RECOVERY" ]; then

    section_start "NTP statistics"
    catifexec ntpq -n -w -c 'timeout 300' -c peers

    section_start "NTP system variables"
    catifexec ntpq -n -w -c 'timeout 300' -c rv

    section_start "NTP current offsets"
    catifexec ntpdate -q -t 0.4 127.0.0.1 `ntpq -n -w -c 'timeout 300' -c peers | sed -n '/^.[^[:space:]=]/{s/^.//;s/[[:space:]].*//;p}'`

    section_start "NTP saved drift"
    catiffile /var/lib/ntp/drift

    section_start "NTP MRU list"
    catifexec ntpq -n -w -c 'timeout 300' -c mrulist

fi # end ! DO_RECOVERY

section_start "Startup services"
catifexec ls -R /etc/rc*.d

if [ -z "$DO_RECOVERY" ]; then
    section_start "IPC"
    catifexec ipcs -a
fi

section_start "Disk"
catifexec df -al

section_start "Partitions"
catiffile /proc/partitions

section_start "Block devices"
catifexec lsblk -o "NAME,MAJ:MIN,RM,SIZE,UUID,FSTYPE,LABEL,MOUNTPOINT,PARTTYPE,PARTLABEL,PARTUUID,PARTFLAGS,TRAN,VENDOR"

section_start "MMC/SD partition tables"
catifexec sfdisk -d /dev/mmcblk[0-9]

if [ "$HWTYPE" = "Sakura" ]; then

    section_start "IDE/ATA partition tables"
    catifexec sfdisk -d /dev/hd[a-d]

    section_start "IDE/ATA drive data"
    for f in /proc/ide/ide*/*{,/*}; do
        if [ -f "$f" -a -r "$f" ]; then
            section_start "$f"
            catiffile "$f"
        fi
    done

    section_start "IDE/ATA extended drive data"
    for d in /dev/hd[a-d]; do
        if [ -b "$d" ]; then
            section_start "$d"
            catifexec hdparm -adkmruiI "$d"
        fi
    done

fi # end HWTYPE = Sakura

section_start "USB/SCSI partition tables"
catifexec sfdisk -d /dev/sd[a-d]

section_start "NVMe partition tables"
catifexec sfdisk -d /dev/nvme[0-9]n[0-9]

if [ -e /proc/mtd ]; then
    section_start "Flash partitions"
    catiffile /proc/mtd
fi

if [ -d /sys/firmware/efi ]; then
    section_start "EFI boot manager data"
    catifexec efibootmgr -v
fi

# These requires U-Boot mkimage tool to be installed
if [ -n "$HASUBOOT" ]; then
    section_start "U-Boot version"
    if [ "$HWTYPE" != "ikebana" ]; then
        bootloader="$(LC_ALL=C tr -c '[ -~]' '\n' < /dev/mtd0ro | grep 'U-Boot [0-9]')"
    else
        # on Ikebana main bootloader is in U-Boot image format
        mkimagedev /dev/mtd0ro
    fi
    if [ $? -eq 0 ]; then
        echo "$bootloader"
    else
        echo "ERROR: could not recover bootloader version"
    fi

    section_start "U-Boot environment"
    catifexec fw_printenv
fi

if [ -n "$DO_EXTENDED" ]; then
    if type spxblflash > /dev/null 2>&1; then
        section_start 'Bootloader check'
        catifexec spxblflash -n -c
    fi

    if [ "$HWTYPE" = "Bonsai" -o "$HWTYPE" = "Sakura" ]; then
        section_start 'U-Boot image "kernel"'
        mtd=$(sed -e '/"kernel"$/{s,:.*,,;p};d' /proc/mtd)
        catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "failsafe-kernel"'
        mtd=$(sed -e '/"failsafe-kernel"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "failsafe-fs"'
        mtd=$(sed -e '/"failsafe-fs"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "failsafe-img"'
        mtd=$(sed -e '/"failsafe-img"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'Filesystem "failsafe-res" contents'
        mtdblock="$(sed -e '/"failsafe-res"$/{s,\([0-9]\+\):.*,block\1,;p};d' /proc/mtd)"
        if [ -n "$mtdblock" ]; then
            mpt="$(mktemp -d /tmp/mnt.XXXXXXXX)"
            catifexec mount -t "$FFSTYPE" -r /dev/"$mtdblock" "$mpt"
            if [ $? -eq 0 ]; then
                catifexec ls -laNR --time-style=long-iso "$mpt"
                catifexec umount "$mpt"
            else
                echo 'INFO: no "failsafe-res" filesystem or corrupted'
            fi
            catifexec rmdir "$mpt"
        else
            echo 'INFO: "failsafe-res" mtd partition not found'
        fi
    fi

    if [ "$HWTYPE" = "ikebana" ]; then
        section_start 'U-Boot image "bootloader"'
        mtd=$(sed -e '/"bootloader"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "bootloader-fb"'
        mtd=$(sed -e '/"bootloader-fb"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "ubloader-stage1"'
        mtd=$(sed -e '/"ubloader-stage1"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

        section_start 'U-Boot image "ubloader-stage1-fb"'
        mtd=$(sed -e '/"ubloader-stage1-fb"$/{s,:.*,,;p};d' /proc/mtd)
        [ -n "$mtd" ] && catifexec mkimagedev /dev/"${mtd}"ro

    fi

    if [ -n "$RECOVERYIMGBDEV1" ]; then
        dump_bdev_recovery_section "$RECOVERYIMGBDEV1" 1
    fi

    if [ -n "$RECOVERYIMGBDEV2" ]; then
        dump_bdev_recovery_section "$RECOVERYIMGBDEV2" 2
    fi

    section_start 'Filesystem "failsafe-data" contents'
    fddev=
    if [ "$HWTYPE" = "Bonsai" -o "$HWTYPE" = "Sakura" ]; then
        mtdblock="$(sed -e '/"failsafe-data"$/{s,\([0-9]\+\):.*,block\1,;p};d' /proc/mtd)"
        if [ -n "$mtdblock" ]; then
            fddev=/dev/"$mtdblock"
        else
            echo 'INFO: "failsafe-data" mtd partition not found'
        fi
    else
        for dev in "$RECOVERYDATABDEV1" "$RECOVERYDATABDEV2"; do
            [ -b "$dev" ] || continue
            blkid -c /dev/null -w /dev/null -t TYPE="$RECOVERYDATATYPE" "$dev" > /dev/null || continue
            fddev="$dev"
            break
        done
    fi
    if [ -n "$fddev" ]; then
        mpt="$(mktemp -d /tmp/mnt.XXXXXXXX)"
        catifexec mount -t "$RECOVERYDATATYPE" -r "$fddev" "$mpt"
        if [ $? -eq 0 ]; then
            catifexec ls -laNR --time-style=long-iso "$mpt"
            catifexec umount "$mpt"
        else
            echo 'INFO: no "failsafe-data" filesystem or corrupted'
        fi
        catifexec rmdir "$mpt"
    else
        echo "(not present)"
    fi

    if [ -n "$KDEVPARTNAME" ]; then
        section_start "Kernel image on $KDEVPARTNAME"
        catifexec mkimageblkdev /dev/"$KDEVPARTNAME" "$KDEVBLKIMGSIZE"
    fi

    if type -t mkimage > /dev/null; then
        section_start "Kernel image on $ROOTPREFIX/boot/uImage"
        catifexec mkimage -l "$ROOTPREFIX/boot/uImage"
    fi

else
    section_start "Skipping further flash memory tests (not in extended mode)"
fi

# /boot contents
section_start "Contents of $ROOTPREFIX/boot"
catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} "$ROOTPREFIX/boot"

if [ -n "$HASGRUB" ]; then
    section_start "Boot images SHA256 list $ROOTPREFIX/boot/bootimgs.sha256sum"
    catiffile "$ROOTPREFIX/boot/bootimgs.sha256sum"

    section_start "Boot images SHA256 check"
    run_in_dir "$ROOTPREFIX/boot" sha256sum -c bootimgs.sha256sum

    section_start "GRUB list of boot images"
    if [ -f "$ROOTPREFIX/boot/bootimgs.env" ]; then
        catifexec grub-editenv "$ROOTPREFIX/boot/bootimgs.env" list
    else
        echo "Missing $ROOTPREFIX/boot/bootimgs.env"
    fi
fi

section_start 'EEPROM data'
for d in /sys/bus/i2c/drivers/{spx-eeprom,Bonsai-EEPROM}/*; do
    if [ -f "$d"/subname ]; then
        s="$(cat "$d"/subname 2>/dev/null)"
        echo "EEPROM $s dumped to eeprom-$s from $d"
        catiffile "$d"/data > $ROOT/eeprom-"$s"
    fi
done
for d in /sys/bus/i2c/devices/*; do
    f="$d"/eeprom
    [ -f "$f" -a -r "$f" ] || continue
    catiffile "$f" > $ROOT/eeprom-"${d##*/}"
done

# Restore stdout
restore_stdout

# === DHCP data ===

# From here stdout redirected to file
reassign_stdout $ROOT/dhcp-data

section_start "running dhcpcd data"

for f in /run/dhcpcd/*.pid; do
    if [ ! -f "$f" ]; then
        echo "no running dhcpcd"
        continue
    fi
    iface="${f##*/}"
    iface="${iface%.*}"
    subsection_start "$iface"
    catifexec dhcpcd --dumplease -4 "$iface"
done

section_start "saved dhcpcd leases"

for f in /var/lib/dhcpcd/*.lease; do
    if [ ! -f "$f" ]; then
        echo "no saved dhcpcd leases"
        continue
    fi
    iface="${f##*/}"
    iface="${iface%.*}"
    subsection_start "$iface"
    catifexec stat -c 'last modified: %y' "$f"
    catifexec dhcpcd --dumplease -4  < "$f"
done

# Restore stdout
restore_stdout

# === audio-data ===

if [ -d /proc/asound ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/audio-data

    section_start "ALSA cards"
    catiffile /proc/asound/cards

    section_start "ALSA devices"
    catiffile /proc/asound/devices

    section_start "ALSA PCM devices"
    catiffile /proc/asound/pcm

    section_start "ALSA timers"
    catiffile /proc/asound/timers

    if type spxaudiocfg > /dev/null 2>&1; then
        section_start "spxaudiocfg -l"
        catifexec spxaudiocfg -l
    fi

    if type aplay > /dev/null 2>&1; then
        section_start "aplay -L -l"
        catifexec aplay -L -l
    fi

    if type pactl > /dev/null 2>&1; then
        section_start "pactl stat"
        catifexec pactl stat

        section_start "pactl info"
        catifexec pactl info

        section_start "pactl list"
        catifexec pactl list
    fi

    # Restore stdout
    restore_stdout

fi # end of [ -d /proc/asound ]

# === licenses-data ===

# no licenses in recovery
if [ -z "$DO_RECOVERY" ]; then

    # From here stdout redirected to file
    reassign_stdout $ROOT/licenses-data

    section_start 'currently valid licensed features'
    catifexec licensecheck

    section_start 'licenses dump'
    catiffile /etc/spinetix/licenses

    section_start 'device info'
    catifexec licensecheck -d

    # Restore stdout
    restore_stdout

fi # end ! DO_RECOVERY

# === enroll-data ===

# no enrollment in recovery
if [ -z "$DO_RECOVERY" ]; then

    # From here stdout redirected to file
    reassign_stdout $ROOT/enroll-data

    section_start 'spxenroll status'
    catiffile /var/lib/spxenroll/status

    section_start 'spxenroll diagnostics'
    catifexec dbus-send --system --type=method_call --print-reply --reply-timeout=30000 \
        --dest=com.spinetix.SpxPlayer.Enroller1 /com/spinetix/SpxPlayer/Enroller1 com.spinetix.SpxPlayer.Enroller1.diagnose 

    # Restore stdout
    restore_stdout

fi # end ! DO_RECOVERY

# === tpm2-data ===

# From here stdout redirected to file
reassign_stdout $ROOT/tpm2-data

if [ -e /sys/class/tpm/tpm0 ]; then

    section_start 'fixed properties'
    catifexec tpm2 getcap properties-fixed

    section_start 'variable properties'
    catifexec tpm2 getcap properties-variable

    section_start 'NV indices'
    catifexec tpm2 getcap handles-nv-index

    section_start 'persistent handles'
    catifexec tpm2 getcap handles-persistent

    section_start 'PCR values'
    catifexec tpm2 pcrread

    section_start 'NV indices details'
    catifexec tpm2 nvreadpublic

    for h in $(tpm2 getcap handles-persistent | awk '{print $2}'); do
        suff=
        [ "$h" = "$EK_RSA_PHANDLE" ] && suff="-EK-RSA-pubkey"
        section_start "persistent handle $h$suff"
        echo "saving public key to tpm2-$h$suff.* files"
        echo
        catifexec tpm2 readpublic -c "$h" -f pem -o $ROOT/tpm2-"$h$suff".pem
        catifexec tpm2 readpublic -Q -c "$h" -f tss -o $ROOT/tpm2-"$h$suff".tpm2b
    done

    section_start 'ECC P256 Endorsement Key (EK)'
    echo "saving EK ECC P256 to tpm2-EK-ECC-P256.* files"
    catifexec tpm2 createek -G ecc -f tss -c /dev/null -u $ROOT/tpm2-EK-ECC-P256.tpm2b
    catifexec tpm2 createek -G ecc -f pem -c /dev/null -u $ROOT/tpm2-EK-ECC-P256.pem

    for h in $(tpm2 getcap handles-nv-index | awk '{print $2}'); do
        ektype=
        case "$h" in
            "$EK_RSA_CERT_NV_INDEX")
                ektype="RSA2048"
                ;;
            "$EK_ECC_CERT_NV_INDEX")
                ektype="ECC-P256"
                ;;
            *)
                continue
                ;;
        esac
        section_start "$ektype EK certificate from $h NV index"
        echo "saving $ektype EK certificate to tpm2-"$h"-"$ektype"-EK-cert.pem"
        catifexec tpm2 nvread -C o "$h" | \
            catifexec openssl x509 -inform DER -out $ROOT/tpm2-"$h"-"$ektype"-EK-cert.pem
   done

    section_start 'ECC curves'
    catifexec tpm2 getcap ecc-curves

    section_start 'algorithms'
    catifexec tpm2 getcap algorithms

    section_start 'commands'
    catifexec tpm2 getcap commands

else

    echo "No TPM 2 device"

fi

# Restore stdout
restore_stdout

# === wlan-data ===

# From here stdout redirected to file
reassign_stdout $ROOT/wlan-data

has_wlan=
for d in /sys/class/ieee80211/*; do
    [ -d "$d" ] || continue
    has_wlan=1
    break
done

if [ -n "$has_wlan" ]; then

    section_start 'WLAN adapters'
    catifexec iw phy

    section_start 'WLAN interfaces'
    catifexec iw dev

    section_start 'WLAN regulatory data'
    catifexec iw reg get

    # NOTE: iwctl always embeds terminal escape codes in output
    section_start 'iwd adapters'
    catifexec_term_filter iwctl --dont-ask adapter list

    section_start 'iwd interfaces'
    catifexec_term_filter iwctl --dont-ask device list

    section_start 'iwd known networks'
    catifexec_term_filter iwctl --dont-ask known-networks list

    section_start 'iwd stations'
    catifexec_term_filter iwctl --dont-ask station list

    for d in /sys/class/net/*; do
        [ -d "$d" -a -d "$d"/phy80211 ] || continue
        i="${d##*/}"

        section_start "iwd station $i status"
        catifexec_term_filter iwctl --dont-ask station "$i" show

        section_start "iwd station $i networks"
        catifexec_term_filter iwctl --dont-ask station "$i" get-networks rssi-dbms

    done

    section_start 'RFkill status'
    catifexec rfkill list

else

    echo "No WLAN devices"

fi

# Restore stdout
restore_stdout

# === ether-auth-data ===

# From here stdout redirected to file
reassign_stdout $ROOT/ether-auth-data

section_start 'ead configuration files'
catifexec ls --time-style=full-iso -lsaNU1R /var/lib/ead/

section_start 'ead status'    
if [ -f /var/lib/ead/default.8021x ]; then

    catifexec dbus-send --system --reply-timeout=5000 --print-reply \
        --dest=net.connman.ead  / org.freedesktop.DBus.ObjectManager.GetManagedObjects

else

    echo "No ead configuration"

fi

# Restore stdout
restore_stdout

# === display-info ===

# From here stdout redirected to file
reassign_stdout $ROOT/display-info

catiffile /run/raperca/display-info

# Restore stdout
restore_stdout

# === edid ===

for f in /sys/class/drm/card*-*/edid; do
    [ -f "$f" ] || continue # no such path

    # From here stdout redirected to file
    s="${f#/sys/class/drm/}"
    s="${s//\//-}"

    reassign_stdout $ROOT/"$s"

    section_start "connector status"
    catiffile "${f%/*}"/status

    section_start "edid-decode output"
    catifexec edid-decode --check --fbmode "$f"

    section_start "EDID dump"
    echo "Dumping binary EDID to $s.bin"
    catiffile "$f" > $ROOT/"$s".bin

    # Restore stdout
    restore_stdout
done

# === spxucd-data ===

if [ "$HWTYPE" = "Fukiran" ]; then

    # From here stdout redirected to file
    reassign_stdout $ROOT/spxucd-data

    section_start "spxucd status"
    if [ -f /run/spxucd/spxucd.pid ]; then
        echo "spxucd running"
    else
        echo "spxucd not running, board revision too old for micro-controller?"
    fi

    section_start "spxucd data"
    catifexec dbus-send --system --reply-timeout=5000 --print-reply \
        --dest=com.spinetix.spxucd  / org.freedesktop.DBus.ObjectManager.GetManagedObjects

    section_start "spxucd power event log"
    catifexec dbus-send --system --reply-timeout=5000 --print-reply \
        --dest=com.spinetix.spxucd  /com/spinetix/SpxPlayer/PowerMCU \
        com.spinetix.SpxPlayer.PowerMCU1.PowerMCU.GetEventLog uint32:100

    # Restore stdout
    restore_stdout

fi

# === FS data ===

# From here stdout redirected to file
reassign_stdout $ROOT/fs-data

# NOTE: USB attached disks appear as SCSI; some USB keys have the
# filesystem on the raw device without a partition table
for dev in /dev/mmcblk[0-9]p[0-9]* /dev/hd[a-d][0-9] /dev/sd[a-z]* /dev/nvme[0-9]n[0-9]p[0-9]*; do
    [ -e "$dev" ] || continue # no such path
    [ "$dev" != /dev/"$KDEVPARTNAME" ] || continue
    section_start "Filesystem info for $dev"
    if [ -b "$dev" ]; then
        type="$(blkid -c /dev/null -s TYPE -o value "$dev")"
        case "$type" in
            "")
                echo "no filesystem"
                ;;
            ext*) # one of the ext2/3/4 filesystem, we can get info about it
                tune2fs -l "$dev" 2>&1
                ;;
            exfat|vfat|msdos|fat*)
                echo "no information to display for filesystem of type '$type'"
                ;;
            squashfs)
                unsquashfs -s "$dev" 2>&1
                ;;
            *)
                echo "unknown filesystem of type '$type'"
                ;;
        esac
    else
        echo "ERROR: device $dev not found"
    fi
done

# Try to have as much data as possible committed to disk
# before doing any fsck below to get the best possible info
# if some fs is mounted
if [ -n "$DO_EXTENDED" ]; then
    sync
fi

for dev in /dev/mmcblk[0-9]p[0-9]* /dev/hd[a-d][0-9] /dev/sd[a-z]* /dev/nvme[0-9]n[0-9]p[0-9]*; do
    [ -e "$dev" ] || continue # no such path
    [ "$dev" != /dev/"$KDEVPARTNAME" ] || continue
    section_start "Filesystem check for $dev"
    if [ -n "$DO_EXTENDED" ]; then
        if [ -b "$dev" ] ; then
            type="$(blkid -c /dev/null -s TYPE -o value "$dev")"
            case "$type" in
                "")
                    echo "no filesystem"
                    ;;
                ext*) # one of the ext2/3/4 filesystem, we can check it
                    fsck -n -f "$dev" 2>&1
                    ;;
                exfat|vfat|msdos|fat*) # one of the msdos filesystems
                    fsck -n "$dev" 2>&1
                    ;;
                squashfs)
                    echo "filesystem of type '$type' cannot be checked"
                    ;;
                *)
                    echo "unknown filesystem of type '$type'"
                    ;;
            esac
        else
            echo "ERROR: device $dev not found"
        fi
    else
        echo -e "Skipping filesystem check (not in extended mode)"
    fi
done

# Restore stdout
restore_stdout

# No RPM on recovery system
if [ -z "$DO_RECOVERY" ]; then

    # === RPM data ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/rpm-list

    # Produce list of installed RPMs
    echo -e "name\tepoch\tversion\trelease\tarch\tinstall date\tvendor"
    catifexec rpm -qa --qf \
        '%{N}\t%{E}\t%{V}\t%{R}\t%{ARCH}\t%{INSTALLTIME:date}\t%{VENDOR}\n'

    # Restore stdout
    restore_stdout

    # === RPM verify ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/rpm-verify

    # Verify all RPMs
    if [ -n "$DO_EXTENDED" ]; then
        catifexec rpm -Va
    else
        echo "Skipping RPM verify (not in extended mode)"
    fi

    # Restore stdout
    restore_stdout

    # === RPM DB data ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/rpmdb-list

    # Produce list ofa ll files in RPM DB
    catifexec ls --time-style=full-iso -lsaNU1R /var/lib/rpm

    # Restore stdout
    restore_stdout

fi # end ! DO_RECOVERY

# === dmidecode ===

if [ -d /sys/firmware/dmi/tables ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/dmidecode

    catifexec dmidecode

    # Restore stdout
    restore_stdout
fi

# === drm-info ===

# From here stdout redirected to file
reassign_stdout $ROOT/drm-info

catifexec modetest -a

# Restore stdout
restore_stdout

# === EFI variables ===

if [ -d /sys/firmware/efi ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/efi-variables

    catifexec ls /sys/firmware/efi/efivars/

    # Restore stdout
    restore_stdout

    mkdir $ROOT/efivars

    # Only dump UEFI variables in standard name space, other variables may
    # eventually have BIOS specific side-effects, so are best avoided
    for f in /sys/firmware/efi/efivars/*-"$UEFI_GLBL_GUID" /sys/firmware/efi/efivars/*-"$UEFI_SECDB_GUID"; do
        [ -f "$f" -a -r "$f" ] || continue
        reassign_stdout $ROOT/efivars/"${f##*/}"
        catiffile "$f"
        restore_stdout
    done
fi

# === EFI Secure Boot keys ===

if [ -d /sys/firmware/efi ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/efi-secure-boot-keys

    # this will just dump the contents of the BIOS keys
    sbkeysync --dry-run --verbose --no-default-keystores

    # Restore stdout
    restore_stdout
fi

# === Kernel data ===

# From here stdout redirected to file
reassign_stdout $ROOT/kernel-data

for f in cmdline version cpuinfo driver/bootcount \
    meminfo slabinfo buddyinfo iomem vmstat \
    filesystems partitions mounts \
    modules devices misc mtd fb \
    cmem \
    loadavg uptime interrupts stat yaffs \
    scsi/scsi \
    sysvipc/shm sysvipc/msg sysvipc/sem \
    diskstats locks \
    tty/drivers tty/driver/serial tty/driver/usbserial \
    bus/input/devices bus/input/handlers \
    bus/usb/devices; do

  [ -e /proc/"$f" ] || continue

  section_start "$f"
  catiffile /proc/$f

done

# Restore stdout
restore_stdout

# === Kernel message ring ===

catifexec dmesg 2>/dev/null > $ROOT/kernel-messages

# === Kernel log dumps ===

# From here stdout redirected to file
reassign_stdout $ROOT/kernel-log-dumps

mtd=$([ ! -e /proc/mtd ] || sed -e '/"klog-dumps"$/{s,:.*,,;p};d' /proc/mtd)
if [ -n "$mtd" ]; then
    mtddev=/dev/"${mtd}"ro
    if [ -c "$mtddev" ]; then
        if [ "$MTDTYPE" = "nand" ]; then
            # discard stderr (has nanddump info)
            nanddump -i -b -o "$mtddev" 2>/dev/null
        elif [ "$MTDTYPE" = "nor" ]; then
            cat "$mtddev" 2>&1
        fi
    else
        echo "ERROR: path '$mtddev' not found or not a device"
    fi
else
    echo "No kernel log dumps partition in flash"
fi

# Restore stdout
restore_stdout

# === pci-data ===

if [ -d /sys/bus/pci ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/pci-data

    section_start "lspci"
    catifexec lspci -nnv

    # Restore stdout
    restore_stdout
fi

# === acpi-data ===

if [ -d /sys/devices/LNXSYSTM:00 ]; then
    # From here stdout redirected to file
    reassign_stdout $ROOT/acpi-data

    section_start "ACPI device hierarchy"
    catifexec find /sys/devices/LNXSYSTM:00 -type d \! -name power

    # Restore stdout
    restore_stdout
fi

# === sysfs ===

# From here stdout redirected to file
reassign_stdout $ROOT/sysfs

for f in /sys/spxlicense/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/bus/mmc/devices/*/*; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/kernel/debug/mmc*/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/kernel/debug/usb/devices; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/class/graphics/fb0/device/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/class/x-display/disp0/device/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/devices/platform/vpss/display0/*; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/class/drm/card[0-9]/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/kernel/debug/dri/*{,/*,/*/*,/*/*/*}; do
    # crc/data is an endless or very large dump
    [ "${f%/crc/data}" = "$f" ] || continue
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/bus/thunderbolt/devices/*/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/class/hwmon/*/device/*; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for d in /sys/class/net/*; do
    [ -d "$d" ] || continue
    for f in "$d"/{,statistics/,device/,device/ieee80211/*/,device/slave-*/}*; do
        if [ -f "$f" -a -r "$f" ]; then
            section_start "$f"
            catiffile "$f"
        fi
    done
done

for f in /sys/block/*/*{,/*}; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

for f in /sys/bus/platform/devices/bonsairtc.0/voltage_low; do
    if [ -f "$f" -a -r "$f" ]; then
        section_start "$f"
        catiffile "$f"
    fi
done

# Restore stdout
restore_stdout

# No USB handling in recovery
if [ -z "$DO_RECOVERY" ]; then

    # === USB data ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/usb-data

    section_start "USB driver - device bindings"
    if [ -e /sys/kernel/debug/usb/devices ]; then
        # location in newer kernels, under debugfs
        catiffile /sys/kernel/debug/usb/devices
    else
        # location in older kernels, via usbfs
        catiffile /proc/bus/usb/devices
    fi

    section_start "USB tree"
    catifexec lsusb -t

    section_start "USB devices"
    catifexec lsusb -v

    # Restore stdout
    restore_stdout

    # === input data ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/input-data

    for f in /dev/input/event*; do
        if [ -e "$f" ]; then
            section_start "$f"
            catifexec evtest -i "$f"
        fi
    done

    # Restore stdout
    restore_stdout

    # === hid data ===

    # From here stdout redirected to file
    reassign_stdout $ROOT/hid-data

    for f in /dev/usb/hiddev*; do
        if [ -e "$f" ]; then
            section_start "$f"
            catifexec hidtest -i "$f"
        fi
    done

    # Restore stdout
    restore_stdout

fi # end ! DO_RECOVERY

# No raperca in recovery
if [ -z "$DO_RECOVERY" ]; then

    # === raperca data ===

    reassign_stdout $ROOT/raperca-content

    section_start "/srv/raperca listing"
    catifexec ls --time-style=full-iso -laNU1 /srv/raperca

    section_start "/srv/raperca/content listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/content

    section_start "/srv/.shadow-raperca/content listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/.shadow-raperca/content

    section_start "/srv/raperca/cache listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/cache

    section_start "/srv/raperca/capture listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/capture

    section_start "/srv/raperca/webstorage listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webstorage

    section_start "/srv/raperca/cookiejar listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/cookiejar

    section_start "/srv/raperca/tmp listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/tmp

    section_start "/srv/raperca/interface/protected listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/interface/protected

    section_start "/srv/raperca/interface/public listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/interface/public

    section_start "/srv/raperca/webservice/tmp listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/tmp

    section_start "/srv/raperca/webservice/cache listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/cache

    section_start "/srv/raperca/webservice/cookiejar listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/cookiejar

    section_start "/srv/raperca/webservice/webstorage listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/webstorage

    section_start "/srv/raperca/webservice/offstorage listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/offstorage

    section_start "/srv/raperca/webservice/cefdata listing"
    catifexec ls --time-style=full-iso -laNU1 ${DO_EXTENDED:+-R} /srv/raperca/webservice/cefdata

    # Restore stdout
    restore_stdout

fi # end ! DO_RECOVERY

# No pluggable content storage in recovery
if [ -z "$DO_RECOVERY" ]; then

    # === media data ===

    reassign_stdout $ROOT/media-content

    section_start "/media/usb1 listing"
    catifexec ls --time-style=full-iso -laNU1 /media/usb1

    # Restore stdout
    restore_stdout

fi

#
# TAR to stdout
#

# archive includes all files gathered above
# plus the ones given in the "here document" below and the
# ones in the dynamically generated list $EXTRAFILELIST

# WARNING: /etc/ssh contains private host keys, it should not be included

EXTRATAR=''
if [ -z "$DO_RECOVERY" ]; then
    if [ -n "$DO_EXTENDED" ]; then
        # add RPM DB to archive in extended mode
        EXTRATAR="${EXTRATAR} --add-file=var/lib/rpm"
        # add ESP contents to archive in extended mode
        EXTRATAR="${EXTRATAR} --add-file=efi"
    fi
else
    # always add ESP and ESP recovery in recovery
    EXTRATAR="${EXTRATAR} --add-file=efi --add-file=efi-recovery"
fi

# The file $EXTRAFILELIST contains a list of extra files to be added to the tar archive,
# one per line. File paths are relative to the / directory, but without leading /, and
# should not start with a dash (-). Leading and trailing whitespace is removed.
EXTRAFILELIST=$(mktemp)
if [ -n "$EXTRAFILELIST" ]; then
    if [ -z "$DO_RECOVERY" ]; then
        # add HTTP traffic capture files subject to file size and total cumulative size
        find /srv/raperca/capture -type f -name "HTTP-*.json" -printf "%s %p\n" | \
                awk -v fmax=524288 -v tmax=3145728 \
                'BEGIN { t=0 } {if ($1 <= fmax && t + $1 <= tmax) {print gensub(/^[^/]*\//, "", 1); t+=$1 }}' \
                >> "$EXTRAFILELIST"
    fi
    EXTRATAR="${EXTRATAR} --files-from=$EXTRAFILELIST"
fi

# WARNING: tabs in here-doc below (EOD markers) **must not** be replaced by spaces!
(tar -c -f - -C / --exclude="spxpasswd.xml*" --exclude="htpasswd*" --exclude-tag=SPXSECRETDIR.TAG --files-from=- $EXTRATAR --ignore-failed-read ${ROOT#/} \
    2>/dev/null <<-EOD
	etc/apache2
	etc/chatscripts
	etc/cron.d
	etc/cron.daily
	etc/cron.hourly
	etc/cron.monthly
	etc/cron.weekly
	etc/crontab
	etc/default
	etc/dev.d
	etc/dhcpcd.conf
	etc/fonts
	etc/fstab
	etc/group
	etc/host.conf
	etc/hostname
	etc/hosts
	etc/hosts.allow
	etc/hosts.deny
	etc/hotplug
	etc/hotplug.d
	etc/init.d
	etc/inittab
	etc/ld.so.conf
	etc/limits
	etc/localtime
	etc/logrotate.conf
	etc/logrotate.d
	etc/modprobe.conf
	etc/modprobe.d
	etc/modules
	etc/network
	etc/nscd.conf
	etc/nsswitch.conf
	etc/ntp.conf
	etc/ntp
	etc/os-release
	etc/passwd
	etc/pki/spxmanage/certs
	etc/pki/spxmanage/server
	etc/php
	etc/ppp/options
	etc/ppp/peers
	etc/raperca
	etc/rc0.d
	etc/rc1.d
	etc/rc2.d
	etc/rc3.d
	etc/rc4.d
	etc/rc5.d
	etc/rc6.d
	etc/rcS.d
	etc/resolv.conf
	run/resolv.conf
	etc/snmp
	etc/spinetix
	etc/spxdispmanager
	etc/spxmanage
	etc/spinetix-release
	etc/spxrecovery
	etc/ssl/certs
	etc/sysctl.conf
	etc/sysctl.conf.procps
	etc/syslog.conf
	etc/syslog.conf.sysklogd
	etc/timezone
	etc/udev/udev.conf
	etc/udev/rules.d
	etc/updater.conf
	etc/version
	etc/xinetd.conf
	etc/xinetd.d
	usr/lib/os-release
	usr/lib/ssl/certs
	usr/lib/ssl/cert.pem
	usr/share/vidmode/vid.modes
	var/cache/spxmanage/spxbackup.xml
	var/lib/dhcpcd
	var/lib/hwwatchdog
	var/lib/net-snmp
	var/lib/spxenroll
	var/log
	var/lib/spinetix/ldrenv
	var/volatile/log
	run/modem
	run/licensecheck/license-features.json
	srv/raperca/log
	srv/raperca/webservice/cefdata/cef.log
	mnt/sys/var/log
	mnt/sys/var/lib/hwwatchdog
	mnt/sys/etc/fstab
	mnt/sys/etc/inittab
	mnt/sys/etc/init.d
	mnt/sys/etc/rc0.d
	mnt/sys/etc/rc1.d
	mnt/sys/etc/rc2.d
	mnt/sys/etc/rc3.d
	mnt/sys/etc/rc4.d
	mnt/sys/etc/rc5.d
	mnt/sys/etc/rc6.d
	mnt/sys/etc/rcS.d
EOD
) | gzip -3
