diff --git a/.editorconfig b/.editorconfig index b343f278..dd58ec8c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ shell_variant = posix insert_final_newline = true trim_trailing_whitespace = true indent_style = tab -indent_size = 1 +indent_size = 8 switch_case_indent = false function_next_line = true binary_next_line = false @@ -20,13 +20,3 @@ space_redirects = true # Some scripts will only run with bash [{mkfadumprd,mkdumprd,kdumpctl,kdump-lib.sh}] shell_variant = bash - -# Use dracut code style for *-module-setup.sh -[*-module-setup.sh,dracut-early-kdump.sh] -shell_variant = bash -indent_style = space -indent_size = 4 -switch_case_indent = true -function_next_line = false -binary_next_line = true -space_redirects = true diff --git a/Makefile b/Makefile index 538e4f49..5029a59c 100644 --- a/Makefile +++ b/Makefile @@ -12,27 +12,14 @@ udevrulesdir ?= ${libdir}/udev/rules.d systemdsystemunitdir ?= ${libdir}/systemd/system/ ARCH ?= $(shell uname -m) dracutmoddir = $(DESTDIR)${libdir}/dracut/modules.d -kdumpbasemoddir = $(dracutmoddir)/99kdumpbase dracut-modules: mkdir -p $(dracutmoddir) - mkdir -p -m755 $(kdumpbasemoddir) - - install -m 755 dracut-kdump.sh $(kdumpbasemoddir)/kdump.sh - install -m 755 dracut-module-setup.sh $(kdumpbasemoddir)/module-setup.sh - install -m 755 dracut-monitor_dd_progress.sh $(kdumpbasemoddir)/monitor_dd_progress.sh - install -m 644 dracut-kdump-emergency.service $(kdumpbasemoddir)/kdump-emergency.service - install -m 644 dracut-kdump-capture.service $(kdumpbasemoddir)/kdump-capture.service - install -m 644 dracut-kdump-emergency.target $(kdumpbasemoddir)/kdump-emergency.target - - mkdir -p -m755 $(dracutmoddir)/99earlykdump - install -m 755 dracut-early-kdump.sh $(dracutmoddir)/99earlykdump/early-kdump.sh - install -m 755 dracut-early-kdump-module-setup.sh $(dracutmoddir)/99earlykdump/module-setup.sh + cp -r dracut/99kdumpbase $(dracutmoddir) + cp -r dracut/99earlykdump $(dracutmoddir) ifeq ($(ARCH), $(filter ppc64le ppc64,$(ARCH))) - mkdir -p -m755 $(dracutmoddir)/99zz-fadumpinit - install -m 755 dracut-fadump-init-fadump.sh $(dracutmoddir)/99zz-fadumpinit/init-fadump.sh - install -m 755 dracut-fadump-module-setup.sh $(dracutmoddir)/99zz-fadumpinit/module-setup.sh + cp -r dracut/99zz-fadumpinit $(dracutmoddir) endif kdump-conf: gen-kdump-conf.sh diff --git a/dracut-fadump-init-fadump.sh b/dracut-fadump-init-fadump.sh deleted file mode 100755 index 94a37512..00000000 --- a/dracut-fadump-init-fadump.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh -export PATH=/usr/bin:/usr/sbin -export SYSTEMD_IN_INITRD=lenient - -[ -e /proc/mounts ] || - (mkdir -p /proc && mount -t proc -o nosuid,noexec,nodev proc /proc) - -grep -q '^sysfs /sys sysfs' /proc/mounts || - (mkdir -p /sys && mount -t sysfs -o nosuid,noexec,nodev sysfs /sys) - -grep -q '^none / ' /proc/mounts || grep -q '^rootfs / ' /proc/mounts && ROOTFS_IS_RAMFS=1 - -if [ -f /proc/device-tree/rtas/ibm,kernel-dump ] || [ -f /proc/device-tree/ibm,opal/dump/mpipl-boot ]; then - mkdir /newroot - mount -t ramfs ramfs /newroot - - if [ $ROOTFS_IS_RAMFS ]; then - for FILE in $(ls -A /fadumproot/); do - mv /fadumproot/$FILE /newroot/ - done - exec switch_root /newroot /init - else - mkdir /newroot/sys /newroot/proc /newroot/dev /newroot/run /newroot/oldroot - - grep -q '^devtmpfs /dev devtmpfs' /proc/mounts && mount --move /dev /newroot/dev - grep -q '^tmpfs /run tmpfs' /proc/mounts && mount --move /run /newroot/run - mount --move /sys /newroot/sys - mount --move /proc /newroot/proc - - cp --reflink=auto --sparse=auto --preserve=mode,timestamps,links -dfr /fadumproot/. /newroot/ - cd /newroot && pivot_root . oldroot - - loop=1 - while [ $loop ]; do - unset loop - while read -r _ mp _; do - case $mp in - /oldroot/*) umount -d "$mp" && loop=1 ;; - esac - done $KDUMP_CONF_PARSED - -get_kdump_confs() -{ - while read -r config_opt config_val; do - # remove inline comments after the end of a directive. - case "$config_opt" in - path) - KDUMP_PATH="$config_val" - ;; - core_collector) - [ -n "$config_val" ] && CORE_COLLECTOR="$config_val" - ;; - sshkey) - if [ -f "$config_val" ]; then - SSH_KEY_LOCATION=$config_val - fi - ;; - kdump_pre) - KDUMP_PRE="$config_val" - ;; - kdump_post) - KDUMP_POST="$config_val" - ;; - fence_kdump_args) - FENCE_KDUMP_ARGS="$config_val" - ;; - fence_kdump_nodes) - FENCE_KDUMP_NODES="$config_val" - ;; - failure_action | default) - case $config_val in - shell) - FAILURE_ACTION="kdump_emergency_shell" - ;; - reboot) - FAILURE_ACTION="systemctl reboot -f && exit" - ;; - halt) - FAILURE_ACTION="halt && exit" - ;; - poweroff) - FAILURE_ACTION="systemctl poweroff -f && exit" - ;; - dump_to_rootfs) - FAILURE_ACTION="dump_to_rootfs" - ;; - esac - ;; - final_action) - case $config_val in - reboot) - FINAL_ACTION="systemctl reboot -f" - ;; - halt) - FINAL_ACTION="halt" - ;; - poweroff) - FINAL_ACTION="systemctl poweroff -f" - ;; - esac - ;; - esac - done < "$KDUMP_CONF_PARSED" - - if [ -z "$CORE_COLLECTOR" ]; then - CORE_COLLECTOR="$DEFAULT_CORE_COLLECTOR" - if is_ssh_dump_target || is_raw_dump_target; then - CORE_COLLECTOR="$CORE_COLLECTOR -F" - fi - fi -} - -# store the kexec kernel log to a file. -save_log() -{ - # LOG_OP is empty when log can't be saved, eg. raw target - [ -n "$KDUMP_LOG_OP" ] || return - - dmesg -T > $KDUMP_LOG_FILE - - if command -v journalctl > /dev/null; then - journalctl -ab >> $KDUMP_LOG_FILE - fi - chmod 600 $KDUMP_LOG_FILE - - dinfo "saving the $KDUMP_LOG_FILE to $KDUMP_LOG_DEST/" - - eval "$KDUMP_LOG_OP" -} - -# $1: dump path, must be a mount point -dump_fs() -{ - ddebug "dump_fs _mp=$1" - - if ! is_mounted "$1"; then - dinfo "dump path '$1' is not mounted, trying to mount..." - if ! mount --target "$1"; then - derror "failed to dump to '$1', it's not a mount point!" - return 1 - fi - fi - - # Remove -F in makedumpfile case. We don't want a flat format dump here. - case $CORE_COLLECTOR in - *makedumpfile*) - CORE_COLLECTOR=$(echo "$CORE_COLLECTOR" | sed -e "s/-F//g") - ;; - esac - - _dump_fs_path=$(echo "$1/$KDUMP_PATH/$HOST_IP-$DATEDIR/" | tr -s /) - dinfo "saving to $_dump_fs_path" - - # Only remount to read-write mode if the dump target is mounted read-only. - _dump_mnt_op=$(get_mount_info OPTIONS target "$1" -f) - case $_dump_mnt_op in - ro*) - dinfo "Remounting the dump target in rw mode." - mount -o remount,rw "$1" || return 1 - ;; - esac - - mkdir -p "$_dump_fs_path" || return 1 - - save_vmcore_dmesg_fs ${DMESG_COLLECTOR} "$_dump_fs_path" - save_opalcore_fs "$_dump_fs_path" - - dinfo "saving vmcore" - KDUMP_LOG_DEST=$_dump_fs_path/ - KDUMP_LOG_OP="mv '$KDUMP_LOG_FILE' '$KDUMP_LOG_DEST/'" - - $CORE_COLLECTOR /proc/vmcore "$_dump_fs_path/vmcore-incomplete" - _dump_exitcode=$? - if [ $_dump_exitcode -eq 0 ]; then - sync -f "$_dump_fs_path/vmcore-incomplete" - _sync_exitcode=$? - if [ $_sync_exitcode -eq 0 ]; then - mv "$_dump_fs_path/vmcore-incomplete" "$_dump_fs_path/vmcore" - dinfo "saving vmcore complete" - else - derror "sync vmcore failed, exitcode:$_sync_exitcode" - return 1 - fi - else - derror "saving vmcore failed, exitcode:$_dump_exitcode" - return 1 - fi - - # improper kernel cmdline can cause the failure of echo, we can ignore this kind of failure - return 0 -} - -# $1: dmesg collector -# $2: dump path -save_vmcore_dmesg_fs() -{ - dinfo "saving vmcore-dmesg.txt to $2" - if $1 /proc/vmcore > "$2/vmcore-dmesg-incomplete.txt"; then - mv "$2/vmcore-dmesg-incomplete.txt" "$2/vmcore-dmesg.txt" - chmod 600 "$2/vmcore-dmesg.txt" - - # Make sure file is on disk. There have been instances where later - # saving vmcore failed and system rebooted without sync and there - # was no vmcore-dmesg.txt available. - sync - dinfo "saving vmcore-dmesg.txt complete" - else - if [ -f "$2/vmcore-dmesg-incomplete.txt" ]; then - chmod 600 "$2/vmcore-dmesg-incomplete.txt" - fi - derror "saving vmcore-dmesg.txt failed" - fi -} - -# $1: dump path -save_opalcore_fs() -{ - if [ ! -f $OPALCORE ]; then - # Check if we are on an old kernel that uses a different path - if [ -f /sys/firmware/opal/core ]; then - OPALCORE="/sys/firmware/opal/core" - else - return 0 - fi - fi - - dinfo "saving opalcore:$OPALCORE to $1/opalcore" - if ! cp $OPALCORE "$1/opalcore"; then - derror "saving opalcore failed" - return 1 - fi - - sync - dinfo "saving opalcore complete" - return 0 -} - -dump_to_rootfs() -{ - - if [ "$(systemctl status dracut-initqueue | sed -n "s/^\s*Active: \(\S*\)\s.*$/\1/p")" = "inactive" ]; then - dinfo "Trying to bring up initqueue for rootfs mount" - systemctl start dracut-initqueue - fi - - dinfo "Clean up dead systemd services" - systemctl cancel - dinfo "Waiting for rootfs mount, will timeout after 90 seconds" - systemctl start --no-block sysroot.mount - - _loop=0 - while [ $_loop -lt 90 ] && ! is_mounted /sysroot; do - sleep 1 - _loop=$((_loop + 1)) - done - - if ! is_mounted /sysroot; then - derror "Failed to mount rootfs" - return - fi - - ddebug "NEWROOT=$NEWROOT" - dump_fs $NEWROOT -} - -kdump_emergency_shell() -{ - ddebug "Switching to kdump emergency shell..." - - [ -f /etc/profile ] && . /etc/profile - export PS1='kdump:${PWD}# ' - - . /lib/dracut-lib.sh - if [ -f /dracut-state.sh ]; then - . /dracut-state.sh 2> /dev/null - fi - - source_conf /etc/conf.d - - type plymouth > /dev/null 2>&1 && plymouth quit - - source_hook "emergency" - while read -r _tty rest; do - ( - echo - echo - echo 'Entering kdump emergency mode.' - echo 'Type "journalctl" to view system logs.' - echo 'Type "rdsosreport" to generate a sosreport, you can then' - echo 'save it elsewhere and attach it to a bug report.' - echo - echo - ) > "/dev/$_tty" - done < /proc/consoles - sh -i -l - /bin/rm -f -- /.console_lock -} - -do_failure_action() -{ - dinfo "Executing failure action $FAILURE_ACTION" - eval $FAILURE_ACTION -} - -do_final_action() -{ - dinfo "Executing final action $FINAL_ACTION" - eval $FINAL_ACTION -} - -do_dump() -{ - eval $DUMP_INSTRUCTION - _ret=$? - - if [ $_ret -ne 0 ]; then - derror "saving vmcore failed" - fi - - return $_ret -} - -do_kdump_pre() -{ - if [ -n "$KDUMP_PRE" ]; then - "$KDUMP_PRE" - _ret=$? - if [ $_ret -ne 0 ]; then - derror "$KDUMP_PRE exited with $_ret status" - return $_ret - fi - fi - - # if any script fails, it just raises warning and continues - if [ -d /etc/kdump/pre.d ]; then - for file in /etc/kdump/pre.d/*; do - "$file" - _ret=$? - if [ $_ret -ne 0 ]; then - derror "$file exited with $_ret status" - fi - done - fi - return 0 -} - -do_kdump_post() -{ - if [ -d /etc/kdump/post.d ]; then - for file in /etc/kdump/post.d/*; do - "$file" "$1" - _ret=$? - if [ $_ret -ne 0 ]; then - derror "$file exited with $_ret status" - fi - done - fi - - if [ -n "$KDUMP_POST" ]; then - "$KDUMP_POST" "$1" - _ret=$? - if [ $_ret -ne 0 ]; then - derror "$KDUMP_POST exited with $_ret status" - fi - fi -} - -# $1: block target, eg. /dev/sda -dump_raw() -{ - [ -b "$1" ] || return 1 - - dinfo "saving to raw disk $1" - - if ! echo "$CORE_COLLECTOR" | grep -q makedumpfile; then - _src_size=$(stat --format %s /proc/vmcore) - _src_size_mb=$((_src_size / 1048576)) - /kdumpscripts/monitor_dd_progress.sh $_src_size_mb & - fi - - dinfo "saving vmcore" - $CORE_COLLECTOR /proc/vmcore | dd of="$1" bs=$DD_BLKSIZE >> /tmp/dd_progress_file 2>&1 || return 1 - sync - - dinfo "saving vmcore complete" - return 0 -} - -# $1: ssh key file -# $2: ssh address in @ format -dump_ssh() -{ - _ret=0 - _ssh_opt="-i $1 -o BatchMode=yes -o StrictHostKeyChecking=yes" - _ssh_dir="$KDUMP_PATH/$HOST_IP-$DATEDIR" - if is_ipv6_address "$2"; then - _scp_address=${2%@*}@"[${2#*@}]" - else - _scp_address=$2 - fi - - dinfo "saving to $2:$_ssh_dir" - - cat /var/lib/random-seed > /dev/urandom - ssh -q $_ssh_opt "$2" mkdir -p "$_ssh_dir" || return 1 - - save_vmcore_dmesg_ssh "$DMESG_COLLECTOR" "$_ssh_dir" "$_ssh_opt" "$2" - - dinfo "saving vmcore" - - KDUMP_LOG_DEST=$2:$_ssh_dir/ - KDUMP_LOG_OP="scp -q $_ssh_opt '$KDUMP_LOG_FILE' '$_scp_address:$_ssh_dir/'" - - save_opalcore_ssh "$_ssh_dir" "$_ssh_opt" "$2" "$_scp_address" - - if [ "${CORE_COLLECTOR%%[[:blank:]]*}" = "scp" ]; then - scp -q $_ssh_opt /proc/vmcore "$_scp_address:$_ssh_dir/vmcore-incomplete" - _ret=$? - _vmcore="vmcore" - else - $CORE_COLLECTOR /proc/vmcore | ssh $_ssh_opt "$2" "umask 0077 && dd bs=512 of='$_ssh_dir/vmcore-incomplete'" - _ret=$? - _vmcore="vmcore.flat" - fi - - if [ $_ret -eq 0 ]; then - ssh $_ssh_opt "$2" "mv '$_ssh_dir/vmcore-incomplete' '$_ssh_dir/$_vmcore'" - _ret=$? - if [ $_ret -ne 0 ]; then - derror "moving vmcore failed, exitcode:$_ret" - else - dinfo "saving vmcore complete" - fi - else - derror "saving vmcore failed, exitcode:$_ret" - fi - - return $_ret -} - -# $1: dump path -# $2: ssh opts -# $3: ssh address in @ format -# $4: scp address, similar with ssh address but IPv6 addresses are quoted -save_opalcore_ssh() -{ - if [ ! -f $OPALCORE ]; then - # Check if we are on an old kernel that uses a different path - if [ -f /sys/firmware/opal/core ]; then - OPALCORE="/sys/firmware/opal/core" - else - return 0 - fi - fi - - dinfo "saving opalcore:$OPALCORE to $3:$1" - - if ! scp $2 $OPALCORE "$4:$1/opalcore-incomplete"; then - derror "saving opalcore failed" - return 1 - fi - - ssh $2 "$3" mv "$1/opalcore-incomplete" "$1/opalcore" - dinfo "saving opalcore complete" - return 0 -} - -# $1: dmesg collector -# $2: dump path -# $3: ssh opts -# $4: ssh address in @ format -save_vmcore_dmesg_ssh() -{ - dinfo "saving vmcore-dmesg.txt to $4:$2" - if $1 /proc/vmcore | ssh $3 "$4" "umask 0077 && dd of='$2/vmcore-dmesg-incomplete.txt'"; then - ssh -q $3 "$4" mv "$2/vmcore-dmesg-incomplete.txt" "$2/vmcore-dmesg.txt" - dinfo "saving vmcore-dmesg.txt complete" - else - derror "saving vmcore-dmesg.txt failed" - fi -} - -wait_online_network() -{ - # In some cases, network may still not be ready because nm-online is called - # with "-s" which means to wait for NetworkManager startup to complete, rather - # than waiting for network connectivity specifically. Wait 10mins more for the - # network to be truely ready in these cases. - _loop=0 - while [ $_loop -lt 600 ]; do - sleep 1 - _loop=$((_loop + 1)) - if _route=$(kdump_get_ip_route "$1" 2> /dev/null); then - printf "%s" "$_route" - return - else - dwarn "Waiting for network to be ready (${_loop}s / 10min)" - fi - done - - derror "Oops. The network still isn't ready after waiting 10mins." - exit 1 -} - -get_host_ip() -{ - - if ! is_nfs_dump_target && ! is_ssh_dump_target; then - return 0 - fi - - _kdump_remote_ip=$(getarg kdump_remote_ip=) - - if [ -z "$_kdump_remote_ip" ]; then - derror "failed to get remote IP address!" - return 1 - fi - - if ! _route=$(wait_online_network "$_kdump_remote_ip"); then - return 1 - fi - - _netdev=$(kdump_get_ip_route_field "$_route" "dev") - - if ! _kdumpip=$(ip addr show dev "$_netdev" | grep '[ ]*inet'); then - derror "Failed to get IP of $_netdev" - return 1 - fi - - _kdumpip=$(echo "$_kdumpip" | head -n 1 | awk '{print $2}') - _kdumpip="${_kdumpip%%/*}" - HOST_IP=$_kdumpip -} - -read_kdump_confs() -{ - if [ ! -f "$KDUMP_CONFIG_FILE" ]; then - derror "$KDUMP_CONFIG_FILE not found" - return - fi - - get_kdump_confs - - # rescan for add code for dump target - while read -r config_opt config_val; do - # remove inline comments after the end of a directive. - case "$config_opt" in - dracut_args) - config_val=$(get_dracut_args_target "$config_val") - if [ -n "$config_val" ]; then - config_val=$(get_mntpoint_from_target "$config_val") - DUMP_INSTRUCTION="dump_fs $config_val" - fi - ;; - ext[234] | xfs | btrfs | minix | nfs | virtiofs) - config_val=$(get_mntpoint_from_target "$config_val") - DUMP_INSTRUCTION="dump_fs $config_val" - ;; - raw) - DUMP_INSTRUCTION="dump_raw $config_val" - ;; - ssh) - DUMP_INSTRUCTION="dump_ssh $SSH_KEY_LOCATION $config_val" - ;; - esac - done < "$KDUMP_CONF_PARSED" -} - -fence_kdump_notify() -{ - if [ -n "$FENCE_KDUMP_NODES" ]; then - # shellcheck disable=SC2086 - $FENCE_KDUMP_SEND $FENCE_KDUMP_ARGS $FENCE_KDUMP_NODES & - fi -} - -if [ "$1" = "--error-handler" ]; then - get_kdump_confs - do_failure_action - save_log - do_final_action - - exit $? -fi - -# continue here only if we have to save dump. -if [ -f /etc/fadump.initramfs ] && [ ! -f /proc/device-tree/rtas/ibm,kernel-dump ] && [ ! -f /proc/device-tree/ibm,opal/dump/mpipl-boot ]; then - exit 0 -fi - -read_kdump_confs -fence_kdump_notify - -if ! get_host_ip; then - derror "get_host_ip exited with non-zero status!" - exit 1 -fi - -if [ -z "$DUMP_INSTRUCTION" ]; then - DUMP_INSTRUCTION="dump_fs $NEWROOT" -fi - -if ! do_kdump_pre; then - derror "kdump_pre script exited with non-zero status!" - do_final_action - # During systemd service to reboot the machine, stop this shell script running - exit 1 -fi -make_trace_mem "kdump saving vmcore" '1:shortmem' '2+:mem' '3+:slab' -do_dump -DUMP_RETVAL=$? - -if ! do_kdump_post $DUMP_RETVAL; then - derror "kdump_post script exited with non-zero status!" -fi - -save_log - -if [ $DUMP_RETVAL -ne 0 ]; then - exit 1 -fi - -do_final_action diff --git a/dracut-monitor_dd_progress.sh b/dracut-monitor_dd_progress.sh deleted file mode 100644 index e139d333..00000000 --- a/dracut-monitor_dd_progress.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -SRC_FILE_MB=$1 - -while true -do - DD_PID=`pidof dd` - if [ -n "$DD_PID" ]; then - break - fi -done - -while true -do - sleep 5 - if [ ! -d /proc/$DD_PID ]; then - break - fi - - kill -s USR1 $DD_PID - CURRENT_SIZE=`tail -n 1 /tmp/dd_progress_file | sed "s/[^0-9].*//g"` - [ -n "$CURRENT_SIZE" ] && { - CURRENT_MB=$(($CURRENT_SIZE / 1048576)) - echo -e "Copied $CURRENT_MB MB / $SRC_FILE_MB MB\r" - } -done - -rm -f /tmp/dd_progress_file diff --git a/dracut/.editorconfig b/dracut/.editorconfig new file mode 100644 index 00000000..254f8704 --- /dev/null +++ b/dracut/.editorconfig @@ -0,0 +1,9 @@ +# Use dracut code style for dracut modules +[*] +shell_variant = bash +indent_style = space +indent_size = 4 +switch_case_indent = true +function_next_line = false +binary_next_line = true +space_redirects = true diff --git a/dracut-early-kdump.sh b/dracut/99earlykdump/early-kdump.sh similarity index 84% rename from dracut-early-kdump.sh rename to dracut/99earlykdump/early-kdump.sh index 4fd8e901..c56ed821 100755 --- a/dracut-early-kdump.sh +++ b/dracut/99earlykdump/early-kdump.sh @@ -8,26 +8,28 @@ EARLY_KDUMP_KERNEL="" EARLY_KDUMP_CMDLINE="" EARLY_KEXEC_ARGS="" +# shellcheck source=/dev/null . /etc/sysconfig/kdump +# shellcheck source=/dev/null . /lib/dracut-lib.sh +# shellcheck source=SCRIPTDIR/../../kdump-lib.sh . /lib/kdump-lib.sh +# shellcheck source=SCRIPTDIR/../../kdump-logger.sh . /lib/kdump-logger.sh # initiate the kdump logger if ! dlog_init; then - echo "failed to initiate the kdump logger." - exit 1 + echo "failed to initiate the kdump logger." + exit 1 fi -prepare_parameters() -{ +prepare_parameters() { EARLY_KDUMP_CMDLINE=$(prepare_cmdline "${KDUMP_COMMANDLINE}" "${KDUMP_COMMANDLINE_REMOVE}" "${KDUMP_COMMANDLINE_APPEND}") EARLY_KDUMP_KERNEL="/boot/kernel-earlykdump" EARLY_KDUMP_INITRD="/boot/initramfs-earlykdump" } -early_kdump_load() -{ +early_kdump_load() { if ! check_kdump_feasibility; then return 1 fi @@ -43,6 +45,7 @@ early_kdump_load() prepare_parameters + # shellcheck disable=SC2153 EARLY_KEXEC_ARGS=$(prepare_kexec_args "${KEXEC_ARGS}") # Here, only output the messages, but do not save these messages @@ -64,8 +67,7 @@ early_kdump_load() fi } -set_early_kdump() -{ +set_early_kdump() { if getargbool 0 rd.earlykdump; then dinfo "early-kdump is enabled." early_kdump_load diff --git a/dracut-early-kdump-module-setup.sh b/dracut/99earlykdump/module-setup.sh similarity index 91% rename from dracut-early-kdump-module-setup.sh rename to dracut/99earlykdump/module-setup.sh index 0451118c..98486c59 100755 --- a/dracut-early-kdump-module-setup.sh +++ b/dracut/99earlykdump/module-setup.sh @@ -1,5 +1,6 @@ #!/bin/bash +# shellcheck source=/dev/null . /etc/sysconfig/kdump KDUMP_KERNEL="" @@ -19,11 +20,13 @@ depends() { } prepare_kernel_initrd() { + # shellcheck source=SCRIPTDIR/../../kdump-lib.sh . /lib/kdump/kdump-lib.sh prepare_kdump_bootinfo # $kernel is a variable from dracut + # shellcheck disable=SC2154 if [[ $KDUMP_KERNELVER != "$kernel" ]]; then dwarn "Using kernel version '$KDUMP_KERNELVER' for early kdump," \ "but the initramfs is generated for kernel version '$kernel'" @@ -54,6 +57,7 @@ install() { inst_script "/lib/kdump/kdump-lib.sh" "/lib/kdump-lib.sh" inst_script "/lib/kdump/kdump-lib-initramfs.sh" "/lib/kdump/kdump-lib-initramfs.sh" inst_script "/lib/kdump/kdump-logger.sh" "/lib/kdump-logger.sh" + # shellcheck disable=SC2154 inst_hook cmdline 00 "$moddir/early-kdump.sh" inst_binary "$KDUMP_KERNEL" inst_binary "$KDUMP_INITRD" @@ -61,5 +65,6 @@ install() { ln_r "$KDUMP_KERNEL" "/boot/kernel-earlykdump" ln_r "$KDUMP_INITRD" "/boot/initramfs-earlykdump" + # shellcheck disable=SC2154 chmod -x "${initdir}/$KDUMP_KERNEL" } diff --git a/dracut/99kdumpbase/.editorconfig b/dracut/99kdumpbase/.editorconfig new file mode 100644 index 00000000..feb7df23 --- /dev/null +++ b/dracut/99kdumpbase/.editorconfig @@ -0,0 +1,3 @@ +# These files run in the initrd and need to be posix compliant +[{monitor_dd_progress.sh,kdump.sh}] +shell_variant = posix diff --git a/dracut-kdump-capture.service b/dracut/99kdumpbase/kdump-capture.service similarity index 100% rename from dracut-kdump-capture.service rename to dracut/99kdumpbase/kdump-capture.service diff --git a/dracut-kdump-emergency.service b/dracut/99kdumpbase/kdump-emergency.service similarity index 100% rename from dracut-kdump-emergency.service rename to dracut/99kdumpbase/kdump-emergency.service diff --git a/dracut-kdump-emergency.target b/dracut/99kdumpbase/kdump-emergency.target similarity index 100% rename from dracut-kdump-emergency.target rename to dracut/99kdumpbase/kdump-emergency.target diff --git a/dracut/99kdumpbase/kdump.sh b/dracut/99kdumpbase/kdump.sh new file mode 100755 index 00000000..dea83ee4 --- /dev/null +++ b/dracut/99kdumpbase/kdump.sh @@ -0,0 +1,615 @@ +#!/bin/sh +# +# The main kdump routine in capture kernel, bash may not be the +# default shell. Any code added must be POSIX compliant. + +# shellcheck source=/dev/null +. /lib/dracut-lib.sh +# shellcheck source=SCRIPTDIR/../../kdump-logger.sh +. /lib/kdump-logger.sh +# shellcheck source=SCRIPTDIR/../../kdump-lib-initramfs.sh +. /lib/kdump-lib-initramfs.sh + +#initiate the kdump logger +if ! dlog_init; then + echo "failed to initiate the kdump logger." + exit 1 +fi + +KDUMP_PATH="/var/crash" +KDUMP_LOG_FILE="/run/initramfs/kexec-dmesg.log" +KDUMP_LOG_DEST="" +KDUMP_LOG_OP="" +CORE_COLLECTOR="" +DEFAULT_CORE_COLLECTOR="makedumpfile -l --message-level 7 -d 31" +DMESG_COLLECTOR="/sbin/vmcore-dmesg" +FAILURE_ACTION="systemctl reboot -f" +DATEDIR=$(date +%Y-%m-%d-%T) +HOST_IP='127.0.0.1' +DUMP_INSTRUCTION="" +SSH_KEY_LOCATION=$DEFAULT_SSHKEY +DD_BLKSIZE=512 +FINAL_ACTION="systemctl reboot -f" +KDUMP_PRE="" +KDUMP_POST="" +NEWROOT="/sysroot" +OPALCORE="/sys/firmware/opal/mpipl/core" +KDUMP_CONF_PARSED="/tmp/kdump.conf.$$" + +# POSIX doesn't have pipefail, only apply when using bash +# shellcheck disable=SC3040 +[ -n "$BASH" ] && set -o pipefail + +DUMP_RETVAL=0 + +kdump_read_conf > $KDUMP_CONF_PARSED + +get_kdump_confs() { + while read -r config_opt config_val; do + # remove inline comments after the end of a directive. + case "$config_opt" in + path) + KDUMP_PATH="$config_val" + ;; + core_collector) + [ -n "$config_val" ] && CORE_COLLECTOR="$config_val" + ;; + sshkey) + if [ -f "$config_val" ]; then + SSH_KEY_LOCATION=$config_val + fi + ;; + kdump_pre) + KDUMP_PRE="$config_val" + ;; + kdump_post) + KDUMP_POST="$config_val" + ;; + fence_kdump_args) + FENCE_KDUMP_ARGS="$config_val" + ;; + fence_kdump_nodes) + FENCE_KDUMP_NODES="$config_val" + ;; + failure_action | default) + case $config_val in + shell) + FAILURE_ACTION="kdump_emergency_shell" + ;; + reboot) + FAILURE_ACTION="systemctl reboot -f && exit" + ;; + halt) + FAILURE_ACTION="halt && exit" + ;; + poweroff) + FAILURE_ACTION="systemctl poweroff -f && exit" + ;; + dump_to_rootfs) + FAILURE_ACTION="dump_to_rootfs" + ;; + esac + ;; + final_action) + case $config_val in + reboot) + FINAL_ACTION="systemctl reboot -f" + ;; + halt) + FINAL_ACTION="halt" + ;; + poweroff) + FINAL_ACTION="systemctl poweroff -f" + ;; + esac + ;; + esac + done < "$KDUMP_CONF_PARSED" + + if [ -z "$CORE_COLLECTOR" ]; then + CORE_COLLECTOR="$DEFAULT_CORE_COLLECTOR" + if is_ssh_dump_target || is_raw_dump_target; then + CORE_COLLECTOR="$CORE_COLLECTOR -F" + fi + fi +} + +# store the kexec kernel log to a file. +save_log() { + # LOG_OP is empty when log can't be saved, eg. raw target + [ -n "$KDUMP_LOG_OP" ] || return + + dmesg -T > $KDUMP_LOG_FILE + + if command -v journalctl > /dev/null; then + journalctl -ab >> $KDUMP_LOG_FILE + fi + chmod 600 $KDUMP_LOG_FILE + + dinfo "saving the $KDUMP_LOG_FILE to $KDUMP_LOG_DEST/" + + eval "$KDUMP_LOG_OP" +} + +# $1: dump path, must be a mount point +dump_fs() { + ddebug "dump_fs _mp=$1" + + if ! is_mounted "$1"; then + dinfo "dump path '$1' is not mounted, trying to mount..." + if ! mount --target "$1"; then + derror "failed to dump to '$1', it's not a mount point!" + return 1 + fi + fi + + # Remove -F in makedumpfile case. We don't want a flat format dump here. + case $CORE_COLLECTOR in + *makedumpfile*) + CORE_COLLECTOR=$(echo "$CORE_COLLECTOR" | sed -e "s/-F//g") + ;; + esac + + _dump_fs_path=$(echo "$1/$KDUMP_PATH/$HOST_IP-$DATEDIR/" | tr -s /) + dinfo "saving to $_dump_fs_path" + + # Only remount to read-write mode if the dump target is mounted read-only. + _dump_mnt_op=$(get_mount_info OPTIONS target "$1" -f) + case $_dump_mnt_op in + ro*) + dinfo "Remounting the dump target in rw mode." + mount -o remount,rw "$1" || return 1 + ;; + esac + + mkdir -p "$_dump_fs_path" || return 1 + + save_vmcore_dmesg_fs ${DMESG_COLLECTOR} "$_dump_fs_path" + save_opalcore_fs "$_dump_fs_path" + + dinfo "saving vmcore" + KDUMP_LOG_DEST=$_dump_fs_path/ + KDUMP_LOG_OP="mv '$KDUMP_LOG_FILE' '$KDUMP_LOG_DEST/'" + + $CORE_COLLECTOR /proc/vmcore "$_dump_fs_path/vmcore-incomplete" + _dump_exitcode=$? + if [ $_dump_exitcode -eq 0 ]; then + sync -f "$_dump_fs_path/vmcore-incomplete" + _sync_exitcode=$? + if [ $_sync_exitcode -eq 0 ]; then + mv "$_dump_fs_path/vmcore-incomplete" "$_dump_fs_path/vmcore" + dinfo "saving vmcore complete" + else + derror "sync vmcore failed, exitcode:$_sync_exitcode" + return 1 + fi + else + derror "saving vmcore failed, exitcode:$_dump_exitcode" + return 1 + fi + + # improper kernel cmdline can cause the failure of echo, we can ignore this kind of failure + return 0 +} + +# $1: dmesg collector +# $2: dump path +save_vmcore_dmesg_fs() { + dinfo "saving vmcore-dmesg.txt to $2" + if $1 /proc/vmcore > "$2/vmcore-dmesg-incomplete.txt"; then + mv "$2/vmcore-dmesg-incomplete.txt" "$2/vmcore-dmesg.txt" + chmod 600 "$2/vmcore-dmesg.txt" + + # Make sure file is on disk. There have been instances where later + # saving vmcore failed and system rebooted without sync and there + # was no vmcore-dmesg.txt available. + sync + dinfo "saving vmcore-dmesg.txt complete" + else + if [ -f "$2/vmcore-dmesg-incomplete.txt" ]; then + chmod 600 "$2/vmcore-dmesg-incomplete.txt" + fi + derror "saving vmcore-dmesg.txt failed" + fi +} + +# $1: dump path +save_opalcore_fs() { + if [ ! -f $OPALCORE ]; then + # Check if we are on an old kernel that uses a different path + if [ -f /sys/firmware/opal/core ]; then + OPALCORE="/sys/firmware/opal/core" + else + return 0 + fi + fi + + dinfo "saving opalcore:$OPALCORE to $1/opalcore" + if ! cp $OPALCORE "$1/opalcore"; then + derror "saving opalcore failed" + return 1 + fi + + sync + dinfo "saving opalcore complete" + return 0 +} + +dump_to_rootfs() { + + if [ "$(systemctl status dracut-initqueue | sed -n "s/^\s*Active: \(\S*\)\s.*$/\1/p")" = "inactive" ]; then + dinfo "Trying to bring up initqueue for rootfs mount" + systemctl start dracut-initqueue + fi + + dinfo "Clean up dead systemd services" + systemctl cancel + dinfo "Waiting for rootfs mount, will timeout after 90 seconds" + systemctl start --no-block sysroot.mount + + _loop=0 + while [ $_loop -lt 90 ] && ! is_mounted /sysroot; do + sleep 1 + _loop=$((_loop + 1)) + done + + if ! is_mounted /sysroot; then + derror "Failed to mount rootfs" + return + fi + + ddebug "NEWROOT=$NEWROOT" + dump_fs $NEWROOT +} + +kdump_emergency_shell() { + ddebug "Switching to kdump emergency shell..." + + # shellcheck source=/dev/null + [ -f /etc/profile ] && . /etc/profile + export PS1='kdump:${PWD}# ' + + # shellcheck source=/dev/null + . /lib/dracut-lib.sh + if [ -f /dracut-state.sh ]; then + # shellcheck source=/dev/null + . /dracut-state.sh 2> /dev/null + fi + + source_conf /etc/conf.d + + type plymouth > /dev/null 2>&1 && plymouth quit + + source_hook "emergency" + while read -r _tty rest; do + ( + echo + echo + echo 'Entering kdump emergency mode.' + echo 'Type "journalctl" to view system logs.' + echo 'Type "rdsosreport" to generate a sosreport, you can then' + echo 'save it elsewhere and attach it to a bug report.' + echo + echo + ) > "/dev/$_tty" + done < /proc/consoles + sh -i -l + /bin/rm -f -- /.console_lock +} + +do_failure_action() { + dinfo "Executing failure action $FAILURE_ACTION" + eval "$FAILURE_ACTION" +} + +do_final_action() { + dinfo "Executing final action $FINAL_ACTION" + eval "$FINAL_ACTION" +} + +do_dump() { + eval "$DUMP_INSTRUCTION" + _ret=$? + + if [ $_ret -ne 0 ]; then + derror "saving vmcore failed" + fi + + return $_ret +} + +do_kdump_pre() { + if [ -n "$KDUMP_PRE" ]; then + "$KDUMP_PRE" + _ret=$? + if [ $_ret -ne 0 ]; then + derror "$KDUMP_PRE exited with $_ret status" + return $_ret + fi + fi + + # if any script fails, it just raises warning and continues + if [ -d /etc/kdump/pre.d ]; then + for file in /etc/kdump/pre.d/*; do + "$file" + _ret=$? + if [ $_ret -ne 0 ]; then + derror "$file exited with $_ret status" + fi + done + fi + return 0 +} + +do_kdump_post() { + if [ -d /etc/kdump/post.d ]; then + for file in /etc/kdump/post.d/*; do + "$file" "$1" + _ret=$? + if [ $_ret -ne 0 ]; then + derror "$file exited with $_ret status" + fi + done + fi + + if [ -n "$KDUMP_POST" ]; then + "$KDUMP_POST" "$1" + _ret=$? + if [ $_ret -ne 0 ]; then + derror "$KDUMP_POST exited with $_ret status" + fi + fi +} + +# $1: block target, eg. /dev/sda +dump_raw() { + [ -b "$1" ] || return 1 + + dinfo "saving to raw disk $1" + + if ! echo "$CORE_COLLECTOR" | grep -q makedumpfile; then + _src_size=$(stat --format %s /proc/vmcore) + _src_size_mb=$((_src_size / 1048576)) + /kdumpscripts/monitor_dd_progress.sh $_src_size_mb & + fi + + dinfo "saving vmcore" + $CORE_COLLECTOR /proc/vmcore | dd of="$1" bs=$DD_BLKSIZE >> /tmp/dd_progress_file 2>&1 || return 1 + sync + + dinfo "saving vmcore complete" + return 0 +} + +# $1: ssh key file +# $2: ssh address in @ format +dump_ssh() { + _ret=0 + _ssh_opt="-i $1 -o BatchMode=yes -o StrictHostKeyChecking=yes" + _ssh_dir="$KDUMP_PATH/$HOST_IP-$DATEDIR" + if is_ipv6_address "$2"; then + _scp_address=${2%@*}@"[${2#*@}]" + else + _scp_address=$2 + fi + + dinfo "saving to $2:$_ssh_dir" + + cat /var/lib/random-seed > /dev/urandom + ssh -q "$_ssh_opt" "$2" mkdir -p "$_ssh_dir" || return 1 + + save_vmcore_dmesg_ssh "$DMESG_COLLECTOR" "$_ssh_dir" "$_ssh_opt" "$2" + + dinfo "saving vmcore" + + KDUMP_LOG_DEST=$2:$_ssh_dir/ + KDUMP_LOG_OP="scp -q $_ssh_opt '$KDUMP_LOG_FILE' '$_scp_address:$_ssh_dir/'" + + save_opalcore_ssh "$_ssh_dir" "$_ssh_opt" "$2" "$_scp_address" + + if [ "${CORE_COLLECTOR%%[[:blank:]]*}" = "scp" ]; then + scp -q "$_ssh_opt" /proc/vmcore "$_scp_address:$_ssh_dir/vmcore-incomplete" + _ret=$? + _vmcore="vmcore" + else + # shellcheck disable=SC2029 + $CORE_COLLECTOR /proc/vmcore | ssh "$_ssh_opt" "$2" "umask 0077 && dd bs=512 of='$_ssh_dir/vmcore-incomplete'" + _ret=$? + _vmcore="vmcore.flat" + fi + + if [ $_ret -eq 0 ]; then + # shellcheck disable=SC2029 + ssh "$_ssh_opt" "$2" "mv '$_ssh_dir/vmcore-incomplete' '$_ssh_dir/$_vmcore'" + _ret=$? + if [ $_ret -ne 0 ]; then + derror "moving vmcore failed, exitcode:$_ret" + else + dinfo "saving vmcore complete" + fi + else + derror "saving vmcore failed, exitcode:$_ret" + fi + + return $_ret +} + +# $1: dump path +# $2: ssh opts +# $3: ssh address in @ format +# $4: scp address, similar with ssh address but IPv6 addresses are quoted +save_opalcore_ssh() { + if [ ! -f $OPALCORE ]; then + # Check if we are on an old kernel that uses a different path + if [ -f /sys/firmware/opal/core ]; then + OPALCORE="/sys/firmware/opal/core" + else + return 0 + fi + fi + + dinfo "saving opalcore:$OPALCORE to $3:$1" + + if ! scp "$2" "$OPALCORE" "$4:$1/opalcore-incomplete"; then + derror "saving opalcore failed" + return 1 + fi + + # shellcheck disable=SC2029 + ssh "$2" "$3" mv "$1/opalcore-incomplete" "$1/opalcore" + dinfo "saving opalcore complete" + return 0 +} + +# $1: dmesg collector +# $2: dump path +# $3: ssh opts +# $4: ssh address in @ format +save_vmcore_dmesg_ssh() { + dinfo "saving vmcore-dmesg.txt to $4:$2" + # shellcheck disable=SC2029 + if "$1" /proc/vmcore | ssh "$3" "$4" "umask 0077 && dd of='$2/vmcore-dmesg-incomplete.txt'"; then + ssh -q "$3" "$4" mv "$2/vmcore-dmesg-incomplete.txt" "$2/vmcore-dmesg.txt" + dinfo "saving vmcore-dmesg.txt complete" + else + derror "saving vmcore-dmesg.txt failed" + fi +} + +wait_online_network() { + # In some cases, network may still not be ready because nm-online is called + # with "-s" which means to wait for NetworkManager startup to complete, rather + # than waiting for network connectivity specifically. Wait 10mins more for the + # network to be truely ready in these cases. + _loop=0 + while [ $_loop -lt 600 ]; do + sleep 1 + _loop=$((_loop + 1)) + if _route=$(kdump_get_ip_route "$1" 2> /dev/null); then + printf "%s" "$_route" + return + else + dwarn "Waiting for network to be ready (${_loop}s / 10min)" + fi + done + + derror "Oops. The network still isn't ready after waiting 10mins." + exit 1 +} + +get_host_ip() { + + if ! is_nfs_dump_target && ! is_ssh_dump_target; then + return 0 + fi + + _kdump_remote_ip=$(getarg kdump_remote_ip=) + + if [ -z "$_kdump_remote_ip" ]; then + derror "failed to get remote IP address!" + return 1 + fi + + if ! _route=$(wait_online_network "$_kdump_remote_ip"); then + return 1 + fi + + _netdev=$(kdump_get_ip_route_field "$_route" "dev") + + if ! _kdumpip=$(ip addr show dev "$_netdev" | grep '[ ]*inet'); then + derror "Failed to get IP of $_netdev" + return 1 + fi + + _kdumpip=$(echo "$_kdumpip" | head -n 1 | awk '{print $2}') + _kdumpip="${_kdumpip%%/*}" + HOST_IP=$_kdumpip +} + +read_kdump_confs() { + if [ ! -f "$KDUMP_CONFIG_FILE" ]; then + derror "$KDUMP_CONFIG_FILE not found" + return + fi + + get_kdump_confs + + # rescan for add code for dump target + while read -r config_opt config_val; do + # remove inline comments after the end of a directive. + case "$config_opt" in + dracut_args) + config_val=$(get_dracut_args_target "$config_val") + if [ -n "$config_val" ]; then + config_val=$(get_mntpoint_from_target "$config_val") + DUMP_INSTRUCTION="dump_fs $config_val" + fi + ;; + ext[234] | xfs | btrfs | minix | nfs | virtiofs) + config_val=$(get_mntpoint_from_target "$config_val") + DUMP_INSTRUCTION="dump_fs $config_val" + ;; + raw) + DUMP_INSTRUCTION="dump_raw $config_val" + ;; + ssh) + DUMP_INSTRUCTION="dump_ssh $SSH_KEY_LOCATION $config_val" + ;; + esac + done < "$KDUMP_CONF_PARSED" +} + +fence_kdump_notify() { + if [ -n "$FENCE_KDUMP_NODES" ]; then + # shellcheck disable=SC2086 + $FENCE_KDUMP_SEND $FENCE_KDUMP_ARGS $FENCE_KDUMP_NODES & + fi +} + +if [ "$1" = "--error-handler" ]; then + get_kdump_confs + do_failure_action + save_log + do_final_action + + exit $? +fi + +# continue here only if we have to save dump. +if [ -f /etc/fadump.initramfs ] && [ ! -f /proc/device-tree/rtas/ibm,kernel-dump ] && [ ! -f /proc/device-tree/ibm,opal/dump/mpipl-boot ]; then + exit 0 +fi + +read_kdump_confs +fence_kdump_notify + +if ! get_host_ip; then + derror "get_host_ip exited with non-zero status!" + exit 1 +fi + +if [ -z "$DUMP_INSTRUCTION" ]; then + DUMP_INSTRUCTION="dump_fs $NEWROOT" +fi + +if ! do_kdump_pre; then + derror "kdump_pre script exited with non-zero status!" + do_final_action + # During systemd service to reboot the machine, stop this shell script running + exit 1 +fi +make_trace_mem "kdump saving vmcore" '1:shortmem' '2+:mem' '3+:slab' +do_dump +DUMP_RETVAL=$? + +if ! do_kdump_post $DUMP_RETVAL; then + derror "kdump_post script exited with non-zero status!" +fi + +save_log + +if [ $DUMP_RETVAL -ne 0 ]; then + exit 1 +fi + +do_final_action diff --git a/dracut-module-setup.sh b/dracut/99kdumpbase/module-setup.sh similarity index 95% rename from dracut-module-setup.sh rename to dracut/99kdumpbase/module-setup.sh index 24dade69..50dd67cc 100755 --- a/dracut-module-setup.sh +++ b/dracut/99kdumpbase/module-setup.sh @@ -11,12 +11,14 @@ _get_kdump_netifs() { } kdump_module_init() { + # shellcheck disable=SC2154 if ! [[ -d "${initdir}/tmp" ]]; then mkdir -p "${initdir}/tmp" fi mkdir -p "$_DRACUT_KDUMP_NM_TMP_DIR" + # shellcheck source=SCRIPTDIR/../../kdump-lib.sh . /lib/kdump/kdump-lib.sh } @@ -27,7 +29,7 @@ check() { return 1 fi if [[ "$(uname -m)" == "s390x" ]]; then - require_binaries chzdev || return 1 + require_binaries chzdev || return 1 fi return 0 } @@ -38,15 +40,10 @@ depends() { kdump_module_init add_opt_module() { + # shellcheck disable=SC2154 [[ " $omit_dracutmodules " != *\ $1\ * ]] && _dep="$_dep $1" } - if is_squash_available; then - add_opt_module squash - else - dwarning "Required modules to build a squashed kdump image is missing!" - fi - if is_wdt_active; then add_opt_module watchdog fi @@ -56,11 +53,7 @@ depends() { fi if is_lvm2_thinp_dump_target; then - if grep -q lvmthinpool-monitor <<< $(dracut --list-modules); then - add_opt_module lvmthinpool-monitor - else - dwarning "Required lvmthinpool-monitor modules is missing! Please upgrade dracut >= 057." - fi + add_opt_module lvmthinpool-monitor fi if [[ "$(uname -m)" == "s390x" ]]; then @@ -351,7 +344,7 @@ kdump_install_nm_netif_allowlist() { for _netif in $1; do _per_mac=$(kdump_get_perm_addr "$_netif") - if [[ "$_per_mac" != 'not set' ]]; then + if [[ $_per_mac != 'not set' ]]; then _except_netif="mac:$_per_mac" else _except_netif="interface-name:$_netif" @@ -384,7 +377,7 @@ _get_hpyerv_physical_driver() { _get_physical_function_driver() { local _physfn_dir=/sys/class/net/"$1"/device/physfn - if [[ -e "$_physfn_dir" ]]; then + if [[ -e $_physfn_dir ]]; then basename "$(readlink -f "$_physfn_dir"/driver)" fi } @@ -499,21 +492,22 @@ kdump_setup_ovs() { # setup s390 znet kdump_setup_znet() { local _netif - local _tempfile=$(mktemp --tmpdir="$_DRACUT_KDUMP_NM_TMP_DIR" kdump-dracut-zdev.XXXXXX) + local _tempfile if [[ "$(uname -m)" != "s390x" ]]; then return fi + _tempfile=$(mktemp --tmpdir="$_DRACUT_KDUMP_NM_TMP_DIR" kdump-dracut-zdev.XXXXXX) + for _netif in $1; do - chzdev --export "$_tempfile" --active --by-interface "$_netif" \ - 2>&1 | ddebug - sed -i -e 's/^\[active /\[persistent /' "$_tempfile" - ddebug < "$_tempfile" - chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \ - --yes --no-root-update --force 2>&1 | ddebug - lszdev --configured --persistent --info --by-interface "$_netif" \ - --base "/etc=$initdir/etc" 2>&1 | ddebug + chzdev --export "$_tempfile" --active --by-interface "$_netif" |& ddebug + sed -i -e 's/^\[active /\[persistent /' "$_tempfile" + ddebug < "$_tempfile" + chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \ + --yes --no-root-update --force |& ddebug + lszdev --configured --persistent --info --by-interface "$_netif" \ + --base "/etc=$initdir/etc" |& ddebug done rm -f "$_tempfile" } @@ -536,10 +530,10 @@ kdump_get_remote_ip() { # The physical network interface has the same MAC address as the Ovs bridge ovs_find_phy_if() { local _mac _dev - _mac=$(kdump_get_mac_addr $1) + _mac=$(kdump_get_mac_addr "$1") - for _dev in $(ovs-vsctl list-ifaces $1); do - if [[ $_mac == $("${initdir}/etc/sysconfig/openvswitch" + echo "OPTIONS=\"--ovsdb-server-options='--disable-file-column-diff'\"" > "${initdir}/etc/sysconfig/openvswitch" KDUMP_DROP_IN_DIR="${initdir}/etc/systemd/system/nm-initrd.service.d" mkdir -p "$KDUMP_DROP_IN_DIR" - printf "[Unit]\nAfter=openvswitch.service\n" >$KDUMP_DROP_IN_DIR/01-after-ovs.conf + printf "[Unit]\nAfter=openvswitch.service\n" > "$KDUMP_DROP_IN_DIR"/01-after-ovs.conf $SYSTEMCTL -q --root "$initdir" enable openvswitch.service $SYSTEMCTL -q --root "$initdir" add-wants basic.target openvswitch.service @@ -654,7 +648,7 @@ kdump_install_net() { local _netifs _netifs=$(_get_kdump_netifs) - if [[ -n "$_netifs" ]]; then + if [[ -n $_netifs ]]; then kdump_install_nmconnections apply_nm_initrd_generator_timeouts kdump_setup_znet "$_netifs" @@ -898,6 +892,7 @@ kdump_check_iscsi_targets() { # If our prerequisites are not met, fail anyways. type -P iscsistart > /dev/null || return 1 + # shellcheck disable=SC2317 kdump_check_setup_iscsi() { local _dev _dev=$1 @@ -955,7 +950,7 @@ is_localhost() { get_pcs_fence_kdump_nodes() { local nodes - pcs cluster sync > /dev/null 2>&1 && pcs cluster cib-upgrade > /dev/null 2>&1 + pcs cluster sync &> /dev/null && pcs cluster cib-upgrade &> /dev/null # get cluster nodes from cluster cib, get interface and ip address nodelist=$(pcs cluster cib | xmllint --xpath "/cib/status/node_state/@uname" -) @@ -964,6 +959,7 @@ get_pcs_fence_kdump_nodes() { for node in ${nodelist}; do # convert $node from 'uname="nodeX"' to 'nodeX' eval "$node" + # shellcheck disable=SC2154 nodename="$uname" # Skip its own node name if is_localhost "$nodename"; then @@ -978,6 +974,7 @@ get_pcs_fence_kdump_nodes() { # retrieves fence_kdump args from config file get_pcs_fence_kdump_args() { if [[ -f $FENCE_KDUMP_CONFIG_FILE ]]; then + # shellcheck disable=SC1090 . "$FENCE_KDUMP_CONFIG_FILE" echo "$FENCE_KDUMP_OPTS" fi @@ -1058,7 +1055,6 @@ kdump_install_systemd_conf() { DefaultTimeoutStartSec=300s EOF - # Forward logs to console directly, and don't read Kmsg, this avoids # unneccessary memory consumption and make console output more useful. # Only do so for non fadump image. @@ -1083,6 +1079,7 @@ install() { kdump_install_random_seed fi dracut_install -o /etc/adjtime /etc/localtime + # shellcheck disable=SC2154 inst "$moddir/monitor_dd_progress.sh" "/kdumpscripts/monitor_dd_progress.sh" inst "/bin/dd" "/bin/dd" inst "/bin/tail" "/bin/tail" @@ -1101,6 +1098,7 @@ install() { inst "/lib/kdump/kdump-lib-initramfs.sh" "/lib/kdump-lib-initramfs.sh" inst "/lib/kdump/kdump-logger.sh" "/lib/kdump-logger.sh" inst "$moddir/kdump.sh" "/usr/bin/kdump.sh" + # shellcheck disable=SC2154 inst "$moddir/kdump-capture.service" "$systemdsystemunitdir/kdump-capture.service" systemctl -q --root "$initdir" add-wants initrd.target kdump-capture.service # Replace existing emergency service and emergency target diff --git a/dracut/99kdumpbase/monitor_dd_progress.sh b/dracut/99kdumpbase/monitor_dd_progress.sh new file mode 100755 index 00000000..f6ed4099 --- /dev/null +++ b/dracut/99kdumpbase/monitor_dd_progress.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +SRC_FILE_MB=$1 + +while true; do + DD_PID=$(pidof dd) + if [ -n "$DD_PID" ]; then + break + fi +done + +while true; do + sleep 5 + if [ ! -d /proc/"$DD_PID" ]; then + break + fi + + kill -s USR1 "$DD_PID" + CURRENT_SIZE=$(tail -n 1 /tmp/dd_progress_file | sed "s/[^0-9].*//g") + [ -n "$CURRENT_SIZE" ] && { + CURRENT_MB=$((CURRENT_SIZE / 1048576)) + printf "Copied %s MB / %s MB\r" "$CURRENT_MB" "$SRC_FILE_MB" + } +done + +rm -f /tmp/dd_progress_file diff --git a/dracut/99zz-fadumpinit/.editorconfig b/dracut/99zz-fadumpinit/.editorconfig new file mode 100644 index 00000000..fdcbad4b --- /dev/null +++ b/dracut/99zz-fadumpinit/.editorconfig @@ -0,0 +1,3 @@ +# These files run in the initrd and need to be posix compliant +[{init-fadump.sh}] +shell_variant = posix diff --git a/dracut/99zz-fadumpinit/init-fadump.sh b/dracut/99zz-fadumpinit/init-fadump.sh new file mode 100755 index 00000000..911cf9ec --- /dev/null +++ b/dracut/99zz-fadumpinit/init-fadump.sh @@ -0,0 +1,46 @@ +#!/bin/sh +export PATH=/usr/bin:/usr/sbin +export SYSTEMD_IN_INITRD=lenient + +[ -e /proc/mounts ] \ + || (mkdir -p /proc && mount -t proc -o nosuid,noexec,nodev proc /proc) + +grep -q '^sysfs /sys sysfs' /proc/mounts \ + || (mkdir -p /sys && mount -t sysfs -o nosuid,noexec,nodev sysfs /sys) + +grep -q '^none / ' /proc/mounts || grep -q '^rootfs / ' /proc/mounts && ROOTFS_IS_RAMFS=1 + +if [ -f /proc/device-tree/rtas/ibm,kernel-dump ] || [ -f /proc/device-tree/ibm,opal/dump/mpipl-boot ]; then + mkdir /newroot + mount -t ramfs ramfs /newroot + + if [ "$ROOTFS_IS_RAMFS" ]; then + mv /fadumproot/* /newroot/ + exec switch_root /newroot /init + else + mkdir /newroot/sys /newroot/proc /newroot/dev /newroot/run /newroot/oldroot + + grep -q '^devtmpfs /dev devtmpfs' /proc/mounts && mount --move /dev /newroot/dev + grep -q '^tmpfs /run tmpfs' /proc/mounts && mount --move /run /newroot/run + mount --move /sys /newroot/sys + mount --move /proc /newroot/proc + + cp --reflink=auto --sparse=auto --preserve=mode,timestamps,links -dfr /fadumproot/. /newroot/ + cd /newroot && pivot_root . oldroot + + loop=1 + while [ $loop ]; do + unset loop + while read -r _ mp _; do + case $mp in + /oldroot/*) umount -d "$mp" && loop=1 ;; + esac + done < /proc/mounts + done + umount -d -l oldroot + + exec /init + fi +else + exec /init.dracut +fi diff --git a/dracut-fadump-module-setup.sh b/dracut/99zz-fadumpinit/module-setup.sh old mode 100644 new mode 100755 similarity index 88% rename from dracut-fadump-module-setup.sh rename to dracut/99zz-fadumpinit/module-setup.sh index f0624868..8fa5e168 --- a/dracut-fadump-module-setup.sh +++ b/dracut/99zz-fadumpinit/module-setup.sh @@ -9,7 +9,9 @@ depends() { } install() { + # shellcheck disable=SC2154 mv -f "$initdir/init" "$initdir/init.dracut" + # shellcheck disable=SC2154 inst_script "$moddir/init-fadump.sh" /init chmod a+x "$initdir/init" diff --git a/kdump-lib.sh b/kdump-lib.sh index f46c2e96..c14e75e8 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -43,27 +43,11 @@ is_sme_or_sev_active() journalctl -q --dmesg --grep "^Memory Encryption Features active: AMD (SME|SEV)$" >/dev/null 2>&1 } -is_squash_available() -{ - local _version kmodule - - _version=$(_get_kdump_kernel_version) - for kmodule in squashfs overlay loop; do - modprobe -S "$_version" --dry-run $kmodule &> /dev/null || return 1 - done -} - has_command() { [[ -x $(command -v "$1") ]] } -dracut_have_option() -{ - local _option=$1 - ! dracut "$_option" 2>&1 | grep -q "unrecognized option" -} - perror_exit() { derror "$@" diff --git a/kdumpctl b/kdumpctl index 1b6f7b93..612a159a 100755 --- a/kdumpctl +++ b/kdumpctl @@ -33,9 +33,9 @@ if [[ ${__SOURCED__:+x} ]]; then else KDUMP_LIB_PATH=/lib/kdump fi -# shellcheck source=./kdump-lib.sh +# shellcheck source=SCRIPTDIR/kdump-lib.sh . $KDUMP_LIB_PATH/kdump-lib.sh -# shellcheck source=./kdump-logger.sh +# shellcheck source=SCRIPTDIR/kdump-logger.sh . $KDUMP_LIB_PATH/kdump-logger.sh #initiate the kdump logger @@ -1199,16 +1199,15 @@ get_kernel_size() check_vmlinux "$img" && get_vmlinux_size "$img" && return 0 # That didn't work, so retry after decompression. - try_decompress '\037\213\010' xy gunzip "$img" "$tmp" || + if try_decompress '\037\213\010' xy gunzip "$img" "$tmp" || try_decompress '\3757zXZ\000' abcde unxz "$img" "$tmp" || try_decompress 'BZh' xy bunzip2 "$img" "$tmp" || try_decompress '\135\0\0\0' xxx unlzma "$img" "$tmp" || try_decompress '\211\114\132' xy 'lzop -d' "$img" "$tmp" || try_decompress '\002!L\030' xxx 'lz4 -d' "$img" "$tmp" || - try_decompress '(\265/\375' xxx unzstd "$img" "$tmp" - - # Finally check for uncompressed images or objects: - [[ $? -eq 0 ]] && get_vmlinux_size "$tmp" && return 0 + try_decompress '(\265/\375' xxx unzstd "$img" "$tmp"; then + get_vmlinux_size "$tmp" && return 0 + fi # Fallback to use iomem local _size=0 _seg @@ -1475,14 +1474,14 @@ reset_crashkernel() exit 1 fi _opt_fadump=${_opt#*=} - if ! _dump_mode=$(get_dump_mode_by_fadump_val $_opt_fadump); then + if ! _dump_mode=$(get_dump_mode_by_fadump_val "$_opt_fadump"); then derror "failed to determine dump mode" exit fi ;; --kernel=*) _val=${_opt#*=} - if ! _valid_grubby_kernel_path $_val; then + if ! _valid_grubby_kernel_path "$_val"; then derror "Invalid $_opt, please specify a valid kernel path, ALL or DEFAULT" exit fi diff --git a/mkdumprd b/mkdumprd index 27eed5e3..8c1b191f 100644 --- a/mkdumprd +++ b/mkdumprd @@ -11,8 +11,11 @@ if [[ -f /etc/sysconfig/kdump ]]; then fi [[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut -. $dracutbasedir/dracut-functions.sh +# shellcheck source=/dev/null +. "$dracutbasedir"/dracut-functions.sh +# shellcheck source=SCRIPTDIR/kdump-lib.sh . /lib/kdump/kdump-lib.sh +# shellcheck source=SCRIPTDIR/kdump-logger.sh . /lib/kdump/kdump-logger.sh export IN_KDUMP=1 @@ -25,13 +28,22 @@ fi SSH_KEY_LOCATION=$DEFAULT_SSHKEY SAVE_PATH=$(get_save_path) -extra_modules="" -dracut_args=(--add kdumpbase --quiet --hostonly --hostonly-cmdline --hostonly-i18n --hostonly-mode strict --hostonly-nics '' --aggressive-strip -o "plymouth resume ifcfg earlykdump") +declare -a dracut_args +dracut_args+=(--add kdumpbase) +dracut_args+=(--quiet) +dracut_args+=(--hostonly) +dracut_args+=(--hostonly-cmdline) +dracut_args+=(--hostonly-i18n) +dracut_args+=(--hostonly-mode strict) +dracut_args+=(--hostonly-nics '') +dracut_args+=(--aggressive-strip) +dracut_args+=(--omit "plymouth resume ifcfg earlykdump") MKDUMPRD_TMPDIR="$(mktemp -d -t mkdumprd.XXXXXX)" [ -d "$MKDUMPRD_TMPDIR" ] || perror_exit "dracut: mktemp -p -d -t dracut.XXXXXX failed." MKDUMPRD_TMPMNT="$MKDUMPRD_TMPDIR/target" +# shellcheck disable=SC2154 trap ' ret=$?; is_mounted $MKDUMPRD_TMPMNT && umount -f $MKDUMPRD_TMPMNT; @@ -42,19 +54,20 @@ trap ' # clean up after ourselves no matter how we die. trap 'exit 1;' SIGINT -add_dracut_arg() +# check whether the given dracut module is installed. If multiple modules are +# provided return true if any of them is installed. +has_dracut_module() { - dracut_args+=("$@") -} + local -a _args + local _e -add_dracut_mount() -{ - add_dracut_arg "--mount" "$1" -} + [[ $# -ge 1 ]] || return 1 -add_dracut_sshkey() -{ - add_dracut_arg "--sshkey" "$1" + for _e in "$@"; do + _args+=(-e "$_e") + done + + grep -x -q "${_args[@]}" <<< "$(dracut --list-modules)" } # caller should ensure $1 is valid and mounted in 1st kernel @@ -256,7 +269,7 @@ check_user_configured_target() verify_core_collector() { local _cmd="${1%% *}" - local _params="${1#${_cmd}}" + local _params="${1#"${_cmd}"}" if [[ $_cmd != "makedumpfile" ]]; then if is_raw_dump_target; then @@ -282,11 +295,7 @@ verify_core_collector() add_mount() { - local _mnt - - _mnt=$(to_mount "$@") || exit 1 - - add_dracut_mount "$_mnt" + dracut_args+=(--mount "$(to_mount "$@")") || exit 1 } #handle the case user does not specify the dump target explicitly @@ -334,7 +343,7 @@ while read -r config_opt config_val; do # remove inline comments after the end of a directive. case "$config_opt" in extra_modules) - extra_modules="$extra_modules $config_val" + dracut_args+=(--add-drivers "$config_val") ;; ext[234] | xfs | btrfs | minix | nfs | virtiofs) check_user_configured_target "$config_val" "$config_opt" @@ -349,14 +358,14 @@ while read -r config_opt config_val; do if [[ -z $_praw ]]; then exit 1 fi - add_dracut_arg "--device" "$_praw" + dracut_args+=(--device "$_praw") check_size raw "$config_val" ;; ssh) if strstr "$config_val" "@"; then mkdir_save_path_ssh "$config_val" check_size ssh "$config_val" - add_dracut_sshkey "$SSH_KEY_LOCATION" + dracut_args+=(--sshkey "$SSH_KEY_LOCATION") else perror_exit "Bad ssh dump target $config_val" fi @@ -371,11 +380,11 @@ while read -r config_opt config_val; do # because we call dracut with --hostonly-mode strict. So manually install # nfsv4-related drivers. if [[ $(get_dracut_args_fstype "$config_val") == nfs* ]]; then - add_dracut_arg "--add-drivers" nfs_layout_nfsv41_files + dracut_args+=(--add-drivers "nfs_layout_nfsv41_files") fi while read -r dracut_arg; do - add_dracut_arg "$dracut_arg" + dracut_args+=("$dracut_arg") done <<< "$(echo "$config_val" | xargs -n 1 echo)" ;; *) ;; @@ -386,17 +395,23 @@ done <<< "$(kdump_read_conf)" handle_default_dump_target if ! have_compression_in_dracut_args; then - if is_squash_available && dracut_have_option "--squash-compressor"; then - add_dracut_arg "--squash-compressor" "zstd" - elif has_command zstd; then - add_dracut_arg "--compress" "zstd" + # With dracut 104 the 99squash module got split up into 99squash and + # 95squash-squashfs as well as the new 95squash-erofs. Explicitly set + # which image type is required otherwise the requested compression + # algorithm might not be supported. + if has_dracut_module squash-squashfs && has_command mksquashfs; then + dracut_args+=(--add squash-squashfs) + dracut_args+=(--squash-compressor zstd) + elif has_dracut_module squash-erofs && has_command mkfs.erofs; then + dracut_args+=(--add squash-erofs) + dracut_args+=(--squash-compressor lz4hc) + elif has_command mksquashfs; then + # only true for dracut <= 103 + dracut_args+=(--add squash) + dracut_args+=(--squash-compressor zstd) fi fi -if [[ -n $extra_modules ]]; then - add_dracut_arg "--add-drivers" "$extra_modules" -fi - # TODO: The below check is not needed anymore with the introduction of # 'zz-fadumpinit' module, that isolates fadump's capture kernel initrd, # but still sysroot.mount unit gets generated based on 'root=' kernel @@ -407,10 +422,10 @@ if ! is_fadump_capable; then # so it doesn't affect the logic of check_dump_fs_modified(). is_dump_to_rootfs && add_mount "$(to_dev_name "$(get_root_fs_device)")" - add_dracut_arg "--no-hostonly-default-device" + dracut_args+=(--no-hostonly-default-device) if fips-mode-setup --is-enabled 2> /dev/null; then - add_dracut_arg --add-device "$(findmnt -n -o SOURCE --target /boot)" + dracut_args+=(--add-device "$(findmnt -n -o SOURCE --target /boot)") fi fi diff --git a/mkfadumprd b/mkfadumprd index dd6840f6..37dc05d6 100755 --- a/mkfadumprd +++ b/mkfadumprd @@ -7,8 +7,11 @@ if [[ -f /etc/sysconfig/kdump ]]; then fi [[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut -. $dracutbasedir/dracut-functions.sh +# shellcheck source=/dev/null +. "$dracutbasedir"/dracut-functions.sh +# shellcheck source=SCRIPTDIR/kdump-lib.sh . /lib/kdump/kdump-lib.sh +# shellcheck source=SCRIPTDIR/kdump-logger.sh . /lib/kdump/kdump-logger.sh #initiate the kdump logger @@ -19,6 +22,7 @@ fi MKFADUMPRD_TMPDIR="$(mktemp -d -t mkfadumprd.XXXXXX)" [ -d "$MKFADUMPRD_TMPDIR" ] || perror_exit "mkfadumprd: mktemp -d -t mkfadumprd.XXXXXX failed." +# shellcheck disable=SC2154 trap ' ret=$?; [[ -d $MKFADUMPRD_TMPDIR ]] && rm --one-file-system -rf -- "$MKFADUMPRD_TMPDIR"; @@ -42,7 +46,7 @@ ddebug "rebuild fadump initrd: $FADUMP_INITRD" # compression ratio and increases the size of the initramfs image. # Don't compress the capture image as uncompressed image is needed immediately. # Also, early microcode would not be needed here. -if ! $MKDUMPRD "$FADUMP_INITRD" -i "$MKFADUMPRD_TMPDIR/fadump.initramfs" /etc/fadump.initramfs --omit squash --no-compress --no-early-microcode; then +if ! $MKDUMPRD "$FADUMP_INITRD" -i "$MKFADUMPRD_TMPDIR/fadump.initramfs" /etc/fadump.initramfs --omit squash --omit squash-squashfs --omit squash-erofs --no-compress --no-early-microcode; then perror_exit "mkfadumprd: failed to build image with dump capture support" fi