Files
s390-tools/scripts/dbginfo.sh
Shalini Chellathurai Saroja 783ae9735e dbginfo: Collect CPI data of the KVM guests
Collect Control-Program Identification (CPI) data of the KVM
guests. The CPI data contains system name, system type,
system level and sysplex name of the guests. It also provides
the timestamp in which these data were colleceted from the
guests.

Note:
CPI information for SEL guests require explicit enablement.

Signed-off-by: Shalini Chellathurai Saroja <shalini@linux.ibm.com>
Suggested-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Jörn Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-11-07 14:33:26 +01:00

1640 lines
45 KiB
Bash
Executable File

#!/bin/sh
#
# dbginfo.sh - Tool to collect runtime, configuration, and trace information
#
# Copyright IBM Corp. 2002, 2025
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
#
# Switching to neutral locale
LC_ALL=C
export LC_ALL
########################################
# Global used variables
readonly SCRIPTNAME="${0##*/}" # general name of this script
readonly FULLPATHSCRIPT="$(readlink -f "${0}")"
#
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
readonly DOCKER=$(if type docker >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly DUMP2TAR_OK=$(if type dump2tar >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly HW="$(uname -m 2>/dev/null)"
readonly IFS_ORI="${IFS}"
# retrieve and split kernel version
readonly KERNEL_BASE="$(uname -r 2>/dev/null)"
readonly KERNEL_VERSION=$(echo ${KERNEL_BASE} | cut -d'.' -f1 )
readonly KERNEL_MAJOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f2 )
readonly KERNEL_MINOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f3 | sed 's/[^0-9].*//g')
readonly KERNEL_INFO=${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION}
readonly KUBERNETES=$(if type kubectl >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly KUBERNETES_LOG_LINES=50
readonly KVM=$(if type virsh >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
# The file to indicate that another instance of the script is already running
readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock"
# check limits for logfiles like /var/log/messages
readonly LOG_FILE_SIZE_CHECK=50 # max logfile size in MB
readonly LOG_FILE_AGE_CHECK=7 # age in days to include for size checking
# Mount point of the debug file system
readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug"
# Network devices
readonly NETWORK_DEVS=$(cd /sys/class/net; ls -d */ 2>/dev/null | sed 's/\///g')
# distro info
readonly OSPRETTY="$(cat /etc/os* 2>/dev/null | grep -m1 PRETTY_NAME | sed 's/\"//g')"
readonly OS_NAME="${OSPRETTY##*=}"
readonly PODMAN=$(if type podman >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
# The processor ID for the first processor
readonly PROCESSORID="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
readonly PROCESSORVERSION="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then
RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo | \
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
else
RUNTIME_ENVIRONMENT="LPAR"
fi
# check for Dynamic Partition Mode
readonly DPM_UUID=$(grep "LPAR UUID" /proc/sysinfo | \
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
if test "${#DPM_UUID}" -eq 0; then
DPM_MODE="NO"
else
DPM_MODE="YES"
fi
readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)" # hostname of system being analysed
readonly TERMINAL="$(tty 2>/dev/null)"
# timeout seconds TOS / kill timeout TOKS
readonly TOS=15
readonly TOKS=30
readonly TIMEOUT=$(if type timeout >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
readonly TIMEOUT_OK=$(if test "x${TIMEOUT}" = "xYES"; then
if test $(timeout -k ${TOKS} ${TOS} uname 2>/dev/null) = $(uname); then
echo "YES"; else echo "WRONG_VERSION"; fi
else echo "NO"; fi)
readonly ZDEV_CONF=$(lszdev --configured 2>/dev/null | wc -l)
readonly ZDEV_OFF=$(lszdev --offline 2>/dev/null | wc -l)
readonly ZDEV_ONL=$(lszdev --online 2>/dev/null | wc -l)
paramWORKDIR_BASE="/tmp" # initial default path
########################################
# print dbginfo.sh version info
print_version() {
cat <<EOF
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
Copyright IBM Corp. 2002, 2025
EOF
}
########################################
# print how to use this script
print_usage() {
print_version
cat <<EOF
Usage: ${SCRIPTNAME} [OPTION]
This script collects runtime, configuration and trace information on
a Linux on IBM Z installation for debugging purposes.
It also traces information about z/VM if the Linux runs under z/VM.
Virtualization platform data is collected on a host serving this.
Default location for data collection and final tar file is "/tmp/".
The collected information is written to a TAR archive named
DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
where [date] and [time] are the date and time when debug data is collected.
[hostname] indicates the hostname of the system the data was collected from.
The [processorid] is taken from the processor 0 and indicates the processor
identification.
Options:
-d|--directory specify the directory where the data collection
stores the temporary data and the final archive.
-h|--help print this help
-v|--version print version information
-c|--check online base system information (no data collection)
EOF
}
########################################
# check for oversize logfiles and missing rotation
logfile_checker() {
local counter
local logfile
local logfiles
# find files bigger than recommended
counter=$(find $1 -maxdepth 1 -type f -mtime -${LOG_FILE_AGE_CHECK} \
-size ${LOG_FILE_SIZE_CHECK}M | wc -l)
echo " ${counter} logfiles over ${LOG_FILE_SIZE_CHECK} MB"
# maybe check for rotation of base names
if [ ${counter} -ne 0 ]; then
for logfile in $(find $1 -maxdepth 1 -type f -mtime -${LOG_FILE_AGE_CHECK} \
-size ${LOG_FILE_SIZE_CHECK}M -print); do
# use a neutral separtor ':' as concat is different in some bash
# insert the 'blank' for later use in for loop
# add the base name before '.' or '-' only for checks
logfiles="${logfiles}: ${logfile%%[.-]*}"
done
# change separator to new line for sorting
logfiles=$(echo "${logfiles}" | sed s'/:/\n/g' | sort -u)
for logfile in ${logfiles}; do
counter=$(ls ${logfile}* 2>/dev/null | wc -l)
if [ ${counter} -eq 1 ]; then
echo " CHECK - ${logfile} may miss a rotation"
else
echo " OK - ${logfile}* may have a rotation "\
"in place: ${counter} files"
fi
done
fi
}
########################################
# print basic info and online checks
print_check() {
print_version
cat <<EOF
Hardware platform = ${HW}
Runtime environment = ${RUNTIME_ENVIRONMENT} - DPM: ${DPM_MODE}
$(cat /proc/sysinfo | grep 'Name')
Kernel version = ${KERNEL_INFO}
OS version / distro = ${OS_NAME}
KVM host = ${KVM}
container host Kubernetes: ${KUBERNETES} - docker: ${DOCKER} - podman: ${PODMAN}
Current user = $(whoami) (must be root for data collection)
Date and time = $(date)
Uptime =$(uptime)
Number of coredumps = $(corecumpctl 2>/dev/null | wc -l)
zdevice onl/conf/offl = ${ZDEV_ONL} / ${ZDEV_CONF} / ${ZDEV_OFF}
Log file check =$(logfile_checker "/var/log*")
Working directory = $(ls -d ${paramWORKDIR_BASE} 2>&1 && df -k ${paramWORKDIR_BASE})
$(ls -ltr ${paramWORKDIR_BASE}/DBGINFO*tgz 2>/dev/null | tail -2)
$(ls ${LOCKFILE} 2>/dev/null && echo " Warning: dbginfo running since: $(cat ${LOCKFILE})")
Tool/command dependency check successful:
timeout = ${TIMEOUT_OK}
dump2tar = ${DUMP2TAR_OK}
This is a console output only - no data was saved using option -c !
EOF
}
#######################################
# Parsing the command line and pre checks
while [ ${#} -gt 0 ]; do
case ${1} in
--help|-h)
print_usage
exit 0
;;
--version|-v)
print_version
exit 0
;;
--directory|-d)
paramWORKDIR_BASE=${2}
if test -z "${paramWORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: No directory specified for data collection!"
echo
exit 1
elif test ! -d "${paramWORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: The specified directory does not exist!"
echo
exit 1
else
# jump to next param
shift
fi
;;
--check|-c)
print_check
exit 0
;;
-*|--*|*)
echo
echo "${SCRIPTNAME}: invalid option \"${1}\""
echo "Try '${SCRIPTNAME} --help' for more information"
echo
exit 1
;;
esac
# next parameter, if already last the final shift will do termination
shift
done
# finally verification to run as root
if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then
echo "${SCRIPTNAME}: Error: You must be user root to run data collection \"${SCRIPTNAME}\"!"
echo "${SCRIPTNAME}: Hint: for base system information you may run \"${SCRIPTNAME} -c\""
exit 1
fi
#########################################
# The base working directory and derieved path info
readonly WORKDIR_BASE="$(echo "${paramWORKDIR_BASE}" | sed -e 's#/$##')/"
# The current working directory for the actual script execution
if test -z "${PROCESSORID}"; then
readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}"
else
readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}-${PROCESSORID}"
fi
# The current path where the collected information is put together
readonly WORKPATH="${WORKDIR_BASE}${WORKDIR_CURRENT}/"
# The current TAR archive that finally includes all collected information
readonly WORKARCHIVE="${WORKDIR_BASE}${WORKDIR_CURRENT}.tgz"
# The log file of activities from this script execution
readonly LOGFILE="${WORKPATH}dbginfo.log"
# File names for output files per section (duplicates are ok)
readonly OUTPUT_FILE_BRIDGE="${WORKPATH}network.out"
readonly OUTPUT_FILE_CMD="${WORKPATH}runtime.out"
readonly OUTPUT_FILE_COREDUMPCTL="${WORKPATH}coredump.out" # separate file needed
readonly OUTPUT_FILE_DASD="${WORKPATH}dasd.out"
readonly OUTPUT_FILE_DOCKER="${WORKPATH}docker_runtime.out"
readonly OUTPUT_FILE_ETHTOOL="${WORKPATH}network.out"
readonly OUTPUT_FILE_HYPTOP="${WORKPATH}runtime.out"
readonly OUTPUT_FILE_JOURNALCTL="${WORKPATH}journalctl.out"
readonly OUTPUT_FILE_KVM="${WORKPATH}kvm_runtime.out"
readonly OUTPUT_FILE_LSOF="${WORKPATH}open_files.out"
readonly OUTPUT_FILE_NETWORK="${WORKPATH}network.out"
readonly OUTPUT_FILE_NVME="${WORKPATH}runtime.out"
readonly OUTPUT_FILE_OVS="${WORKPATH}network.out"
readonly OUTPUT_FILE_PODMAN="${WORKPATH}podman_runtime.out"
readonly OUTPUT_FILE_ISW="${WORKPATH}installed_sw.out"
readonly OUTPUT_FILE_TC="${WORKPATH}network.out"
readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
readonly OUTPUT_FILE_UDEVDB="${WORKPATH}udevdb.out"
# Base file names for different output files - no extension !
readonly OUTPUT_FILE_KUBERNETES="${WORKPATH}kubernetes"
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}network"
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfs"
# define order of collection steps
# - state/debug files are collected first, to avoid overwriting by command execution
ALL_STEPS="\
collect_sysfs\
collect_procfs\
collect_configfiles\
collect_initrd_configfiles\
collect_cmdsout\
collect_hyptop\
collect_vmcmdsout\
collect_network\
collect_osaoat\
collect_ethtool\
collect_tc\
collect_bridge\
collect_ovs\
collect_kvm\
collect_container\
collect_nvme\
collect_dasd\
collect_logfiles\
post_processing\
create_package\
environment_cleanup\
"
# The amount of steps running the whole collections, without last cleanup
readonly COLLECTION_COUNT=`expr $(echo ${ALL_STEPS} | wc -w) - 1`
########################################
# Collection of proc fs entries
PROCFILES="\
/proc/buddyinfo\
/proc/cio_ignore\
/proc/cmdline\
/proc/cpuinfo\
/proc/crypto\
/proc/dasd/devices\
/proc/dasd/statistics\
/proc/devices\
/proc/diskstats\
/proc/interrupts\
/proc/iomem\
/proc/kallsyms\
/proc/mdstat\
/proc/meminfo\
/proc/misc\
/proc/modules\
/proc/mounts\
/proc/net/vlan\
/proc/net/bonding\
/proc/net/sockstat\
/proc/net/sockstat6\
/proc/net/softnet_stat\
/proc/partitions\
/proc/qeth\
/proc/qeth_perf\
/proc/qeth_ipa_takeover\
/proc/sched_debug\
/proc/schedstat\
/proc/service_levels\
/proc/slabinfo\
/proc/softirqs\
/proc/stat\
/proc/swaps\
/proc/sys/kernel\
/proc/sys/vm\
/proc/sysinfo\
/proc/version\
/proc/zoneinfo\
"
# Adding files to PROCFILES in case scsi devices are available
if test -e /proc/scsi; then
PROCFILES="${PROCFILES}\
$(find /proc/scsi -type f -perm /444 2>/dev/null)\
"
fi
########################################
LOGFILES="\
/root/anaconda-ks.cfg\
/root/original-ks.cfg\
/run/containerd/events.log\
/run/docker/libcontainerd/containerd/events.log\
/run/udev/chreiplzfcpmp-[0-9][0-9][a-z][a-z][a-z][a-z]-*\
/var/log/anaconda\
/var/log/anaconda.*\
/var/log/audit\
/var/log/boot*\
/var/log/crash/*/vmcore-dmesg.txt\
/var/log/cron*\
/var/log/dmesg*\
/var/log/dnf.*\
/var/log/dracut.log*\
/var/log/IBMtape.trace\
/var/log/IBMtape.errorlog\
/var/log/kdump.log\
/var/log/libvirt\
/var/log/lin_tape.trace\
/var/log/lin_tape.errorlog\
/var/log/messages*\
/var/log/openvswitch/ovs-vswitchd.log\
/var/log/openvswitch/ovsdb-server.log\
/var/log/pbl.log\
/var/log/sa\
/var/log/syslog*\
/var/log/yum.log\
/var/log/YaST2\
/var/log/zypp*\
"
########################################
CONFIGFILES="\
/boot/grub2/grub.cfg\
/boot/loader/entries/*.conf\
/boot/zipl/active_devices.txt\
/boot/zipl/config\
/conf/\
/etc/*.conf\
/etc/*release\
/etc/anacrontab\
/etc/apparmor.d\
/etc/audit\
/etc/auto.*\
/etc/cmdline\
/etc/cmdline.d\
/etc/conf.d\
/etc/containers\
/etc/cron.*\
/etc/crontab\
/etc/crypttab\
/etc/dasd.*\
/etc/default\
/etc/depmod.d\
/etc/dnf/dnf.conf\
/etc/docker\
/etc/dracut.conf.d\
/etc/exports\
/etc/fstab\
/etc/groups\
/etc/grub.d\
/etc/hosts*\
/etc/iscsi\
/etc/inittab\
/etc/kdump\
/etc/libvirt\
/etc/logrotate.d\
/etc/lvm\
/etc/modprobe.conf*\
/etc/modprobe.d\
/etc/modules-load.d/*.conf\
/etc/mtab\
/etc/multipath\
/etc/netplan\
/etc/network\
/etc/networks\
/etc/NetworkManager\
/etc/opencryptoki/*.conf\
/etc/pam.d\
/etc/profile\
/etc/profile.d\
/etc/pki/tls/openssl.cnf\
/etc/rc.d\
/etc/rc.local\
/etc/resolv.*\
/etc/rsyslog.d\
/etc/selinux\
/etc/ssl/openssl.conf\
/etc/ssl/openssl.cnf\
/etc/sysconfig\
/etc/sysctl.d\
/etc/syslog*\
/etc/systemd\
/etc/udev*\
/etc/xinet.d\
/etc/zfcp.*\
/lib/modprobe.d\
$(find /lib/modules -name modules.dep 2>/dev/null)\
/lib/systemd/system/docker.service\
/lib/udev/rules.d\
/usr/lib/modprobe.d\
/usr/lib/modules-load.d/*.conf\
/usr/lib/systemd/system/\
/usr/local/lib/modprobe.d\
/usr/local/lib/modules-load.d/*.conf\
/run/modprobe.d\
/run/modules-load.d/*.conf\
/run/udev/chreiplzfcpmp-ipl-volume-id\
/run/udev/rules.d\
/run/zdev_id.env\
/run/zdev.*\
"
########################################
# system state commands (non alphabetical order here)
CMDS="uname -a\
:uptime\
:timedatectl\
:chronyc sources\
:chronyc sourcestats\
:chronyc ntpdata\
:chronyc tracking\
:zhypinfo\
:last\
:lscpu -ae\
:lscpu -ye\
:lscpumf -i\
:lsmem\
:lsmod\
:lsshut\
:runlevel\
:sysctl -a\
:systemctl --all --no-pager show\
:systemctl --all --no-pager list-units\
:systemctl --all --no-pager list-unit-files\
:systemd-delta\
:ulimit -a\
:env --null | sort --zero-terminated | tr '\000' '\n'\
:find /boot -print0 | sort -z | xargs -0 -n 10 ls -ld\
:find /var/crash -print0 | sort -z | xargs -0 -n 10 ls -ld\
:df -h\
:df -i\
:mount\
"
# Z device subsystem commands (first commands in non alphabetical order)
CMDS="${CMDS}\
:lschp\
:lscss --vpm\
:lszdev\
:find /dev -print0 | sort -z | xargs -0 -n 10 ls -ld\
:lspci -t\
:lspci -vvv\
:lstape\
:smc_dbg\
"
# block device and other scsi device commands (in non alphabetical order)
# command groups itself have an intentional topical grouping and order
CMDS="${CMDS}\
:lsblk\
:lsblk -o +FSTYPE,UUID\
:lsdasd\
:lsdasd -u\
:pvpath -qa\
:dmsetup ls\
:dmsetup ls --tree\
:dmsetup table\
:dmsetup table --target multipath\
:dmsetup status\
:multipathd -k'show config'\
:multipathd -k'show config local'\
:multipathd -k'show maps'\
:multipathd -k'show topo'\
:multipathd -k'show paths'\
:multipathd -k'show paths format %w|%a|%r|%L|%p|%i|%d|%D|%t|%T|%o|%0|%C' | sort\
:multipathd -k'show maps stats'\
:multipathd -k'show maps status'\
:multipathd -k'show status'\
:multipathd -k'show daemon'\
:multipathd -k'show blacklist'\
:multipathd -k'show devices'\
:multipath -v6 -ll\
:multipath -d\
:multipath -t\
:lszfcp\
:lszfcp -D\
:lszfcp -V\
:ziorep_config -ADM\
:lsscsi\
:fdisk -l\
:lvmconfig\
:lvdisplay -m\
:vgs -v\
:pvs -v\
:lvs -v\
"
# crypto specific commands
CMDS="${CMDS}\
:cpacfinfo # for MSA 13 info\
:cpacfinfo -m -f -a -n\
:ep11info -D\
:ep11info -H\
:ep11info -M\
:icainfo\
:icastats --all\
:ivp.e # IBM CCA package install check\
:ls -al /usr/lib64/opencryptoki/stdll\
:lszcrypt -VV\
:pkcsconf -i\
:pkcsconf -s\
:pkcsconf -l\
:pkcsconf -t\
:pkcsconf -m\
:systemctl status pkcsslotd\
"
# product / distro specific commands
CMDS="${CMDS}\
:java -version\
:qemu-ga -V\
:SPident # SLES service package\
"
# commands with long output at the end
CMDS="${CMDS}\
:cat /root/.bash_history\
:dmesg -s 1048576\
:ps -emo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\
:ps -eHo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\
:ps axX\
"
# commands with separate output file
CMDS="${CMDS}\
:apt list >> '${OUTPUT_FILE_ISW}'\
:snap list --all >> '${OUTPUT_FILE_ISW}'\
:coredumpctl && coredumpctl info -o ${OUTPUT_FILE_COREDUMPCTL}\
:journalctl --all --no-pager --lines=100000 --output=short-precise\
>> '${OUTPUT_FILE_JOURNALCTL}'\
:lsof >> '${OUTPUT_FILE_LSOF}'\
:rpm -qa | sort >> '${OUTPUT_FILE_ISW}'\
:udevadm info --export-db >> '${OUTPUT_FILE_UDEVDB}'\
"
########################################
# network comamnds for separate output file with ip -br a as header command
NETWORK_CMDS="ip -br a\
:firewall-cmd --list-all\
:ifconfig -a\
:ip a sh\
:ip -s -s link\
:ip link show\
:ip neigh list\
:ip ntable\
:ip route list\
:ip route list table all\
:ip rule list\
:ipcs -a\
:iptables -L # about to be replaced by nftables\
:lsqeth\
:netstat -pantu\
:netstat -s\
:nft list tables # is replacing iptables\
:nm-tool\
:nstat -az\
:openssl engine # deprecated in OpenSSL 3.n\
:openssl list --providers 2>/dev/null # redirect help on older systems\
:opticsmon --module-info\
:route -n\
"
########################################
DOCKER_CMDS="docker version\
:docker info\
:docker images\
:docker network ls\
:docker ps -a\
:docker stats --no-stream\
:systemctl status docker.service\
"
########################################
PODMAN_CMDS="podman version\
:podman info\
:podman images\
:podman network ls\
:podman ps -a\
:podman stats --no-stream\
:podman pod stats -a --no-stream\
:ls -l /var/lib/containers/storage/overlay/* # list of files is sufficent\
:ls -l /var/log/crio/pods/*/*\
"
########################################
KUBERNETES_CMDS="kubectl version\
:oc version # get also OCP version if installed\
:kubectl top pod\
:kubectl top node\
:kubectl describe pod -A\
:kubectl describe node -A\
:kubectl describe endpoints\
:ls -l /var/log/containers # list of logs is sufficent\
:kubectl cluster-info dump >>${OUTPUT_FILE_KUBERNETES}.dmp\
"
########################################
VM_CMDS="q userid\
:q users\
:q privclass\
:q cplevel\
:q cpservice\
:q cpprot user\
:q specex\
:q ssi\
:q cpus\
:q srm\
:q vtod\
:q time full\
:q timezone\
:q loaddev\
:q v osa\
:q v dasd\
:q v crypto\
:q v fcp\
:q v pav\
:q v sw\
:q v st\
:q v nic\
:q st\
:q xstore\
:q xstore user system\
:q sxspages\
:q vmlan\
:q vswitch\
:q vswitch details\
:q vswitch access\
:q vswitch active\
:q vswitch accesslist\
:q vswitch promiscuous\
:q vswitch controller\
:q port group all active details\
:q set\
:q comm\
:q controller all\
:q fcp\
:q frames\
:q lan\
:q lan all details\
:q lan all access\
:q memassist\
:q nic\
:q pav\
:q proc\
:q proc topology\
:q mt\
:q qioass\
:q spaces\
:q swch all\
:q trace\
:q mdcache\
:q alloc page\
:q alloc spool\
:q dump\
:q dumpdev\
:q reorder VMUSERID\
:q quickdsp VMUSERID\
:q pcifunction\
:q vmrelocate\
:q syscontrol\
:ind load\
:ind sp\
:ind user\
"
###############################################################################
KVM_CMDS="virsh version\
:virsh nodeinfo\
:virsh nodememstats\
:virsh nodecpustats\
:virsh list --all\
:virsh iface-list\
:virsh net-list\
:virsh nwfilter-list\
:virsh nodedev-list --tree\
:virsh pool-list\
:virt-host-validate\
:kvm_stat --once --debugfs --debugfs-include-past --tracepoints\
"
########################################
collect_cmdsout() {
local cmd
pr_collect_output "command"
IFS=:
for cmd in ${CMDS}; do
IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_CMD}"
done
IFS="${IFS_ORI}"
}
########################################
collect_network() {
local cmd
pr_collect_output "network"
IFS=:
for cmd in ${NETWORK_CMDS}; do
IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_NETWORK}"
done
IFS="${IFS_ORI}"
}
########################################
collect_vmcmdsout() {
local vm_command
local cp_command
local vm_cmds
local vm_userid
local module_loaded=1
local cp_buffer_size
local rc_buffer_check
if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then
pr_collect_output "z/VM"
if type vmcp >/dev/null; then
cp_command="vmcp"
if ! lsmod 2>/dev/null | grep -q vmcp && modinfo vmcp >/dev/null 2>&1; then
modprobe vmcp && module_loaded=0 && sleep 2
fi
elif type hcp >/dev/null; then
cp_command="hcp"
if ! lsmod 2>/dev/null | grep -q cpint; then
modprobe cpint && module_loaded=0 && sleep 2
fi
else
pr_log_stdout "${SCRIPTNAME}: Warning: No program to communicate to z/VM CP"
pr_skip "z/VM: vmcp not available"
return 1
fi
vm_userid=$(${cp_command} q userid 2>/dev/null | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
vm_cmds=$(echo "${VM_CMDS}" | sed "s/VMUSERID/${vm_userid}/g")
IFS=:
for vm_command in ${vm_cmds}; do
IFS="${IFS_ORI}"
# initialize buffer values for automatic resizing
cp_buffer_size=2
rc_buffer_check=2
while test ${rc_buffer_check} -eq 2 && test ${cp_buffer_size} -lt 1024; do
cp_buffer_size=$(( cp_buffer_size * 2 ))
eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" \
>/dev/null 2>&1
rc_buffer_check=$?
done
call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" \
"${OUTPUT_FILE_VMCMD}"
IFS=:
done
IFS="${IFS_ORI}"
if test ${module_loaded} -eq 0 && test "x${cp_command}" = "xhcp"; then
rmmod cpint
elif test ${module_loaded} -eq 0 && test "x${cp_command}" = "xvmcp"; then
rmmod vmcp
fi
else
pr_skip "z/VM: no z/VM environment"
fi
}
########################################
collect_hyptop() {
local param
local delay=1 # seconds
local iter=5
local sec=`expr ${delay} \\* ${iter}`
case ${RUNTIME_ENVIRONMENT} in
"z/VM")
param="\#,c,m,C:s,M:s,o" # z/VM guest fields
;;
"LPAR")
param="\#,T,c,e,m,C:s,E:s,M:s,o" # all LPAR fields
;;
*) # KVM guest
pr_skip "hyptop: not available for ${RUNTIME_ENVIRONMENT}"
return 1
;;
esac
pr_collect_output "hyptop for ${RUNTIME_ENVIRONMENT} - ${sec}s"
call_run_command "hyptop -b -d ${delay} -n ${iter} -f ${param} -S c" "${OUTPUT_FILE_HYPTOP}"
}
########################################
collect_procfs() {
local file_name
pr_collect "procfs"
for file_name in ${PROCFILES}; do
call_collect_file "${file_name}"
done
}
########################################
collect_sysfs() {
local debugfs_mounted=0
local dir_name
local file_name
local rc=0
pr_collect "sysfs"
if ! grep -qE "${MOUNT_POINT_DEBUGFS}.*debugfs" /proc/mounts 2>/dev/null; then
if mount -t debugfs debugfs "${MOUNT_POINT_DEBUGFS}" >/dev/null 2>&1; then
sleep 2
debugfs_mounted=1
else
pr_log_stdout "${SCRIPTNAME}: Warning: Unable to mount debugfs \
at \"${MOUNT_POINT_DEBUGFS}\""
fi
fi
# Collect sysfs files using multiple threads (-J 1) while excluding
# files known to block on read (-x). Stop reading a file that takes
# more than 5 seconds (-T 5) such as an active ftrace buffer.
# error messages are not written to the log
if test "x${DUMP2TAR_OK}" = "xYES"; then
dump2tar /sys -z -o "${OUTPUT_FILE_SYSFS}.tgz" \
-x '*/tracing/trace_pipe*' \
-x '*/page_idle/bitmap*' \
-x '*/tracing/per_cpu/*' \
-x '*/tracing/free_buffer' \
-x '*/devices/css[0-9]*/[0-9]*.*.*/[0-9]*.*.*/host[0-9]*/rport-[0-9]*:*-*/target[0-9]*:*:*/[0-9]*:*:*:*/block/sd*/mq/0/cpu[0-9]*' \
--ignore-failed-read -J 1 -T 5 2>>${OUTPUT_FILE_SYSFS}.err
rc=$?
else
echo "${SCRIPTNAME}: Warning: dump2tar is unavailable"
rc=255
fi
if [ ${rc} -eq 0 ] ; then
echo " all failed entries are logged to ${OUTPUT_FILE_SYSFS}.err"
else
if [ $rc -ne 255 ]; then
echo "${SCRIPTNAME}: Warning: dump2tar failed with $rc"
fi
pr_log_stdout " Warning: falling back to slow path"
call_run_command "find /sys -print0 | sort -z \
| xargs -0 -n 10 ls -ld" "${OUTPUT_FILE_SYSFS}.out"
find /sys -noleaf -type d 2>/dev/null | while IFS= read -r dir_name; do
mkdir -p "${WORKPATH}${dir_name}"
done
find /sys -noleaf -type f -perm /444 \
-a -not \( -path '*tracing*' -a -name "trace_pipe*" \) \
-a -not \( -path '*page_idle*' -a -name 'bitmap*' \) \
-a -not -path '*tracing/per_cpu*' \
-a -not -path '*/tracing/free_buffer' \
-a -not -path '*/devices/css[0-9]*/[0-9]*.*.*/[0-9]*.*.*/host[0-9]*/rport-[0-9]*:*-*/target[0-9]*:*:*/[0-9]*:*:*:*/block/sd*/mq/0/cpu[0-9]*' \
2>/dev/null | while IFS= read -r file_name; do
echo " ${file_name}"
if ! dd if="${file_name}" status=noxfer iflag=nonblock \
of="${WORKPATH}${file_name}" >/dev/null 2>&1; then
echo "${SCRIPTNAME}: Warning: failed to copy \"${file_name}\""
fi
done
fi
if test ${debugfs_mounted} -eq 1; then
umount "${MOUNT_POINT_DEBUGFS}"
fi
}
########################################
collect_logfiles() {
local file_name
pr_collect "log files"
for file_name in ${LOGFILES}; do
call_collect_file "${file_name}"
done
pr_log_stdout "$(logfile_checker "/var/log*")"
}
########################################
collect_configfiles() {
local file_name
pr_collect "config files"
for file_name in ${CONFIGFILES}; do
call_collect_file "${file_name}"
done
}
########################################
collect_initrd_configfiles() {
local file_name
local rd rdbase rdreldir rdextract mypopd noglob mycat patterns=""
pr_collect "initrd config files"
cd "${WORKPATH}" || return
mypopd="$OLDPWD" # (pushd alternative)
# prevent expansion of glob special characters such as '*'
noglob=$(set +o | grep -F noglob)
set -o noglob
# prepare relative configfiles paths
for file_name in ${CONFIGFILES}; do
# strip leading slash to make path relative as in initrd
file_name=${file_name#/}
# in contrast to call_collect_file(), cpio extract with patterns
# does not recurse, but cpio wildcards match slash, so add suffix;
# otherwise e.g. /lib/udev/rules.d or /etc/multipath only extracts
# just that directory without any of its content
file_name="${file_name}*"
patterns="$patterns ${file_name}"
done
patterns="$patterns squash-root.img"
$noglob
# iterate over initrds
for rd in /boot/initr* /boot/zipl/initr* /var/lib/kdump/initr*; do
[ -f "$rd" ] || continue # (nullglob alternative)
rdbase=${rd##*/} # basename
rdreldir=${rd%/*} # dirname
rdreldir=${rdreldir#/} # strip leading slash to make path relative
mkdir -p "${rdreldir}"
cd "${rdreldir}" || break # (pushd alternative)
if type lsinitrd >/dev/null 2>&1; then
lsinitrd "${rd}" > "${rdbase}.lsinitrd"
fi
if type lsinitramfs >/dev/null 2>&1; then
lsinitramfs -l "${rd}" > "${rdbase}.lsinitramfs"
fi
cd - || break # change to $OLDPWD (popd alternative)
rdextract=${rd#/}
mkdir -p "${rdextract}"
cd "${rdextract}" || break # (pushd alternative)
case $(file "${rd}") in
*cpio*) mycat="cat" ;;
*gzip*) mycat="zcat" ;;
*XZ*) mycat="xzcat" ;;
*Zstandard*) mycat="zstdcat" ;;
*bzip2*) mycat="bzcat" ;;
*LZMA*) mycat="lzcat" ;;
*)
# change to $OLDPWD (popd alternative)
cd - || { cd "$mypopd" || return; return; }
continue
;;
esac
# prevent expansion of glob special characters such as '*'
noglob=$(set +o | grep -F noglob)
set -o noglob
# we expect no early-initrd stuff on s390 so extract directly.
# globbing is turned off and we want word split on $patterns:
# shellcheck disable=SC2086
"$mycat" "${rd}" | cpio --extract --no-absolute-filenames \
--make-directories \
--preserve-modification-time $patterns
$noglob
if [ ! -f "squash-root.img" ]; then
cd - || break # change to $OLDPWD (popd alternative)
continue
fi
# prevent expansion of glob special characters such as '*'
noglob=$(set +o | grep -F noglob)
set -o noglob
# globbing is turned off and we want word split on $patterns:
# shellcheck disable=SC2086
unsquashfs -no-progress -dest "squashfs-root" "squash-root.img" \
$patterns
$noglob
rm -f "squash-root.img"
cd - || break # change to $OLDPWD (popd alternative)
done
cd "$mypopd" || return
}
########################################
collect_osaoat() {
local osa_devices
local osa_device
if type qethqoat >/dev/null; then
osa_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \
| sed 's/[()]//g' )
if test -n "${osa_devices}"; then
pr_collect_output "osa oat"
for osa_device in ${osa_devices}; do
call_run_command "qethqoat ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}.out" &&
call_run_command "qethqoat -r ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}_${osa_device}.raw"
done
else
pr_skip "osa oat: no devices"
fi
else
pr_skip "osa oat: qethqoat not available"
fi
}
########################################
collect_ethtool() {
local network_device
if type ethtool >/dev/null; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "ethtool"
for network_device in ${NETWORK_DEVS}; do
call_run_command "ethtool ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -k ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -a ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -c ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool --per-queue ${network_device} \
--show-coalesce" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -g ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -i ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -l ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -P ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -S ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -T ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool --module-info ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
done
else
pr_skip "ethtool: no devices"
fi
else
pr_skip "ethtool: not available"
fi
}
########################################
collect_tc() {
local network_device
if type tc >/dev/null; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "Traffic Control"
for network_device in ${NETWORK_DEVS}; do
call_run_command "tc -s qdisc show dev ${network_device}" \
"${OUTPUT_FILE_TC}"
done
else
pr_skip "Traffic Control: no devices"
fi
else
pr_skip "Traffic Control: tc not available"
fi
}
########################################
collect_bridge() {
local network_device
if type bridge >/dev/null; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "bridge"
for network_device in ${NETWORK_DEVS}; do
call_run_command "bridge -d link show dev ${network_device}" \
"${OUTPUT_FILE_BRIDGE}"
call_run_command "bridge -s fdb show dev ${network_device}" \
"${OUTPUT_FILE_BRIDGE}"
call_run_command "bridge -d mdb show dev ${network_device}" \
"${OUTPUT_FILE_BRIDGE}"
done
else
pr_skip "bridge: no devices"
fi
else
pr_skip "bridge: not available"
fi
}
########################################
# OpenVSwitch
collect_ovs() {
local ovscmd
local bridge
local ovscmds
local ovsbrcmd
local ovsbrcmds
ovscmds="ovs-dpctl -s show\
:ovs-vsctl -t 5 show\
:ovsdb-client dump\
"
if type ovs-vsctl >/dev/null; then
pr_collect_output "OpenVSwitch"
IFS=:
for ovscmd in ${ovscmds}; do
IFS=${IFS_ORI} call_run_command "${ovscmd}" "${OUTPUT_FILE_OVS}"
done
IFS="${IFS_ORI}"
for bridge in ${ovs-vsctl list-br}; do
ovsbrcmds="ovs-ofctl show ${bridge}\
:ovs-ofctl dump-flows ${bridge}\
:ovs-appctl fdb/show ${bridge}\
"
IFS=:
for ovsbrcmd in ${ovsbrcmds}; do
IFS=${IFS_ORI} call_run_command "${ovsbrcmd}" "${OUTPUT_FILE_OVS}"
done
IFS="${IFS_ORI}"
done
else
pr_skip "OpenVSwitch: ovs-vsctl not available"
fi
}
########################################
collect_container() {
local container_list
local item
# check if container environment exists
if test "x${DOCKER}" = "xYES" || test "x${KUBERNETES}" = "xYES" || test "x${PODMAN}" = "xYES"; then
pr_collect_output "container host"
else
pr_skip "container host: not found"
fi
# for docker command exists
if test "x${DOCKER}" = "xYES"; then
pr_syslog_stdout " docker ..."
IFS=:
for item in ${DOCKER_CMDS}; do
IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_DOCKER}"
done
IFS="${IFS_ORI}"
fi
# for podman command exists
if test "x${PODMAN}" = "xYES"; then
pr_syslog_stdout " podman ..."
IFS=:
for item in ${PODMAN_CMDS}; do
IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_PODMAN}"
done
IFS="${IFS_ORI}"
fi
# for kubectl command exists
if test "x${KUBERNETES}" = "xYES"; then
pr_syslog_stdout " Kubernetes ..."
container_list=$(kubectl top pod | grep -v "MEMORY(bytes)" | cut -d' ' -f1)
IFS=:
for item in ${KUBERNETES_CMDS}; do
IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_KUBERNETES}.out"
done
IFS="${IFS_ORI}"
if test -n "${container_list}"; then
for item in ${container_list}; do
call_run_command "kubectl logs --tail=${KUBERNETES_LOG_LINES} ${item}"\
"${OUTPUT_FILE_KUBERNETES}.log"
done
fi
fi
}
########################################
collect_nvme() {
local device
if type nvme >/dev/null; then
pr_collect_output "NVME storage"
call_run_command "nvme list" "${OUTPUT_FILE_NVME}"
for device in /dev/nvme[0-9]*; do
if [ -c $device ]; then
call_run_command "smartctl -x $device" "${OUTPUT_FILE_NVME}"
call_run_command "nvme fw-log $device" "${OUTPUT_FILE_NVME}"
call_run_command "nvme smart-log $device" "${OUTPUT_FILE_NVME}"
call_run_command "nvme error-log $device" "${OUTPUT_FILE_NVME}"
fi
done
else
pr_skip "nvme: not available"
fi
}
########################################
collect_dasd() {
local device
if type dasdview >/dev/null; then
pr_collect_output "DASD storage"
call_run_command "lsdasd" "${OUTPUT_FILE_DASD}" # duplicate as file header
for device in /dev/dasd*; do
if [ -b $device ]; then
call_run_command "dasdview -i $device" "${OUTPUT_FILE_DASD}"
call_run_command "dasdview -t info $device" "${OUTPUT_FILE_DASD}"
else
echo "$device is no block device" >> ${OUTPUT_FILE_DASD}
fi
done
else
pr_skip "dasdview: not available"
fi
}
########################################
collect_kvm() {
local cmd
local domain_list
local domain
# check if KVM virsh command exists
if [ "x${KVM}" = "xYES" ]; then
pr_collect_output "KVM"
IFS=:
for cmd in ${KVM_CMDS}; do
IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_KVM}"
done
IFS="${IFS_ORI}"
# domain/guest specific commands
domain_list=$(virsh list --all --name)
path="/machine/sclp/s390-sclp-event-facility/sclpcpi"
props="timestamp\
system_name\
system_type\
system_level\
sysplex_name"
if test -n "${domain_list}"; then
for domain in ${domain_list}; do
call_run_command "virsh dominfo ${domain}" "${OUTPUT_FILE_KVM}"
call_run_command "virsh domblklist ${domain}" "${OUTPUT_FILE_KVM}"
call_run_command "virsh domstats ${domain}" "${OUTPUT_FILE_KVM}"
for prop in ${props}; do
qm=$(printf '{
"execute": "qom-get",
"arguments": {
"path": "%s",
"property": "%s"
}
}' "${path}" "${prop}")
call_run_command "virsh qemu-monitor-command ${domain} \
--pretty '${qm}'" "${OUTPUT_FILE_KVM}"
done
done
else
echo "no KVM domains found" | tee -a ${OUTPUT_FILE_KVM}
fi
else
pr_skip "KVM: no virsh command"
fi
}
########################################
post_processing() {
local file_mtime
local file_mtime_epoche
local tmp_file
local file_name
local base_dir
local dir_list
pr_syslog_stdout ${step_num} "Postprocessing"
# wipe possible passwords
dir_list="${WORKPATH} \
${WORKPATH}etc/ssl/ \
${WORKPATH}etc/libvirt/"
for base_dir in ${dir_list}; do
find "${base_dir}" -maxdepth 2 -name "*xml" -o -name "*conf" -o -name "*cnf" \
2>/dev/null | while read -r file_name; do
file_mtime_epoche=$(stat --format=%Y "${file_name}")
file_mtime=$(date +%Y%m%d%H%M.%S --date="@${file_mtime_epoche}")
tmp_file=${file_name}.$$
echo " clean pw: ${file_name}"
if ! sed "s/\(.*[Pp]assw.*=\).*/\1********/g" "${file_name}" > "${tmp_file}"; then
echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
fi
mv "${tmp_file}" "${file_name}"
touch --time=mtime -t "${file_mtime}" "${file_name}"
done
done
# compressing data folder to avoid full unpack for any DBGINFO
base_dir="${WORKPATH}proc/"
search4="kallsyms"
find "${base_dir}" -name ${search4} 2>/dev/null | while read -r file_name; do
tmp_file=${file_name}-${KERNEL_BASE}.tgz
echo " compress: ${file_name}"
if ! tar -czf "${tmp_file}" -C "${base_dir}" "${search4}"; then
echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
echo
else
rm -f "${file_name}"
fi
done
echo "log work directory state after post processing:"
echo "du summary: $(du -ks ${WORKPATH})"
df -k ${WORKPATH}
}
########################################
# Be aware that this output must be
# redirected into a separate logfile
call_run_command() {
local rc=0
local cmd="${1}"
local logfile="${2}"
# extract the raw_command and set cmd_type, as some shell might split into
# several lines restrict the cmd echo to the first line only
local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
local cmd_type=$(type -t ${raw_cmd})
echo "#######################################################" >> "${logfile}"
echo "${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
# check calling command type
if [ "X${cmd_type}" = "Xbuiltin" ]; then
# command is a builtin (use of timeout not possible)
eval "${cmd}" >> ${logfile} 2>&1
rc=$?
elif [ "X${cmd_type}" != "X" ]; then
# command was found and has a type
if [ "x${TIMEOUT_OK}" = "xYES" ]; then
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
rc=$?
else
# fall back - call all existing commands without timeout
eval "${cmd}" >> ${logfile} 2>&1
rc=$?
fi
else
echo "Command \"${raw_cmd}\" not available" >> "${logfile}"
echo >> "${logfile}"
return 1
fi
# add return code or new line and log a warning if rc is not 0 and define return
if [ ${rc} -eq 0 ]; then
echo >> "${logfile}"
return 0
elif [ "x${TIMEOUT_OK}" = "xYES" ] && [ ${rc} -eq 124 ]; then
echo "Warning: \"${cmd}\" cancelled by timeout"
echo "-> command cancelled by timeout" >> "${logfile}"
return 1
else
echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}"
echo "rc=${rc}" >> "${logfile}"
echo "Warning: Command \"${cmd}\" ended with ${rc}"
return 1
fi
}
########################################
call_collect_file() {
local file_name="${1}"
local directory_name=$(dirname "${file_name}" 2>/dev/null)
echo " ${file_name}"
if test ! -e "${WORKPATH}${directory_name}"; then
mkdir -p "${WORKPATH}${directory_name}" 2>&1
fi
if ! cp -r --preserve=mode,timestamps -dL --parents "${file_name}" "${WORKPATH}" 2>&1; then
return 1
else
return 0
fi
}
########################################
# print that an instance is already running
print_alreadyrunning() {
print_version
cat <<EOF
Please check the system if another instance of '${SCRIPTNAME}' is already
running. If this is not the case, please remove the lock file
'${LOCKFILE}'.
EOF
}
########################################
# Setup the environment
environment_setup() {
if test ! -e "${WORKDIR_BASE}"; then
mkdir -p "${WORKDIR_BASE}"
elif test ! -d "${WORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: \"${WORKDIR_BASE}\" exists but this is a file!"
echo " Please make sure \"${WORKDIR_BASE}\" is a directory."
exit 1
fi
if test -e "${LOCKFILE}"; then
print_alreadyrunning
exit 1
else
touch "${LOCKFILE}"
echo "${DATETIME}" > "${LOCKFILE}"
fi
if ! mkdir "${WORKPATH}" 2>/dev/null; then
echo "${SCRIPTNAME}: Error: Target directory \"${WORKPATH}\" already exists or"
echo " \"${WORKDIR_BASE}\" does not exist!"
exit 1
fi
chmod 0700 "${WORKPATH}"
}
########################################
# create gzip-ped tar file
create_package() {
local rc_tar
pr_syslog_stdout ${step_num} "Finalizing: Creating archive with collected data"
# get a copy of the script used
cp -p "${FULLPATHSCRIPT}" "${WORKPATH}"
# create the archive
cd "${WORKDIR_BASE}"
touch "${WORKARCHIVE}"
chmod 0600 "${WORKARCHIVE}"
tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}"
rc_tar=$?
if [ $rc_tar -eq 0 ]; then
chmod 0600 "${WORKARCHIVE}"
pr_stdout " "
pr_stdout "Collected data was saved to:"
pr_stdout " >> ${WORKARCHIVE} <<"
pr_stdout " "
pr_stdout "Please review all collected data before sending to your" \
"service organization."
pr_stdout " "
elif [ $rc_tar -eq 127 ]; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Error: tar command is not available!"
pr_stdout " Please install the corresponding package!"
else
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Error: Collection of data failed!"
pr_stdout " The creation of \"${WORKARCHIVE}\" was not successful."
pr_stdout " Please check the directory \"${WORKDIR_BASE}\""
pr_stdout " to provide enough free available space."
fi
}
########################################
# Cleaning up the prepared/collected information
environment_cleanup() {
if ! rm -rf "${WORKPATH}" 2>/dev/null; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKPATH}\" failed"
pr_stdout " Please remove the directory manually"
pr_stdout " "
fi
if ! rm -f "${LOCKFILE}" 2>/dev/null; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${LOCKFILE}\" failed"
pr_stdout " Please remove the file manually"
pr_stdout " "
fi
}
########################################
# Function to perform a cleanup in case of a received signal
emergency_exit() {
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Info: Data collection has been interrupted"
pr_stdout " Cleanup of temporary collected data"
environment_cleanup
pr_stdout "${SCRIPTNAME}: Info: Emergency exit processed"
pr_stdout " "
logger -t "${SCRIPTNAME}" "Data collection interrupted"
exit
}
########################################
# Function to print to stdout when rediretion is active
pr_stdout() {
echo "${@}" >&8
}
########################################
# Function to print to stdout and into log file when rediretion is active
pr_log_stdout() {
echo "$@"
echo "$@" >&8
}
########################################
# Function to print to stdout and into log file when rediretion is active
pr_syslog_stdout() {
echo "$@" >&8
echo
echo "$(date +%H:%M:%S.%N) - " "$@"
logger -t "${SCRIPTNAME}" "$@"
}
########################################
# print "Collecting ... output"
pr_collect_output() {
pr_syslog_stdout ${step_num} "Collecting" $1 "output"
}
########################################
# print "Collecting ..." like fs
pr_collect() {
pr_syslog_stdout ${step_num} "Collecting" "$@"
}
########################################
# print "Skipping ..." info with reason
pr_skip() {
pr_syslog_stdout ${step_num} "Skip" "$@"
}
###############################################################################
# Running the script (main)
environment_setup
print_version
# saving stdout/stderr and redirecting stdout/stderr into log file
exec 8>&1 9>&2 >"${LOGFILE}" 2>&1
# run print_version again to be logged in dbginfo.log
print_version
# trap on SIGHUP=1 SIGINT=2 SIGTERM=15
trap emergency_exit SIGHUP SIGINT SIGTERM
pr_log_stdout ""
pr_log_stdout "Hardware platform = ${HW}"
pr_log_stdout "Runtime environment = ${RUNTIME_ENVIRONMENT} - DPM: ${DPM_MODE}"
pr_log_stdout "Kernel version = ${KERNEL_INFO} (${KERNEL_BASE})"
pr_log_stdout "OS version / distro = ${OS_NAME}"
pr_log_stdout "Date and time of info = ${DATETIME}"
pr_log_stdout ""
logger -t "${SCRIPTNAME}" "Starting data collection"
# step counter
current_step=1
# run all collection steps
for step in ${ALL_STEPS}; do
# generate step numbering
step_num="${current_step} of ${COLLECTION_COUNT}: "
# calling step procedure
${step}
current_step=`expr ${current_step} + 1`
done
logger -t "${SCRIPTNAME}" "Data collection completed"
exec 1>&8 2>&9 8>&- 9>&-
#EOF