nmount (6101B)
1 #!/usr/bin/env sh 2 3 # Description: Toggle mount status of a device using pmount 4 # If the device is not mounted, it will be mounted. 5 # If the device is mounted, it will be unmounted and powered down. 6 # 7 # Dependencies: lsblk, pmount (optional), udisks2 8 # 9 # Usage: Runs `lsblk` on 'l', unmounts all USB HDDs on 'u', exits on 'Return`. 10 # 11 # Notes: 12 # - The script uses Linux-specific lsblk to list block devices. Alternatives: 13 # macOS: "diskutil list" 14 # BSD: "geom disk list" 15 # - The script uses udisksctl (from udisks2) to power down devices. This is also Linux-specific. 16 # Users on non-Linux platforms can comment it and use an alternative to power-down disks. 17 # 18 # Shell: POSIX compliant 19 # Author: Arun Prakash Jana 20 21 prompt="device name [e.g. sdXn] ('l'ist, 'u'nmount all USB, 'q'uit): " 22 23 lsblk 24 25 printf "\nEnsure you aren't still in the mounted device.\n" 26 printf "%s" "$prompt" 27 read -r dev 28 29 while [ -n "$dev" ]; do 30 if [ "$dev" = "l" ]; then 31 lsblk 32 elif [ "$dev" = "q" ]; then 33 exit 34 elif [ "$dev" = "u" ]; then 35 usb_devs=$(lsblk -ndo NAME,TRAN | awk '$2=="usb"{print $1}') 36 if [ -z "$usb_devs" ]; then 37 echo "No USB block devices found." 38 else 39 for disk in $usb_devs; do 40 # Get only real partitions (e.g. sda1), skip device-mapper entries like luks-* 41 parts=$(lsblk -nlpo NAME,TYPE "/dev/$disk" | awk '$2=="part"{print $1}') 42 if [ -z "$parts" ]; then 43 echo "/dev/$disk has no partitions, skipping." 44 continue 45 fi 46 mounted=0 47 for part in $parts; do 48 mnt=$(lsblk -n "$part" -o MOUNTPOINT | sed '/^$/d') 49 # Check if this partition or any child (e.g. LUKS) is mounted 50 child_mnt=$(lsblk -nro MOUNTPOINT "$part" | sed '/^$/d') 51 if [ -z "$mnt" ] && [ -z "$child_mnt" ]; then 52 continue 53 fi 54 mounted=1 55 if type pumount >/dev/null 2>&1; then 56 sync "$child_mnt" 57 pumount "$part" 58 prc=$? 59 elif lsblk -n "$part" -o FSTYPE | grep -q "crypto_LUKS"; then 60 dev_map=$(udisksctl info -b "$part" | grep "CleartextDevice" | grep -o "dm_2d[[:digit:]]*" | sed "s/_2d/-/") 61 sync "$child_mnt" 62 udisksctl unmount -b "/dev/$dev_map" --no-user-interaction >/dev/null 63 prc=$? 64 udisksctl lock -b "$part" --no-user-interaction >/dev/null 65 else 66 sync "$mnt" 67 udisksctl unmount -b "$part" --no-user-interaction >/dev/null 68 prc=$? 69 fi 70 if [ $prc -eq 0 ]; then 71 echo "$part unmounted." 72 else 73 echo "Failed to unmount $part." 74 fi 75 done 76 if [ $mounted -eq 0 ]; then 77 echo "/dev/$disk has no mounted partitions, skipping." 78 continue 79 fi 80 if udisksctl power-off -b "/dev/$disk" --no-user-interaction 2>/dev/null; then 81 echo "/dev/$disk ejected." 82 fi 83 done 84 fi 85 else 86 # LUKS volumes mounted with udisksctl appear differently than with pmount 87 if grep -qs "$dev " /proc/mounts || [ -n "$(lsblk -n "/dev/$dev" -o MOUNTPOINT)" ]; then 88 sync "$(lsblk -n "/dev/$dev" -o MOUNTPOINT | sed "/^$/d")" 89 if type pumount >/dev/null 2>&1; then 90 pumount "/dev/$dev" 91 exit_code="$?" 92 else 93 # Unlike pmount, udisksctl does not transparently handle LUKS volumes 94 # We need to manually get the unlocked device, unmount it, and then lock the volume 95 if lsblk -n "/dev/$dev" -o FSTYPE | grep "crypto_LUKS" >/dev/null; then 96 dev_map="$(udisksctl info -b /dev/"$dev" | grep "CleartextDevice" | grep -o "dm_2d[[:digit:]]*" | sed "s/_2d/-/")" 97 udisksctl unmount -b "/dev/$dev_map" --no-user-interaction >/dev/null 98 exit_code="$?" 99 udisksctl lock -b "/dev/$dev" --no-user-interaction >/dev/null 100 else 101 udisksctl unmount -b "/dev/$dev" --no-user-interaction >/dev/null 102 exit_code="$?" 103 fi 104 fi 105 if [ $exit_code -eq 0 ]; then 106 echo "/dev/$dev unmounted." 107 if udisksctl power-off -b "/dev/$dev" --no-user-interaction; then 108 echo "/dev/$dev ejected." 109 fi 110 fi 111 elif [ -b "/dev/$dev" ]; then 112 if type pmount >/dev/null 2>&1; then 113 pmount "/dev/$dev" 114 exit_code="$?" 115 else 116 # Unlike pmount, udisksctl does not transparently handle LUKS volumes 117 # We need to manually get the unlocked device and mount that instead of the volume itself 118 if [ "$(lsblk "/dev/$dev" -n -o FSTYPE)" = "crypto_LUKS" ]; then 119 dev_map=$(udisksctl unlock -b "/dev/$dev" --no-user-interaction | grep -o "dm-[[:digit:]]*") 120 udisksctl mount -b "/dev/$dev_map" --no-user-interaction >/dev/null 121 exit_code="$?" 122 else 123 udisksctl mount -b "/dev/$dev" --no-user-interaction >/dev/null 124 exit_code="$?" 125 fi 126 fi 127 if [ $exit_code -eq 0 ]; then 128 sleep 1 129 echo "/dev/$dev mounted to $(lsblk -n "/dev/$dev" -o MOUNTPOINT | sed "/^$/d")." 130 fi 131 else 132 echo "/dev/$dev does not exist or is not a block device." 133 fi 134 fi 135 136 echo 137 printf "%s" "$prompt" 138 read -r dev 139 done