summaryrefslogtreecommitdiffstats
path: root/modules.d/90lvm/lvm_scan.sh
blob: 980e449071b8602effb606ad275021499b7cf755 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/bin/sh

# run lvm scan if udev has settled

extraargs="$*"
type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh

VGS=$(getargs rd.lvm.vg -d rd_LVM_VG=)
LVS=$(getargs rd.lvm.lv -d rd_LVM_LV=)

# shellcheck disable=SC2174
[ -d /etc/lvm ] || mkdir -m 0755 -p /etc/lvm
[ -d /run/lvm ] || mkdir -m 0755 -p /run/lvm
# build a list of devices to scan
lvmdevs=$(
    for f in /tmp/.lvm_scan-*; do
        [ -e "$f" ] || continue
        printf '%s' "${f##/tmp/.lvm_scan-} "
    done
)

check_lvm_ver() {
    maj=$1
    min=$2
    ver=$3
    # --poll is supported since 2.2.57
    [ "$4" -lt "$maj" ] && return 1
    [ "$4" -gt "$maj" ] && return 0
    [ "$5" -lt "$min" ] && return 1
    [ "$5" -gt "$min" ] && return 0
    [ "$6" -ge "$ver" ] && return 0
    return 1
}

no_lvm_conf_filter() {
    if [ ! -e /etc/lvm/lvm.conf ]; then
        return 0
    fi

    if [ -e /run/lvm/initrd_no_filter ]; then
        return 0
    fi

    if [ -e /run/lvm/initrd_filter ]; then
        return 1
    fi

    if [ -e /run/lvm/initrd_global_filter ]; then
        return 1
    fi

    # Save lvm config results in /run to avoid running
    # lvm config commands for every PV that's scanned.

    filter=$(lvm config devices/filter 2> /dev/null | grep "$filter=")
    if [ -n "$filter" ]; then
        printf '%s\n' "$filter" > /run/lvm/initrd_filter
        return 1
    fi

    global_filter=$(lvm config devices/global_filter 2> /dev/null | grep "$global_filter=")
    if [ -n "$global_filter" ]; then
        printf '%s\n' "$global_filter" > /run/lvm/initrd_global_filter
        return 1
    fi

    # /etc/lvm/lvm.conf exists with no filter setting
    true > /run/lvm/initrd_no_filter
    return 0
}

# If no lvm.conf exists, create a basic one with a global section.
if [ ! -e /etc/lvm/lvm.conf ]; then
    {
        echo 'global {'
        echo '}'
    } > /etc/lvm/lvm.conf
    lvmwritten=1
fi

# Save the original lvm.conf before appending a filter setting.
if [ ! -e /etc/lvm/lvm.conf.orig ]; then
    cp /etc/lvm/lvm.conf /etc/lvm/lvm.conf.orig
fi

# If the original lvm.conf does not contain a filter setting,
# then generate a filter and append it to the original lvm.conf.
# The filter is generated from the list PVs that have been seen
# so far (each has been processed by the lvm udev rule.)
if no_lvm_conf_filter; then
    {
        echo 'devices {'
        printf '    filter = [ '
        for dev in $lvmdevs; do
            printf '"a|^/dev/%s$|", ' "$dev"
        done
        echo '"r/.*/" ]'
        echo '}'
    } > /etc/lvm/lvm.conf.filter
    lvmfilter=1
    cat /etc/lvm/lvm.conf.orig /etc/lvm/lvm.conf.filter > /etc/lvm/lvm.conf
fi

# hopefully this output format will never change, e.g.:
#   LVM version:     2.02.53(1) (2009-09-25)
OLDIFS=$IFS
IFS=.
# shellcheck disable=SC2046
set -- $(lvm version 2> /dev/null)
IFS=$OLDIFS
maj=${1##*:}
min=$2
sub=${3%% *}
sub=${sub%%\(*}

# For lvchange and vgchange use --sysinit which:
# disables polling (--poll n)
# ignores monitoring (--ignoremonitoring)
# ignores locking failures (--ignorelockingfailure)
# disables hints (--nohints)
#
# For lvs and vgscan:
# disable locking (--nolocking)
# disable hints (--nohints)

activate_args="--sysinit $extraargs"
unset extraargs

export LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES=1

scan_args="--nolocking"

check_lvm_ver 2 3 14 "$maj" "$min" "$sub" \
    && scan_args="$scan_args --nohints"

if [ -n "$LVS" ]; then
    info "Scanning devices $lvmdevs for LVM logical volumes $LVS"
    # shellcheck disable=SC2086
    LVSLIST=$(lvm lvs $scan_args --noheading -o lv_full_name,segtype $LVS)
    info "$LVSLIST"

    # Only attempt to activate an LV if it appears in the lvs output.
    for LV in $LVS; do
        if strstr "$LVSLIST" "$LV"; then
            # This lvchange is expected to fail if all PVs used by
            # the LV are not yet present.  Premature/failed lvchange
            # could be avoided by reporting if an LV is complete
            # from the lvs command above and skipping this lvchange
            # if the LV is not lised as complete.
            # shellcheck disable=SC2086
            lvm lvchange --yes -K -ay $activate_args "$LV" 2>&1 | vinfo
        fi
    done
fi

if [ -z "$LVS" ] || [ -n "$VGS" ]; then
    info "Scanning devices $lvmdevs for LVM volume groups $VGS"
    # shellcheck disable=SC2086
    lvm vgscan $scan_args 2>&1 | vinfo
    # shellcheck disable=SC2086
    lvm vgchange -ay $activate_args $VGS 2>&1 | vinfo
fi

if [ "$lvmwritten" ]; then
    rm -f -- /etc/lvm/lvm.conf
elif [ "$lvmfilter" ]; then
    # revert filter that was appended to existing lvm.conf
    cp /etc/lvm/lvm.conf.orig /etc/lvm/lvm.conf
    rm -f -- /etc/lvm/lvm.conf.filter
fi
unset lvmwritten
unset lvmfilter

udevadm settle

need_shutdown