Krolyxon
Commit 3235e63

Branches

Refs →

aurora.localbinmounter

90 lines ยท 3.6 KB ยท Bourne Again Shell (bash)

1#!/bin/sh
2# Menu to mount drives, LUKS partitions and Android phones, the standard way:
3# udisks mounts drives to /run/media/$USER/<label> (fstab entries to their fstab
4# target) and unlocks LUKS with a passphrase asked in aurora's runner; phones go
5# through gvfs (/run/user/$UID/gvfs/mtp:host=...). Root is asked via polkit. See unmounter.
6# `mounter DEV|mtp://URI` mounts that one without the menu (aurora's device notifications use it).
7
8menu() { aurora-run -d -p "$1"; }
9notify() { notify-send -a mounter "$@"; }
10# Lock a LUKS device we unlocked if we bail out before mounting it.
11cancel() { [ -n "$unlocked" ] && udisksctl lock -b "$unlocked" >/dev/null 2>&1; exit 1; }
12die() { notify -u critical "๐Ÿ’พ Mount failed" "$1"; cancel; }
13
14# "๐Ÿ’พ /dev/sdb1 32G LABEL (vfat)" per unmounted filesystem; LUKS only while locked
15# (an open one is listed as its mapper). jq instead of parsing lsblk: labels are arbitrary.
16drives() {
17 lsblk -Jpo NAME,TYPE,FSTYPE,SIZE,LABEL,MOUNTPOINT | jq -r '
18 .. | objects | select(has("name"))
19 | select((.type | test("^(part|disk|rom|crypt)$")) and .fstype != null and .mountpoint == null)
20 | select(.fstype | test("^(swap|LVM2_member|linux_raid_member)$") | not)
21 | select(.fstype != "crypto_LUKS" or ((.children // []) | length) == 0)
22 | (if .fstype == "crypto_LUKS" then "๐Ÿ”’" else "๐Ÿ’พ" end) as $icon
23 | "\($icon) \(.name) \(.size) \(if .label then .label + " " else "" end)(\(.fstype | sub("crypto_"; "")))"'
24}
25
26# "๐Ÿ“ฑ mtp://Google_Pixel_7_XXXX/ Pixel 7" per unmounted phone, from gvfs's volume list.
27phones() {
28 mounted="$(gio mount -l 2>/dev/null)"
29 gio mount -li 2>/dev/null | awk '
30 /^ *Volume\([0-9]+\): / { sub(/^ *Volume\([0-9]+\): /, ""); name = $0 }
31 /^ *activation_root=mtp:\/\// { sub(/^ *activation_root=/, ""); print $0 "\t" name }' |
32 while IFS=" " read -r uri name; do
33 printf '%s\n' "$mounted" | grep -qF -- "-> $uri" && continue
34 printf '๐Ÿ“ฑ %s %s\n' "$uri" "$name"
35 done
36}
37
38mountphone() {
39 uri="$1"
40 err="$(gio mount "$uri" 2>&1)" ||
41 die "$err
42Unlock the phone and set USB to File transfer."
43 notify "๐Ÿ“ฑ Phone mounted" "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/gvfs/mtp:host=$(echo "$uri" | sed 's|^mtp://||; s|/$||')"
44}
45
46mountdrive() {
47 dev="$1"
48
49 if [ "$(lsblk -dno FSTYPE "$dev")" = "crypto_LUKS" ]; then
50 pass="$(aurora-run -d --password --lines=0 --prompt="๐Ÿ”’" --placeholder="Passphrase for $dev" </dev/null)" || exit 1
51 out="$(printf '%s' "$pass" | udisksctl unlock -b "$dev" --key-file /dev/stdin 2>&1)" ||
52 die "Could not unlock $dev: $out"
53 unset pass
54 unlocked="$dev"
55 # "Unlocked /dev/sda2 as /dev/dm-3."
56 dev="$(echo "$out" | sed -n 's/.* as \(.*\)\.$/\1/p')"
57 udevadm settle 2>/dev/null
58 [ -n "$(lsblk -dno FSTYPE "$dev")" ] || die "$unlocked unlocked, but has no filesystem on it."
59 fi
60
61 # "Mounted /dev/sdb1 at /run/media/k/LABEL" (older udisks add a trailing dot)
62 out="$(udisksctl mount -b "$dev" 2>&1)" || die "$out"
63 mp="$(echo "$out" | sed -n 's/^Mounted .* at \(.*\)$/\1/p' | sed 's/\.$//')"
64
65 # Linux filesystems keep their owner: offer to take the root dir of the drive.
66 if [ -n "$mp" ] && [ ! -w "$mp" ]; then
67 owner="$(stat -c %U "$mp")"
68 [ "$(printf 'Yes\nNo' | menu "$mp is owned by $owner. Make it yours?")" = "Yes" ] &&
69 pkexec chown "$(id -u):$(id -g)" "$mp"
70 fi
71 notify "๐Ÿ’พ Mounted" "$dev โ†’ ${mp:-$out}"
72}
73
74if [ -n "$1" ]; then
75 case "$1" in
76 mtp://*) mountphone "$1" ;;
77 *) mountdrive "$1" ;;
78 esac
79 exit 0
80fi
81
82list="$(drives; phones)"
83[ -z "$list" ] && { notify "๐Ÿ’พ Mounter" "Nothing to mount."; exit 0; }
84
85chosen="$(echo "$list" | menu "Mount")" || exit 1
86target="$(echo "$chosen" | awk '{print $2}')"
87case "$chosen" in
88 "๐Ÿ“ฑ"*) mountphone "$target" ;;
89 *) mountdrive "$target" ;;
90esac