diff options
| author | Lukasz Kasprzak <lukasz.kasprzak@pm.me> | 2026-04-13 17:37:26 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukasz.kasprzak@pm.me> | 2026-04-13 17:37:26 +0200 |
| commit | 51d43498b07dc97d795947964534f0903cd05db5 (patch) | |
| tree | 735712bd50b5c244ef922a3b873c709ecce604cd | |
| parent | 39711cf6c2ec5a3b4480dcb4800cc3802bda5bf2 (diff) | |
| download | bin-51d43498b07dc97d795947964534f0903cd05db5.tar.gz bin-51d43498b07dc97d795947964534f0903cd05db5.zip | |
routine backup
| -rwxr-xr-x | bookmarkthis | 53 | ||||
| -rwxr-xr-x | dino | 2 | ||||
| -rwxr-xr-x | electrum | 3 | ||||
| -rw-r--r-- | env | 11 | ||||
| -rw-r--r-- | env.fish | 4 | ||||
| -rwxr-xr-x | feh | 3 | ||||
| -rwxr-xr-x | firefox | 4 | ||||
| -rwxr-xr-x | firefox-raw | 2 | ||||
| l--------- | hledger-web | 1 | ||||
| -rwxr-xr-x | monero | 3 | ||||
| -rwxr-xr-x | mutt-pdf | 5 | ||||
| -rwxr-xr-x | piecash | 8 | ||||
| -rwxr-xr-x | profanity-aesgcm-handler | 113 | ||||
| -rwxr-xr-x | profanity-aesgcm-watch | 45 | ||||
| -rwxr-xr-x | shield-backup | 51 | ||||
| -rw-r--r-- | sorter/rules.toml | 13 | ||||
| -rw-r--r-- | sorter/sort_downloads.py | 5 | ||||
| -rwxr-xr-x | ssh-tunnel-all | 31 | ||||
| -rwxr-xr-x | ssh-tunnel-all-stop | 8 | ||||
| -rwxr-xr-x | thunderbird | 2 | ||||
| -rwxr-xr-x | tor-browser | 2 | ||||
| -rwxr-xr-x | typebookmarks | 16 | ||||
| -rwxr-xr-x | usb-backup | 161 | ||||
| -rwxr-xr-x | uv | bin | 0 -> 35891072 bytes | |||
| -rwxr-xr-x | uvx | bin | 0 -> 358696 bytes | |||
| -rwxr-xr-x | zathura | 3 |
26 files changed, 420 insertions, 129 deletions
diff --git a/bookmarkthis b/bookmarkthis index 22aff66..40f299e 100755 --- a/bookmarkthis +++ b/bookmarkthis @@ -5,57 +5,36 @@ file="$HOME/.snippets" mkdir -p "$(dirname "$file")" touch "$file" -# wybierz JEDNO: -bookmark="$(xclip -o -selection primary 2>/dev/null || true)" # zaznaczenie myszką -# bookmark="$(xclip -o -selection clipboard 2>/dev/null || true)" # Ctrl+C +# Choose ONE source: +bookmark="$(xclip -o -selection primary 2>/dev/null || true)" # mouse highlight +# bookmark="$(xclip -o -selection clipboard 2>/dev/null || true)" # Ctrl+C clipboard -# usuń końcowe nowe linie (częste przy kopiowaniu) -bookmark="$(printf '%s' "$bookmark" | sed -e 's/[[:space:]]\+$//')" +# Strip trailing whitespace/newlines (common when copying) +bookmark="$(printf '%s' "$bookmark" | sed 's/[[:space:]]\+$//')" if [ -z "$bookmark" ]; then notify-send "No string highlighted!" "Please select/copy a string to bookmark" exit 1 fi -# literalne dopasowanie całej linii -if grep -Fxq -- "$bookmark" "$file"; then +# Duplicate check: strip tab-separated comments before comparing +if awk -F'\t' '{print $1}' "$file" | grep -Fxq -- "$bookmark"; then notify-send "Oops" "Already Bookmarked!" exit 0 fi -# edycja w dmenu: domyślnie pokazuje już wartość +# Optionally edit the bookmark text before saving bookmarkEdit="$(printf '%s' "$bookmark" | dmenu -p "Edit bookmark:")" +out="${bookmarkEdit:-$bookmark}" -# jeśli ESC albo pusto -> użyj oryginału -if [ -z "${bookmarkEdit:-}" ]; then - out="$bookmark" +# Optionally add a comment (shown in dmenu search, not pasted) +comment="$(printf '' | dmenu -p "Add comment (optional):")" + +if [ -n "${comment:-}" ]; then + line="$(printf '%s\t# %s' "$out" "$comment")" else - out="$bookmarkEdit" + line="$out" fi -# zapisz jako nową linię -printf '%s\n' "$out" >> "$file" +printf '%s\n' "$line" >> "$file" notify-send "Bookmark added!" "$out is now saved to the file" - -# bookmark="$(xclip -o)" -# file="${HOME}/.snippets" -# -# if [ -z "$bookmark" ] -# then -# notify-send "No string highlighted!" "Please select a string to bookmark" -# exit 1 -# else -# if grep -q "^$bookmark$" "$file"; then -# notify-send "Oops" "Already Bookmarked!" -# else -# bookmarkEdit=$(:|dmenu -p "Make changes to the bookmark: $bookmark" & sleep 0.1 && xdotool type $bookmark) -# if [ -z "$bookmarkEdit" ] -# then -# notify-send "Bookmark added!" "$bookmark is now saved to the file" -# echo "$bookmark" >> $file -# else -# notify-send "Bookmark added!" "$bookmarkEdit is now saved to the file" -# echo "$bookmarkEdit" >> "$file" -# fi -# fi -# fi @@ -0,0 +1,2 @@ +#!/bin/sh +exec env GTK_THEME=Adwaita:dark /usr/bin/dino "$@" diff --git a/electrum b/electrum new file mode 100755 index 0000000..9fc63bc --- /dev/null +++ b/electrum @@ -0,0 +1,3 @@ +#!/bin/sh +cd "$HOME/opt/Electrum-4.7.0" || exit 1 +exec ./run_electrum @@ -0,0 +1,11 @@ +#!/bin/sh +# add binaries to PATH if they aren't added yet +# affix colons on either side of $PATH to simplify matching +case ":${PATH}:" in + *:"$HOME/.local/share/../bin":*) + ;; + *) + # Prepending path in case a system-installed binary needs to be overridden + export PATH="$HOME/.local/share/../bin:$PATH" + ;; +esac diff --git a/env.fish b/env.fish new file mode 100644 index 0000000..6e5c4a8 --- /dev/null +++ b/env.fish @@ -0,0 +1,4 @@ +if not contains "$HOME/.local/share/../bin" $PATH + # Prepending path in case a system-installed binary needs to be overridden + set -x PATH "$HOME/.local/share/../bin" $PATH +end @@ -1,3 +0,0 @@ -#!/bin/sh -exec firejail /usr/bin/feh "$@" - diff --git a/firefox b/firefox deleted file mode 100755 index 20dac09..0000000 --- a/firefox +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -exec firejail --profile=/etc/firejail/firefox-esr.profile \ - /usr/bin/firefox-esr --no-remote "$@" - diff --git a/firefox-raw b/firefox-raw deleted file mode 100755 index e29362a..0000000 --- a/firefox-raw +++ /dev/null @@ -1,2 +0,0 @@ -!/bin/sh -exec /usr/bin/firefox-esr --no-remote "$@" diff --git a/hledger-web b/hledger-web new file mode 120000 index 0000000..98dfc41 --- /dev/null +++ b/hledger-web @@ -0,0 +1 @@ +../state/cabal/store/ghc-9.6.6/hledger-web-1.52-e-hledger-web-f99d9bf291aa377724b7120cc87a3823a2f96994ead3e9140765ba87d007e3df/bin/hledger-web
\ No newline at end of file @@ -0,0 +1,3 @@ +#!/bin/sh +cd "$HOME/opt/monero-gui-v0.18.4.5" || exit 1 +exec ./monero-wallet-gui @@ -1,5 +1,2 @@ #!/bin/sh -mkdir -p ~/Downloads/mail -dest=~/Downloads/mail/mutt_$$.pdf -cp "$1" "$dest" -zathura "$dest" +exec zathura "$1" @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from piecash.scripts.export import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli()) diff --git a/profanity-aesgcm-handler b/profanity-aesgcm-handler new file mode 100755 index 0000000..0ea5e19 --- /dev/null +++ b/profanity-aesgcm-handler @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +import os +import re +import sys +import urllib.parse +from pathlib import Path + +import requests +from cryptography.hazmat.primitives.ciphers.aead import AESGCM + + +home = Path.home() +if (home / "downloads").exists(): + DOWNLOAD_DIR = home / "downloads" / "xmpp" + DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True) +else: + DOWNLOAD_DIR = home / "Downloads" / "xmpp" + DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True) + +def log(msg: str) -> None: + log_file = Path.home() / ".local" / "share" / "profanity-aesgcm" / "handler.log" + log_file.parent.mkdir(parents=True, exist_ok=True) + with log_file.open("a", encoding="utf-8") as f: + f.write(msg + "\n") + + +def extract_uri(message: str) -> str | None: + m = re.search(r'(aesgcm://\S+)', message) + return m.group(1) if m else None + + +def parse_aesgcm_uri(uri: str): + parsed = urllib.parse.urlparse(uri) + + if parsed.scheme != "aesgcm": + raise ValueError("Unsupported scheme") + + fragment = parsed.fragment.strip() + frag_hex = "".join(fragment.split()) # remove accidental spaces/newlines + + # XEP-0454 style: 12-byte IV (24 hex chars) + 32-byte key (64 hex chars) + if len(frag_hex) < 88: + raise ValueError(f"Fragment too short: {len(frag_hex)} hex chars") + + iv_hex = frag_hex[:24] + key_hex = frag_hex[24:88] + + iv = bytes.fromhex(iv_hex) + key = bytes.fromhex(key_hex) + + https_url = urllib.parse.urlunparse(("https", parsed.netloc, parsed.path, "", "", "")) + filename = os.path.basename(parsed.path) or "download.bin" + + return https_url, filename, key, iv + + +def download_file(url: str) -> bytes: + r = requests.get(url, timeout=60) + r.raise_for_status() + return r.content + + +def decrypt_xmpp_aesgcm(data: bytes, key: bytes, iv: bytes) -> bytes: + # AESGCM.decrypt expects ciphertext || tag + aesgcm = AESGCM(key) + return aesgcm.decrypt(iv, data, None) + + +def unique_path(path: Path) -> Path: + if not path.exists(): + return path + + stem = path.stem + suffix = path.suffix + + for i in range(1, 1000): + candidate = path.with_name(f"{stem}-{i}{suffix}") + if not candidate.exists(): + return candidate + + raise RuntimeError("Could not create unique filename") + +def main(): + if len(sys.argv) < 2: + sys.exit(0) + + message = sys.argv[1] + uri = extract_uri(message) + if not uri: + sys.exit(0) + + try: + https_url, filename, key, iv = parse_aesgcm_uri(uri) + enc = download_file(https_url) + plain = decrypt_xmpp_aesgcm(enc, key, iv) + + out_path = unique_path(DOWNLOAD_DIR / filename) + out_path.write_bytes(plain) + + log(f"OK saved: {out_path}") + print(str(out_path)) + + # Optional: uncomment if you want auto-open on desktop + # os.system(f'xdg-open "{out_path}" >/dev/null 2>&1 &') + + except Exception as e: + log(f"ERROR: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/profanity-aesgcm-watch b/profanity-aesgcm-watch new file mode 100755 index 0000000..f4a23f9 --- /dev/null +++ b/profanity-aesgcm-watch @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import re +import subprocess +import sys +import time +from pathlib import Path + +URI_RE = re.compile(r'aesgcm://[^\s<>"\']+') + +if len(sys.argv) != 2: + print("usage: profanity-aesgcm-watch /path/to/chat.log", file=sys.stderr) + sys.exit(1) + +log_path = Path(sys.argv[1]) +if not log_path.exists(): + print(f"log file not found: {log_path}", file=sys.stderr) + sys.exit(1) + +seen = set() + +with log_path.open("r", encoding="utf-8", errors="replace") as f: + f.seek(0, 2) # start at end + + while True: + line = f.readline() + if not line: + time.sleep(0.5) + continue + + m = URI_RE.search(line) + if not m: + continue + + uri = m.group(0) + if uri in seen: + continue + + seen.add(uri) + + subprocess.run( + [str(Path.home() / ".local/bin/profanity-aesgcm-handler"), uri], + check=False, + ) + diff --git a/shield-backup b/shield-backup index 96f9269..05c8945 100755 --- a/shield-backup +++ b/shield-backup @@ -13,7 +13,6 @@ LUKS_NAME="shield" LUKS_MOUNTPOINT="/mnt/shield" # Local folders to back up with rsync → LUKS volume -# Each folder will be mirrored into ${LUKS_MOUNTPOINT}/<basename-of-folder> BACKUP_ITEMS=( "$HOME/" "/media/.secrets/" @@ -24,12 +23,49 @@ MACHINE_DIR="T480" #################### INTERNALS – NO NEED TO EDIT ############ +RSYNC_EXCLUDES_FILE="/tmp/rsync_excludes_$$.txt" + +cat > "${RSYNC_EXCLUDES_FILE}" <<'EOF' +# Caches & regenerable state +.cache/ +.fasd +.viminfo +.zcompdump +.sqlite_history +.wget-hsts +.dbus/ +.Xauthority +.pki/ +tmp/ + +# Browser internals +.mozilla/firefox/*/cache2/ +.mozilla/firefox/*/storage/ +.mozilla/firefox/*/datareporting/ +.mullvad-browser/*/cache2/ +.mullvad-browser/*/storage/ + +# Crypto node data (re-syncs) +.bitmonero/ +.shared-ringdb/ + +# Large reinstallable tooling +.cargo/registry +.cargo/git +.texlive2025/ + +# Java runtime junk +.java/ +EOF + need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing command: $1" >&2; exit 1; }; } cleanup() { echo echo ">>> Cleaning up..." + rm -f "${RSYNC_EXCLUDES_FILE}" + # Try to unmount, if mounted if mountpoint -q "${LUKS_MOUNTPOINT}"; then echo ">>> Unmounting ${LUKS_MOUNTPOINT}..." @@ -86,9 +122,9 @@ sudo mkdir -p \ "${LUKS_MOUNTPOINT}/SP_Skyscale" echo ">>> Starting rclone syncs (SharePoint -> ${LUKS_MOUNTPOINT}/SP_*)..." -rclone sync 'SP_Administration:' "${LUKS_MOUNTPOINT}/SP_Administration" --create-empty-src-dirs --fast-list --progress -rclone sync 'SP_Operations:' "${LUKS_MOUNTPOINT}/SP_Operations" --create-empty-src-dirs --fast-list --progress -rclone sync 'SP_SkyscaleCommerce:' "${LUKS_MOUNTPOINT}/SP_Skyscale" --create-empty-src-dirs --fast-list --progress +rclone sync 'SP_Administration:' "${LUKS_MOUNTPOINT}/SP_Administration" --create-empty-src-dirs --fast-list --progress +rclone sync 'SP_Operations:' "${LUKS_MOUNTPOINT}/SP_Operations" --create-empty-src-dirs --fast-list --progress +rclone sync 'SP_SkyscaleCommerce:' "${LUKS_MOUNTPOINT}/SP_Skyscale" --create-empty-src-dirs --fast-list --progress echo ">>> All rclone syncs finished." echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHINE_DIR}/..." @@ -96,9 +132,6 @@ echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHIN # Ensure machine root exists sudo mkdir -p "${LUKS_MOUNTPOINT}/${MACHINE_DIR}" -# Explicit destination mapping while keeping your BACKUP_ITEMS sources as-is -# - "$HOME/" -> /mnt/shield/T480/HOME -# - "/media/.secrets/"-> /mnt/shield/T480/secrets for SRC in "${BACKUP_ITEMS[@]}"; do if [ ! -d "$SRC" ]; then echo ">>> WARNING: source folder does not exist, skipping: $SRC" @@ -113,8 +146,6 @@ for SRC in "${BACKUP_ITEMS[@]}"; do RELDEST="${MACHINE_DIR}/secrets" ;; *) - # Fallback (shouldn't happen with your current BACKUP_ITEMS) - # Note: basename "$SRC" with trailing slash becomes "home"; avoid relying on it. NAME="$(basename "${SRC%/}")" RELDEST="${MACHINE_DIR}/${NAME}" ;; @@ -125,6 +156,7 @@ for SRC in "${BACKUP_ITEMS[@]}"; do echo ">>> rsync: ${SRC} -> ${DEST}" rsync -a --delete --progress \ + --exclude-from="${RSYNC_EXCLUDES_FILE}" \ "${SRC%/}/" \ "${DEST}/" done @@ -132,4 +164,3 @@ done echo ">>> All rsync backups finished." echo ">>> Unmounting and closing LUKS (via trap)..." # cleanup() will run automatically on script exit - diff --git a/sorter/rules.toml b/sorter/rules.toml index 0c0a7ac..ae1cbfd 100644 --- a/sorter/rules.toml +++ b/sorter/rules.toml @@ -29,6 +29,15 @@ supported_extensions = [ ".xls", ".md", ".odt", + ".sh", + ".py", + ".js", + ".ts", + ".html", + ".css", + ".json", + ".yaml", + ".toml", ] # ── Categories ──────────────────────────────────────────────────────────────── @@ -79,6 +88,10 @@ filename_keywords = [ "akw", ] +[[categories]] +name = "Code" +description = "Scripts and source code files" +filename_keywords = [".sh", ".py", ".js", ".ts", ".html", ".css", ".json", ".yaml", ".toml"] # ── Add more categories below ───────────────────────────────────────────────── # [[categories]] diff --git a/sorter/sort_downloads.py b/sorter/sort_downloads.py index 10cc696..1ced5e3 100644 --- a/sorter/sort_downloads.py +++ b/sorter/sort_downloads.py @@ -215,11 +215,11 @@ def main(): sys.exit(1) parser = argparse.ArgumentParser( - description="Sort ~/Downloads using rules defined in rules.toml" + description="Sort ~/downloads using rules defined in rules.toml" ) parser.add_argument( "--dir", - default=str(Path.home() / "Downloads"), + default=str(Path.home() / "downloads"), help="Path to Downloads folder (default: ~/Downloads)", ) parser.add_argument( @@ -246,6 +246,7 @@ def main(): extensions = set(config.get("supported_extensions", [ ".pdf", ".docx", ".doc", ".txt", ".xlsx", ".xls", ".md", ".odt" + ".sh", ".py", ".js", ".ts", ".html", ".css", ".json", ".yaml", ".toml" ])) if args.list_rules: diff --git a/ssh-tunnel-all b/ssh-tunnel-all new file mode 100755 index 0000000..9c35202 --- /dev/null +++ b/ssh-tunnel-all @@ -0,0 +1,31 @@ +#!/usr/bin/env zsh +set -euo pipefail + +SOCK="${XDG_RUNTIME_DIR:-/tmp}/sanctum-all.sock" +HOST="lukasz@192.168.33.5" +PORT="57385" + +if [[ -S "$SOCK" ]]; then + if ssh -S "$SOCK" -O check -p "$PORT" "$HOST" >/dev/null 2>&1; then + echo "Tunnel already running" + exit 0 + else + rm -f "$SOCK" + fi +fi + +ssh -fN \ + -M \ + -S "$SOCK" \ + -p "$PORT" \ + -L 127.0.0.1:18080:127.0.0.1:8080 \ + -L 127.0.0.1:8502:127.0.0.1:8502 \ + -L 127.0.0.1:5055:127.0.0.1:5055 \ + -L 127.0.0.1:8000:127.0.0.1:8000 \ + -L 127.0.0.1:4444:127.0.0.1:4444 \ + -L 127.0.0.1:7070:127.0.0.1:7070 \ + -L 127.0.0.1:7659:127.0.0.1:7659 \ + -L 127.0.0.1:7660:127.0.0.1:7660 \ + "$HOST" + +echo "Tunnel started" diff --git a/ssh-tunnel-all-stop b/ssh-tunnel-all-stop new file mode 100755 index 0000000..15088cd --- /dev/null +++ b/ssh-tunnel-all-stop @@ -0,0 +1,8 @@ +#!/usr/bin/env zsh +set -euo pipefail + +SOCK="${XDG_RUNTIME_DIR:-/tmp}/sanctum-all.sock" +HOST="lukasz@192.168.33.5" +PORT="57385" + +ssh -S "$SOCK" -O exit -p "$PORT" "$HOST" diff --git a/thunderbird b/thunderbird deleted file mode 100755 index 9a764e1..0000000 --- a/thunderbird +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec firejail /usr/bin/thunderbird "$@" diff --git a/tor-browser b/tor-browser new file mode 100755 index 0000000..d8c20b2 --- /dev/null +++ b/tor-browser @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$HOME/opt/tor-browser/Browser/start-tor-browser" --detach diff --git a/typebookmarks b/typebookmarks index 559075a..a0bcab7 100755 --- a/typebookmarks +++ b/typebookmarks @@ -1,5 +1,15 @@ -#! /bin/sh +#!/bin/sh +# Bind this script to a shortcut key in dwm config.h +# Lines starting with # are section headers and hidden from results +# Tab-separated comments are shown in dmenu for context but stripped before pasting -# map this script as a shortcut key for dwm in config.h +xdotool type "$(grep -v '^#' "$HOME/.snippets" | dmenu -i -l 50 | sed 's/\t.*//')" +``` -xdotool type "$(grep -v '^#' ${HOME}/.snippets | dmenu -i -l 50 | sed 's/\s*#.*//')" +--- + +In `.snippets` manually edited it looks like this (tab between bookmark and `# comment`): +``` +# --- git --- +git log --oneline --graph --all # pretty git tree +git reset --soft HEAD~1 # undo last commit @@ -3,29 +3,36 @@ set -euo pipefail #################### CONFIG #################### -# LUKS2 container UUID (LUKS header UUID) LUKS_SOURCE="/dev/disk/by-uuid/bdd2a016-b6db-493d-ad0a-e8dd8a93dfbd" - -# Name for /dev/mapper/<name> MAPPER_NAME="usb_safe" - -# Mountpoint for decrypted filesystem MOUNTPOINT="/mnt/usb_safe" - -# Optional underlying block device for power-off (set "" to disable) RAW_DEVICE="" -# Items to mirror from $HOME to the encrypted drive (files and directories) +# Source vault (docs/vault) +SOURCE_VAULT_MOUNT="$HOME/docs/vault" +SOURCE_VAULT_IMAGE="$HOME/secure/containers/work-vault.img" +SOURCE_VAULT_MAPPER="vault" + +# Tomb +SECRETS_TOMB_FILE="$HOME/.secrets.tomb" +SECRETS_TOMB_KEY="$HOME/.secrets.tomb.key" +SECRETS_TOMB_MOUNT="/media/.secrets" + BACKUP_ITEMS=( "$HOME/.abook" "$HOME/.config" - "$HOME/Documents" - "$HOME/dotfiles" + "$HOME/docs" + "$HOME/git" "$HOME/.password-store" "$HOME/secure" - "$HOME/vimwiki" "$HOME/.password.tomb" + "$HOME/.password.tomb.key" "$HOME/.secrets.tomb" + "$HOME/.secrets.tomb.key" + "/media/.secrets" + "$HOME/.bitmonero" + "$HOME/.electrum" + "$HOME/.zotero-data" ) RSYNC_OPTS=( @@ -48,108 +55,144 @@ die() { echo "ERROR: $*" >&2; exit 1; } is_mapper_open() { [[ -e "/dev/mapper/${MAPPER_NAME}" ]]; } is_mounted() { mountpoint -q "${MOUNTPOINT}"; } -busy_report() { - echo ">>> Busy report for ${MOUNTPOINT}:" - echo ">>> lsof:" - sudo lsof +f -- "${MOUNTPOINT}" || true - echo ">>> fuser:" - sudo fuser -vm "${MOUNTPOINT}" || true +require_mount() { + local path="$1" + local label="$2" + [[ -e "$path" ]] || die "${label} path missing: ${path}" + mountpoint -q "$path" || die "${label} is not mounted: ${path}" +} + +OPENED_SOURCE_VAULT=0 +OPENED_SECRETS_TOMB=0 + +ensure_source_vault_open() { + if mountpoint -q "${SOURCE_VAULT_MOUNT}"; then + echo ">>> Source vault already mounted" + return + fi + + echo ">>> Opening source vault..." + mkdir -p "${SOURCE_VAULT_MOUNT}" + + if [[ ! -e "/dev/mapper/${SOURCE_VAULT_MAPPER}" ]]; then + sudo cryptsetup open "${SOURCE_VAULT_IMAGE}" "${SOURCE_VAULT_MAPPER}" + fi + + sudo mount "/dev/mapper/${SOURCE_VAULT_MAPPER}" "${SOURCE_VAULT_MOUNT}" + OPENED_SOURCE_VAULT=1 +} + +ensure_secrets_tomb_open() { + if mountpoint -q "${SECRETS_TOMB_MOUNT}"; then + echo ">>> Secrets tomb already mounted" + return + fi + + echo ">>> Opening secrets tomb..." + tomb open "${SECRETS_TOMB_FILE}" -k "${SECRETS_TOMB_KEY}" "${SECRETS_TOMB_MOUNT}" + OPENED_SECRETS_TOMB=1 } cleanup() { set +e if is_mounted; then - echo ">>> Unmounting ${MOUNTPOINT}..." - sudo umount "${MOUNTPOINT}" || { - echo ">>> Unmount failed (device busy)." - busy_report - echo ">>> Last resort: sudo umount -l '${MOUNTPOINT}'" - } + echo ">>> Unmounting backup drive..." + sudo umount "${MOUNTPOINT}" || true fi if is_mapper_open; then - echo ">>> Closing LUKS mapper ${MAPPER_NAME}..." + echo ">>> Closing backup mapper..." sudo cryptsetup close "${MAPPER_NAME}" || true fi + if [[ "${OPENED_SECRETS_TOMB}" -eq 1 ]]; then + echo ">>> Closing secrets tomb..." + tomb close "${SECRETS_TOMB_MOUNT}" || true + fi + + if [[ "${OPENED_SOURCE_VAULT}" -eq 1 ]]; then + echo ">>> Closing source vault..." + sudo umount "${SOURCE_VAULT_MOUNT}" || true + sudo cryptsetup close "${SOURCE_VAULT_MAPPER}" || true + fi + if [[ -n "${RAW_DEVICE}" ]]; then - echo ">>> Powering off ${RAW_DEVICE}..." udisksctl power-off -b "${RAW_DEVICE}" >/dev/null 2>&1 || true fi } trap cleanup EXIT -#################### OPEN + MOUNT #################### +#################### OPEN DESTINATION #################### -echo ">>> Checking LUKS source exists: ${LUKS_SOURCE}" -[[ -e "${LUKS_SOURCE}" ]] || die "LUKS source not found: ${LUKS_SOURCE}" - -echo ">>> Ensuring mountpoint exists: ${MOUNTPOINT}" +echo ">>> Ensuring backup mountpoint exists..." sudo mkdir -p "${MOUNTPOINT}" -is_mounted && die "Mountpoint already in use: ${MOUNTPOINT}" -is_mapper_open && die "Mapper already open: /dev/mapper/${MAPPER_NAME}" +if is_mounted; then + echo ">>> Backup drive already mounted at ${MOUNTPOINT}" +elif is_mapper_open; then + echo ">>> Backup mapper already open, mounting..." + sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}" +else + echo ">>> Opening backup LUKS..." + sudo cryptsetup open "${LUKS_SOURCE}" "${MAPPER_NAME}" + echo ">>> Mounting backup filesystem..." + sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}" +fi + +#################### OPEN SOURCES #################### -echo ">>> Opening LUKS2 container (prompt for passphrase)..." -sudo cryptsetup open --type luks2 "${LUKS_SOURCE}" "${MAPPER_NAME}" +ensure_source_vault_open +ensure_secrets_tomb_open -echo ">>> Mounting decrypted filesystem..." -sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}" +require_mount "${SOURCE_VAULT_MOUNT}" "Vault" +require_mount "${SECRETS_TOMB_MOUNT}" "Secrets" -echo ">>> Mounted at ${MOUNTPOINT}" -echo ">>> Logging rsync output to: ${LOGFILE}" -echo "[$(date)] Backup run started" >> "${LOGFILE}" +#################### BACKUP #################### -#################### RSYNC LOOP #################### +echo "[$(date)] Backup start" >> "$LOGFILE" ANY_WARNING=0 for SRC in "${BACKUP_ITEMS[@]}"; do - [[ -e "${SRC}" ]] || die "Missing source item: ${SRC}" + [[ -e "${SRC}" ]] || die "Missing: ${SRC}" NAME="$(basename "${SRC}")" DEST="${MOUNTPOINT}/${NAME}" - echo ">>> rsync: ${SRC} -> ${DEST}" - echo "[$(date)] rsync: ${SRC} -> ${DEST}" >> "${LOGFILE}" + echo ">>> ${SRC} -> ${DEST}" + echo "[$(date)] rsync: ${SRC} -> ${DEST}" >> "$LOGFILE" set +e if [[ -d "${SRC}" ]]; then - rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${DEST}/" 2>> "${LOGFILE}" + rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${DEST}/" 2>>"$LOGFILE" else - # file - rsync "${RSYNC_OPTS[@]}" "${SRC}" "${DEST}" 2>> "${LOGFILE}" + rsync "${RSYNC_OPTS[@]}" "${SRC}" "${DEST}" 2>>"$LOGFILE" fi RS=$? set -e if [[ "${RS}" -eq 0 ]]; then echo ">>> rsync OK: ${SRC}" - echo "[$(date)] rsync OK: ${SRC}" >> "${LOGFILE}" + echo "[$(date)] rsync OK: ${SRC}" >> "$LOGFILE" elif [[ "${RS}" -eq 23 || "${RS}" -eq 24 ]]; then echo ">>> WARNING: rsync non-critical issues (code ${RS}) for: ${SRC}" - echo ">>> See ${LOGFILE}." - echo "[$(date)] WARNING: rsync code ${RS} for: ${SRC}" >> "${LOGFILE}" + echo "[$(date)] WARNING: rsync code ${RS} for: ${SRC}" >> "$LOGFILE" ANY_WARNING=1 else - echo "[$(date)] ERROR: rsync failed code ${RS} for: ${SRC}" >> "${LOGFILE}" + echo "[$(date)] ERROR: rsync failed code ${RS} for: ${SRC}" >> "$LOGFILE" exit "${RS}" fi done -echo ">>> Backup loop finished." if [[ "${ANY_WARNING}" -eq 1 ]]; then - echo ">>> Backup completed WITH WARNINGS. See ${LOGFILE}." - echo "[$(date)] Backup completed WITH WARNINGS" >> "${LOGFILE}" + echo ">>> Backup completed WITH WARNINGS" + echo "[$(date)] Backup completed WITH WARNINGS" >> "$LOGFILE" else - echo ">>> Backup completed OK." - echo "[$(date)] Backup completed OK" >> "${LOGFILE}" + echo ">>> Backup completed OK" + echo "[$(date)] Backup completed OK" >> "$LOGFILE" fi -echo ">>> Syncing..." +echo ">>> Done" sync - -echo ">>> Done. (Auto-cleanup will unmount + close mapper.)" - Binary files differBinary files differdiff --git a/zathura b/zathura deleted file mode 100755 index f67a790..0000000 --- a/zathura +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -exec firejail /usr/bin/zathura "$@" - |
