umountiscsi.sh 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. #!/bin/sh
  2. #
  3. # This script umounts mounted iSCSI devices on shutdown, if possible.
  4. # It is supposed to catch most use cases but is not designed to work
  5. # for every corner-case. It handles LVM and multipath, but only if
  6. # one of the following stackings is used:
  7. # LVM -> multipath -> iSCSI
  8. # multipath -> iSCSI
  9. # LVM -> iSCSI
  10. # LVM -> LUKS -> multipath -> iSCSI
  11. # LVM -> LUKS -> iSCSI
  12. # LUKS -> LVM -> multipath -> iSCSI
  13. # LUKS -> multipath -> iSCSI
  14. # LUKS -> LVM -> iSCSI
  15. # LUKS -> iSCSI
  16. # It does not try to umount anything belonging to any device that is
  17. # also used as a backing store for the root filesystem. Any iSCSI
  18. # device part of the backing store of the root filesystem will be noted
  19. # in /run/open-iscsi/shutdown-keep-sessions, so that the session not be
  20. # closed on shutdown.
  21. #
  22. # KNOWN ISSUES:
  23. # - It doesn't handle submounts properly in all corner cases.
  24. # Specifically, it doesn't handle a non-iSCSI mount below an
  25. # iSCSI mount if it isn't also marked _netdev in /etc/fstab.
  26. # - It does not handle other things device mapper can do, such as
  27. # RAID, crypto, manual mappings of parts of disks, etc.
  28. # - It doesn't try to kill programs still accessing those mounts,
  29. # umount will just fail then.
  30. # - It doesn't handle more complicated stackings such as overlayfs,
  31. # FUSE filesystems, loop devices, etc.
  32. # - It doesn't handle swap.
  33. #
  34. # LONG TERM GOAL:
  35. # - In the long term, there should be a solution where for each part
  36. # of the stacking (device mapper, LVM, overlayfs, etc.) explicit
  37. # depdendencies are declared with the init system such that it can
  38. # be automatically dismantled. That would make this script
  39. # superfluous and also not be a layering violation, as it
  40. # currently is.
  41. #
  42. # CODING CHOICES:
  43. # - On systems running sysvinit, this script might be called without
  44. # /usr being mounted, so a lot of very useful commands are not
  45. # available: head, tail, stat, awk, etc. This makes the script
  46. # quite ugly at places, but that can't be avoided.
  47. #
  48. # Author: Christian Seiler <christian@iwakd.de>
  49. #
  50. # Make sure we don't include /usr in our path, else future modifications
  51. # to this script might accidentally use something from there and cause
  52. # failure on separate-/usr sysvinit systems that isn't immediately
  53. # noticed.
  54. PATH=/sbin:/bin
  55. EXCLUDE_MOUNTS_AT_SHUTDOWN=""
  56. if [ -f /etc/default/open-iscsi ]; then
  57. . /etc/default/open-iscsi
  58. fi
  59. MULTIPATH=/sbin/multipath
  60. PVS=/sbin/pvs
  61. LVS=/sbin/lvs
  62. VGS=/sbin/vgs
  63. VGCHANGE=/sbin/vgchange
  64. CRYPTSETUP=/sbin/cryptsetup
  65. DMSETUP=/sbin/dmsetup
  66. if [ -x $PVS ] && [ -x $LVS ] && [ -x $VGCHANGE ] ; then
  67. HAVE_LVM=1
  68. else
  69. HAVE_LVM=0
  70. fi
  71. if [ -x $CRYPTSETUP ] && [ -x $DMSETUP ] ; then
  72. HAVE_LUKS=1
  73. else
  74. HAVE_LUKS=0
  75. fi
  76. DRY_RUN=0
  77. # We need to make sure that we don't try to umount the root device
  78. # and for systemd systems, also /usr (which is pre-mounted in initrd
  79. # there).
  80. EXCLUDE_MOUNTS="/"
  81. if [ -d /run/systemd/system ] ; then
  82. EXCLUDE_MOUNTS="$EXCLUDE_MOUNTS /usr"
  83. fi
  84. EXCLUDE_MOUNTS="${EXCLUDE_MOUNTS}${EXCLUDE_MOUNTS_AT_SHUTDOWN+ $EXCLUDE_MOUNTS_AT_SHUTDOWN}"
  85. unset _EXCLUDE_MOUNTS
  86. error_usage() {
  87. echo "Usage: $0 [--dry-run | --timeout secs]" >&2
  88. exit 1
  89. }
  90. timeout=0
  91. if [ $# -gt 2 ] ; then
  92. error_usage
  93. fi
  94. if [ $# -eq 2 ] ; then
  95. if [ x"$1"x != x"--timeout"x ] ; then
  96. error_usage
  97. fi
  98. case "$2" in
  99. (-1) timeout="$2" ;;
  100. (*[!0-9]*|"") error_usage ;;
  101. (*) timeout="$2" ;;
  102. esac
  103. elif [ $# -eq 1 ] ; then
  104. if [ x"$1"x != x"--dry-run"x ] ; then
  105. error_usage
  106. fi
  107. DRY_RUN=1
  108. fi
  109. # poor man's hash implementation using shell variables
  110. hash_keys() {
  111. _hash_keys_hash_key_prefix="${1}_"
  112. (
  113. IFS='='
  114. set | while read var value ; do
  115. if [ x"${var#$_hash_keys_hash_key_prefix}"x != x"${var}"x ] ; then
  116. printf '%s\n' "${var#$_hash_keys_hash_key_prefix}"
  117. fi
  118. done
  119. )
  120. }
  121. hash_clear() {
  122. for k in $(hash_keys "$1") ; do
  123. unset "${1}_${k}"
  124. done
  125. }
  126. hash_get() {
  127. _hash_get_var="$2_$(printf '%s' "$3" | sed 's%[^A-Za-z0-9_]%_%g')"
  128. eval _hash_get_value=\$${_hash_get_var}
  129. eval $1=\${_hash_get_value}
  130. }
  131. hash_set() {
  132. _hash_set_var="$1_$(printf '%s' "$2" | sed 's%[^A-Za-z0-9_]%_%g')"
  133. eval ${_hash_set_var}=\${3}
  134. }
  135. hash_unset() {
  136. _hash_set_var="$1_$(printf '%s' "$2" | sed 's%[^A-Za-z0-9_]%_%g')"
  137. unset ${_hash_set_var}
  138. }
  139. in_set() {
  140. eval _set=\$$1
  141. case "${_set}" in
  142. ("$2"|*" $2"|"$2 "*|*" $2 "*) return 0 ;;
  143. (*) return 1 ;;
  144. esac
  145. }
  146. _add_to_set() {
  147. eval _set=\$$1
  148. case "${_set}" in
  149. ("$2"|*" $2"|"$2 "*|*" $2 "*) ;;
  150. ("") _set="$2" ;;
  151. (*) _set="${_set} $2" ;;
  152. esac
  153. eval $1=\${_set}
  154. }
  155. add_to_set() {
  156. _add_to_set_set="$1"
  157. shift
  158. for _add_to_set_val in "$@" ; do
  159. _add_to_set "${_add_to_set_set}" "${_add_to_set_val}"
  160. done
  161. }
  162. hash_add_to_set() {
  163. _hash_add_to_set_var="$1_$(printf '%s' "$2" | sed 's%[^A-Za-z0-9_]%_%g')"
  164. shift
  165. shift
  166. add_to_set "${_hash_add_to_set_var}" "$@"
  167. }
  168. device_majmin() {
  169. eval $1=\"\"
  170. _majmin_dec=$(LC_ALL=C ls -lnd /dev/"$2" | while read _perms _links _uid _gid _majcomma _min _rest ; do
  171. if [ x"${_majcomma%,}"x != x"${_majcomma}"x ] ; then
  172. printf '%s' ${_majcomma%,}:${_min}
  173. fi
  174. break
  175. done)
  176. [ -n "${_majmin_dec}" ] || return
  177. eval $1=\${_majmin_dec}
  178. }
  179. get_lvm_vgs() {
  180. # handle the case where we didn't get passed any PVs
  181. # at all
  182. [ $# -gt 0 ] || return 0
  183. # subshell for pwd change
  184. (
  185. cd /dev
  186. $PVS --noheadings -o vg_name "$@" 2>/dev/null
  187. )
  188. }
  189. enumerate_luks() {
  190. hash_clear LUKS_DEVICES_REVERSE_MAP
  191. _all_crypt_devices=$($DMSETUP info --noheadings -o name -c -S subsystem=CRYPT 2>/dev/null || :)
  192. for _crypt_device in ${_all_crypt_devices} ; do
  193. [ -b "/dev/mapper/${_crypt_device}" ] || continue
  194. _crypt_device="$(readlink -fe "/dev/mapper/${_crypt_device}" 2>/dev/null || :)"
  195. _crypt_device="${_crypt_device#/dev/}"
  196. [ -b "/dev/${_crypt_device}" ] || continue
  197. # dmsetup deps is weird, it outputs the following:
  198. # 1 dependencies : (XYZ)
  199. _dep=$($DMSETUP deps -o blkdevname "/dev/${_crypt_device}" | sed -n '1s%.*: (\(.*\)).*%\1%p')
  200. if [ -n "$_dep" ] && [ -b "/dev/${_dep}" ] ; then
  201. _dep="$(readlink -fe "/dev/$_dep" 2>/dev/null || :)"
  202. _dep="${_dep#/dev/}"
  203. fi
  204. if [ -n "$_dep" ] && [ -b "/dev/${_dep}" ] ; then
  205. hash_set LUKS_DEVICES_REVERSE_MAP "${_dep}" "${_crypt_device}"
  206. fi
  207. done
  208. }
  209. enumerate_iscsi_devices() {
  210. # Empty arrays
  211. iscsi_disks=""
  212. iscsi_partitions=""
  213. iscsi_multipath_disks=""
  214. iscsi_multipath_disk_aliases=""
  215. iscsi_multipath_partitions=""
  216. iscsi_lvm_vgs=""
  217. iscsi_lvm_lvs=""
  218. iscsi_potential_mount_sources=""
  219. iscsi_luks_pass1=""
  220. iscsi_luks_pass2=""
  221. hash_clear ISCSI_DEVICE_SESSIONS
  222. hash_clear ISCSI_MPALIAS_SESSIONS
  223. hash_clear ISCSI_LVMVG_SESSIONS
  224. hash_clear ISCSI_NUMDEVICE_SESSIONS
  225. ISCSI_EXCLUDED_SESSIONS=""
  226. # We first need to generate a global reverse mapping of all
  227. # cryptsetup (e.g. LUKS) devices, because there's no easy way
  228. # to query "is this the encrypted backing of an active crypto
  229. # mapping?
  230. enumerate_luks
  231. # Look for all iscsi disks
  232. for _host_dir in /sys/devices/platform/host* /sys/devices/pci*/*/*/host* ; do
  233. if ! [ -d "$_host_dir"/iscsi_host* ] || ! [ -d "$_host_dir"/iscsi_host/host* ] ; then
  234. continue
  235. fi
  236. for _session_dir in "$_host_dir"/session* ; do
  237. [ -d "$_session_dir"/target* ] || continue
  238. for _block_dev_dir in "$_session_dir"/target*/*\:*/block/* ; do
  239. _block_dev=${_block_dev_dir##*/}
  240. [ x"${_block_dev}"x != x"*"x ] || continue
  241. add_to_set iscsi_disks "${_block_dev}"
  242. hash_add_to_set ISCSI_DEVICE_SESSIONS "${_block_dev}" ${_session_dir}
  243. done
  244. done
  245. done
  246. # Look for all partitions on those disks
  247. for _disk in $iscsi_disks ; do
  248. hash_get _disk_sessions ISCSI_DEVICE_SESSIONS "${_disk}"
  249. for _part_dir in /sys/class/block/"${_disk}"/"${_disk}"?* ; do
  250. _part="${_part_dir##*/}"
  251. [ x"${_part}"x != x"${_disk}?*"x ] || continue
  252. add_to_set iscsi_partitions "${_part}"
  253. hash_set ISCSI_DEVICE_SESSIONS "${_part}" "${_disk_sessions}"
  254. done
  255. done
  256. if [ -x $MULTIPATH ] ; then
  257. # Look for all multipath disks
  258. for _disk in $iscsi_disks ; do
  259. hash_get _disk_sessions ISCSI_DEVICE_SESSIONS "${_disk}"
  260. for _alias in $($MULTIPATH -v1 -l /dev/"$_disk") ; do
  261. _mp_dev="$(readlink -fe "/dev/mapper/${_alias}" || :)"
  262. [ -n "${_mp_dev}" ] || continue
  263. add_to_set iscsi_multipath_disks "${_mp_dev#/dev/}"
  264. add_to_set iscsi_multipath_disk_aliases "${_alias}"
  265. hash_add_to_set ISCSI_DEVICE_SESSIONS "${_mp_dev#/dev/}" ${_disk_sessions}
  266. hash_add_to_set ISCSI_MPALIAS_SESSIONS "${_alias}" ${_disk_sessions}
  267. done
  268. done
  269. # Look for partitions on these multipath disks
  270. for _alias in $iscsi_multipath_disk_aliases ; do
  271. hash_get _mp_sessions ISCSI_MPALIAS_SESSIONS "${_alias}"
  272. for _part_name in /dev/mapper/"${_alias}"-part* ; do
  273. _part="$(readlink -fe "$_part_name" 2>/dev/null || :)"
  274. [ -n "${_part}" ] || continue
  275. add_to_set iscsi_multipath_partitions "${_part#/dev/}"
  276. hash_set ISCSI_DEVICE_SESSIONS "${_part#/dev/}" "${_mp_sessions}"
  277. done
  278. done
  279. fi
  280. if [ $HAVE_LUKS -eq 1 ] ; then
  281. # Look for all LUKS devices.
  282. for _dev in $iscsi_disks $iscsi_partitions $iscsi_multipath_disks $iscsi_multipath_partitions ; do
  283. hash_get _luksDev LUKS_DEVICES_REVERSE_MAP "${_dev}"
  284. [ -n "${_luksDev}" ] || continue
  285. add_to_set iscsi_luks_pass1 "${_luksDev}"
  286. hash_get _currentSession ISCSI_DEVICE_SESSIONS "${_dev}"
  287. if [ -n "${_currentSession}" ] ; then
  288. hash_set ISCSI_DEVICE_SESSIONS "${_luksDev}" "${_currentSession}"
  289. fi
  290. done
  291. fi
  292. if [ $HAVE_LVM -eq 1 ] ; then
  293. # Look for all LVM volume groups that have a backing store
  294. # on any iSCSI device we found. Also, add $LVMGROUPS set in
  295. # /etc/default/open-iscsi (for more complicated stacking
  296. # configurations we don't automatically detect).
  297. for _vg in $(get_lvm_vgs $iscsi_disks $iscsi_partitions $iscsi_multipath_disks $iscsi_multipath_partitions $iscsi_luks_pass1) $LVMGROUPS ; do
  298. add_to_set iscsi_lvm_vgs "$_vg"
  299. done
  300. # $iscsi_lvm_vgs is now unique list
  301. for _vg in $iscsi_lvm_vgs ; do
  302. # get PVs to track iSCSI sessions
  303. for _pv in $($VGS --noheadings -o pv_name "$_vg" 2>/dev/null) ; do
  304. _pv_dev="$(readlink -fe "$_pv" 2>/dev/null || :)"
  305. [ -n "${_pv_dev}" ] || continue
  306. hash_get _pv_sessions ISCSI_DEVICE_SESSIONS "${_pv_dev#/dev/}"
  307. hash_add_to_set ISCSI_LVMVG_SESSIONS "${_vg}" ${_pv_sessions}
  308. done
  309. # now we collected all sessions belonging to this VG
  310. hash_get _vg_sessions ISCSI_LVMVG_SESSIONS "${_vg}"
  311. # find all LVs
  312. for _lv in $($VGS --noheadings -o lv_name "$_vg" 2>/dev/null) ; do
  313. _dev="$(readlink -fe "/dev/${_vg}/${_lv}" 2>/dev/null || :)"
  314. [ -n "${_dev}" ] || continue
  315. iscsi_lvm_lvs="$iscsi_lvm_lvs ${_dev#/dev/}"
  316. hash_set ISCSI_DEVICE_SESSIONS "${_dev#/dev/}" "${_vg_sessions}"
  317. done
  318. done
  319. fi
  320. if [ $HAVE_LUKS -eq 1 ] ; then
  321. # Look for all LUKS devices.
  322. for _dev in $iscsi_lvm_lvs ; do
  323. hash_get _luksDev LUKS_DEVICES_REVERSE_MAP "${_dev}"
  324. [ -n "${_luksDev}" ] || continue
  325. add_to_set iscsi_luks_pass2 "${_luksDev}"
  326. hash_get _currentSession ISCSI_DEVICE_SESSIONS "${_dev}"
  327. if [ -n "${_currentSession}" ] ; then
  328. hash_set ISCSI_DEVICE_SESSIONS "${_luksDev}" "${_currentSession}"
  329. fi
  330. done
  331. fi
  332. # Gather together all mount sources
  333. iscsi_potential_mount_sources="$iscsi_potential_mount_sources $iscsi_disks $iscsi_partitions"
  334. iscsi_potential_mount_sources="$iscsi_potential_mount_sources $iscsi_multipath_disks $iscsi_multipath_partitions"
  335. iscsi_potential_mount_sources="$iscsi_potential_mount_sources $iscsi_lvm_lvs"
  336. iscsi_potential_mount_sources="$iscsi_potential_mount_sources $iscsi_luks_pass1 $iscsi_luks_pass2"
  337. # Convert them to numerical representation
  338. iscsi_potential_mount_sources_majmin=""
  339. for _src in $iscsi_potential_mount_sources ; do
  340. device_majmin _src_majmin "$_src"
  341. [ -n "$_src_majmin" ] || continue
  342. iscsi_potential_mount_sources_majmin="${iscsi_potential_mount_sources_majmin} ${_src_majmin}"
  343. hash_get _dev_sessions ISCSI_DEVICE_SESSIONS "${_src}"
  344. hash_set ISCSI_NUMDEVICE_SESSIONS "${_src_majmin}" "${_dev_sessions}"
  345. done
  346. # Enumerate mount points
  347. iscsi_mount_points=""
  348. iscsi_mount_point_ids=""
  349. while read _mpid _mppid _mpdev _mpdevpath _mppath _mpopts _other ; do
  350. if in_set iscsi_potential_mount_sources_majmin "$_mpdev" ; then
  351. if in_set EXCLUDE_MOUNTS "${_mppath}" ; then
  352. hash_get _dev_sessions ISCSI_NUMDEVICE_SESSIONS "${_mpdev}"
  353. add_to_set ISCSI_EXCLUDED_SESSIONS $_dev_sessions
  354. continue
  355. fi
  356. # list mountpoints in reverse order (in case
  357. # some are stacked) mount --move may cause the
  358. # order of /proc/self/mountinfo to not always
  359. # reflect the stacking order, so this is not
  360. # fool-proof, but it's better than nothing
  361. iscsi_mount_points="$_mppath $iscsi_mount_points"
  362. iscsi_mount_point_ids="$_mpid $iscsi_mount_points"
  363. fi
  364. done < /proc/self/mountinfo
  365. }
  366. try_umount() {
  367. # in order to handle stacking try twice; together with the fact
  368. # that the list of mount points is in reverse order of the
  369. # contents /proc/self/mountinfo this should catch most cases
  370. for retry in 1 2 ; do
  371. for path in $iscsi_mount_points ; do
  372. # first try to see if it really is a mountpoint
  373. # still (might be the second round this is done
  374. # and the mount is already gone, or something
  375. # else umounted it first)
  376. if ! fstab-decode mountpoint -q "$path" ; then
  377. continue
  378. fi
  379. # try to umount it
  380. if ! fstab-decode umount "$path" ; then
  381. # unfortunately, umount's exit code
  382. # may be a false negative, i.e. it
  383. # might give a failure exit code, even
  384. # though it succeeded, so check again
  385. if fstab-decode mountpoint -q "$path" ; then
  386. echo "Could not unmount $path" >&2
  387. any_umount_failed=1
  388. fi
  389. fi
  390. done
  391. done
  392. }
  393. try_deactivate_lvm() {
  394. [ $HAVE_LVM -eq 1 ] || return
  395. for vg in $iscsi_lvm_vgs ; do
  396. vg_excluded=0
  397. hash_get vg_sessions ISCSI_LVMVG_SESSIONS "$vg"
  398. for vg_session in $vg_sessions ; do
  399. if in_set ISCSI_EXCLUDED_SESSIONS "$vg_session" ; then
  400. vg_excluded=1
  401. fi
  402. done
  403. if [ $vg_excluded -eq 1 ] ; then
  404. # volume group on same iSCSI session as excluded
  405. # mount, don't disable it
  406. # (FIXME: we should only exclude VGs that contain
  407. # those mounts, not also those that happen to be
  408. # in the same iSCSI session)
  409. continue
  410. fi
  411. if ! $VGCHANGE --available=n $vg ; then
  412. # Make sure the volume group (still) exists. If
  413. # it doesn't we count that as deactivated, so
  414. # don't fail then.
  415. _vg_test=$(vgs -o vg_name --noheadings $vg 2>/dev/null || :)
  416. if [ -n "${_vg_test}" ] ; then
  417. echo "Cannot deactivate Volume Group $vg" >&2
  418. any_umount_failed=1
  419. fi
  420. fi
  421. done
  422. }
  423. try_dismantle_multipath() {
  424. [ -x $MULTIPATH ] || return
  425. for mpalias in $iscsi_multipath_disk_aliases ; do
  426. mp_excluded=0
  427. hash_get mp_sessions ISCSI_MPALIAS_SESSIONS "$mpalias"
  428. for mp_session in $mp_sessions ; do
  429. if in_set ISCSI_EXCLUDED_SESSIONS "$mp_session" ; then
  430. mp_excluded=1
  431. fi
  432. done
  433. if [ $mp_excluded -eq 1 ] ; then
  434. # multipath device on same iSCSI session as
  435. # excluded mount, don't disable it
  436. # (FIXME: we should only exclude multipath mounts
  437. # that contain those mounts, not also those that
  438. # happen to be in the same iSCSI session)
  439. continue
  440. fi
  441. if ! $MULTIPATH -f $mpalias ; then
  442. echo "Cannot dismantle Multipath Device $mpalias" >&2
  443. any_umount_failed=1
  444. fi
  445. done
  446. }
  447. try_dismantle_luks() {
  448. [ $HAVE_LUKS -eq 1 ] || return
  449. case "$1" in
  450. 1) iscsi_luks_current_pass="$iscsi_luks_pass1" ;;
  451. 2|*) iscsi_luks_current_pass="$iscsi_luks_pass2" ;;
  452. esac
  453. for luksDev in $iscsi_luks_current_pass ; do
  454. luks_excluded=0
  455. hash_get device_sessions ISCSI_DEVICE_SESSIONS "$luksDev"
  456. for device_session in $device_sessions ; do
  457. if in_set ISCSI_EXCLUDED_SESSIONS "$device_session" ; then
  458. luks_excluded=1
  459. fi
  460. done
  461. if [ $luks_excluded -eq 1 ] ; then
  462. continue
  463. fi
  464. _luksName="$($DMSETUP info -c --noheadings -o name /dev/"$luksDev" 2>/dev/null || :)"
  465. [ -n "${_luksName}" ] || continue
  466. if ! $CRYPTSETUP close "${_luksName}" ; then
  467. echo "Cannot dismantle cryptsetup device ${_luksName}" >&2
  468. any_umount_failed=1
  469. fi
  470. done
  471. }
  472. # Don't do this if we are using systemd as init system, since systemd
  473. # takes care of network filesystems (including those marked _netdev) by
  474. # itself.
  475. if ! [ -d /run/systemd/system ] && [ $HANDLE_NETDEV -eq 1 ] && [ $DRY_RUN -eq 0 ]; then
  476. echo "Unmounting all devices marked _netdev";
  477. umount -a -O _netdev >/dev/null 2>&1
  478. fi
  479. enumerate_iscsi_devices
  480. # Dry run? Just print what we want to do (useful for administrator to check).
  481. if [ $DRY_RUN -eq 1 ] ; then
  482. echo "$0: would umount the following mount points:"
  483. had_mount=0
  484. if [ -n "$iscsi_mount_points" ] ; then
  485. for v in $iscsi_mount_points ; do
  486. echo " $v"
  487. had_mount=1
  488. done
  489. fi
  490. [ $had_mount -eq 1 ] || echo " (none)"
  491. echo "$0: would disable the following LUKS devices (second pass):"
  492. had_luks=0
  493. if [ -n "$iscsi_luks_pass2" ] ; then
  494. for v in ${iscsi_luks_pass2} ; do
  495. luks_excluded=0
  496. hash_get device_sessions ISCSI_DEVICE_SESSIONS "$v"
  497. for device_session in $device_sessions ; do
  498. if in_set ISCSI_EXCLUDED_SESSIONS "$device_session" ; then
  499. luks_excluded=1
  500. fi
  501. done
  502. if [ $luks_excluded -eq 1 ] ; then
  503. continue
  504. fi
  505. _luksName="$($DMSETUP info -c --noheadings -o name /dev/"$v" 2>/dev/null || :)"
  506. [ -n "${_luksName}" ] || continue
  507. echo " ${_luksName}"
  508. had_luks=1
  509. done
  510. fi
  511. [ $had_luks -eq 1 ] || echo " (none)"
  512. echo "$0: would deactivate the following LVM Volume Groups:"
  513. had_vg=0
  514. if [ -n "$iscsi_lvm_vgs" ] ; then
  515. for v in $iscsi_lvm_vgs ; do
  516. # sync this exclusion logic with try_deactivate_lvm
  517. vg_excluded=0
  518. hash_get vg_sessions ISCSI_LVMVG_SESSIONS "$v"
  519. for vg_session in $vg_sessions ; do
  520. if in_set ISCSI_EXCLUDED_SESSIONS "$vg_session" ; then
  521. vg_excluded=1
  522. fi
  523. done
  524. if [ $vg_excluded -eq 1 ] ; then
  525. continue
  526. fi
  527. echo " $v"
  528. had_vg=1
  529. done
  530. fi
  531. [ $had_vg -eq 1 ] || echo " (none)"
  532. echo "$0: would disable the following LUKS devices (first pass):"
  533. had_luks=0
  534. if [ -n "$iscsi_luks_pass1" ] ; then
  535. for v in ${iscsi_luks_pass1} ; do
  536. luks_excluded=0
  537. hash_get device_sessions ISCSI_DEVICE_SESSIONS "$v"
  538. for device_session in $device_sessions ; do
  539. if in_set ISCSI_EXCLUDED_SESSIONS "$device_session" ; then
  540. luks_excluded=1
  541. fi
  542. done
  543. if [ $luks_excluded -eq 1 ] ; then
  544. continue
  545. fi
  546. _luksName="$($DMSETUP info -c --noheadings -o name /dev/"$v" 2>/dev/null || :)"
  547. [ -n "${_luksName}" ] || continue
  548. echo " ${_luksName}"
  549. had_luks=1
  550. done
  551. fi
  552. [ $had_luks -eq 1 ] || echo " (none)"
  553. echo "$0: would deactivate the following multipath volumes:"
  554. had_mp=0
  555. if [ -n "$iscsi_multipath_disk_aliases" ] ; then
  556. for v in $iscsi_multipath_disk_aliases ; do
  557. # sync this exclusion logic with try_dismantle_multipath
  558. mp_excluded=0
  559. hash_get mp_sessions ISCSI_MPALIAS_SESSIONS "$v"
  560. for mp_session in $mp_sessions ; do
  561. if in_set ISCSI_EXCLUDED_SESSIONS "$mp_session" ; then
  562. mp_excluded=1
  563. fi
  564. done
  565. if [ $mp_excluded -eq 1 ] ; then
  566. continue
  567. fi
  568. echo " $v"
  569. had_mp=1
  570. done
  571. fi
  572. [ $had_mp -eq 1 ] || echo " (none)"
  573. if [ -n "$ISCSI_EXCLUDED_SESSIONS" ] ; then
  574. echo "$0: the following sessions are excluded from disconnection (because / or another excluded mount is on them):"
  575. for v in $ISCSI_EXCLUDED_SESSIONS ; do
  576. echo " $v"
  577. done
  578. fi
  579. exit 0
  580. fi
  581. # after our first enumeration, write out a list of sessions that
  582. # shouldn't be terminated because excluded mounts are on those
  583. # sessions
  584. if [ -n "$ISCSI_EXCLUDED_SESSIONS" ] ; then
  585. mkdir -p -m 0700 /run/open-iscsi
  586. for session in $ISCSI_EXCLUDED_SESSIONS ; do
  587. printf '%s\n' $session
  588. done > /run/open-iscsi/shutdown-keep-sessions
  589. else
  590. # make sure there's no leftover from a previous call
  591. rm -f /run/open-iscsi/shutdown-keep-sessions
  592. fi
  593. any_umount_failed=0
  594. try_umount
  595. try_dismantle_luks 2
  596. try_deactivate_lvm
  597. try_dismantle_luks 1
  598. try_dismantle_multipath
  599. while [ $any_umount_failed -ne 0 ] && ( [ $timeout -gt 0 ] || [ $timeout -eq -1 ] ) ; do
  600. # wait a bit, perhaps there was still a program that
  601. # was terminating
  602. sleep 1
  603. # try again and decrease timeout
  604. enumerate_iscsi_devices
  605. any_umount_failed=0
  606. try_umount
  607. try_dismantle_luks 2
  608. try_deactivate_lvm
  609. try_dismantle_luks 1
  610. try_dismantle_multipath
  611. if [ $timeout -gt 0 ] ; then
  612. timeout=$((timeout - 1))
  613. fi
  614. done
  615. # Create signaling file (might be useful)
  616. if [ $any_umount_failed -eq 1 ] ; then
  617. touch /run/open-iscsi/some_umount_failed
  618. else
  619. rm -f /run/open-iscsi/some_umount_failed
  620. fi
  621. exit $any_umount_failed