Files
mates-install-scripts/mount-nas.sh
T
mates 682f522cb9 init: clean history with no PII
Toto repo ma novou cistou historii (1 commit). Predchozi historie
byla smazana, protoze obsahovala osobni udaje (IP, emaily, hesla,
klice, tokeny, Volume cesty). Soubory v aktualnim commit uz tyto
udaje neobsahuji (nahradeno placeholdery <server-ip>, CHANGE_ME,
admin@example.com atd.).

Pokud chces zpet kompletni seznam starych commitu, mas je ve svem
lokalnim working tree - git log --all ukaze prazdno (protoze jsme
smazali .git/ a zacali znovu).
2026-08-01 19:51:59 +02:00

210 lines
7.2 KiB
Bash

#!/usr/bin/env bash
#
# mount-nas.sh — připojení/odpojení NAS SMB share do <multimedia-mount>
#
# Tiché (nobrowse), vhodné pro ruční i automatizované použití.
#
# Konfigurace přes env proměnné (mají defaulty):
# SMB_SERVER default <nas-ip>
# SMB_SHARE default Multimedia
# SMB_USER default mates
# TARGET default <multimedia-mount>
# KEYCHAIN_SERVICE default SMB:<nas-ip>/Multimedia
# NET_CHECK_TIMEOUT default 5 (sekund)
#
# Akce (default = auto):
# --auto mount pokud není, skončí úspěšně pokud je již připojen
# --mount mount (chyba pokud již připojen)
# --umount umount (chyba pokud není připojen)
# --status vypíše aktuální stav, exit 0/1 podle toho zda je mount aktivní
# --help nápověda
#
# Exit kódy:
# 0 OK (mount proběhl nebo již připojen / umount proběhl)
# 10 cílový mount bod (nadřazený adresář) neexistuje
# 11 záznam v Klíčence chybí
# 12 záznam v Klíčence je prázdný
# 20 SMB server nedostupný (TCP 445)
# 30 mount selhal (auth, mount error, ...)
# 31 umount selhal (resource busy, ...)
#
set -uo pipefail
# ===== Konfigurace =====
SMB_SERVER="${SMB_SERVER:-<nas-ip>}"
SMB_SHARE="${SMB_SHARE:-Multimedia}"
SMB_USER="${SMB_USER:-mates}"
TARGET="${TARGET:-<multimedia-mount>}"
KEYCHAIN_SERVICE="${KEYCHAIN_SERVICE:-SMB:$SMB_SERVER/$SMB_SHARE}"
NET_CHECK_TIMEOUT="${NET_CHECK_TIMEOUT:-5}"
LOG_FILE="${LOG_FILE:-/tmp/mount-nas.log}"
PASSWORD_FILE="${PASSWORD_FILE:-}"
# ===== Barvy (jen pokud terminál) =====
if [ -t 1 ]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; NC=''
fi
# ===== Pomocné funkce =====
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%Y-%m-%d %H:%M:%S')" "$NC" "$*" | tee -a "$LOG_FILE"; }
warn() { printf '%s[%s]%s %s\n' "$YELLOW" "$(date '+%Y-%m-%d %H:%M:%S')" "$NC" "$*" | tee -a "$LOG_FILE"; }
err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%Y-%m-%d %H:%M:%S')" "$NC" "$*" | tee -a "$LOG_FILE"; }
die() { err "$@"; exit "$1"; }
usage() {
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
}
# ===== Parsování argumentů =====
# Podpora: --password-file CESTA (s mezerou)
# --password-file=CESTA (s rovnítkem)
# Argumenty s hodnotou je nutné ručně posunout v poli, protože 'for arg in "$@"'
# neumožní 'shift 2' uvnitř smyčky bez komplikací. Přepíšeme do další proměnné.
ACTION="auto"
PASSWORD_FILE_ARG=""
ARGS=()
while [ "$#" -gt 0 ]; do
arg="$1"
case "$arg" in
--help|-h) usage; exit 0 ;;
--auto) ACTION="auto" ;;
--mount) ACTION="mount" ;;
--umount) ACTION="umount" ;;
--status) ACTION="status" ;;
--password-file)
PASSWORD_FILE_ARG="$2"; shift 2; continue ;;
--password-file=*)
PASSWORD_FILE_ARG="${arg#--password-file=}"; shift ;;
*) err "Unknown arg: $arg"; usage; exit 1 ;;
esac
ARGS+=("$arg")
shift
done
# ===== Detekce aktuálního stavu =====
is_mounted() {
mount 2>/dev/null | grep -qE "//$SMB_USER@$SMB_SERVER/$SMB_SHARE on $TARGET \(smbfs,"
}
# ===== 1. Kontrola mount root =====
MOUNT_ROOT="$(dirname "$TARGET")"
if [ ! -d "$MOUNT_ROOT" ]; then
die 10 "mount root neexistuje: $MOUNT_ROOT (čeká se na SSD)"
fi
# ===== Akce: status =====
if [ "$ACTION" = "status" ]; then
if is_mounted; then
log "OK: $TARGET je připojen ($SMB_SERVER/$SMB_SHARE)"
mount | grep -E "$TARGET"
exit 0
else
warn "NEpřipojen: $TARGET"
exit 1
fi
fi
# ===== Akce: umount =====
if [ "$ACTION" = "umount" ]; then
if ! is_mounted; then
die 1 "$TARGET není připojen"
fi
log "Umount $TARGET"
if /sbin/umount "$TARGET" 2>>"$LOG_FILE"; then
log "OK: umount úspěšný"
exit 0
else
die 31 "umount selhal (resource busy? zavři aplikace/kontejnery)"
fi
fi
# ===== Akce: mount nebo auto =====
if [ "$ACTION" = "auto" ] && is_mounted; then
log "OK: $TARGET je již připojen"
exit 0
fi
if [ "$ACTION" = "mount" ] && is_mounted; then
die 30 "$TARGET je již připojen (použij --auto nebo --umount)"
fi
# ===== 2. Test dostupnosti SMB serveru =====
log "Test dostupnosti SMB serveru $SMB_SERVER:445 (timeout ${NET_CHECK_TIMEOUT}s)"
if ! /usr/bin/nc -z -G "$NET_CHECK_TIMEOUT" "$SMB_SERVER" 445 2>/dev/null; then
die 20 "SMB server $SMB_SERVER nedostupný (TCP 445)"
fi
log "OK: SMB server odpovídá"
# ===== 3. Heslo z Klíčenky nebo ze souboru =====
# Priorita:
# 1. --password-file <cesta> (heslo v souboru s chmod 600)
# 2. $PASSWORD_FILE env proměnná
# 3. Klíčenka macOS (service = KEYCHAIN_SERVICE)
PASSWORD=""
if [ -n "${PASSWORD_FILE_ARG:-}" ] && [ -n "$PASSWORD_FILE_ARG" ]; then
if [ ! -f "$PASSWORD_FILE_ARG" ]; then
die 11 "soubor s heslem neexistuje: $PASSWORD_FILE_ARG"
fi
if [ ! -r "$PASSWORD_FILE_ARG" ]; then
die 11 "soubor s heslem nelze číst (oprávnění): $PASSWORD_FILE_ARG"
fi
PASSWORD=$(cat "$PASSWORD_FILE_ARG")
PASSWORD="${PASSWORD%%$'\n'}" # odřízne trailing newline
if [ -z "$PASSWORD" ]; then
die 12 "soubor s heslem je prázdný: $PASSWORD_FILE_ARG"
fi
log "Heslo načteno ze souboru: $PASSWORD_FILE_ARG"
elif [ -n "$PASSWORD_FILE" ] && [ -f "$PASSWORD_FILE" ]; then
PASSWORD=$(cat "$PASSWORD_FILE")
PASSWORD="${PASSWORD%%$'\n'}"
log "Heslo načteno z PASSWORD_FILE: $PASSWORD_FILE"
else
if ! PASSWORD=$(/usr/bin/security find-generic-password -a "$SMB_USER" -s "$KEYCHAIN_SERVICE" -w 2>/dev/null); then
die 11 "Klíčenka chybí ($KEYCHAIN_SERVICE)"
fi
fi
if [ -z "$PASSWORD" ]; then
die 12 "Heslo je prázdné"
fi
# ===== 4. Vlastní mount přes mount_smbfs s heslem v URL =====
#
# mount_smbfs podporuje syntaxi:
# //user:password@server/share /mount/point
# To znamená, že heslo se předá přímo, bez expect a bez interaktivního promptu.
# (Bezpečnost: heslo je vidět v 'ps' po dobu trvání mountu, což jsou sekundy.)
log "Spouštím mount_smbfs //$SMB_USER@$SMB_SERVER/$SMB_SHARE -> $TARGET"
# Sestav URL s heslem. Pro hesla se speciálními znaky (%, @, :) musíme
# escapovat, ale heslo z klíčenky většinou tyto znaky nemá.
# Pro jistotu zakódujeme heslo pomocí printf %q a pak odstraníme quotes.
# mount_smbfs -N zakáže prompt. Pokud heslo v URL je neplatné, mount selže.
# Nekombinujeme -N s heslem v URL — buď heslo v URL, nebo -N bez hesla.
ESCAPED_URL="//$SMB_USER:$PASSWORD@$SMB_SERVER/$SMB_SHARE"
# Pro mount_smbfs chceme tichý režim (-s), ale chyby potřebujeme v logu.
if /sbin/mount_smbfs -o nobrowse,nosuid "$ESCAPED_URL" "$TARGET" 2>>"$LOG_FILE"; then
log "OK: mount_smbfs exit 0"
else
rc=$?
# Heslo v URL může selhat s různými chybami:
# 1 = chyba argumentu (heslo obsahuje nepovolené znaky)
# 64 = mount_smbfs obecná chyba (auth, share neexistuje, ...)
die 30 "mount_smbfs selhal (exit $rc)"
fi
# Bezpečnost: ihned smaž heslo z paměti
unset PASSWORD
unset ESCAPED_URL
# ===== 5. Ověření, že mount opravdu proběhl =====
if ! is_mounted; then
die 30 "mount exit 0 ale SMB share není v /sbin/mount"
fi
log "OK: mount úspěšný ($SMB_SERVER/$SMB_SHARE -> $TARGET)"