aboutsummaryrefslogtreecommitdiff
path: root/bashmount
diff options
context:
space:
mode:
Diffstat (limited to 'bashmount')
-rwxr-xr-xbashmount855
1 files changed, 855 insertions, 0 deletions
diff --git a/bashmount b/bashmount
new file mode 100755
index 0000000..331ebe4
--- /dev/null
+++ b/bashmount
@@ -0,0 +1,855 @@
+#!/bin/bash
+set -u
+
+declare -r VERSION="4.3.2"
+
+#=============================================================================#
+# FILE: bashmount #
+# WEBSITE: https://github.com/jamielinux/bashmount #
+# DESCRIPTION: bashmount is a menu-driven bash script that can use different #
+# backends to easily mount, unmount or eject removable devices #
+# without dependencies on any GUI. An extensive configuration #
+# file allows many aspects of the script to be modified and #
+# custom commands to be run on devices. #
+# LICENSE: GPLv2 #
+# AUTHORS: Jamie Nguyen <j@jamielinux.com> #
+# Lukas B. #
+#=============================================================================#
+
+# Copyright (C) 2013-2020 Jamie Nguyen <j@jamielinux.com>
+# Copyright (C) 2014 Lukas B.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License v2 as published by the
+# Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+if (( $# > 0 )) && [[ "$1" == "-v" || "$1" == "--version" ]]; then
+ printf '%s\n' "$VERSION"
+ exit 0
+fi
+
+declare -ri EXIT_CMDNOTFOUND=127
+declare -ri EXIT_CONFIG=78
+
+# {{{ ··· CONFIGURATION
+# Make sure that user defined options will not interfere with grep.
+unset GREP_OPTIONS
+
+# Set defaults.
+declare udisks="auto"
+declare mount_options=""
+declare -i show_internal=1
+declare -i show_removable=1
+declare -i show_optical=1
+declare -i show_commands=1
+declare -i colourize=1
+declare -i pretty=1
+declare -i custom4_show=0
+declare -i custom5_show=0
+declare -i custom6_show=0
+declare custom4_desc=""
+declare custom5_desc=""
+declare custom6_desc=""
+declare -i run_post_mount=0
+declare -i run_post_unmount=0
+declare -a exclude=()
+
+# Backwards compat
+declare default_mount_options=""
+declare -a blacklist=()
+
+filemanager() {
+ ( cd "$1" && "$SHELL" )
+}
+
+post_mount() {
+ error "No 'post_mount' command specified in bashmount configuration file."
+ return 1
+}
+
+post_unmount() {
+ error "No 'post_unmount' command specified in bashmount configuration file."
+ return 1
+}
+
+declare CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/bashmount/config"
+[[ ! -e "$CONFIG_FILE" ]] && CONFIG_FILE="/etc/bashmount.conf"
+if [[ -e "$CONFIG_FILE" ]]; then
+ if ! . "$CONFIG_FILE"; then
+ printf '%s\n' "bashmount: Failed to source configuration file."
+ exit $EXIT_CONFIG
+ fi
+fi
+
+if [[ "$udisks" != 0 ]]; then
+ if type -p udisksctl >/dev/null 2>&1; then
+ [[ "$udisks" == "auto" ]] && udisks=1
+ elif [[ "$udisks" == "auto" ]]; then
+ udisks=0
+ else
+ printf '%s\n' "bashmount: 'udisksctl': command not found"
+ exit $EXIT_CMDNOTFOUND
+ fi
+fi
+
+if ! type -p lsblk >/dev/null 2>&1; then
+ printf '%s\n' "bashmount: 'lsblk': command not found"
+ exit $EXIT_CMDNOTFOUND
+fi
+
+# Backwards compat
+[[ -n "$default_mount_options" ]] && mount_options="$default_mount_options"
+(( ${#exclude[@]} == 0 )) && exclude=("${blacklist[@]}")
+# }}}
+
+# {{{ ··· PRINTING FUNCTIONS
+if (( colourize )); then
+ if tput setaf 0 >/dev/null 2>&1; then
+ declare -r ALL_OFF="$(tput sgr0)"
+ declare -r BOLD="$(tput bold)"
+ declare -r BLUE="${BOLD}$(tput setaf 4)"
+ declare -r GREEN="${BOLD}$(tput setaf 2)"
+ declare -r RED="${BOLD}$(tput setaf 1)"
+ else
+ declare -r ALL_OFF="\e[1;0m"
+ declare -r BOLD="\e[1;1m"
+ declare -r BLUE="${BOLD}\e[1;34m"
+ declare -r GREEN="${BOLD}\e[1;32m"
+ declare -r RED="${BOLD}\e[1;31m"
+ fi
+else
+ declare -r ALL_OFF="" BOLD="" BLUE="" GREEN="" RED=""
+fi
+declare -r ARROW="==>"
+
+msg() {
+ printf '\n%b\n\n' "${GREEN}${ARROW}${ALL_OFF}${BOLD} ${*}${ALL_OFF}" >&2
+}
+error() {
+ printf '\n%b\n\n' "${RED}${ARROW}${BOLD} ERROR: ${*}${ALL_OFF}" >&2
+ enter_to_continue
+}
+enter_to_continue() {
+ printf '\n'
+ read -r -e -p "Press [${BLUE}enter${ALL_OFF}] to continue: "
+}
+invalid_command() {
+ printf '\n'
+ error "Invalid command. See the help menu."
+}
+
+print_commands() {
+ printf '\n\n'
+ print_separator_commands
+ printf '%s' " ${BLUE}e${ALL_OFF}: eject"
+ printf '%s' " ${BLUE}i${ALL_OFF}: info"
+ printf '%s' " ${BLUE}m${ALL_OFF}: mount"
+ printf '%s' " ${BLUE}o${ALL_OFF}: open"
+ printf '%s' " ${BLUE}u${ALL_OFF}: unmount"
+ printf '\n\n'
+ printf '%s' " ${BLUE}[Enter]${ALL_OFF}: refresh"
+ printf '%s' " ${BLUE}a${ALL_OFF}: unmount all"
+ printf '%s' " ${BLUE}q${ALL_OFF}: quit"
+ printf '%s' " ${BLUE}?${ALL_OFF}: help"
+ printf '\n\n'
+}
+
+print_submenu_commands() {
+ printf '\n\n'
+ print_separator_commands
+ printf '%s' " ${BLUE}e${ALL_OFF}: eject"
+ printf '%s' " ${BLUE}i${ALL_OFF}: info"
+ if check_mounted "$devname"; then
+ printf '%s' " ${BLUE}u${ALL_OFF}: unmount"
+ else
+ printf '%s' " ${BLUE}m${ALL_OFF}: mount "
+ fi
+ printf '%s' " ${BLUE}o${ALL_OFF}: open"
+ printf '\n\n'
+ printf '%s' " ${BLUE}[Enter]${ALL_OFF}: refresh"
+ printf '%s' " ${BLUE}b${ALL_OFF}: back"
+ printf '%s' " ${BLUE}q${ALL_OFF}: quit"
+ printf '%s' " ${BLUE}?${ALL_OFF}: help"
+ printf '\n\n'
+ printf '%s' " ${BLUE}1${ALL_OFF}: luksDump"
+ printf '%s' " ${BLUE}2${ALL_OFF}: luksOpen"
+ printf '%s' " ${BLUE}3${ALL_OFF}: luksClose"
+ printf '\n'
+
+ if (( custom4_show )) || (( custom5_show )) || (( custom6_show )); then
+ local -i col_width=18
+ printf '\n'
+ (( custom4_show )) && [[ -n "$custom4_desc" ]] \
+ && printf '%s' " ${BLUE}4${ALL_OFF}: $custom4_desc"
+ local -i custom4_desc_len="${#custom4_desc}"
+ if (( custom4_desc_len < col_width )); then
+ for (( i=18; i>custom4_desc_len; i-- )); do
+ printf '%s' " "
+ done
+ fi
+ (( custom5_show )) && [[ -n "$custom5_desc" ]] \
+ && printf '%s' " ${BLUE}5${ALL_OFF}: $custom5_desc"
+ local -i custom5_desc_len="${#custom5_desc}"
+ if (( custom5_desc_len < col_width )); then
+ for (( i=18; i>custom5_desc_len; i-- )); do
+ printf '%s' " "
+ done
+ fi
+ (( custom6_show )) && [[ -n "$custom6_desc" ]] \
+ && printf '%s' " ${BLUE}6${ALL_OFF}: $custom6_desc"
+ printf '\n'
+ fi
+}
+
+__print() {
+ printf "${BOLD}"
+ if (( pretty )); then
+ printf '%s\n\n' "$1" | sed -e "s/-/━/g"
+ else
+ printf '%s\n\n' "$1"
+ fi
+ printf "${ALL_OFF}"
+}
+print_separator() {
+ __print "-----------------------------------------------------------------------------"
+}
+print_separator_commands() {
+ __print "-------------------------------- Commands ---------------------------------"
+}
+print_separator_device() {
+ __print "------------------------------- Device Menu -------------------------------"
+}
+print_separator_optical() {
+ __print "------------------------------ Optical Media ------------------------------"
+}
+print_separator_removable() {
+ __print "----------------------------- Removable Media -----------------------------"
+}
+print_separator_internal() {
+ __print "----------------------------- Internal Media ------------------------------"
+}
+
+print_help() {
+ clear
+ print_commands
+ print_separator
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BOLD}To mount the first device, enter ${ALL_OFF}"
+ printf '%s' "${BLUE}1m${ALL_OFF}${BOLD}.${ALL_OFF}"
+ printf '\n\n'
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BOLD}To open the mountpath directory of the first${ALL_OFF}"
+ printf '\n\n'
+ printf '%s' " ${BOLD}device (mounting if required), enter "
+ printf '%s' "${BLUE}1o${ALL_OFF}${BOLD}.${ALL_OFF}"
+ printf '\n\n'
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BOLD}To view a device sub-menu, just enter the number.${ALL_OFF}"
+ printf '\n\n'
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BLUE}[Enter]${ALL_OFF}"
+ printf '%s' "${BOLD}, "
+ printf '%s' "${BLUE}a${ALL_OFF}"
+ printf '%s' "${BOLD}, "
+ printf '%s' "${BLUE}q${ALL_OFF} "
+ printf '%s' "${BOLD}and "
+ printf '%s' "${BLUE}?${ALL_OFF} "
+ printf '%s' "${BOLD}do not require a number.${ALL_OFF}"
+ printf '\n\n'
+ print_separator
+ enter_to_continue
+}
+
+print_help_submenu() {
+ clear
+ print_submenu_commands
+ printf '\n'
+ print_separator
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BOLD}To perform a command, enter a character and press ${ALL_OFF}"
+ printf '%s' "${BLUE}[Enter]${ALL_OFF}${BOLD}.${ALL_OFF}"
+ printf '\n\n'
+ printf '%b' " ${GREEN}${ARROW}${ALL_OFF} "
+ printf '%s' "${BOLD}For example, to mount this device, type ${ALL_OFF}"
+ printf '%s' "${BLUE}m${ALL_OFF} and press ${BLUE}[Enter]${ALL_OFF}"
+ printf '%s' "${BOLD}.${ALL_OFF}"
+ printf '\n\n'
+ print_separator
+ enter_to_continue
+}
+
+print_device() {
+ local -i padding_name=13
+ local -i padding_label=18
+ local -i padding_size=6
+
+ local label="$(info_fslabel "$devname")"
+ local fstype="$(info_fstype "$devname")"
+
+ [[ -z "$label" ]] && (( $# == 1 )) && [[ "$1" == "optical" ]] \
+ && label="$(lsblk -dno MODEL "$devname")"
+ [[ -z "$label" ]] && label="$(info_partlabel "$devname")"
+ [[ -z "$label" ]] && [[ "$fstype" == "crypto_LUKS" ]] && label="crypto_LUKS"
+ [[ -z "$label" ]] && label="-"
+
+ label="$(printf '%s' "$label" | sed -e 's/\\x20/ /g')"
+
+ listed[device_number]="$devname"
+ (( device_number++ ))
+
+ printf '%s' " ${BLUE}${device_number})${ALL_OFF}"
+
+ devname_short="${devname##*/}"
+ label_short="$label"
+ if (( ${#devname_short} > padding_name )); then
+ len=$(( padding_name - 4 ))
+ devname_short="${devname_short:0:len}..."
+ elif (( ${#label} > padding_label )); then
+ label_len=$(( padding_label - 4 ))
+ label_short="${label:0:label_len}..."
+ fi
+
+ printf '%s' " ${devname_short}:"
+ for (( i=padding_name; i>${#devname_short}; i-- )); do
+ printf '%s' " "
+ done
+
+ printf '%s' " $label_short"
+ for (( i=padding_label; i>${#label_short}; i-- )); do
+ printf '%s' " "
+ done
+
+ size="$(info_size "$devname")"
+ printf '%s' " $size"
+ if (( ${#size} < padding_size )); then
+ for (( i=padding_size; i>${#size}; i-- )); do
+ printf '%s' " "
+ done
+ fi
+
+ if [[ "$fstype" == "crypto_LUKS" ]]; then
+ local uuid="$(info_uuid "$devname")"
+ if [[ -n "$uuid" ]]; then
+ for dev in "${all[@]}"; do
+ if [[ "$dev" == "/dev/mapper/luks-$uuid" ]]; then
+ printf '%s' " ${GREEN}decrypted [luks-${uuid:0:4}...]${ALL_OFF}"
+ fi
+ done
+ fi
+ elif check_mounted "$devname"; then
+ mountpath="$(info_mountpath "$devname")"
+ printf '%s' " ${GREEN}[$mountpath]${ALL_OFF}"
+ mounted[${#mounted[*]}]="$devname"
+ fi
+ printf '\n'
+}
+# }}}
+
+# {{{ ··· INFORMATION RETRIEVAL
+# Returns 0 (ie, success) if the device still exists. Otherwise it returns 1.
+check_device() {
+ if [[ ! -b "$1" ]]; then
+ error "$1 is no longer available."
+ return 1
+ fi
+}
+# Returns 0 (ie, success) if the device is mounted. Otherwise it returns 1.
+check_mounted() {
+ findmnt -no TARGET "$1" >/dev/null 2>&1
+}
+# Returns 0 (ie, success) if the device is registered as a removable device in
+# the kernel. Otherwise it returns 1.
+check_removable() {
+ [[ "$(lsblk -drno RM "$1")" == "1" ]]
+}
+
+info_fslabel() {
+ lsblk -drno LABEL "$1" 2>/dev/null
+}
+info_fstype() {
+ lsblk -drno FSTYPE "$1" 2>/dev/null
+}
+info_mountpath() {
+ findmnt -no TARGET "$1" 2>/dev/null
+}
+info_partlabel() {
+ lsblk -drno PARTLABEL "$1" 2>/dev/null
+}
+info_size() {
+ lsblk -drno SIZE "$1" 2>/dev/null
+}
+info_type() {
+ lsblk -drno TYPE "$1" 2>/dev/null
+}
+info_uuid() {
+ lsblk -drno UUID "$1" 2>/dev/null
+}
+info_used() {
+ lsblk -drno FSUSE% "$1" 2>/dev/null
+}
+
+get_luks_child() {
+ local uuid="$(info_uuid "$1")"
+ printf '%s' "/dev/mapper/luks-$uuid"
+}
+# }}}
+
+# {{{ ··· DEVICE MANIPULATION
+__mount() {
+ msg "Mounting $1 ..."
+ if (( udisks )); then
+ udisksctl mount $mount_options --block-device "$1"
+ else
+ read -r -e -p "Choose the mountpoint directory: " dir
+ [[ -z "$dir" ]] && return 1
+ if [[ ! -e "$dir" ]]; then
+ if ! mkdir -p "$dir"; then
+ error "'$dir': Could not create directory."
+ return 1
+ fi
+ fi
+ sudo mount $mount_options "$1" "$dir"
+ fi
+}
+
+__unmount() {
+ msg "Unmounting $1 ..."
+ if (( udisks )); then
+ udisksctl unmount --block-device "$1"
+ else
+ sudo umount "$1"
+ fi
+}
+
+action_eject() {
+ check_device "$1" || return 1
+
+ if [[ "$(info_fstype "$1")" == "crypto_LUKS" ]]; then
+ action_unmount "$1" || return 1
+ else
+ check_mounted "$1" && action_unmount "$1"
+ fi
+
+ local -i retval=0
+ if ! check_mounted "$1"; then
+ msg "Ejecting $1 ..."
+ device_type=$(info_type "$1")
+ if (( udisks )) && [[ "$device_type" != "rom" ]]; then
+ udisksctl power-off -b "$1"
+ retval=$?
+ else
+ sudo eject "$1"
+ retval=$?
+ fi
+ if (( retval == 0 )); then
+ # Give the device some time to eject.
+ sleep 1.5s
+ else
+ enter_to_continue
+ fi
+ fi
+}
+
+action_info() {
+ check_device "$1" || return 1
+ if (( udisks )); then
+ udisksctl info -b "$devname" | less
+ else
+ lsblk -po NAME,FSTYPE,SIZE,FSUSE%,MOUNTPOINT "$1" | less
+ fi
+}
+
+action_mount() {
+ check_device "$1" || return 1
+ if check_mounted "$1"; then
+ error "$1 is already mounted."
+ return 1
+ fi
+
+ if [[ "$(info_fstype "$1")" == "crypto_LUKS" ]]; then
+ luks_child="$(get_luks_child "$1")"
+ if [[ ! -b "$luks_child" ]]; then
+ action_unlock "$1" || return 1
+ fi
+ action_mount "$luks_child"
+ return $?
+ fi
+
+ if __mount "$1"; then
+ msg "$1 mounted successfully."
+ (( run_post_mount )) && post_mount "$1"
+ sleep 0.1s
+ return 0
+ fi
+ error "$1 could not be mounted."
+ return 1
+}
+
+action_open() {
+ check_device "$1" || return 1
+ if [[ "$(info_fstype "$1")" == "crypto_LUKS" ]]; then
+ luks_child="$(get_luks_child "$1")"
+ if [[ ! -b "$luks_child" ]]; then
+ action_mount "$1" || return 1
+ fi
+ action_open "$luks_child"
+ return $?
+ elif ! check_mounted "$1"; then
+ action_mount "$1" || return 1
+ fi
+ msg "Opening $1 ..."
+ filemanager "$(info_mountpath "$1")" || enter_to_continue
+}
+
+action_unmount() {
+ check_device "$1" || return 1
+
+ if [[ "$(info_fstype "$1")" == "crypto_LUKS" ]]; then
+ luks_child="$(get_luks_child "$1")"
+ if [[ -b "$luks_child" ]]; then
+ if check_mounted "$luks_child"; then
+ action_unmount "$luks_child" || return 1
+ fi
+ action_lock "$1" || return 1
+ fi
+ return $?
+ fi
+
+ if ! check_mounted "$1"; then
+ error "$1 is already unmounted."
+ return 1
+ fi
+
+ if __unmount "$1"; then
+ msg "$1 unmounted successfully."
+ (( run_post_unmount )) && post_unmount "$1"
+ sleep 0.1s
+ return 0
+ fi
+ error "$1 could not be unmounted."
+ return 1
+}
+
+action_unlock() {
+ msg "Opening luks volume ..."
+ local -i retval=0
+ if (( udisks )); then
+ udisksctl unlock --block-device "$devname"
+ retval=$?
+ else
+ sudo cryptsetup open --type luks -v "$devname" "luks-${devname##*/}"
+ retval=$?
+ fi
+ (( retval != 0 )) && enter_to_continue
+ return $retval
+}
+
+action_lock() {
+ msg "Closing luks volume ..."
+ local -i retval=0
+ if (( udisks )); then
+ udisksctl lock --block-device "$devname"
+ retval=$?
+ else
+ sudo cryptsetup close --type luks "$devname"
+ retval=$?
+ fi
+ (( retval != 0 )) && enter_to_continue
+ return $retval
+}
+# }}}
+
+# {{{ ··· MENU FUNCTIONS
+list_devices() {
+ all=() # all devices
+ listed=() # all devices that are shown (ie, not hidden)
+ mounted=() # all devices that are shown and mounted
+ device_number=0
+
+ local -a removable=()
+ local -a internal=()
+ local -a optical=()
+
+ while IFS='' read -r device; do
+ all+=( "$device" )
+ done < <(lsblk -plno NAME)
+
+ for devname in "${all[@]}"; do
+ # Hide excluded devices.
+ for string in "${exclude[@]}"; do
+ lsblk -dPno NAME,TYPE,FSTYPE,LABEL,MOUNTPOINT,PARTLABEL,UUID "$devname" \
+ | grep -E "$string" >/dev/null 2>&1
+ (( $? )) || continue 2
+ done
+
+ # Sort devices into arrays removable, internal, and optical.
+ local device_type=$(info_type "$devname")
+ if [[ "$device_type" == "part" ]]; then
+ if check_removable "$devname"; then
+ removable[${#removable[*]}]="$devname"
+ else
+ internal[${#internal[*]}]="$devname"
+ fi
+ elif [[ "$device_type" == "crypt" ]]; then
+ # luks-xxxxx devices are never marked as removable, so we judge
+ # whether they are removable by their parent crypto_LUKS device.
+ local -i parent_found=0
+ for parent_devname in "${all[@]}"; do
+ local parent_uuid="$(info_uuid "$parent_devname")"
+ [[ -z "$parent_uuid" ]] && continue
+ if [[ "/dev/mapper/luks-$parent_uuid" == "$devname" ]]; then
+ if check_removable "$parent_devname"; then
+ removable[${#removable[*]}]="$devname"
+ parent_found=1
+ break
+ else
+ internal[${#internal[*]}]="$devname"
+ parent_found=1
+ break
+ fi
+ fi
+ done
+ (( !parent_found )) && internal[${#internal[*]}]="$devname"
+ # Normally we don't want to see a "disk", but if it has no partitions
+ # (eg, internal storage on some portable media devices) then it should
+ # be visible.
+ elif [[ "$device_type" == "disk" ]]; then
+ for (( i=0; i<${#all[@]}; i++ )); do
+ [[ "${all[$i]}" =~ "$devname".+ ]] && continue 2
+ done
+ if check_removable "$devname"; then
+ removable[${#removable[*]}]="$devname"
+ else
+ internal[${#internal[*]}]="$devname"
+ fi
+ elif [[ "$device_type" == "rom" ]]; then
+ optical[${#optical[*]}]="$devname"
+ else
+ continue
+ fi
+ done
+
+ clear
+ # List internal media.
+ if (( show_internal )) && (( ${#internal[*]} )); then
+ print_separator_internal
+ for devname in "${internal[@]}"; do
+ print_device
+ done
+ printf '\n'
+ fi
+ # List removable media.
+ if (( show_removable )) && (( ${#removable[*]} )); then
+ print_separator_removable
+ for devname in "${removable[@]}"; do
+ print_device
+ done
+ printf '\n'
+ fi
+ # List optical media.
+ if (( show_optical )) && (( ${#optical[*]} )); then
+ print_separator_optical
+ for devname in "${optical[@]}"; do
+ print_device optical
+ done
+ printf '\n'
+ fi
+ (( !device_number )) && printf '%s\n' "No devices."
+}
+
+submenu() {
+ # Make sure device is still valid.
+ check_device "$devname" || return 1
+
+ # Try to use a useful label to identify the device.
+ local label="$(info_fslabel "$devname")"
+ [[ -z "$label" ]] && label="$(info_partlabel "$devname")"
+ [[ -z "$label" ]] && label="-"
+
+ local fstype="$(info_fstype "$devname")"
+ local size="$(info_size "$devname")"
+ local used="$(info_used "$devname")"
+ local uuid="$(info_uuid "$devname")"
+
+ local -i mounted=0
+ check_mounted "$devname" && mounted=1
+
+ # Display the user interface.
+ clear
+ print_separator_device
+ printf '%s\n' " device : $devname"
+ printf '%s\n' " label : $label"
+ if [[ "$fstype" == "crypto_LUKS" ]]; then
+ printf '%s' " status : "
+ local -i unlocked=0
+ if [[ -n "$uuid" ]]; then
+ for dev in "${all[@]}"; do
+ if [[ "$dev" == "/dev/mapper/luks-$uuid" ]]; then
+ printf '%s\n' "${GREEN}decrypted [luks-${uuid:0:4}...]${ALL_OFF}"
+ unlocked=1
+ break
+ fi
+ done
+ fi
+ (( !unlocked )) && printf '%s\n' "encrypted"
+ else
+ printf '%s' " mounted : "
+ if (( mounted )); then
+ printf '%s\n' "${GREEN}yes${ALL_OFF}"
+ printf '%s\n' " mountpath : $(info_mountpath "$devname")"
+ else
+ printf '%s\n' "${RED}no${ALL_OFF}"
+ fi
+ fi
+ printf '%s\n' " fstype : $fstype"
+ printf '%s\n' " uuid : $uuid"
+ printf '%s\n' " size : $size"
+ (( mounted )) && printf '%s\n' " used : $used"
+ if (( show_commands == 1 )); then
+ printf '\n'
+ print_submenu_commands
+ fi
+ printf '\n'
+ print_separator
+
+ # Receive user input.
+ local -i retval
+ if ! read -r -e -p "${BOLD}Command:${ALL_OFF} " action; then
+ # Exit on ctrl-d.
+ printf '\n'
+ exit 0
+ fi
+ case "$action" in
+ "e") action_eject "$devname" || true;;
+ "i") action_info "$devname" || true;;
+ "m") action_mount "$devname" || true;;
+ "o") action_open "$devname" || true;;
+ "u") action_unmount "$devname" || true;;
+ "b") return 1;;
+ ""|"r") return 0;;
+ "q") exit;;
+ "?") print_help_submenu; return 0;;
+ "1") sudo sh -c "cryptsetup luksDump '$devname' | less"; return 0;;
+ "2") action_unlock "$devname"; return 1;;
+ "3") action_lock "$devname"; return 1;;
+ "4")
+ if (( custom4_show )); then
+ msg "Running custom command: $custom4_desc ..."
+ custom4_command "$devname"
+ enter_to_continue
+ else
+ invalid_command
+ fi
+ return 0;;
+ "5")
+ if (( custom5_show )); then
+ msg "Running custom command: $custom5_desc ..."
+ custom5_command "$devname"
+ enter_to_continue
+ else
+ invalid_command
+ fi
+ return 0;;
+ "6")
+ if (( custom6_show )); then
+ msg "Running custom command: $custom6_desc ..."
+ custom6_command "$devname"
+ enter_to_continue
+ else
+ invalid_command
+ fi
+ return 0;;
+ *) invalid_command; return 0;;
+ esac
+}
+
+select_action() {
+ local devname
+ local letter
+ print_separator
+ if ! read -r -e -p "${BOLD}Command:${ALL_OFF} " action; then
+ # Exit on ctrl-d.
+ printf '\n'
+ exit 0
+ fi
+ if [[ "$action" =~ ^[1-9] ]]; then
+ if [[ "$action" =~ ^[1-9][0-9]*$ ]]; then
+ # Zero-based numbering for array elements, so subtract one.
+ local number="$(( action - 1 ))"
+ if (( number >= device_number )); then
+ invalid_command
+ return 1
+ fi
+ devname=${listed[number]}
+ while true; do
+ submenu || break
+ done
+ elif [[ "$action" =~ ^[1-9][0-9]*[eimou]$ ]]; then
+ # Zero-based numbering for array elements, so subtract one.
+ local number="$(( ${action%?} - 1 ))"
+
+ if (( number >= device_number )); then
+ invalid_command
+ return 1
+ fi
+ devname="${listed[number]}"
+
+ letter="${action: -1}"
+ case "$letter" in
+ "e") action_eject "$devname";;
+ "i") action_info "$devname";;
+ "m") action_mount "$devname";;
+ "o") action_open "$devname";;
+ "u") action_unmount "$devname";;
+ *) return 1;;
+ esac
+ return 0
+ else
+ invalid_command
+ return 1
+ fi
+ else
+ case "$action" in
+ "a")
+ if (( ! ${#mounted[*]} )); then
+ error "No devices mounted."
+ return 1
+ fi
+ printf '\n'
+ read -r -e -p "Unmount all devices [y/N]?: " unmount
+ [[ "$unmount" != "y" ]] && [[ "$unmount" != "Y" ]] && return 0
+ clear
+ for devname in "${mounted[@]}"; do
+ action_unmount "$devname" || continue
+ done
+ enter_to_continue
+ return 1;;
+ "r"|"") return 0;;
+ "q"|"b") exit 0;;
+ "?") print_help; return 0;;
+ *) invalid_command; return 1;;
+ esac
+ fi
+}
+# }}}
+
+declare -i device_number=0
+declare -a all=()
+declare -a listed=()
+declare -a mounted=()
+
+while true; do
+ list_devices
+ (( show_commands )) && print_commands
+ select_action
+done
+