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).
210 lines
7.2 KiB
Bash
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)" |