93fb6b6b37
x-systemd.automount in a unit file's Options= is only honored when read from /etc/fstab, so persisted mounts never armed an automount; the unit was also static (no [Install]) so enable was a no-op. Result: after a reboot the share sat inactive and the disk never mounted on access. persist now writes both <name>.mount and <name>.automount units (the automount with WantedBy=multi-user.target) and enables/starts the automount; unpersist stops/disables/removes both units.
218 lines
7.2 KiB
Bash
Executable File
218 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# POS: share smb-client — Mount SMB/CIFS shares (ephemeral or persistent systemd mount units)
|
|
|
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
|
source "$(dirname "$0")/../lib/notify.sh" 2>/dev/null || source "$(dirname "$0")/notify.sh"
|
|
|
|
SMB_CREDS_DIR="${SMB_CREDS_DIR:-/etc/samba/credentials}"
|
|
UNIT_DIR="${UNIT_DIR:-/etc/systemd/system}"
|
|
command -v mount.cifs &>/dev/null || err "mount.cifs not found (install cifs-utils)"
|
|
command -v systemd-escape &>/dev/null || err "systemd-escape not found"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: pos share smb-client <command> [args]
|
|
|
|
Mount and manage SMB/CIFS shares from remote servers (cifs-utils).
|
|
|
|
Commands:
|
|
mount <//server/share> <local-dir> [user] One-shot mount (creates local-dir if needed)
|
|
unmount <local-dir> Unmount the share
|
|
list Show active + persistent SMB mounts
|
|
persist <//server/share> <local-dir> [user] Persistent mount via systemd .mount + .automount
|
|
units (mounts on first access — never blocks boot)
|
|
unpersist <local-dir> Stop, disable and remove the mount/automount units
|
|
|
|
With no user, guest access is attempted. With a user, you are prompted for
|
|
the Samba password — one-shot mounts use a throwaway chmod-600 credentials
|
|
file; persistent mounts keep one at /etc/samba/credentials/.
|
|
|
|
Examples:
|
|
pos share smb-client mount //100.100.100.1/media /mnt/smb/media
|
|
pos share smb-client persist //100.100.100.1/media /mnt/smb/media bob
|
|
pos share smb-client list
|
|
pos share smb-client unmount /mnt/smb/media
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
cmd="${1:-}"
|
|
case "$cmd" in
|
|
-h|--help) usage ;;
|
|
mount|unmount|list|persist|unpersist) ;;
|
|
"") err "Missing command (mount|unmount|list|persist|unpersist)" ;;
|
|
*) err "Unknown command '$cmd' (see --help)" ;;
|
|
esac
|
|
|
|
validate_share() {
|
|
case "$1" in
|
|
//*) ;;
|
|
*) err "Invalid share '$1' — expected <//server/share> (e.g. //10.0.0.5/media)" ;;
|
|
esac
|
|
}
|
|
|
|
validate_dir() {
|
|
case "$1" in
|
|
/*) ;;
|
|
*) err "Mount point must be an absolute path: $1" ;;
|
|
esac
|
|
}
|
|
|
|
# Prompt for the Samba password (no echo, via /dev/tty) and write a fresh
|
|
# chmod-600 credentials file; prints its path, caller removes it.
|
|
make_creds() {
|
|
local user="$1" pw tmp
|
|
read -rsp "Samba password for $user: " pw </dev/tty || true
|
|
echo >&2
|
|
[ -n "$pw" ] || err "empty password"
|
|
tmp="$(mktemp)"
|
|
chmod 600 "$tmp"
|
|
printf 'username=%s\npassword=%s\n' "$user" "$pw" > "$tmp"
|
|
printf '%s' "$tmp"
|
|
}
|
|
|
|
mount_opts() {
|
|
local user="$1"
|
|
printf 'uid=%s,gid=%s' "$(id -u)" "$(id -g)"
|
|
}
|
|
|
|
case "$cmd" in
|
|
mount)
|
|
what="${2:-}"
|
|
where="${3:-}"
|
|
user="${4:-}"
|
|
[ -n "$what" ] && [ -n "$where" ] || err "Usage: pos share smb-client mount <//server/share> <local-dir> [user]"
|
|
validate_share "$what"
|
|
validate_dir "$where"
|
|
|
|
sudo mkdir -p "$where"
|
|
if [ -n "$user" ]; then
|
|
creds="$(make_creds "$user")"
|
|
trap 'rm -f "$creds"' EXIT
|
|
sudo mount -t cifs "$what" "$where" -o "credentials=$creds,$(mount_opts "$user")"
|
|
else
|
|
warn "No user — attempting guest mount (works only if the server allows guest access)"
|
|
sudo mount -t cifs "$what" "$where" -o "guest,$(mount_opts "")"
|
|
fi
|
|
log "Mounted $what at $where"
|
|
;;
|
|
|
|
unmount)
|
|
where="${2:-}"
|
|
[ -n "$where" ] || err "Usage: pos share smb-client unmount <local-dir>"
|
|
validate_dir "$where"
|
|
|
|
sudo umount "$where"
|
|
log "Unmounted $where"
|
|
;;
|
|
|
|
list)
|
|
found=0
|
|
active_mounts="$(findmnt -t cifs 2>/dev/null || true)"
|
|
if [ -n "$active_mounts" ]; then
|
|
printf 'Active mounts:\n'
|
|
printf '%s\n' "$active_mounts"
|
|
found=1
|
|
fi
|
|
persistent=()
|
|
for unit in "${UNIT_DIR}"/*.mount; do
|
|
[ -e "$unit" ] || continue
|
|
grep -q '^Type=cifs$' "$unit" || continue
|
|
what="$(sed -n 's/^What=//p' "$unit")"
|
|
where="$(sed -n 's/^Where=//p' "$unit")"
|
|
[ -n "$what" ] && [ -n "$where" ] || continue
|
|
persistent+=("$where|$what")
|
|
done
|
|
if [ "${#persistent[@]}" -gt 0 ]; then
|
|
found=1
|
|
printf 'Persistent (automount):\n'
|
|
for entry in "${persistent[@]}"; do
|
|
printf ' %-44s %s\n' "${entry%%|*}" "${entry#*|}"
|
|
done
|
|
fi
|
|
[ "$found" -eq 1 ] || echo "No SMB mounts"
|
|
;;
|
|
|
|
persist)
|
|
what="${2:-}"
|
|
where="${3:-}"
|
|
user="${4:-}"
|
|
[ -n "$what" ] && [ -n "$where" ] || err "Usage: pos share smb-client persist <//server/share> <local-dir> [user]"
|
|
validate_share "$what"
|
|
validate_dir "$where"
|
|
|
|
unit="$(systemd-escape --path --suffix=mount "$where")"
|
|
auto_unit="${unit%.mount}.automount"
|
|
unit_file="${UNIT_DIR}/${unit}"
|
|
auto_file="${UNIT_DIR}/${auto_unit}"
|
|
opts="$(mount_opts "$user"),_netdev,noexec"
|
|
if [ -n "$user" ]; then
|
|
creds_file="$SMB_CREDS_DIR/$(basename "$where")"
|
|
sudo mkdir -p "$SMB_CREDS_DIR"
|
|
tmp="$(make_creds "$user")"
|
|
sudo install -m 600 "$tmp" "$creds_file"
|
|
rm -f "$tmp"
|
|
opts="credentials=$creds_file,$opts"
|
|
else
|
|
warn "No user — persisting a guest mount (works only if the server allows guest access)"
|
|
opts="guest,$opts"
|
|
fi
|
|
|
|
sudo mkdir -p "$where"
|
|
cat <<UNIT | sudo tee "$unit_file" >/dev/null
|
|
[Unit]
|
|
Description=SMB mount of ${what} at ${where}
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Mount]
|
|
What=${what}
|
|
Where=${where}
|
|
Type=cifs
|
|
Options=${opts}
|
|
UNIT
|
|
cat <<UNIT | sudo tee "$auto_file" >/dev/null
|
|
[Unit]
|
|
Description=Automount of SMB share ${what} at ${where}
|
|
|
|
[Automount]
|
|
Where=${where}
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
UNIT
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now "$auto_unit"
|
|
log "Persistent SMB mount (automount): ${what} → ${where} (${auto_unit})"
|
|
notify_send "SMB mount persisted: ${what} → ${where}"
|
|
;;
|
|
|
|
unpersist)
|
|
where="${2:-}"
|
|
[ -n "$where" ] || err "Usage: pos share smb-client unpersist <local-dir>"
|
|
validate_dir "$where"
|
|
|
|
unit="$(systemd-escape --path --suffix=mount "$where")"
|
|
auto_unit="${unit%.mount}.automount"
|
|
unit_file="${UNIT_DIR}/${unit}"
|
|
auto_file="${UNIT_DIR}/${auto_unit}"
|
|
|
|
if [ ! -f "$unit_file" ] && [ ! -f "$auto_file" ]; then
|
|
warn "No systemd mount unit for $where (${unit})"
|
|
exit 0
|
|
fi
|
|
|
|
sudo systemctl disable "$auto_unit" 2>/dev/null || true
|
|
sudo systemctl stop "$auto_unit" 2>/dev/null || true
|
|
sudo systemctl disable "$unit" 2>/dev/null || true
|
|
sudo systemctl stop "$unit" 2>/dev/null || true
|
|
sudo rm -f "$unit_file" "$auto_file"
|
|
sudo rm -f "$SMB_CREDS_DIR/$(basename "$where")"
|
|
sudo rmdir "$SMB_CREDS_DIR" 2>/dev/null || true
|
|
sudo systemctl daemon-reload
|
|
log "Removed persistent SMB mount: $where"
|
|
notify_send "SMB persistent mount removed: $where"
|
|
;;
|
|
esac
|