summaryrefslogtreecommitdiffstats
path: root/shutdown
blob: b0934b7c39688bf77dd44684c996f5a31c07e866 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/ash

# teardown a single device by node name
#   $1: device node name, e.g. md0, dm-2
stop_device() {
    local devtype= devname=

    # the device must still be active
    [ -e "/sys/class/block/$1" ] || return 1

    # this can sometimes fail on stopped md devices, when the
    # sysfs node doesn't go away as quickly as i'd like it to.
    devtype=$(lsblk -drno TYPE "/dev/$1" 2>/dev/null) || return 1
    case $devtype in
        crypt)
            read devname <"$1/dm/name"
            cryptsetup remove "$devname"
            ;;
        lvm|dm)
            read devname <"$1/dm/name"
            dmsetup --noudevsync remove "$devname"
            ;;
        raid*)
            # wait for arrays with external metadata to be marked as
            # clean. unfortunately, there isn't a whole lot we can do
            # if this fails, and the array may need a full rebuild on
            # the next reboot.
            IFS=: read metadata _ < "/sys/class/block/$1/md/metadata_version"
            if [ "$metadata" = external ]; then
                mdadm --wait-clean "/dev/$1"
            fi
            mdadm --stop "/dev/$1"
            ;;
        dmraid)
            read devname <"$1/dm/name"
            dmraid -an "$devname"
            ;;
        # silently ignore unstacked devices
    esac
}

# recursively disassemble a device chain
#   $1: device node name, e.g. md0, dm-2
disassemble() {
    local holder=

    for holder in "$1"/holders/*; do
        [ -e "$holder" ] && disassemble "${holder##*/}"
        stop_device "$1"
    done
}

copy_binary_from_host() {
    local bin=$1

    # hardcode a sane PATH
    for p in '/usr/sbin' '/usr/bin' '/sbin' '/bin'; do
        if [ -e "/oldroot/$p/$bin" ]; then
            cp "/oldroot/$p/$bin" "/usr/bin/$1"
            return 0
        fi
    done

    return 1
}

# XXX: Discourage libdevmapper from thinking that udev
# might be in a useful state. FS#30995.
rm -rf /run/udev

if [ "$1" = kexec ] && ! command -v kexec >/dev/null; then
    copy_binary_from_host kexec
fi

# chdir, so that we can avoid a lot of path chopping
cd /sys/class/block

printf '%s\n' "Unmounting all devices."

# unmount everything in /oldroot
findmnt -Rruno TARGET /oldroot | awk '
BEGIN { i = 0 }
{
    i++
    mounts[i] = $0
}
END {
    for (j = i; j > 0; j--) {
        print mounts[j]
    }
}
' | while read -r mount; do
    umount "$mount"
done

printf '%s\n' 'Detaching loop devices.'

for loop in loop*/loop; do
    [ -e "$loop" ] && losetup -d "${loop%/loop}"
done

printf '%s\n' "Disassembling stacked devices."

# iterate over devices with holders
for part in */holders/*; do
    [ -e "$part" ] && disassemble "${part%%/*}"
done

case $1 in
    poweroff|shutdown|halt)
        "$1" -f
        ;;
    *)
        type kexec >/dev/null && kexec -e
        reboot -f
        ;;
esac

# vim: ft=sh ts=4 sw=4