3
# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5
# This file is part of LVM2.
7
# This copyrighted material is made available to anyone wishing to use,
8
# modify, copy, or redistribute it subject to the terms and conditions
9
# of the GNU General Public License v.2.
11
# You should have received a copy of the GNU General Public License
12
# along with this program; if not, write to the Free Software Foundation,
13
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Author: Peter Rajnoha <prajnoha at redhat.com>
17
# Script for deactivating block devices
20
# bash >= 4.0 (associative array support)
21
# lsblk >= 2.22 (lsblk -s support)
23
# dmsetup >= 1.02.68 (--retry option support)
24
# lvm >= 2.2.89 (activation/retry_deactivation config support)
28
shopt -s dotglob nullglob
33
SYS_BLK_DIR='/sys/block'
36
DMSETUP="@sbindir@/dmsetup"
39
LSBLK="/bin/lsblk -r --noheadings -o TYPE,KNAME,NAME,MOUNTPOINT"
40
LSBLK_VARS="local devtype local kname local name local mnt"
41
LSBLK_READ="read -r devtype kname name mnt"
43
# Do not unmount mounted devices by default.
46
# Deactivate each LV separately by default (not the whole VG).
48
# Do not retry LV deactivation by default.
49
LVM_CONFIG="activation{retry_deactivation=0}"
52
# List of device names and/or VGs to be skipped.
53
# Device name is the KNAME from lsblk output.
55
# If deactivation of any device fails, it's automatically
56
# added to the SKIP_DEVICE_LIST (also a particular VG
57
# added to the SKIP_VG_LIST for a device that is an LV).
59
# These lists provide device tree pruning to skip
60
# particular device/VG deactivation that failed already.
61
# (lists are associative arrays!)
63
declare -A SKIP_DEVICE_LIST=()
64
declare -A SKIP_VG_LIST=()
67
# List of mountpoints to be skipped. Any device that is mounted on the mountpoint
68
# listed here will be added to SKIP_DEVICE_LIST (and SKIP_VG_LIST) automatically.
69
# (list is an associative array!)
71
declare -A SKIP_UMOUNT_LIST=(["/"]=1 ["/boot"]=1 \
72
["/lib"]=1 ["/lib64"]=1 \
73
["/bin"]=1 ["/sbin"]=1 \
75
["/usr/lib"]=1 ["/usr/lib64"]=1 \
76
["/usr/sbin"]=1 ["/usr/bin"]=1)
77
# Bash can't properly handle '[' and ']' used as a subscript
78
# within the '()'initialization - it needs to be done separately!
79
SKIP_UMOUNT_LIST["[SWAP]"]=1
82
echo "${TOOL}: Utility to deactivate block devices"
84
echo " ${TOOL} [options] [device...]"
85
echo " - Deactivate block device tree."
86
echo " If devices are specified, deactivate only supplied devices and their holders."
89
echo " -h | --help Show this help message"
90
echo " -d | --dmoption DM_OPTIONS Comma separated DM specific options"
91
echo " -l | --lvmoption LVM_OPTIONS Comma separated LVM specific options"
92
echo " -u | --umount Unmount the device if mounted"
94
echo " Device specific options:"
96
echo " retry retry removal several times in case of failure"
97
echo " force force device removal"
99
echo " retry retry removal several times in case of failure"
100
echo " wholevg deactivate the whole VG when processing an LV"
105
add_device_to_skip_list() {
106
SKIP_DEVICE_LIST+=(["$kname"]=1)
110
add_vg_to_skip_list() {
111
SKIP_VG_LIST+=(["$DM_VG_NAME"]=1)
115
is_top_level_device() {
116
# top level devices do not have any holders, that is
117
# the SYS_BLK_DIR/<device_name>/holders dir is empty
118
files="`echo $SYS_BLK_DIR/$kname/holders/*`"
123
test -z "$mnt" && return 0;
125
if test -z "${SKIP_UMOUNT_LIST["$mnt"]}" -a "$DO_UMOUNT" -eq "1"; then
126
echo " UMOUNT: unmounting $name ($kname) mounted on $mnt"
127
$UMOUNT "$mnt" || add_device_to_skip_list
129
echo " [SKIP]: unmount of $name ($kname) mounted on $mnt"
130
add_device_to_skip_list
134
deactivate_holders () {
135
local skip=1; $LSBLK_VARS
137
# Get holders for the device - either a mount or another device.
138
# First line on the lsblk output is the device itself - skip it for
139
# the deactivate call as this device is already being deactivated.
140
while $LSBLK_READ; do
141
test -e $SYS_BLK_DIR/$kname || continue
142
# check if the device not on the skip list already
143
test -z ${SKIP_DEVICE_LIST["$kname"]} || return 1
145
# try to unmount it if mounted
146
device_umount || return 1
148
# try to deactivate the holder
149
test $skip -eq 1 && skip=0 && continue
150
deactivate || return 1
151
done <<< "`$LSBLK $1`"
155
local name=$(printf $name)
156
test -b "$DEV_DIR/mapper/$name" || return 0
157
test -z ${SKIP_DEVICE_LIST["$kname"]} || return 1
159
deactivate_holders "$DEV_DIR/mapper/$name" || return 1
161
echo " DM: deactivating $devtype device $name ($kname)"
162
$DMSETUP $DMSETUP_OPTS remove "$name" || add_device_to_skip_list
166
local DM_VG_NAME; local DM_LV_NAME; local DM_LV_LAYER
168
eval $($DMSETUP splitname --nameprefixes --noheadings --rows "$name" LVM)
169
test -b "$DEV_DIR/$DM_VG_NAME/$DM_LV_NAME" || return 0
170
test -z ${SKIP_VG_LIST["$DM_VG_NAME"]} || return 1
172
# Deactivating only the LV specified
173
test $LVM_DO_WHOLE_VG -eq 0 && {
174
deactivate_holders "$DEV_DIR/$DM_VG_NAME/$DM_LV_NAME" || {
175
add_device_to_skip_list
179
echo " LVM: deactivating Logical Volume $DM_VG_NAME/$DM_LV_NAME"
180
$LVM lvchange --config "log{prefix=\"\"} $LVM_CONFIG" -aln $DM_VG_NAME/$DM_LV_NAME || {
181
add_device_to_skip_list
187
# Deactivating the whole VG the LV is part of
188
lv_list=$($LVM vgs --config "$LVM_CONFIG" --noheadings --rows -o lv_name $DM_VG_NAME)
189
for lv in $lv_list; do
190
test -b "$DEV_DIR/$DM_VG_NAME/$lv" || continue
191
deactivate_holders "$DEV_DIR/$DM_VG_NAME/$lv" || {
197
echo " LVM: deactivating Volume Group $DM_VG_NAME"
198
$LVM vgchange --config "log{prefix=\" \"} $LVM_CONFIG" -aln $DM_VG_NAME || add_vg_to_skip_list
202
######################################################################
203
# DEACTIVATION HOOKS FOR NEW DEVICE TYPES GO HERE! #
205
# Identify a new device type either by inspecting the TYPE provided #
206
# by lsblk directly ($devtype) or by any other mean that is suitable #
207
# e.g. the KNAME provided by lsblk ($kname). See $LSBLK_VARS for #
208
# complete list of variables that may be used. Then call a #
209
# device-specific deactivation function that handles the exact type. #
211
# This device-specific function will certainly need to call #
212
# deactivate_holders first to recursively deactivate any existing #
213
# holders it might have before deactivating the device it processes. #
214
######################################################################
215
if test "$devtype" = "lvm"; then
217
elif test "${kname:0:3}" = "dm-"; then
226
echo "Deactivating block devices:"
228
if test $# -eq 0; then
229
# Deactivate all devices
230
while $LSBLK_READ; do
231
# 'disk' is at the bottom already and it's a real device
232
test "$devtype" = "disk" && continue
234
# if deactivation of any device fails, skip processing
235
# any subsequent devices within its subtree as the
236
# top-level device could not be deactivated anyway
237
test $skip -eq 1 && {
238
# reset 'skip' on top level device
239
is_top_level_device && skip=0 || continue
242
# check if the device is not on the skip list already
243
test -z ${SKIP_DEVICE_LIST["$kname"]} || continue
245
# try to deactivate top-level device, set 'skip=1'
246
# if it fails to do so - this will cause all the
247
# device's subtree to be skipped when processing
248
# devices further in this loop
250
done <<< "`$LSBLK -s`"
252
# Deactivate only specified devices
253
while test $# -ne 0; do
254
# Single dm device tree deactivation.
255
if test -b "$1"; then
256
$LSBLK_READ <<< "`$LSBLK --nodeps $1`"
258
# check if the device is not on the skip list already
259
test -z ${SKIP_DEVICE_LIST["$kname"]} || continue
263
echo "$1: device not found"
272
ORIG_IFS=$IFS; IFS=','
277
"retry") DMSETUP_OPTS+="--retry " ;;
278
"force") DMSETUP_OPTS+="--force " ;;
279
*) echo "$opt: unknown DM option"
287
ORIG_IFS=$IFS; IFS=','
292
"retry") LVM_CONFIG="activation{retry_deactivation=1}" ;;
293
"wholevg") LVM_DO_WHOLE_VG=1 ;;
294
*) echo "$opt: unknown LVM option"
301
while test $# -ne 0; do
304
"-h"|"--help") usage ;;
305
"-d"|"--dmoption ") get_dmopts "$2" ; shift ;;
306
"-l"|"--lvmoption ") get_lvmopts "$2" ; shift ;;
307
"-u"|"--umount") DO_UMOUNT=1 ;;