#!/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 < [args] Mount and manage SMB/CIFS shares from remote servers (cifs-utils). Commands: mount [user] One-shot mount (creates local-dir if needed) unmount Unmount the share list Show active + persistent SMB mounts persist [user] Persistent mount via systemd .mount + .automount units (mounts on first access — never blocks boot) unpersist 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 (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 &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 [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 " 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 [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 </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 </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 " 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