summaryrefslogtreecommitdiff
path: root/mkdumprd
diff options
context:
space:
mode:
Diffstat (limited to 'mkdumprd')
-rw-r--r--mkdumprd439
1 files changed, 439 insertions, 0 deletions
diff --git a/mkdumprd b/mkdumprd
new file mode 100644
index 0000000..35f5eed
--- /dev/null
+++ b/mkdumprd
@@ -0,0 +1,439 @@
+#!/bin/bash --norc
+# New mkdumprd
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Written by Cong Wang <amwang@redhat.com>
+#
+
+[[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut
+. $dracutbasedir/dracut-functions.sh
+. /lib/kdump/kdump-lib.sh
+export IN_KDUMP=1
+
+conf_file="/etc/kdump.conf"
+SSH_KEY_LOCATION="/root/.ssh/kdump_id_rsa"
+SAVE_PATH=$(get_save_path)
+OVERRIDE_RESETTABLE=0
+
+extra_modules=""
+dracut_args="--quiet --hostonly --hostonly-cmdline --hostonly-i18n --hostonly-mode strict -o \"plymouth dash resume ifcfg earlykdump\""
+
+is_wdt_addition_needed() {
+ local active
+
+ is_wdt_mod_omitted
+ [[ $? -eq 0 ]] && return 1
+ [[ -d /sys/class/watchdog/ ]] || return 1
+ for dir in /sys/class/watchdog/*; do
+ [[ -f "$dir/state" ]] || continue
+ active=$(< "$dir/state")
+ [[ "$active" = "active" ]] && return 0
+ done
+ return 1
+}
+
+add_dracut_arg() {
+ dracut_args="$dracut_args $@"
+}
+
+add_dracut_module() {
+ add_dracut_arg "--add" "\"$1\""
+}
+
+add_dracut_mount() {
+ add_dracut_arg "--mount" "\"$1\""
+}
+
+add_dracut_sshkey() {
+ add_dracut_arg "--sshkey" "\"$1\""
+}
+
+# caller should ensure $1 is valid and mounted in 1st kernel
+to_mount() {
+ local _dev=$1 _source _target _fstype _options _mntopts _pdev
+
+ _source=$(findmnt -k -f -n -r -o SOURCE $_dev)
+ _target=$(get_mntpoint_from_target $_dev)
+ # mount under /sysroot if dump to root disk or mount under
+ #/kdumproot/$_target in other cases in 2nd kernel. systemd
+ #will be in charge to umount it.
+
+ if [ "$_target" = "/" ];then
+ _target="/sysroot"
+ else
+ _target="/kdumproot/$_target"
+ fi
+
+ _fstype=$(findmnt -k -f -n -r -o FSTYPE $_dev)
+ [[ -e /etc/fstab ]] && _options=$(findmnt --fstab -f -n -r -o OPTIONS $_dev)
+ if [ -z "$_options" ]; then
+ _options=$(findmnt -k -f -n -r -o OPTIONS $_dev)
+ if [[ $_fstype == "nfs"* ]]; then
+ _options=$(echo $_options | sed 's/,addr=[^,]*//')
+ _options=$(echo $_options | sed 's/,proto=[^,]*//')
+ _options=$(echo $_options | sed 's/,clientaddr=[^,]*//')
+ fi
+ fi
+ # mount fs target as rw in 2nd kernel
+ _options=$(echo $_options | sed 's/\(^\|,\)ro\($\|,\)/\1rw\2/g')
+ # filter out 'noauto' here, it will be force appended later, avoid duplication
+ _options=$(echo $_options | sed 's/\(^\|,\)noauto\($\|,\)/\1/g')
+ # drop nofail or nobootwait
+ _options=$(echo $_options | sed 's/\(^\|,\)nofail\($\|,\)/\1/g')
+ _options=$(echo $_options | sed 's/\(^\|,\)nobootwait\($\|,\)/\1/g')
+ # only mount the dump target when needed.
+ _options="$_options,noauto"
+
+ _mntopts="$_target $_fstype $_options"
+ #for non-nfs _dev converting to use udev persistent name
+ if [ -b "$_source" ]; then
+ _pdev="$(get_persistent_dev $_source)"
+ if [ -z "$_pdev" ]; then
+ return 1
+ fi
+
+ else
+ _pdev=$_dev
+ fi
+
+ echo "$_pdev $_mntopts"
+}
+
+is_readonly_mount() {
+ local _mnt
+ _mnt=$(findmnt -k -f -n -r -o OPTIONS $1)
+
+ #fs/proc_namespace.c: show_mountinfo():
+ #seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
+ [[ "$_mnt" =~ ^ro ]]
+}
+
+#Function: get_ssh_size
+#$1=dump target
+#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
+get_ssh_size() {
+ local _opt _out _size
+ _opt="-i $SSH_KEY_LOCATION -o BatchMode=yes -o StrictHostKeyChecking=yes"
+ _out=$(ssh -q -n $_opt $1 "df -P $SAVE_PATH")
+ [ $? -ne 0 ] && {
+ perror_exit "checking remote ssh server available size failed."
+ }
+
+ #ssh output removed the line break, so print field NF-2
+ _size=$(echo -n $_out| awk '{avail=NF-2; print $avail}')
+ echo -n $_size
+}
+
+#mkdir if save path does not exist on ssh dump target
+#$1=ssh dump target
+#caller should ensure write permission on $1:$SAVE_PATH
+#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
+mkdir_save_path_ssh()
+{
+ local _opt _dir
+ _opt="-i $SSH_KEY_LOCATION -o BatchMode=yes -o StrictHostKeyChecking=yes"
+ ssh -qn $_opt $1 mkdir -p $SAVE_PATH 2>&1 > /dev/null
+ _ret=$?
+ if [ $_ret -ne 0 ]; then
+ perror_exit "mkdir failed on $1:$SAVE_PATH"
+ fi
+
+ #check whether user has write permission on $1:$SAVE_PATH
+ _dir=$(ssh -qn $_opt $1 mktemp -dqp $SAVE_PATH 2>/dev/null)
+ _ret=$?
+ if [ $_ret -ne 0 ]; then
+ perror_exit "Could not create temporary directory on $1:$SAVE_PATH. Make sure user has write permission on destination"
+ fi
+ ssh -qn $_opt $1 rmdir $_dir
+
+ return 0
+}
+
+#Function: get_fs_size
+#$1=dump target
+get_fs_size() {
+ local _mnt=$(get_mntpoint_from_target $1)
+ echo -n $(df -P "${_mnt}/$SAVE_PATH"|tail -1|awk '{print $4}')
+}
+
+#Function: get_raw_size
+#$1=dump target
+get_raw_size() {
+ echo -n $(fdisk -s "$1")
+}
+
+#Function: check_size
+#$1: dump type string ('raw', 'fs', 'ssh')
+#$2: dump target
+check_size() {
+ local avail memtotal
+
+ memtotal=$(awk '/MemTotal/{print $2}' /proc/meminfo)
+ case "$1" in
+ raw)
+ avail=$(get_raw_size "$2")
+ ;;
+ ssh)
+ avail=$(get_ssh_size "$2")
+ ;;
+ fs)
+ avail=$(get_fs_size "$2")
+ ;;
+ *)
+ return
+ esac
+
+ if [ $? -ne 0 ]; then
+ perror_exit "Check dump target size failed"
+ fi
+
+ if [ $avail -lt $memtotal ]; then
+ echo "Warning: There might not be enough space to save a vmcore."
+ echo " The size of $2 should be greater than $memtotal kilo bytes."
+ fi
+}
+
+# $1: core_collector config value
+verify_core_collector() {
+ local _cmd="${1%% *}"
+ local _params="${1#* }"
+
+ if [ "$_cmd" != "makedumpfile" ]; then
+ if is_raw_dump_target; then
+ echo "Warning: specifying a non-makedumpfile core collector, you will have to recover the vmcore manually."
+ fi
+ return
+ fi
+
+ if is_ssh_dump_target || is_raw_dump_target; then
+ if ! strstr "$_params" "-F"; then
+ perror_exit "The specified dump target needs makedumpfile \"-F\" option."
+ fi
+ _params="$_params vmcore"
+ else
+ _params="$_params vmcore dumpfile"
+ fi
+
+ if ! $_cmd --check-params $_params; then
+ perror_exit "makedumpfile parameter check failed."
+ fi
+}
+
+add_mount() {
+ local _mnt=$(to_mount "$1")
+
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+
+ add_dracut_mount "$_mnt"
+}
+
+#handle the case user does not specify the dump target explicitly
+handle_default_dump_target()
+{
+ local _target
+ local _mntpoint
+
+ is_user_configured_dump_target && return
+
+ check_save_path_fs $SAVE_PATH
+
+ _save_path=$(get_bind_mount_source $SAVE_PATH)
+ _target=$(get_target_from_path $_save_path)
+ _mntpoint=$(get_mntpoint_from_target $_target)
+
+ SAVE_PATH=${_save_path##"$_mntpoint"}
+ add_mount "$_target"
+ check_size fs $_target
+}
+
+get_override_resettable()
+{
+ local override_resettable
+
+ override_resettable=$(grep "^override_resettable" $conf_file)
+ if [ -n "$override_resettable" ]; then
+ OVERRIDE_RESETTABLE=$(echo $override_resettable | cut -d' ' -f2)
+ if [ "$OVERRIDE_RESETTABLE" != "0" ] && [ "$OVERRIDE_RESETTABLE" != "1" ];then
+ perror_exit "override_resettable value $OVERRIDE_RESETTABLE is invalid"
+ fi
+ fi
+}
+
+
+# $1: function name
+for_each_block_target()
+{
+ local dev majmin
+
+ for dev in $(get_kdump_targets); do
+ [ -b "$dev" ] || continue
+ majmin=$(get_maj_min $dev)
+ check_block_and_slaves $1 $majmin && return 1
+ done
+
+ return 0
+}
+
+
+
+#judge if a specific device with $1 is unresettable
+#return false if unresettable.
+is_unresettable()
+{
+ local path="/sys/$(udevadm info --query=all --path=/sys/dev/block/$1 | awk '/^P:/ {print $2}' | sed -e 's/\(cciss[0-9]\+\/\).*/\1/g' -e 's/\/block\/.*$//')/resettable"
+ local resettable=1
+
+ if [ -f "$path" ]
+ then
+ resettable="$(cat $path)"
+ [ $resettable -eq 0 -a "$OVERRIDE_RESETTABLE" -eq 0 ] && {
+ local device=$(udevadm info --query=all --path=/sys/dev/block/$1 | awk -F= '/DEVNAME/{print $2}')
+ echo "Error: Can not save vmcore because device $device is unresettable"
+ return 0
+ }
+ fi
+
+ return 1
+}
+
+#check if machine is resettable.
+#return true if resettable
+check_resettable()
+{
+ local _ret _target
+
+ get_override_resettable
+
+ for_each_block_target is_unresettable
+ _ret=$?
+
+ [ $_ret -eq 0 ] && return
+
+ return 1
+}
+
+# $1: maj:min
+is_crypt()
+{
+ local majmin=$1 dev line ID_FS_TYPE=""
+
+ line=$(udevadm info --query=property --path=/sys/dev/block/$majmin \
+ | grep "^ID_FS_TYPE")
+ eval "$line"
+ [[ "$ID_FS_TYPE" = "crypto_LUKS" ]] && {
+ dev=$(udevadm info --query=all --path=/sys/dev/block/$majmin | awk -F= '/DEVNAME/{print $2}')
+ echo "Device $dev is encrypted."
+ return 0
+ }
+ return 1
+}
+
+check_crypt()
+{
+ local _ret _target
+
+ for_each_block_target is_crypt
+ _ret=$?
+
+ [ $_ret -eq 0 ] && return
+
+ return 1
+}
+
+if ! check_resettable; then
+ exit 1
+fi
+
+if ! check_crypt; then
+ echo "Warning: Encrypted device is in dump path. User will prompted for password during second kernel boot."
+fi
+
+# firstly get right SSH_KEY_LOCATION
+keyfile=$(awk '/^sshkey/ {print $2}' $conf_file)
+if [ -f "$keyfile" ]; then
+ # canonicalize the path
+ SSH_KEY_LOCATION=$(/usr/bin/readlink -m $keyfile)
+fi
+
+if [ "$(uname -m)" = "s390x" ]; then
+ add_dracut_module "znet"
+fi
+
+if is_wdt_addition_needed; then
+ add_dracut_arg "-a" "watchdog"
+fi
+
+while read 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"
+ ;;
+ ext[234]|xfs|btrfs|minix|nfs)
+ if ! findmnt $config_val >/dev/null; then
+ perror_exit "Dump target $config_val is probably not mounted."
+ fi
+
+ # User configured target, use $SAVE_PATH as the dump path within the target
+ check_save_path_user_configured "$config_val" "$SAVE_PATH"
+ check_size fs "$config_val"
+ add_mount "$config_val"
+ ;;
+ raw)
+ # checking raw disk writable
+ dd if=$config_val count=1 of=/dev/null > /dev/null 2>&1 || {
+ perror_exit "Bad raw disk $config_val"
+ }
+ _praw=$(persistent_policy="by-id" get_persistent_dev $config_val)
+ if [ -z "$_praw" ]; then
+ exit 1
+ fi
+ add_dracut_arg "--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_module "ssh-client"
+ add_dracut_sshkey "$SSH_KEY_LOCATION"
+ else
+ perror_exit "Bad ssh dump target $config_val"
+ fi
+ ;;
+ core_collector)
+ verify_core_collector "$config_val"
+ ;;
+ dracut_args)
+ add_dracut_arg $config_val
+ ;;
+ *)
+ ;;
+ esac
+done <<< "$(read_strip_comments $conf_file)"
+
+handle_default_dump_target
+
+if [ -n "$extra_modules" ]
+then
+ add_dracut_arg "--add-drivers" "$extra_modules"
+fi
+
+if ! is_fadump_capable; then
+ # The 2nd rootfs mount stays behind the normal dump target mount,
+ # 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"
+fi
+
+echo "$dracut_args $@" | xargs dracut
+
+_rc=$?
+sync
+exit $_rc