aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-04-13 17:37:26 +0200
committerLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-04-13 17:37:26 +0200
commit51d43498b07dc97d795947964534f0903cd05db5 (patch)
tree735712bd50b5c244ef922a3b873c709ecce604cd
parent39711cf6c2ec5a3b4480dcb4800cc3802bda5bf2 (diff)
downloadbin-51d43498b07dc97d795947964534f0903cd05db5.tar.gz
bin-51d43498b07dc97d795947964534f0903cd05db5.zip
routine backup
-rwxr-xr-xbookmarkthis53
-rwxr-xr-xdino2
-rwxr-xr-xelectrum3
-rw-r--r--env11
-rw-r--r--env.fish4
-rwxr-xr-xfeh3
-rwxr-xr-xfirefox4
-rwxr-xr-xfirefox-raw2
l---------hledger-web1
-rwxr-xr-xmonero3
-rwxr-xr-xmutt-pdf5
-rwxr-xr-xpiecash8
-rwxr-xr-xprofanity-aesgcm-handler113
-rwxr-xr-xprofanity-aesgcm-watch45
-rwxr-xr-xshield-backup51
-rw-r--r--sorter/rules.toml13
-rw-r--r--sorter/sort_downloads.py5
-rwxr-xr-xssh-tunnel-all31
-rwxr-xr-xssh-tunnel-all-stop8
-rwxr-xr-xthunderbird2
-rwxr-xr-xtor-browser2
-rwxr-xr-xtypebookmarks16
-rwxr-xr-xusb-backup161
-rwxr-xr-xuvbin0 -> 35891072 bytes
-rwxr-xr-xuvxbin0 -> 358696 bytes
-rwxr-xr-xzathura3
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
diff --git a/dino b/dino
new file mode 100755
index 0000000..bb6b14e
--- /dev/null
+++ b/dino
@@ -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
diff --git a/env b/env
new file mode 100644
index 0000000..4728899
--- /dev/null
+++ b/env
@@ -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
diff --git a/feh b/feh
deleted file mode 100755
index c70e546..0000000
--- a/feh
+++ /dev/null
@@ -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
diff --git a/monero b/monero
new file mode 100755
index 0000000..f14353a
--- /dev/null
+++ b/monero
@@ -0,0 +1,3 @@
+#!/bin/sh
+cd "$HOME/opt/monero-gui-v0.18.4.5" || exit 1
+exec ./monero-wallet-gui
diff --git a/mutt-pdf b/mutt-pdf
index be0ab56..c60dc49 100755
--- a/mutt-pdf
+++ b/mutt-pdf
@@ -1,5 +1,2 @@
#!/bin/sh
-mkdir -p ~/Downloads/mail
-dest=~/Downloads/mail/mutt_$$.pdf
-cp "$1" "$dest"
-zathura "$dest"
+exec zathura "$1"
diff --git a/piecash b/piecash
new file mode 100755
index 0000000..c2df081
--- /dev/null
+++ b/piecash
@@ -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
diff --git a/usb-backup b/usb-backup
index 8c81b69..ca3419d 100755
--- a/usb-backup
+++ b/usb-backup
@@ -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.)"
-
diff --git a/uv b/uv
new file mode 100755
index 0000000..a9a8fe9
--- /dev/null
+++ b/uv
Binary files differ
diff --git a/uvx b/uvx
new file mode 100755
index 0000000..82069de
--- /dev/null
+++ b/uvx
Binary files differ
diff --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 "$@"
-