From c74163f1ecbd809c83bb465ee89fa04369a89972 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 17 Feb 2026 16:20:40 +0100 Subject: cleaned up names and dir --- archive/create-lab | 206 ++++++++++++++++++ archive/kvm-lab-create.bak | 132 ++++++++---- archive/kvm-lab-destroy.bak | 52 +++++ archive/nextcloud | 1 + backup_shield.sh | 135 ------------ backup_usb.sh | 155 -------------- borg-away-from-home-backup | 22 ++ borg-backup | 24 +++ borg-backup.sh | 24 --- borg-internet.sh | 22 -- create-lab | 206 ------------------ dmenu-scripts | 38 ---- docs/officium_readme.md | 135 ++++++++++++ kvm-lab-create.bak | 160 -------------- kvm-lab-destroy.bak | 52 ----- luks-shield | 142 ------------- market-monitor.sh | 499 ++++++++++++++++++++++++++++++++++++++++++++ nextcloud | 1 - officium_readme.md | 135 ------------ passmenu-otp | 24 ++- rofi-task-names | 26 +++ shield-backup | 135 ++++++++++++ shield-mount | 142 +++++++++++++ simplex | 4 + usb-backup | 155 ++++++++++++++ usb-mount | 132 ++++++++++++ usb-safe | 132 ------------ usb-tether | 13 ++ usb-tether.sh | 13 -- 29 files changed, 1651 insertions(+), 1266 deletions(-) create mode 100755 archive/create-lab create mode 100755 archive/kvm-lab-destroy.bak create mode 100644 archive/nextcloud delete mode 100755 backup_shield.sh delete mode 100755 backup_usb.sh create mode 100755 borg-away-from-home-backup create mode 100755 borg-backup delete mode 100755 borg-backup.sh delete mode 100755 borg-internet.sh delete mode 100755 create-lab delete mode 100755 dmenu-scripts create mode 100644 docs/officium_readme.md delete mode 100755 kvm-lab-create.bak delete mode 100755 kvm-lab-destroy.bak delete mode 100755 luks-shield create mode 100755 market-monitor.sh delete mode 100644 nextcloud delete mode 100644 officium_readme.md create mode 100755 rofi-task-names create mode 100755 shield-backup create mode 100755 shield-mount create mode 100755 simplex create mode 100755 usb-backup create mode 100755 usb-mount delete mode 100755 usb-safe create mode 100755 usb-tether delete mode 100755 usb-tether.sh diff --git a/archive/create-lab b/archive/create-lab new file mode 100755 index 0000000..8640ed6 --- /dev/null +++ b/archive/create-lab @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +set -euo pipefail + +URI="qemu:///system" + +IMGROOT="/var/lib/libvirt/images" +BASEDIR="$IMGROOT/base" +LABDIR="$IMGROOT/lab" + +# Template domains (XML donors) +TEMPLATE_DOMAIN_DEBIAN="debian13-template" +TEMPLATE_DOMAIN_NIXOS="nixos-template" + +# Base qcow2 images (backing files) +TEMPLATE_BASE_DEBIAN="$BASEDIR/debian13-template.qcow2" +TEMPLATE_BASE_NIXOS="$BASEDIR/nixos-base.qcow2" + +usage() { + cat <<'EOF' +Usage: kvm-lab-create [--base template|work01|nixos|/abs/path.qcow2] [--start] + +Examples: + kvm-lab-create lab10 --base template --start + kvm-lab-create lab11 --base work01 --start + kvm-lab-create lab20 --base nixos --start + kvm-lab-create lab12 --base /var/lib/libvirt/images/base/custom.qcow2 --start +EOF +} + +# --- Parse args --- +NAME="${1:-}" +shift || true + +BASESEL="template" +START="no" + +while [[ $# -gt 0 ]]; do + case "$1" in + --base) + BASESEL="${2:-}" + shift 2 + ;; + --start) + START="yes" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "ERROR: Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "${NAME:-}" ]]; then + usage + exit 1 +fi + +DISK="${LABDIR}/${NAME}.qcow2" +XML_TMP="$(mktemp "/tmp/${NAME}.xml.XXXXXX")" +cleanup() { rm -f "$XML_TMP"; } +trap cleanup EXIT + +# --- Resolve base selection (qcow2 backing file) --- +resolve_base() { + local sel="$1" + + if [[ "$sel" == "template" ]]; then + echo "$TEMPLATE_BASE_DEBIAN" + return 0 + fi + + if [[ "$sel" == "nixos" ]]; then + echo "$TEMPLATE_BASE_NIXOS" + return 0 + fi + + if [[ "$sel" == "work01" ]]; then + # Pick newest promoted work01 base (must exist first) + local latest + latest="$(ls -1t "$BASEDIR"/work01-base-*.qcow2 2>/dev/null | head -n 1 || true)" + if [[ -z "$latest" ]]; then + echo "ERROR: No work01 base found in $BASEDIR (expected work01-base-*.qcow2)." + echo "Action: run your promote script to create one." + return 1 + fi + echo "$latest" + return 0 + fi + + # Absolute path base + if [[ "$sel" == /* ]]; then + echo "$sel" + return 0 + fi + + echo "ERROR: Invalid --base '$sel' (use template|work01|nixos|/abs/path.qcow2)" + return 1 +} + +# --- Select template domain (XML donor) based on base selection --- +resolve_template_domain() { + local sel="$1" + + if [[ "$sel" == "nixos" ]]; then + echo "$TEMPLATE_DOMAIN_NIXOS" + return 0 + fi + + # If user gave an absolute qcow2, we default to Debian template unless they explicitly chose nixos. + # (You can change this policy later by adding a separate --os flag.) + echo "$TEMPLATE_DOMAIN_DEBIAN" + return 0 +} + +BASE="$(resolve_base "$BASESEL")" +TEMPLATE_DOMAIN="$(resolve_template_domain "$BASESEL")" + +# --- Preconditions (read-only checks) --- +if [[ ! -d "$LABDIR" ]]; then + echo "ERROR: Lab directory missing: $LABDIR" + exit 1 +fi + +if ! sudo test -r "$BASE"; then + echo "ERROR: Base image not readable (via sudo): $BASE" + exit 1 +fi + +if [[ -e "$DISK" ]]; then + echo "ERROR: Disk already exists: $DISK" + exit 1 +fi + +if virsh -c "$URI" dominfo "$NAME" &>/dev/null; then + echo "ERROR: Domain already exists: $NAME" + exit 1 +fi + +if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then + echo "ERROR: Template domain not found in $URI: $TEMPLATE_DOMAIN" + echo "Action:" + if [[ "$TEMPLATE_DOMAIN" == "$TEMPLATE_DOMAIN_NIXOS" ]]; then + echo " - Define a nixos-template domain (UEFI/OVMF) and keep it powered off." + else + echo " - Ensure debian13-template exists." + fi + exit 1 +fi + +# --- Create overlay disk --- +echo "Using template domain: $TEMPLATE_DOMAIN" +echo "Using base: $BASE" +echo "Creating overlay disk: $DISK" +sudo qemu-img create -f qcow2 -F qcow2 -b "$BASE" "$DISK" >/dev/null +sudo chown libvirt-qemu:libvirt "$DISK" +sudo chmod 0660 "$DISK" + +echo "Validating backing file..." +sudo qemu-img info "$DISK" | grep -E "file format:|virtual size:|backing file:" || true + +# --- Clone domain XML --- +echo "Cloning domain XML from: $TEMPLATE_DOMAIN" +virsh -c "$URI" dumpxml "$TEMPLATE_DOMAIN" > "$XML_TMP" + +# Detect original disk path inside the template XML and replace it robustly +ORIG_DISK_PATH="$(grep -oP "(?<= disk path in template XML ($TEMPLATE_DOMAIN)." + echo "Action: ensure the template VM uses a file-backed qcow2 disk." + exit 1 +fi + +# Set name, disk path, and force NAT-only network "default" +sed -i \ + -e "s|${TEMPLATE_DOMAIN}|${NAME}|" \ + -e "s|${ORIG_DISK_PATH}|${DISK}|g" \ + -e "s|[^<]*<\/uuid>/d' "$XML_TMP" + +# Remove any host filesystem passthrough (if present) +perl -0777 -i -pe 's/\n?//g' "$XML_TMP" + +# --- Define domain --- +echo "Defining domain: $NAME" +virsh -c "$URI" define "$XML_TMP" >/dev/null + +# --- Optional start --- +if [[ "$START" == "yes" ]]; then + echo "Starting domain: $NAME" + virsh -c "$URI" start "$NAME" >/dev/null +fi + +echo "OK: Lab '$NAME' created (disk: $DISK)." +if [[ "$START" != "yes" ]]; then + echo "To start: virsh -c $URI start $NAME" +fi + diff --git a/archive/kvm-lab-create.bak b/archive/kvm-lab-create.bak index d0d5865..43d2926 100755 --- a/archive/kvm-lab-create.bak +++ b/archive/kvm-lab-create.bak @@ -1,51 +1,106 @@ #!/usr/bin/env bash set -euo pipefail -# Libvirt context (system) URI="qemu:///system" -# Base/template (immutable parent qcow2) -BASE="/var/lib/libvirt/images/base/debian13-template.qcow2" -TEMPLATE_DOMAIN="debian13-template" +IMGROOT="/var/lib/libvirt/images" +BASEDIR="$IMGROOT/base" +LABDIR="$IMGROOT/lab" -# Lab overlays directory (dedicated) -LABDIR="/var/lib/libvirt/images/lab" +TEMPLATE_DOMAIN="debian13-template" +TEMPLATE_BASE="$BASEDIR/debian13-template.qcow2" usage() { - echo "Usage: kvm-lab-create [--start]" - echo "Example: kvm-lab-create lab02 --start" + echo "Usage: kvm-lab-create [--base template|work01|/abs/path.qcow2] [--start]" + echo "Examples:" + echo " kvm-lab-create lab10 --base template --start" + echo " kvm-lab-create lab11 --base work01 --start" + echo " kvm-lab-create lab12 --base /var/lib/libvirt/images/base/custom.qcow2 --start" } +# --- Parse args --- NAME="${1:-}" -START="${2:-}" +shift || true + +BASESEL="template" +START="no" + +while [[ $# -gt 0 ]]; do + case "$1" in + --base) + BASESEL="${2:-}" + shift 2 + ;; + --start) + START="yes" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "ERROR: Unknown argument: $1" + usage + exit 1 + ;; + esac +done if [[ -z "$NAME" ]]; then usage exit 1 fi -if [[ "${START:-}" != "" && "${START:-}" != "--start" ]]; then - usage - exit 1 -fi - DISK="${LABDIR}/${NAME}.qcow2" XML_TMP="$(mktemp "/tmp/${NAME}.xml.XXXXXX")" - cleanup() { rm -f "$XML_TMP"; } trap cleanup EXIT -# ---- Preconditions (read-only checks) ---- -if ! sudo test -r "$BASE"; then - echo "ERROR: Base image not readable: $BASE" - exit 1 -fi +# --- Resolve base selection --- +resolve_base() { + local sel="$1" + if [[ "$sel" == "template" ]]; then + echo "$TEMPLATE_BASE" + return 0 + fi + + if [[ "$sel" == "work01" ]]; then + # Pick newest promoted work01 base (must exist first) + local latest + latest="$(ls -1t "$BASEDIR"/work01-base-*.qcow2 2>/dev/null | head -n 1 || true)" + if [[ -z "$latest" ]]; then + echo "ERROR: No work01 base found in $BASEDIR (expected work01-base-*.qcow2)." + echo "Action: run your promote script to create one." + return 1 + fi + echo "$latest" + return 0 + fi + + # Absolute path base + if [[ "$sel" == /* ]]; then + echo "$sel" + return 0 + fi + + echo "ERROR: Invalid --base '$sel' (use template|work01|/abs/path.qcow2)" + return 1 +} +BASE="$(resolve_base "$BASESEL")" + +# --- Preconditions (read-only checks) --- if [[ ! -d "$LABDIR" ]]; then echo "ERROR: Lab directory missing: $LABDIR" exit 1 fi +if ! sudo test -r "$BASE"; then + echo "ERROR: Base image not readable (via sudo): $BASE" + exit 1 +fi + if [[ -e "$DISK" ]]; then echo "ERROR: Disk already exists: $DISK" exit 1 @@ -61,50 +116,45 @@ if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then exit 1 fi -# ---- Create overlay disk ---- +# --- Create overlay disk --- +echo "Using base: $BASE" echo "Creating overlay disk: $DISK" sudo qemu-img create -f qcow2 -F qcow2 -b "$BASE" "$DISK" >/dev/null - -# Ensure qemu/libvirt can open it sudo chown libvirt-qemu:libvirt "$DISK" sudo chmod 0660 "$DISK" -# Quick sanity: confirm backing file is correct echo "Validating backing file..." -sudo qemu-img info "$DISK" | grep -E "backing file:|file format:|virtual size:" || true +sudo qemu-img info "$DISK" | grep -E "file format:|virtual size:|backing file:" || true -# ---- Clone domain XML ---- +# --- Clone domain XML --- echo "Cloning domain XML from: $TEMPLATE_DOMAIN" virsh -c "$URI" dumpxml "$TEMPLATE_DOMAIN" > "$XML_TMP" -# Replace and the disk path, and force NAT-only network "default" -# Notes: -# - disk path substitution assumes template points at /var/lib/libvirt/images/base/debian13-template.qcow2 -# - network replacement assumes a standard exists +# Set name, disk path, and force NAT-only network "default" sed -i \ -e "s|${TEMPLATE_DOMAIN}|${NAME}|" \ - -e "s|${BASE}|${DISK}|g" \ + -e "s|${TEMPLATE_BASE}|${DISK}|g" \ -e "s|\n?//g' "$XML_TMP" -# # Remove UUID to avoid collision with template sed -i -E '/[^<]*<\/uuid>/d' "$XML_TMP" -# ---- Define domain ---- + +# Remove any host filesystem passthrough (if present) +perl -0777 -i -pe 's/\n?//g' "$XML_TMP" + +# --- Define domain --- echo "Defining domain: $NAME" virsh -c "$URI" define "$XML_TMP" >/dev/null -# ---- Optional start ---- -if [[ "$START" == "--start" ]]; then +# --- Optional start --- +if [[ "$START" == "yes" ]]; then echo "Starting domain: $NAME" virsh -c "$URI" start "$NAME" >/dev/null fi -echo "OK: Lab '${NAME}' created (disk: ${DISK})." -if [[ "$START" != "--start" ]]; then - echo "To start: virsh -c $URI start ${NAME}" +echo "OK: Lab '$NAME' created (disk: $DISK)." +if [[ "$START" != "yes" ]]; then + echo "To start: virsh -c $URI start $NAME" fi diff --git a/archive/kvm-lab-destroy.bak b/archive/kvm-lab-destroy.bak new file mode 100755 index 0000000..3548ef9 --- /dev/null +++ b/archive/kvm-lab-destroy.bak @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +URI="qemu:///system" +LABDIR="/var/lib/libvirt/images/lab" + +NAME="${1:-}" + +if [[ -z "$NAME" ]]; then + echo "Usage: kvm-lab-destroy " + exit 1 +fi + +if [[ "$NAME" == "work01" || "$NAME" == "debian13-template" ]]; then + echo "Refusing to destroy protected VM: $NAME" + exit 1 +fi + +if ! virsh -c "$URI" dominfo "$NAME" &>/dev/null; then + echo "Domain not found: $NAME" + exit 1 +fi + +DISK="$(virsh -c "$URI" domblklist "$NAME" --details | awk '$3=="vda"{print $4}')" + +if [[ -z "$DISK" || "$DISK" != "$LABDIR/"* ]]; then + echo "Refusing to destroy: disk not under $LABDIR" + echo "Detected disk: $DISK" + exit 1 +fi + +STATE="$(virsh -c "$URI" domstate "$NAME")" + +if [[ "$STATE" == "running" ]]; then + echo "Shutting down $NAME..." + virsh -c "$URI" shutdown "$NAME" + sleep 3 +fi + +if [[ "$(virsh -c "$URI" domstate "$NAME")" == "running" ]]; then + echo "Force stopping $NAME..." + virsh -c "$URI" destroy "$NAME" +fi + +echo "Undefining $NAME..." +virsh -c "$URI" undefine "$NAME" + +echo "Deleting disk: $DISK" +sudo rm -v -- "$DISK" + +echo "Lab '$NAME' destroyed." + diff --git a/archive/nextcloud b/archive/nextcloud new file mode 100644 index 0000000..fae9bdb --- /dev/null +++ b/archive/nextcloud @@ -0,0 +1 @@ +aHR0cHM6Ly9jbG91ZC5sYWJ1bml4Lnh5ejo5NDQzCmxvZ2luOiBhZGFtCm1haWw6IGFkYW0uZ29tdWxrYTk0QGdtYWlsLmNvbSAKaGFzxYJvOiAgYk9PP2BAZD15S1Q5XmNWYAoKdXN0YXcgc29iaWUgMkZBIHBvdGVtIGRsYSBiZXpwaWVjemXFhHN0d2E= diff --git a/backup_shield.sh b/backup_shield.sh deleted file mode 100755 index 96f9269..0000000 --- a/backup_shield.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -#################### CONFIG – EDIT THESE #################### - -# LUKS2 block device -LUKS_DEVICE="/dev/disk/by-uuid/6659e638-d52a-4c32-9781-9dcedd44db35" - -# Name for the mapper device (will appear as /dev/mapper/${LUKS_NAME}) -LUKS_NAME="shield" - -# Mount point for the decrypted filesystem -LUKS_MOUNTPOINT="/mnt/shield" - -# Local folders to back up with rsync → LUKS volume -# Each folder will be mirrored into ${LUKS_MOUNTPOINT}/ -BACKUP_ITEMS=( - "$HOME/" - "/media/.secrets/" -) - -# Machine identifier (destination subdir on the LUKS volume) -MACHINE_DIR="T480" - -#################### INTERNALS – NO NEED TO EDIT ############ - -need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing command: $1" >&2; exit 1; }; } - -cleanup() { - echo - echo ">>> Cleaning up..." - - # Try to unmount, if mounted - if mountpoint -q "${LUKS_MOUNTPOINT}"; then - echo ">>> Unmounting ${LUKS_MOUNTPOINT}..." - sudo umount "${LUKS_MOUNTPOINT}" || true - fi - - # Close LUKS mapping if it exists - if [ -e "/dev/mapper/${LUKS_NAME}" ]; then - echo ">>> Closing LUKS mapping ${LUKS_NAME}..." - sudo cryptsetup close "${LUKS_NAME}" || true - fi -} -trap cleanup EXIT - -need_cmd cryptsetup -need_cmd rclone -need_cmd rsync -need_cmd mountpoint - -# Cache sudo credentials early to avoid surprises mid-run -sudo -v - -echo ">>> Ensuring mountpoint exists: ${LUKS_MOUNTPOINT}" -sudo mkdir -p "${LUKS_MOUNTPOINT}" - -# Check if already open -if [ -e "/dev/mapper/${LUKS_NAME}" ]; then - echo ">>> WARNING: /dev/mapper/${LUKS_NAME} already exists, assuming already open." -else - echo ">>> Opening LUKS volume (interactive prompt)..." - sudo cryptsetup open "${LUKS_DEVICE}" "${LUKS_NAME}" -fi - -# At this point /dev/mapper/${LUKS_NAME} must exist -if [ ! -e "/dev/mapper/${LUKS_NAME}" ]; then - echo "ERROR: /dev/mapper/${LUKS_NAME} not found after cryptsetup open." >&2 - exit 1 -fi - -# Mount if not already mounted -if mountpoint -q "${LUKS_MOUNTPOINT}"; then - echo ">>> WARNING: ${LUKS_MOUNTPOINT} already mounted." -else - echo ">>> Mounting /dev/mapper/${LUKS_NAME} on ${LUKS_MOUNTPOINT}..." - sudo mount "/dev/mapper/${LUKS_NAME}" "${LUKS_MOUNTPOINT}" -fi - -echo ">>> LUKS volume mounted at ${LUKS_MOUNTPOINT}" - -# Ensure SharePoint destinations exist in root of shield -sudo mkdir -p \ - "${LUKS_MOUNTPOINT}/SP_Administration" \ - "${LUKS_MOUNTPOINT}/SP_Operations" \ - "${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 -echo ">>> All rclone syncs finished." - -echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHINE_DIR}/..." - -# 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" - continue - fi - - case "$SRC" in - "$HOME/"|"$HOME") - RELDEST="${MACHINE_DIR}/HOME" - ;; - "/media/.secrets/"|"/media/.secrets") - 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}" - ;; - esac - - DEST="${LUKS_MOUNTPOINT}/${RELDEST}" - sudo mkdir -p "${DEST}" - - echo ">>> rsync: ${SRC} -> ${DEST}" - rsync -a --delete --progress \ - "${SRC%/}/" \ - "${DEST}/" -done - -echo ">>> All rsync backups finished." -echo ">>> Unmounting and closing LUKS (via trap)..." -# cleanup() will run automatically on script exit - diff --git a/backup_usb.sh b/backup_usb.sh deleted file mode 100755 index 8c81b69..0000000 --- a/backup_usb.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -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/ -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) -BACKUP_ITEMS=( - "$HOME/.abook" - "$HOME/.config" - "$HOME/Documents" - "$HOME/dotfiles" - "$HOME/.password-store" - "$HOME/secure" - "$HOME/vimwiki" - "$HOME/.password.tomb" - "$HOME/.secrets.tomb" -) - -RSYNC_OPTS=( - -a - --delete - --no-perms - --no-owner - --no-group - --no-xattrs - --omit-dir-times - --progress -) - -LOGFILE="$HOME/backup_flash_rsync.log" - -#################### HELPERS #################### - -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 -} - -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}'" - } - fi - - if is_mapper_open; then - echo ">>> Closing LUKS mapper ${MAPPER_NAME}..." - sudo cryptsetup close "${MAPPER_NAME}" || 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 #################### - -echo ">>> Checking LUKS source exists: ${LUKS_SOURCE}" -[[ -e "${LUKS_SOURCE}" ]] || die "LUKS source not found: ${LUKS_SOURCE}" - -echo ">>> Ensuring mountpoint exists: ${MOUNTPOINT}" -sudo mkdir -p "${MOUNTPOINT}" - -is_mounted && die "Mountpoint already in use: ${MOUNTPOINT}" -is_mapper_open && die "Mapper already open: /dev/mapper/${MAPPER_NAME}" - -echo ">>> Opening LUKS2 container (prompt for passphrase)..." -sudo cryptsetup open --type luks2 "${LUKS_SOURCE}" "${MAPPER_NAME}" - -echo ">>> Mounting decrypted filesystem..." -sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}" - -echo ">>> Mounted at ${MOUNTPOINT}" -echo ">>> Logging rsync output to: ${LOGFILE}" -echo "[$(date)] Backup run started" >> "${LOGFILE}" - -#################### RSYNC LOOP #################### - -ANY_WARNING=0 - -for SRC in "${BACKUP_ITEMS[@]}"; do - [[ -e "${SRC}" ]] || die "Missing source item: ${SRC}" - - NAME="$(basename "${SRC}")" - DEST="${MOUNTPOINT}/${NAME}" - - echo ">>> rsync: ${SRC} -> ${DEST}" - echo "[$(date)] rsync: ${SRC} -> ${DEST}" >> "${LOGFILE}" - - set +e - if [[ -d "${SRC}" ]]; then - rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${DEST}/" 2>> "${LOGFILE}" - else - # file - 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}" - 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}" - ANY_WARNING=1 - else - 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}" -else - echo ">>> Backup completed OK." - echo "[$(date)] Backup completed OK" >> "${LOGFILE}" -fi - -echo ">>> Syncing..." -sync - -echo ">>> Done. (Auto-cleanup will unmount + close mapper.)" - diff --git a/borg-away-from-home-backup b/borg-away-from-home-backup new file mode 100755 index 0000000..84247d1 --- /dev/null +++ b/borg-away-from-home-backup @@ -0,0 +1,22 @@ +#!/bin/bash +set -euo pipefail + +REPO="borg@sanctum-wg:/tank/backups/borg" +NAME="home-$(date +%F_%H-%M)" +SRC="/home/lukasz" + +export BORG_RSH="ssh -p 57385 -i $HOME/.ssh/sacrum.key -o IdentitiesOnly=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3" + +LOGDIR="$HOME/.local/state/borg" +mkdir -p "$LOGDIR" +LOGFILE="$LOGDIR/backup-$(date +%F).log" + +ARGS=(--compression lz4 --exclude-from "$HOME/.config/borg/excludes") +if [ -t 1 ]; then + ARGS+=(--progress --list --stats) +else + ARGS+=(--stats) +fi + +borg create "${ARGS[@]}" "$REPO"::"$NAME" "$SRC" 2>&1 | tee -a "$LOGFILE" + diff --git a/borg-backup b/borg-backup new file mode 100755 index 0000000..b9572f6 --- /dev/null +++ b/borg-backup @@ -0,0 +1,24 @@ +#!/bin/bash +set -euo pipefail + +REPO="borg@192.168.33.5:/tank/backups/borg" +NAME="home-$(date +%F_%H-%M)" +SRC="/home/lukasz" + +export BORG_RSH="ssh -p 57385 -i $HOME/.ssh/sacrum.key -o IdentitiesOnly=yes" + +LOGDIR="$HOME/.local/state/borg" +mkdir -p "$LOGDIR" +LOGFILE="$LOGDIR/backup-$(date +%F).log" + +# Show progress if stdout is a terminal (manual run) +ARGS=(--compression lz4 --exclude-from "$HOME/.config/borg/excludes") +if [ -t 1 ]; then + ARGS+=(--progress --list --stats) +else + ARGS+=(--stats) +fi + +# Always log; show on screen when interactive +borg create "${ARGS[@]}" "$REPO"::"$NAME" "$SRC" 2>&1 | tee -a "$LOGFILE" + diff --git a/borg-backup.sh b/borg-backup.sh deleted file mode 100755 index b9572f6..0000000 --- a/borg-backup.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -euo pipefail - -REPO="borg@192.168.33.5:/tank/backups/borg" -NAME="home-$(date +%F_%H-%M)" -SRC="/home/lukasz" - -export BORG_RSH="ssh -p 57385 -i $HOME/.ssh/sacrum.key -o IdentitiesOnly=yes" - -LOGDIR="$HOME/.local/state/borg" -mkdir -p "$LOGDIR" -LOGFILE="$LOGDIR/backup-$(date +%F).log" - -# Show progress if stdout is a terminal (manual run) -ARGS=(--compression lz4 --exclude-from "$HOME/.config/borg/excludes") -if [ -t 1 ]; then - ARGS+=(--progress --list --stats) -else - ARGS+=(--stats) -fi - -# Always log; show on screen when interactive -borg create "${ARGS[@]}" "$REPO"::"$NAME" "$SRC" 2>&1 | tee -a "$LOGFILE" - diff --git a/borg-internet.sh b/borg-internet.sh deleted file mode 100755 index 84247d1..0000000 --- a/borg-internet.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -euo pipefail - -REPO="borg@sanctum-wg:/tank/backups/borg" -NAME="home-$(date +%F_%H-%M)" -SRC="/home/lukasz" - -export BORG_RSH="ssh -p 57385 -i $HOME/.ssh/sacrum.key -o IdentitiesOnly=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3" - -LOGDIR="$HOME/.local/state/borg" -mkdir -p "$LOGDIR" -LOGFILE="$LOGDIR/backup-$(date +%F).log" - -ARGS=(--compression lz4 --exclude-from "$HOME/.config/borg/excludes") -if [ -t 1 ]; then - ARGS+=(--progress --list --stats) -else - ARGS+=(--stats) -fi - -borg create "${ARGS[@]}" "$REPO"::"$NAME" "$SRC" 2>&1 | tee -a "$LOGFILE" - diff --git a/create-lab b/create-lab deleted file mode 100755 index 8640ed6..0000000 --- a/create-lab +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -URI="qemu:///system" - -IMGROOT="/var/lib/libvirt/images" -BASEDIR="$IMGROOT/base" -LABDIR="$IMGROOT/lab" - -# Template domains (XML donors) -TEMPLATE_DOMAIN_DEBIAN="debian13-template" -TEMPLATE_DOMAIN_NIXOS="nixos-template" - -# Base qcow2 images (backing files) -TEMPLATE_BASE_DEBIAN="$BASEDIR/debian13-template.qcow2" -TEMPLATE_BASE_NIXOS="$BASEDIR/nixos-base.qcow2" - -usage() { - cat <<'EOF' -Usage: kvm-lab-create [--base template|work01|nixos|/abs/path.qcow2] [--start] - -Examples: - kvm-lab-create lab10 --base template --start - kvm-lab-create lab11 --base work01 --start - kvm-lab-create lab20 --base nixos --start - kvm-lab-create lab12 --base /var/lib/libvirt/images/base/custom.qcow2 --start -EOF -} - -# --- Parse args --- -NAME="${1:-}" -shift || true - -BASESEL="template" -START="no" - -while [[ $# -gt 0 ]]; do - case "$1" in - --base) - BASESEL="${2:-}" - shift 2 - ;; - --start) - START="yes" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "ERROR: Unknown argument: $1" - usage - exit 1 - ;; - esac -done - -if [[ -z "${NAME:-}" ]]; then - usage - exit 1 -fi - -DISK="${LABDIR}/${NAME}.qcow2" -XML_TMP="$(mktemp "/tmp/${NAME}.xml.XXXXXX")" -cleanup() { rm -f "$XML_TMP"; } -trap cleanup EXIT - -# --- Resolve base selection (qcow2 backing file) --- -resolve_base() { - local sel="$1" - - if [[ "$sel" == "template" ]]; then - echo "$TEMPLATE_BASE_DEBIAN" - return 0 - fi - - if [[ "$sel" == "nixos" ]]; then - echo "$TEMPLATE_BASE_NIXOS" - return 0 - fi - - if [[ "$sel" == "work01" ]]; then - # Pick newest promoted work01 base (must exist first) - local latest - latest="$(ls -1t "$BASEDIR"/work01-base-*.qcow2 2>/dev/null | head -n 1 || true)" - if [[ -z "$latest" ]]; then - echo "ERROR: No work01 base found in $BASEDIR (expected work01-base-*.qcow2)." - echo "Action: run your promote script to create one." - return 1 - fi - echo "$latest" - return 0 - fi - - # Absolute path base - if [[ "$sel" == /* ]]; then - echo "$sel" - return 0 - fi - - echo "ERROR: Invalid --base '$sel' (use template|work01|nixos|/abs/path.qcow2)" - return 1 -} - -# --- Select template domain (XML donor) based on base selection --- -resolve_template_domain() { - local sel="$1" - - if [[ "$sel" == "nixos" ]]; then - echo "$TEMPLATE_DOMAIN_NIXOS" - return 0 - fi - - # If user gave an absolute qcow2, we default to Debian template unless they explicitly chose nixos. - # (You can change this policy later by adding a separate --os flag.) - echo "$TEMPLATE_DOMAIN_DEBIAN" - return 0 -} - -BASE="$(resolve_base "$BASESEL")" -TEMPLATE_DOMAIN="$(resolve_template_domain "$BASESEL")" - -# --- Preconditions (read-only checks) --- -if [[ ! -d "$LABDIR" ]]; then - echo "ERROR: Lab directory missing: $LABDIR" - exit 1 -fi - -if ! sudo test -r "$BASE"; then - echo "ERROR: Base image not readable (via sudo): $BASE" - exit 1 -fi - -if [[ -e "$DISK" ]]; then - echo "ERROR: Disk already exists: $DISK" - exit 1 -fi - -if virsh -c "$URI" dominfo "$NAME" &>/dev/null; then - echo "ERROR: Domain already exists: $NAME" - exit 1 -fi - -if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then - echo "ERROR: Template domain not found in $URI: $TEMPLATE_DOMAIN" - echo "Action:" - if [[ "$TEMPLATE_DOMAIN" == "$TEMPLATE_DOMAIN_NIXOS" ]]; then - echo " - Define a nixos-template domain (UEFI/OVMF) and keep it powered off." - else - echo " - Ensure debian13-template exists." - fi - exit 1 -fi - -# --- Create overlay disk --- -echo "Using template domain: $TEMPLATE_DOMAIN" -echo "Using base: $BASE" -echo "Creating overlay disk: $DISK" -sudo qemu-img create -f qcow2 -F qcow2 -b "$BASE" "$DISK" >/dev/null -sudo chown libvirt-qemu:libvirt "$DISK" -sudo chmod 0660 "$DISK" - -echo "Validating backing file..." -sudo qemu-img info "$DISK" | grep -E "file format:|virtual size:|backing file:" || true - -# --- Clone domain XML --- -echo "Cloning domain XML from: $TEMPLATE_DOMAIN" -virsh -c "$URI" dumpxml "$TEMPLATE_DOMAIN" > "$XML_TMP" - -# Detect original disk path inside the template XML and replace it robustly -ORIG_DISK_PATH="$(grep -oP "(?<= disk path in template XML ($TEMPLATE_DOMAIN)." - echo "Action: ensure the template VM uses a file-backed qcow2 disk." - exit 1 -fi - -# Set name, disk path, and force NAT-only network "default" -sed -i \ - -e "s|${TEMPLATE_DOMAIN}|${NAME}|" \ - -e "s|${ORIG_DISK_PATH}|${DISK}|g" \ - -e "s|[^<]*<\/uuid>/d' "$XML_TMP" - -# Remove any host filesystem passthrough (if present) -perl -0777 -i -pe 's/\n?//g' "$XML_TMP" - -# --- Define domain --- -echo "Defining domain: $NAME" -virsh -c "$URI" define "$XML_TMP" >/dev/null - -# --- Optional start --- -if [[ "$START" == "yes" ]]; then - echo "Starting domain: $NAME" - virsh -c "$URI" start "$NAME" >/dev/null -fi - -echo "OK: Lab '$NAME' created (disk: $DISK)." -if [[ "$START" != "yes" ]]; then - echo "To start: virsh -c $URI start $NAME" -fi - diff --git a/dmenu-scripts b/dmenu-scripts deleted file mode 100755 index 6d9c9db..0000000 --- a/dmenu-scripts +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_path="${HOME}/.local/bin" - -# Optional dmenu theming -# shellcheck disable=SC1090 -[[ -r "${HOME}/.local/bin/dmenu-theming" ]] && . "${HOME}/.local/bin/dmenu-theming" - -DMENU_CMD="${DMENU_CMD:-dmenu}" -PROMPT="${PROMPT:-Run: }" - -# List only executable non-binary files in ~/.local/bin -choices="$( - find "${script_path}" -maxdepth 1 -type f -executable -print0 \ - | xargs -0 -I{} sh -c 'grep -Iq . "$1" && printf "%s\n" "$(basename "$1")"' _ {} \ - | sort -u -)" - -picked="$( - printf "%s\n" "$choices" \ - | ${DMENU_CMD} -i -l 20 -p "${PROMPT}" ${lines:-} ${colors:-} ${font:-} \ - || true -)" - -[[ -n "${picked}" ]] || exit 0 - -# Safety: only allow basenames (no slashes) -case "$picked" in - */*|"") exit 0 ;; -esac - -target="${script_path}/${picked}" - -[[ -x "$target" ]] || { printf "ERROR: not executable: %s\n" "$target" >&2; exit 1; } - -exec "$target" - diff --git a/docs/officium_readme.md b/docs/officium_readme.md new file mode 100644 index 0000000..b4e00a5 --- /dev/null +++ b/docs/officium_readme.md @@ -0,0 +1,135 @@ +# Log Projektu: Skrypt Bash do Divinumofficium.com + +## Zapytanie użytkownika: +Utworzyć skrypt bash który: +- Przyjmuje jeden argument ze zbioru: [Prima, Tertia, Sexta, Nona, Vesperae] +- Pobiera dane ze strony https://www.divinumofficium.com/cgi-bin/horas/officium.pl +- Znajduje linki ukryte pod hover dla wybranej modlitwy (Laudes, Prima, Tertia, Sexta, Nona, Vesperae) +- Pobiera tekst od części "Incipit" aż do końca części "Conclusio" +- Wyświetla zawartość w terminalu + +## Krok 1: Analiza struktury strony +Data: 2026-01-13 22:12 + +Pobieram główną stronę aby zrozumieć strukturę HTML i znaleźć linki do poszczególnych modlitw. + +### Wyniki analizy: +- Strona używa parametru `command=pray{NazwaModlitwy}` w URL +- Przykład: `officium.pl?command=prayPrima` +- Treść modlitwy znajduje się w tabeli HTML +- Sekcja "Incipit" to początek (oznaczona znacznikiem `Incipit`) +- Sekcja "Conclusio" to koniec (analogicznie oznaczona) +- Treść jest w formacie HTML z tagami FONT, BR, itd. + +## Krok 2: Tworzenie skryptu bash +Data: 2026-01-13 22:12 + +Tworzę skrypt który: +1. Waliduje argument wejściowy +2. Buduje URL z odpowiednim parametrem command +3. Pobiera stronę za pomocą curl +4. Ekstrahuje treść od "Incipit" do końca "Conclusio" +5. Czyści HTML i formatuje dla terminala + +### Pierwsze podejście: +- Utworzono plik `divinumofficium.sh` +- Użyto sed do ekstrakcji, ale nie działało prawidłowo + +### Poprawki: +- Zmieniono na awk, który czyta od Incipit do zamknięcia +- Dodano filtry do usunięcia linków nawigacyjnych (Top, Next) +- Dodano dekodowanie encji HTML + +## Krok 3: Testowanie skryptu +Data: 2026-01-13 22:12 + +### Testy przeprowadzone: +1. ✅ `./divinumofficium.sh Prima` - działa poprawnie +2. ✅ `./divinumofficium.sh Vesperae` - działa poprawnie +3. ✅ `./divinumofficium.sh Sexta` - działa poprawnie +4. ✅ Brak argumentu - wyświetla instrukcję użycia +5. ✅ Nieprawidłowy argument (Laudes) - wyświetla błąd + +### Wnioski: +Skrypt działa zgodnie z wymaganiami: +- Przyjmuje argumenty: Prima, Tertia, Sexta, Nona, Vesperae +- Pobiera treść od "Incipit" do końca "Conclusio" +- Wyświetla czytelnie w terminalu (z tekstem łacińskim i angielskim) +- Waliduje dane wejściowe +- Obsługuje błędy połączenia + +## Podsumowanie +Data: 2026-01-13 22:13 + +Utworzono działający skrypt bash `divinumofficium.sh`, który: +- Lokalizacja: `/home/adam/copilot_workspace/kasprzak_project/divinumofficium.sh` +- Rozmiar: ~1.8KB +- Uprawnienia: wykonywalne (chmod +x) +- Status: UKOŃCZONE ✅ + +## Krok 4: Modyfikacja obsługi argumentów +Data: 2026-01-13 22:17 + +### Zapytanie użytkownika: +"popraw aby argumenty pisać małymi literami" + +### Zmiany wprowadzone: +- Argumenty przyjmowane są teraz małymi literami: prima, tertia, sexta, nona, vesperae +- Dodano automatyczną konwersję: pierwsza litera na wielką (bo API wymaga Prima, Tertia, etc.) +- Zaktualizowano komunikaty pomocy i błędów +- Zachowano zgodność z API (nadal wysyła "Prima", "Tertia", etc.) + +### Test: +Teraz użytkownik może pisać: `./divinumofficium.sh prima` zamiast `./divinumofficium.sh Prima` + +## Krok 5: Filtrowanie tylko tekstu łacińskiego +Data: 2026-01-13 22:22 + +### Zapytanie użytkownika: +"wynik jest po łacinie i angielsku. Ma być tylko część po łacinie" + +### Analiza problemu: +Strona HTML zawiera tabelę z dwiema kolumnami: +- Pierwsza kolumna (``): tekst łaciński +- Druga kolumna (``): tekst angielski + +### Zmiany wprowadzone: +- Zmodyfikowano skrypt awk aby pomijać drugą kolumnę (angielską) +- Dodano wykrywanie początku drugiej kolumny (po numerze sekcji) +- Dodano filtr usuwający pojedyncze cyfry (numery sekcji) +- Zachowano tylko tekst łaciński w wyniku + +### Test: +```bash +./divinumofficium.sh prima # Wyświetla tylko tekst łaciński +./divinumofficium.sh vesperae # Wyświetla tylko tekst łaciński +``` + +Wynik zawiera teraz wyłącznie modlitwy w języku łacińskim. + +## Krok 6: Dodanie odstępów między sekcjami +Data: 2026-01-13 22:28 + +### Zapytanie użytkownika: +"może dodaj jeszcze wolną linię między częściami " + +### Zmiany wprowadzone: +- Dodano pustą linię po każdym `` (koniec sekcji w tabeli) +- Użyto `cat -s` aby zredukować wielokrotne puste linie do maksymalnie dwóch +- Sekcje są teraz lepiej wizualnie oddzielone + +### Efekt: +Każda sekcja modlitwy (Incipit, Hymnus, Psalmi, Lectio brevis, Conclusio, etc.) jest oddzielona pustą linią, co poprawia czytelność w terminalu. + +Przykład: +``` +Incipit + +℣. Deus ✠ in adiutórium... + +Hymnus + +Iam lucis orto sídere... + +Psalmi {ex Psalterio... +``` diff --git a/kvm-lab-create.bak b/kvm-lab-create.bak deleted file mode 100755 index 43d2926..0000000 --- a/kvm-lab-create.bak +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -URI="qemu:///system" - -IMGROOT="/var/lib/libvirt/images" -BASEDIR="$IMGROOT/base" -LABDIR="$IMGROOT/lab" - -TEMPLATE_DOMAIN="debian13-template" -TEMPLATE_BASE="$BASEDIR/debian13-template.qcow2" - -usage() { - echo "Usage: kvm-lab-create [--base template|work01|/abs/path.qcow2] [--start]" - echo "Examples:" - echo " kvm-lab-create lab10 --base template --start" - echo " kvm-lab-create lab11 --base work01 --start" - echo " kvm-lab-create lab12 --base /var/lib/libvirt/images/base/custom.qcow2 --start" -} - -# --- Parse args --- -NAME="${1:-}" -shift || true - -BASESEL="template" -START="no" - -while [[ $# -gt 0 ]]; do - case "$1" in - --base) - BASESEL="${2:-}" - shift 2 - ;; - --start) - START="yes" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "ERROR: Unknown argument: $1" - usage - exit 1 - ;; - esac -done - -if [[ -z "$NAME" ]]; then - usage - exit 1 -fi - -DISK="${LABDIR}/${NAME}.qcow2" -XML_TMP="$(mktemp "/tmp/${NAME}.xml.XXXXXX")" -cleanup() { rm -f "$XML_TMP"; } -trap cleanup EXIT - -# --- Resolve base selection --- -resolve_base() { - local sel="$1" - if [[ "$sel" == "template" ]]; then - echo "$TEMPLATE_BASE" - return 0 - fi - - if [[ "$sel" == "work01" ]]; then - # Pick newest promoted work01 base (must exist first) - local latest - latest="$(ls -1t "$BASEDIR"/work01-base-*.qcow2 2>/dev/null | head -n 1 || true)" - if [[ -z "$latest" ]]; then - echo "ERROR: No work01 base found in $BASEDIR (expected work01-base-*.qcow2)." - echo "Action: run your promote script to create one." - return 1 - fi - echo "$latest" - return 0 - fi - - # Absolute path base - if [[ "$sel" == /* ]]; then - echo "$sel" - return 0 - fi - - echo "ERROR: Invalid --base '$sel' (use template|work01|/abs/path.qcow2)" - return 1 -} - -BASE="$(resolve_base "$BASESEL")" - -# --- Preconditions (read-only checks) --- -if [[ ! -d "$LABDIR" ]]; then - echo "ERROR: Lab directory missing: $LABDIR" - exit 1 -fi - -if ! sudo test -r "$BASE"; then - echo "ERROR: Base image not readable (via sudo): $BASE" - exit 1 -fi - -if [[ -e "$DISK" ]]; then - echo "ERROR: Disk already exists: $DISK" - exit 1 -fi - -if virsh -c "$URI" dominfo "$NAME" &>/dev/null; then - echo "ERROR: Domain already exists: $NAME" - exit 1 -fi - -if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then - echo "ERROR: Template domain not found in $URI: $TEMPLATE_DOMAIN" - exit 1 -fi - -# --- Create overlay disk --- -echo "Using base: $BASE" -echo "Creating overlay disk: $DISK" -sudo qemu-img create -f qcow2 -F qcow2 -b "$BASE" "$DISK" >/dev/null -sudo chown libvirt-qemu:libvirt "$DISK" -sudo chmod 0660 "$DISK" - -echo "Validating backing file..." -sudo qemu-img info "$DISK" | grep -E "file format:|virtual size:|backing file:" || true - -# --- Clone domain XML --- -echo "Cloning domain XML from: $TEMPLATE_DOMAIN" -virsh -c "$URI" dumpxml "$TEMPLATE_DOMAIN" > "$XML_TMP" - -# Set name, disk path, and force NAT-only network "default" -sed -i \ - -e "s|${TEMPLATE_DOMAIN}|${NAME}|" \ - -e "s|${TEMPLATE_BASE}|${DISK}|g" \ - -e "s|[^<]*<\/uuid>/d' "$XML_TMP" - -# Remove any host filesystem passthrough (if present) -perl -0777 -i -pe 's/\n?//g' "$XML_TMP" - -# --- Define domain --- -echo "Defining domain: $NAME" -virsh -c "$URI" define "$XML_TMP" >/dev/null - -# --- Optional start --- -if [[ "$START" == "yes" ]]; then - echo "Starting domain: $NAME" - virsh -c "$URI" start "$NAME" >/dev/null -fi - -echo "OK: Lab '$NAME' created (disk: $DISK)." -if [[ "$START" != "yes" ]]; then - echo "To start: virsh -c $URI start $NAME" -fi - diff --git a/kvm-lab-destroy.bak b/kvm-lab-destroy.bak deleted file mode 100755 index 3548ef9..0000000 --- a/kvm-lab-destroy.bak +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -URI="qemu:///system" -LABDIR="/var/lib/libvirt/images/lab" - -NAME="${1:-}" - -if [[ -z "$NAME" ]]; then - echo "Usage: kvm-lab-destroy " - exit 1 -fi - -if [[ "$NAME" == "work01" || "$NAME" == "debian13-template" ]]; then - echo "Refusing to destroy protected VM: $NAME" - exit 1 -fi - -if ! virsh -c "$URI" dominfo "$NAME" &>/dev/null; then - echo "Domain not found: $NAME" - exit 1 -fi - -DISK="$(virsh -c "$URI" domblklist "$NAME" --details | awk '$3=="vda"{print $4}')" - -if [[ -z "$DISK" || "$DISK" != "$LABDIR/"* ]]; then - echo "Refusing to destroy: disk not under $LABDIR" - echo "Detected disk: $DISK" - exit 1 -fi - -STATE="$(virsh -c "$URI" domstate "$NAME")" - -if [[ "$STATE" == "running" ]]; then - echo "Shutting down $NAME..." - virsh -c "$URI" shutdown "$NAME" - sleep 3 -fi - -if [[ "$(virsh -c "$URI" domstate "$NAME")" == "running" ]]; then - echo "Force stopping $NAME..." - virsh -c "$URI" destroy "$NAME" -fi - -echo "Undefining $NAME..." -virsh -c "$URI" undefine "$NAME" - -echo "Deleting disk: $DISK" -sudo rm -v -- "$DISK" - -echo "Lab '$NAME' destroyed." - diff --git a/luks-shield b/luks-shield deleted file mode 100755 index 9080f35..0000000 --- a/luks-shield +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -UUID="6659e638-d52a-4c32-9781-9dcedd44db35" -NAME="shield" -MNT="/mnt/shield" - -DEV_BY_UUID="/dev/disk/by-uuid/${UUID}" -MAPPER="/dev/mapper/${NAME}" - -die() { echo "Error: $*" >&2; exit 1; } - -need_cmd() { - command -v "$1" >/dev/null 2>&1 || die "Missing command: $1" -} - -resolve_blockdev() { - # Resolve the UUID symlink to an absolute /dev/... node - local target - target="$(readlink -f "$DEV_BY_UUID")" || return 1 - [[ -b "$target" ]] || return 1 - echo "$target" -} - -is_mapped() { - [[ -e "$MAPPER" ]] -} - -is_mounted() { - mountpoint -q "$MNT" -} - -show_busy() { - echo "Mount appears busy. Processes using $MNT:" - # lsof is optional but useful; show what we can. - if command -v lsof >/dev/null 2>&1; then - sudo lsof +f -- "$MNT" || true - else - echo "(Install lsof: sudo apt install lsof)" - sudo fuser -vm "$MNT" || true - fi -} - -do_open() { - need_cmd cryptsetup - need_cmd mount - need_cmd mountpoint - need_cmd readlink - - [[ -e "$DEV_BY_UUID" ]] || die "UUID device not found: $DEV_BY_UUID (is the drive plugged in?)" - - local dev - dev="$(resolve_blockdev)" || die "Could not resolve block device for UUID: $UUID" - - if is_mapped; then - echo "Mapper already exists: $MAPPER" - else - sudo cryptsetup open "$dev" "$NAME" - fi - - if [[ ! -d "$MNT" ]]; then - sudo mkdir -p "$MNT" - fi - - if is_mounted; then - echo "Already mounted: $MNT" - else - sudo mount "$MAPPER" "$MNT" - fi - - sudo sync - echo "OK: unlocked and mounted at $MNT" -} - -do_close() { - need_cmd cryptsetup - need_cmd umount - need_cmd mountpoint - need_cmd readlink - - if is_mounted; then - sudo sync - if ! sudo umount "$MNT"; then - show_busy - die "Unmount failed" - fi - fi - - if is_mapped; then - sudo cryptsetup close "$NAME" - fi - - # Best-effort power-off - if [[ -e "$DEV_BY_UUID" ]]; then - local dev - dev="$(resolve_blockdev)" || true - if [[ -n "${dev:-}" ]]; then - if command -v udisksctl >/dev/null 2>&1; then - sudo udisksctl power-off -b "$dev" || true - fi - fi - fi - - echo "OK: unmounted, closed, and powered off (if supported)" -} - -do_status() { - echo "UUID device: $DEV_BY_UUID" - if [[ -e "$DEV_BY_UUID" ]]; then - echo "Resolved: $(readlink -f "$DEV_BY_UUID")" - else - echo "Resolved: (not present)" - fi - - echo -n "Mapper: " - if is_mapped; then echo "OPEN ($MAPPER)"; else echo "CLOSED"; fi - - echo -n "Mount: " - if is_mounted; then echo "MOUNTED ($MNT)"; else echo "NOT MOUNTED"; fi -} - -usage() { - cat </dev/null) + gold_price=$(echo "$gold_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + gold_closes=$(echo "$gold_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + gold_month=$(echo "$gold_closes" | tail -31 | head -1) + gold_year=$(echo "$gold_closes" | head -1) + + sleep 1 + silver_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/SI=F?interval=1d&range=1y' 2>/dev/null) + silver_price=$(echo "$silver_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + silver_closes=$(echo "$silver_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + silver_month=$(echo "$silver_closes" | tail -31 | head -1) + silver_year=$(echo "$silver_closes" | head -1) + + if [ -n "$gold_price" ] && [ -n "$silver_price" ]; then + echo " 🥇 Precious Metals (per oz)" + LC_NUMERIC=C printf " Gold: %.2f $\n" "$gold_price" + if [ -n "$gold_month" ] && [ "$gold_month" != "null" ]; then + month_change=$(echo "scale=4; (($gold_price - $gold_month) / $gold_month) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f $ (%.2f%%)\n" "$gold_month" "$month_change" + fi + if [ -n "$gold_year" ] && [ "$gold_year" != "null" ]; then + year_change=$(echo "scale=4; (($gold_price - $gold_year) / $gold_year) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Year ago: %.2f $ (%.2f%%)\n" "$gold_year" "$year_change" + fi + + LC_NUMERIC=C printf " Silver: %.2f $\n" "$silver_price" + if [ -n "$silver_month" ] && [ "$silver_month" != "null" ]; then + month_change=$(echo "scale=4; (($silver_price - $silver_month) / $silver_month) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f $ (%.2f%%)\n" "$silver_month" "$month_change" + fi + if [ -n "$silver_year" ] && [ "$silver_year" != "null" ]; then + year_change=$(echo "scale=4; (($silver_price - $silver_year) / $silver_year) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Year ago: %.2f $ (%.2f%%)\n" "$silver_year" "$year_change" + fi + + echo "" + # Oblicz Gold/Silver Ratio + gold_silver_ratio=$(echo "scale=4; $gold_price / $silver_price" | LC_NUMERIC=C bc) + + echo " 📊 Metal Ratios:" + LC_NUMERIC=C printf " Gold/Silver: %.2f\n" "$gold_silver_ratio" + + # Interpretacja Gold/Silver ratio + ratio_int=$(echo "scale=0; $gold_silver_ratio / 1" | LC_NUMERIC=C bc) + if [ "$ratio_int" -gt 80 ]; then + echo " → Silver undervalued vs Gold" + elif [ "$ratio_int" -lt 50 ]; then + echo " → Gold undervalued vs Silver" + fi + else + echo " ❌ Metals: Error fetching data" + fi + + echo "" + + # Pobierz kurs CSPX.UK (iShares Core S&P 500) + sleep 1 + cspx_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/CSPX.L?interval=1d&range=2y' 2>/dev/null) + cspx_price=$(echo "$cspx_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + cspx_currency=$(echo "$cspx_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + cspx_closes=$(echo "$cspx_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + cspx_prev_day=$(echo "$cspx_closes" | tail -2 | head -1) + cspx_week_price=$(echo "$cspx_closes" | tail -7 | head -1) + cspx_2week_price=$(echo "$cspx_closes" | tail -14 | head -1) + cspx_month_price=$(echo "$cspx_closes" | tail -31 | head -1) + cspx_13month_price=$(echo "$cspx_closes" | tail -283 | head -1) + + if [ -n "$cspx_price" ]; then + echo " 📊 iShares Core S&P 500 (CSPX.L)" + echo " ISIN: IE00B5BMR087" + + # Determine currency symbol + if [ "$cspx_currency" = "GBP" ]; then + currency_symbol="£" + elif [ "$cspx_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$cspx_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$cspx_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$cspx_price" "$currency_symbol" + + if [ -n "$cspx_prev_day" ] && [ "$cspx_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($cspx_price - $cspx_prev_day) / $cspx_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$cspx_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$cspx_week_price" ] && [ "$cspx_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($cspx_price - $cspx_week_price) / $cspx_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$cspx_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$cspx_2week_price" ] && [ "$cspx_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($cspx_price - $cspx_2week_price) / $cspx_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$cspx_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$cspx_month_price" ] && [ "$cspx_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($cspx_price - $cspx_month_price) / $cspx_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$cspx_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$cspx_month_price" ] && [ "$cspx_month_price" != "null" ] && [ -n "$cspx_13month_price" ] && [ "$cspx_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($cspx_month_price - $cspx_13month_price) / $cspx_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ CSPX.L: Error fetching data" + fi + + echo "" + + # Pobierz kurs EIMI.UK (iShares Core MSCI EM IMI) + sleep 1 + eimi_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/EIMI.L?interval=1d&range=2y' 2>/dev/null) + eimi_price=$(echo "$eimi_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + eimi_currency=$(echo "$eimi_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + eimi_closes=$(echo "$eimi_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + eimi_prev_day=$(echo "$eimi_closes" | tail -2 | head -1) + eimi_week_price=$(echo "$eimi_closes" | tail -7 | head -1) + eimi_2week_price=$(echo "$eimi_closes" | tail -14 | head -1) + eimi_month_price=$(echo "$eimi_closes" | tail -31 | head -1) + eimi_13month_price=$(echo "$eimi_closes" | tail -283 | head -1) + + if [ -n "$eimi_price" ]; then + echo " 📈 iShares Core MSCI EM IMI (EIMI.L)" + echo " ISIN: IE00BKM4GZ66" + + # Determine currency symbol + if [ "$eimi_currency" = "GBP" ]; then + currency_symbol="£" + elif [ "$eimi_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$eimi_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$eimi_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$eimi_price" "$currency_symbol" + + if [ -n "$eimi_prev_day" ] && [ "$eimi_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($eimi_price - $eimi_prev_day) / $eimi_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$eimi_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$eimi_week_price" ] && [ "$eimi_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($eimi_price - $eimi_week_price) / $eimi_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$eimi_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$eimi_2week_price" ] && [ "$eimi_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($eimi_price - $eimi_2week_price) / $eimi_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$eimi_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$eimi_month_price" ] && [ "$eimi_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($eimi_price - $eimi_month_price) / $eimi_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$eimi_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$eimi_month_price" ] && [ "$eimi_month_price" != "null" ] && [ -n "$eimi_13month_price" ] && [ "$eimi_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($eimi_month_price - $eimi_13month_price) / $eimi_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ EIMI.L: Error fetching data" + fi + + echo "" + + # Pobierz kurs CBU0.UK (iShares Core Corporate Bond) + sleep 1 + cbu0_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/CBU0.L?interval=1d&range=2y' 2>/dev/null) + cbu0_price=$(echo "$cbu0_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + cbu0_currency=$(echo "$cbu0_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + cbu0_closes=$(echo "$cbu0_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + cbu0_prev_day=$(echo "$cbu0_closes" | tail -2 | head -1) + cbu0_week_price=$(echo "$cbu0_closes" | tail -7 | head -1) + cbu0_2week_price=$(echo "$cbu0_closes" | tail -14 | head -1) + cbu0_month_price=$(echo "$cbu0_closes" | tail -31 | head -1) + cbu0_13month_price=$(echo "$cbu0_closes" | tail -283 | head -1) + + if [ -n "$cbu0_price" ]; then + echo " 💼 iShares Core Corporate Bond (CBU0.L)" + echo " ISIN: IE00BD1DJ122" + + # Determine currency symbol + if [ "$cbu0_currency" = "GBP" ]; then + currency_symbol="£" + elif [ "$cbu0_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$cbu0_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$cbu0_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$cbu0_price" "$currency_symbol" + + if [ -n "$cbu0_prev_day" ] && [ "$cbu0_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($cbu0_price - $cbu0_prev_day) / $cbu0_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$cbu0_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$cbu0_week_price" ] && [ "$cbu0_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($cbu0_price - $cbu0_week_price) / $cbu0_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$cbu0_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$cbu0_2week_price" ] && [ "$cbu0_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($cbu0_price - $cbu0_2week_price) / $cbu0_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$cbu0_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$cbu0_month_price" ] && [ "$cbu0_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($cbu0_price - $cbu0_month_price) / $cbu0_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$cbu0_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$cbu0_month_price" ] && [ "$cbu0_month_price" != "null" ] && [ -n "$cbu0_13month_price" ] && [ "$cbu0_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($cbu0_month_price - $cbu0_13month_price) / $cbu0_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ CBU0.L: Error fetching data" + fi + + echo "" + + # Pobierz kurs IB01.UK (iShares Treasury Bond 0-1yr) + sleep 1 + ib01_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/IB01.L?interval=1d&range=2y' 2>/dev/null) + ib01_price=$(echo "$ib01_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + ib01_currency=$(echo "$ib01_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + ib01_closes=$(echo "$ib01_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + ib01_prev_day=$(echo "$ib01_closes" | tail -2 | head -1) + ib01_week_price=$(echo "$ib01_closes" | tail -7 | head -1) + ib01_2week_price=$(echo "$ib01_closes" | tail -14 | head -1) + ib01_month_price=$(echo "$ib01_closes" | tail -31 | head -1) + ib01_13month_price=$(echo "$ib01_closes" | tail -283 | head -1) + + if [ -n "$ib01_price" ]; then + echo " 🏦 iShares Treasury Bond 0-1yr (IB01.L)" + echo " ISIN: IE00B4WXJJ64" + + # Determine currency symbol + if [ "$ib01_currency" = "GBP" ]; then + currency_symbol="£" + elif [ "$ib01_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$ib01_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$ib01_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$ib01_price" "$currency_symbol" + + if [ -n "$ib01_prev_day" ] && [ "$ib01_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($ib01_price - $ib01_prev_day) / $ib01_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$ib01_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$ib01_week_price" ] && [ "$ib01_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($ib01_price - $ib01_week_price) / $ib01_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$ib01_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$ib01_2week_price" ] && [ "$ib01_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($ib01_price - $ib01_2week_price) / $ib01_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$ib01_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$ib01_month_price" ] && [ "$ib01_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($ib01_price - $ib01_month_price) / $ib01_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$ib01_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$ib01_month_price" ] && [ "$ib01_month_price" != "null" ] && [ -n "$ib01_13month_price" ] && [ "$ib01_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($ib01_month_price - $ib01_13month_price) / $ib01_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ IB01.L: Error fetching data" + fi + + echo "" + + # Pobierz kurs BETAW20TR (Beta ETF WIG20TR) + sleep 1 + betaw20_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/ETFBW20TR.WA?interval=1d&range=2y' 2>/dev/null) + betaw20_price=$(echo "$betaw20_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + betaw20_currency=$(echo "$betaw20_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + betaw20_closes=$(echo "$betaw20_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + betaw20_prev_day=$(echo "$betaw20_closes" | tail -2 | head -1) + betaw20_week_price=$(echo "$betaw20_closes" | tail -7 | head -1) + betaw20_2week_price=$(echo "$betaw20_closes" | tail -14 | head -1) + betaw20_month_price=$(echo "$betaw20_closes" | tail -31 | head -1) + betaw20_7month_price=$(echo "$betaw20_closes" | tail -157 | head -1) + betaw20_13month_price=$(echo "$betaw20_closes" | tail -283 | head -1) + + if [ -n "$betaw20_price" ]; then + echo " 🇵🇱 Beta ETF WIG20TR (BETAW20TR)" + echo " ISIN: PL0ETF000019" + + # Determine currency symbol + if [ "$betaw20_currency" = "PLN" ]; then + currency_symbol="zł" + elif [ "$betaw20_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$betaw20_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$betaw20_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$betaw20_price" "$currency_symbol" + + if [ -n "$betaw20_prev_day" ] && [ "$betaw20_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($betaw20_price - $betaw20_prev_day) / $betaw20_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$betaw20_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$betaw20_week_price" ] && [ "$betaw20_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($betaw20_price - $betaw20_week_price) / $betaw20_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$betaw20_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$betaw20_2week_price" ] && [ "$betaw20_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($betaw20_price - $betaw20_2week_price) / $betaw20_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$betaw20_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$betaw20_month_price" ] && [ "$betaw20_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($betaw20_price - $betaw20_month_price) / $betaw20_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$betaw20_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$betaw20_month_price" ] && [ "$betaw20_month_price" != "null" ] && [ -n "$betaw20_7month_price" ] && [ "$betaw20_7month_price" != "null" ]; then + six_month_gain=$(echo "scale=4; (($betaw20_month_price - $betaw20_7month_price) / $betaw20_7month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 6-month gain (7m ago to 1m ago): %.2f%%\n" "$six_month_gain" + fi + if [ -n "$betaw20_month_price" ] && [ "$betaw20_month_price" != "null" ] && [ -n "$betaw20_13month_price" ] && [ "$betaw20_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($betaw20_month_price - $betaw20_13month_price) / $betaw20_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ BETAW20TR: Error fetching data" + fi + + echo "" + + # Pobierz kurs BETAM40TR (Beta ETF mWIG40TR) + sleep 1 + betam40_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/ETFBM40TR.WA?interval=1d&range=2y' 2>/dev/null) + betam40_price=$(echo "$betam40_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + betam40_currency=$(echo "$betam40_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + betam40_closes=$(echo "$betam40_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + betam40_prev_day=$(echo "$betam40_closes" | tail -2 | head -1) + betam40_week_price=$(echo "$betam40_closes" | tail -7 | head -1) + betam40_2week_price=$(echo "$betam40_closes" | tail -14 | head -1) + betam40_month_price=$(echo "$betam40_closes" | tail -31 | head -1) + betam40_7month_price=$(echo "$betam40_closes" | tail -157 | head -1) + betam40_13month_price=$(echo "$betam40_closes" | tail -283 | head -1) + + if [ -n "$betam40_price" ]; then + echo " 🇵🇱 Beta ETF mWIG40TR (BETAM40TR)" + echo " ISIN: PL0ETF000027" + + # Determine currency symbol + if [ "$betam40_currency" = "PLN" ]; then + currency_symbol="zł" + elif [ "$betam40_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$betam40_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$betam40_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$betam40_price" "$currency_symbol" + + if [ -n "$betam40_prev_day" ] && [ "$betam40_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($betam40_price - $betam40_prev_day) / $betam40_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$betam40_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$betam40_week_price" ] && [ "$betam40_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($betam40_price - $betam40_week_price) / $betam40_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$betam40_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$betam40_2week_price" ] && [ "$betam40_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($betam40_price - $betam40_2week_price) / $betam40_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$betam40_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$betam40_month_price" ] && [ "$betam40_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($betam40_price - $betam40_month_price) / $betam40_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$betam40_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$betam40_month_price" ] && [ "$betam40_month_price" != "null" ] && [ -n "$betam40_7month_price" ] && [ "$betam40_7month_price" != "null" ]; then + six_month_gain=$(echo "scale=4; (($betam40_month_price - $betam40_7month_price) / $betam40_7month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 6-month gain (7m ago to 1m ago): %.2f%%\n" "$six_month_gain" + fi + if [ -n "$betam40_month_price" ] && [ "$betam40_month_price" != "null" ] && [ -n "$betam40_13month_price" ] && [ "$betam40_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($betam40_month_price - $betam40_13month_price) / $betam40_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ BETAM40TR: Error fetching data" + fi + + echo "" + + # Pobierz kurs BETAS80TR (Beta ETF sWIG80TR) + sleep 1 + betas80_response=$(curl -s -A "Mozilla/5.0" 'https://query1.finance.yahoo.com/v8/finance/chart/ETFBS80TR.WA?interval=1d&range=2y' 2>/dev/null) + betas80_price=$(echo "$betas80_response" | grep -o '"regularMarketPrice":[0-9.]*' | head -1 | cut -d':' -f2) + betas80_currency=$(echo "$betas80_response" | grep -o '"currency":"[A-Z]*"' | head -1 | cut -d'"' -f4) + betas80_closes=$(echo "$betas80_response" | grep -o '"close":\[[^]]*\]' | sed 's/"close":\[//;s/\]//' | tr ',' '\n') + betas80_prev_day=$(echo "$betas80_closes" | tail -2 | head -1) + betas80_week_price=$(echo "$betas80_closes" | tail -7 | head -1) + betas80_2week_price=$(echo "$betas80_closes" | tail -14 | head -1) + betas80_month_price=$(echo "$betas80_closes" | tail -31 | head -1) + betas80_7month_price=$(echo "$betas80_closes" | tail -157 | head -1) + betas80_13month_price=$(echo "$betas80_closes" | tail -283 | head -1) + + if [ -n "$betas80_price" ]; then + echo " 🇵🇱 Beta ETF sWIG80TR (BETAS80TR)" + echo " ISIN: PL0ETF000035" + + # Determine currency symbol + if [ "$betas80_currency" = "PLN" ]; then + currency_symbol="zł" + elif [ "$betas80_currency" = "EUR" ]; then + currency_symbol="€" + elif [ "$betas80_currency" = "USD" ]; then + currency_symbol="$" + else + currency_symbol="$betas80_currency" + fi + + LC_NUMERIC=C printf " Price: %.2f %s\n" "$betas80_price" "$currency_symbol" + + if [ -n "$betas80_prev_day" ] && [ "$betas80_prev_day" != "null" ]; then + prev_change=$(echo "scale=4; (($betas80_price - $betas80_prev_day) / $betas80_prev_day) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Previous day: %.2f %s (%.2f%%)\n" "$betas80_prev_day" "$currency_symbol" "$prev_change" + fi + if [ -n "$betas80_week_price" ] && [ "$betas80_week_price" != "null" ]; then + week_change=$(echo "scale=4; (($betas80_price - $betas80_week_price) / $betas80_week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Week ago: %.2f %s (%.2f%%)\n" "$betas80_week_price" "$currency_symbol" "$week_change" + fi + if [ -n "$betas80_2week_price" ] && [ "$betas80_2week_price" != "null" ]; then + week2_change=$(echo "scale=4; (($betas80_price - $betas80_2week_price) / $betas80_2week_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 2 weeks ago: %.2f %s (%.2f%%)\n" "$betas80_2week_price" "$currency_symbol" "$week2_change" + fi + if [ -n "$betas80_month_price" ] && [ "$betas80_month_price" != "null" ]; then + month_change=$(echo "scale=4; (($betas80_price - $betas80_month_price) / $betas80_month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " Month ago: %.2f %s (%.2f%%)\n" "$betas80_month_price" "$currency_symbol" "$month_change" + fi + if [ -n "$betas80_month_price" ] && [ "$betas80_month_price" != "null" ] && [ -n "$betas80_7month_price" ] && [ "$betas80_7month_price" != "null" ]; then + six_month_gain=$(echo "scale=4; (($betas80_month_price - $betas80_7month_price) / $betas80_7month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 6-month gain (7m ago to 1m ago): %.2f%%\n" "$six_month_gain" + fi + if [ -n "$betas80_month_price" ] && [ "$betas80_month_price" != "null" ] && [ -n "$betas80_13month_price" ] && [ "$betas80_13month_price" != "null" ]; then + year_gain=$(echo "scale=4; (($betas80_month_price - $betas80_13month_price) / $betas80_13month_price) * 100" | LC_NUMERIC=C bc) + LC_NUMERIC=C printf " 12-month gain (13m ago to 1m ago): %.2f%%\n" "$year_gain" + fi + else + echo " ❌ BETAS80TR: Error fetching data" + fi + + echo "" + echo " Updated: $(date '+%Y-%m-%d %H:%M:%S')" + echo " Press Ctrl+C to exit" + echo "" + echo " Next update in 30 minutes..." + + # Czekaj 30 minut przed następnym odświeżeniem + sleep 1800 +done diff --git a/nextcloud b/nextcloud deleted file mode 100644 index fae9bdb..0000000 --- a/nextcloud +++ /dev/null @@ -1 +0,0 @@ -aHR0cHM6Ly9jbG91ZC5sYWJ1bml4Lnh5ejo5NDQzCmxvZ2luOiBhZGFtCm1haWw6IGFkYW0uZ29tdWxrYTk0QGdtYWlsLmNvbSAKaGFzxYJvOiAgYk9PP2BAZD15S1Q5XmNWYAoKdXN0YXcgc29iaWUgMkZBIHBvdGVtIGRsYSBiZXpwaWVjemXFhHN0d2E= diff --git a/officium_readme.md b/officium_readme.md deleted file mode 100644 index b4e00a5..0000000 --- a/officium_readme.md +++ /dev/null @@ -1,135 +0,0 @@ -# Log Projektu: Skrypt Bash do Divinumofficium.com - -## Zapytanie użytkownika: -Utworzyć skrypt bash który: -- Przyjmuje jeden argument ze zbioru: [Prima, Tertia, Sexta, Nona, Vesperae] -- Pobiera dane ze strony https://www.divinumofficium.com/cgi-bin/horas/officium.pl -- Znajduje linki ukryte pod hover dla wybranej modlitwy (Laudes, Prima, Tertia, Sexta, Nona, Vesperae) -- Pobiera tekst od części "Incipit" aż do końca części "Conclusio" -- Wyświetla zawartość w terminalu - -## Krok 1: Analiza struktury strony -Data: 2026-01-13 22:12 - -Pobieram główną stronę aby zrozumieć strukturę HTML i znaleźć linki do poszczególnych modlitw. - -### Wyniki analizy: -- Strona używa parametru `command=pray{NazwaModlitwy}` w URL -- Przykład: `officium.pl?command=prayPrima` -- Treść modlitwy znajduje się w tabeli HTML -- Sekcja "Incipit" to początek (oznaczona znacznikiem `Incipit`) -- Sekcja "Conclusio" to koniec (analogicznie oznaczona) -- Treść jest w formacie HTML z tagami FONT, BR, itd. - -## Krok 2: Tworzenie skryptu bash -Data: 2026-01-13 22:12 - -Tworzę skrypt który: -1. Waliduje argument wejściowy -2. Buduje URL z odpowiednim parametrem command -3. Pobiera stronę za pomocą curl -4. Ekstrahuje treść od "Incipit" do końca "Conclusio" -5. Czyści HTML i formatuje dla terminala - -### Pierwsze podejście: -- Utworzono plik `divinumofficium.sh` -- Użyto sed do ekstrakcji, ale nie działało prawidłowo - -### Poprawki: -- Zmieniono na awk, który czyta od Incipit do zamknięcia -- Dodano filtry do usunięcia linków nawigacyjnych (Top, Next) -- Dodano dekodowanie encji HTML - -## Krok 3: Testowanie skryptu -Data: 2026-01-13 22:12 - -### Testy przeprowadzone: -1. ✅ `./divinumofficium.sh Prima` - działa poprawnie -2. ✅ `./divinumofficium.sh Vesperae` - działa poprawnie -3. ✅ `./divinumofficium.sh Sexta` - działa poprawnie -4. ✅ Brak argumentu - wyświetla instrukcję użycia -5. ✅ Nieprawidłowy argument (Laudes) - wyświetla błąd - -### Wnioski: -Skrypt działa zgodnie z wymaganiami: -- Przyjmuje argumenty: Prima, Tertia, Sexta, Nona, Vesperae -- Pobiera treść od "Incipit" do końca "Conclusio" -- Wyświetla czytelnie w terminalu (z tekstem łacińskim i angielskim) -- Waliduje dane wejściowe -- Obsługuje błędy połączenia - -## Podsumowanie -Data: 2026-01-13 22:13 - -Utworzono działający skrypt bash `divinumofficium.sh`, który: -- Lokalizacja: `/home/adam/copilot_workspace/kasprzak_project/divinumofficium.sh` -- Rozmiar: ~1.8KB -- Uprawnienia: wykonywalne (chmod +x) -- Status: UKOŃCZONE ✅ - -## Krok 4: Modyfikacja obsługi argumentów -Data: 2026-01-13 22:17 - -### Zapytanie użytkownika: -"popraw aby argumenty pisać małymi literami" - -### Zmiany wprowadzone: -- Argumenty przyjmowane są teraz małymi literami: prima, tertia, sexta, nona, vesperae -- Dodano automatyczną konwersję: pierwsza litera na wielką (bo API wymaga Prima, Tertia, etc.) -- Zaktualizowano komunikaty pomocy i błędów -- Zachowano zgodność z API (nadal wysyła "Prima", "Tertia", etc.) - -### Test: -Teraz użytkownik może pisać: `./divinumofficium.sh prima` zamiast `./divinumofficium.sh Prima` - -## Krok 5: Filtrowanie tylko tekstu łacińskiego -Data: 2026-01-13 22:22 - -### Zapytanie użytkownika: -"wynik jest po łacinie i angielsku. Ma być tylko część po łacinie" - -### Analiza problemu: -Strona HTML zawiera tabelę z dwiema kolumnami: -- Pierwsza kolumna (``): tekst łaciński -- Druga kolumna (``): tekst angielski - -### Zmiany wprowadzone: -- Zmodyfikowano skrypt awk aby pomijać drugą kolumnę (angielską) -- Dodano wykrywanie początku drugiej kolumny (po numerze sekcji) -- Dodano filtr usuwający pojedyncze cyfry (numery sekcji) -- Zachowano tylko tekst łaciński w wyniku - -### Test: -```bash -./divinumofficium.sh prima # Wyświetla tylko tekst łaciński -./divinumofficium.sh vesperae # Wyświetla tylko tekst łaciński -``` - -Wynik zawiera teraz wyłącznie modlitwy w języku łacińskim. - -## Krok 6: Dodanie odstępów między sekcjami -Data: 2026-01-13 22:28 - -### Zapytanie użytkownika: -"może dodaj jeszcze wolną linię między częściami " - -### Zmiany wprowadzone: -- Dodano pustą linię po każdym `` (koniec sekcji w tabeli) -- Użyto `cat -s` aby zredukować wielokrotne puste linie do maksymalnie dwóch -- Sekcje są teraz lepiej wizualnie oddzielone - -### Efekt: -Każda sekcja modlitwy (Incipit, Hymnus, Psalmi, Lectio brevis, Conclusio, etc.) jest oddzielona pustą linią, co poprawia czytelność w terminalu. - -Przykład: -``` -Incipit - -℣. Deus ✠ in adiutórium... - -Hymnus - -Iam lucis orto sídere... - -Psalmi {ex Psalterio... -``` diff --git a/passmenu-otp b/passmenu-otp index d511944..11a236b 100755 --- a/passmenu-otp +++ b/passmenu-otp @@ -1,24 +1,28 @@ #!/bin/sh set -eu +# Config : "${PASSWORD_STORE_DIR:=$HOME/.password-store}" DMENU="${DMENU:-dmenu}" +OTP_PREFIX="${OTP_PREFIX:-otp}" # expects entries under $PASSWORD_STORE_DIR/otp/ -# Build list of pass entry paths (relative to PASSWORD_STORE_DIR) that contain an otpauth:// line. +otp_dir="$PASSWORD_STORE_DIR/$OTP_PREFIX" + +# If no otp/ subtree exists, exit quietly +[ -d "$otp_dir" ] || exit 0 + +# Build list of entry names from filenames only (no decryption). entries=$( - find "$PASSWORD_STORE_DIR" -type f -name '*.gpg' ! -path '*/.git/*' -print 2>/dev/null | - while IFS= read -r f; do - entry="${f#$PASSWORD_STORE_DIR/}" - entry="${entry%.gpg}" - if pass show "$entry" 2>/dev/null | grep -q '^otpauth://'; then - printf '%s\n' "$entry" - fi - done + find "$otp_dir" -type f -name '*.gpg' ! -path '*/.git/*' -print 2>/dev/null | + sed -e "s|^$PASSWORD_STORE_DIR/||" -e 's|\.gpg$||' | + sort ) [ -n "${entries:-}" ] || exit 0 -choice=$(printf '%s\n' "$entries" | "$DMENU" -i -p "OTP:") +choice=$(printf '%s\n' "$entries" | $DMENU -i -p "OTP:") [ -n "${choice:-}" ] || exit 0 +# Copy OTP to clipboard using pass-otp extension (-c handles clipboard + clearing). pass otp -c "$choice" + diff --git a/rofi-task-names b/rofi-task-names new file mode 100755 index 0000000..965bb93 --- /dev/null +++ b/rofi-task-names @@ -0,0 +1,26 @@ +#!/bin/sh +set -eu + +DB="$HOME/.local/share/cheat-sheets/task-tags.tsv" + +[ -f "$DB" ] || exit 1 + +choice="$( + cat "$DB" | + rofi -dmenu -i -p 'Naming conventions ›' +)" + +[ -n "${choice:-}" ] || exit 0 + +# First column = keystrokes +keys="$(printf '%s' "$choice" | cut -f1)" + +# Clipboard (Wayland/X11) +if command -v wl-copy >/dev/null 2>&1; then + printf '%s' "$keys" | wl-copy +elif command -v xclip >/dev/null 2>&1; then + printf '%s' "$keys" | xclip -selection clipboard +else + printf '%s\n' "$keys" +fi + diff --git a/shield-backup b/shield-backup new file mode 100755 index 0000000..96f9269 --- /dev/null +++ b/shield-backup @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +set -euo pipefail + +#################### CONFIG – EDIT THESE #################### + +# LUKS2 block device +LUKS_DEVICE="/dev/disk/by-uuid/6659e638-d52a-4c32-9781-9dcedd44db35" + +# Name for the mapper device (will appear as /dev/mapper/${LUKS_NAME}) +LUKS_NAME="shield" + +# Mount point for the decrypted filesystem +LUKS_MOUNTPOINT="/mnt/shield" + +# Local folders to back up with rsync → LUKS volume +# Each folder will be mirrored into ${LUKS_MOUNTPOINT}/ +BACKUP_ITEMS=( + "$HOME/" + "/media/.secrets/" +) + +# Machine identifier (destination subdir on the LUKS volume) +MACHINE_DIR="T480" + +#################### INTERNALS – NO NEED TO EDIT ############ + +need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing command: $1" >&2; exit 1; }; } + +cleanup() { + echo + echo ">>> Cleaning up..." + + # Try to unmount, if mounted + if mountpoint -q "${LUKS_MOUNTPOINT}"; then + echo ">>> Unmounting ${LUKS_MOUNTPOINT}..." + sudo umount "${LUKS_MOUNTPOINT}" || true + fi + + # Close LUKS mapping if it exists + if [ -e "/dev/mapper/${LUKS_NAME}" ]; then + echo ">>> Closing LUKS mapping ${LUKS_NAME}..." + sudo cryptsetup close "${LUKS_NAME}" || true + fi +} +trap cleanup EXIT + +need_cmd cryptsetup +need_cmd rclone +need_cmd rsync +need_cmd mountpoint + +# Cache sudo credentials early to avoid surprises mid-run +sudo -v + +echo ">>> Ensuring mountpoint exists: ${LUKS_MOUNTPOINT}" +sudo mkdir -p "${LUKS_MOUNTPOINT}" + +# Check if already open +if [ -e "/dev/mapper/${LUKS_NAME}" ]; then + echo ">>> WARNING: /dev/mapper/${LUKS_NAME} already exists, assuming already open." +else + echo ">>> Opening LUKS volume (interactive prompt)..." + sudo cryptsetup open "${LUKS_DEVICE}" "${LUKS_NAME}" +fi + +# At this point /dev/mapper/${LUKS_NAME} must exist +if [ ! -e "/dev/mapper/${LUKS_NAME}" ]; then + echo "ERROR: /dev/mapper/${LUKS_NAME} not found after cryptsetup open." >&2 + exit 1 +fi + +# Mount if not already mounted +if mountpoint -q "${LUKS_MOUNTPOINT}"; then + echo ">>> WARNING: ${LUKS_MOUNTPOINT} already mounted." +else + echo ">>> Mounting /dev/mapper/${LUKS_NAME} on ${LUKS_MOUNTPOINT}..." + sudo mount "/dev/mapper/${LUKS_NAME}" "${LUKS_MOUNTPOINT}" +fi + +echo ">>> LUKS volume mounted at ${LUKS_MOUNTPOINT}" + +# Ensure SharePoint destinations exist in root of shield +sudo mkdir -p \ + "${LUKS_MOUNTPOINT}/SP_Administration" \ + "${LUKS_MOUNTPOINT}/SP_Operations" \ + "${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 +echo ">>> All rclone syncs finished." + +echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHINE_DIR}/..." + +# 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" + continue + fi + + case "$SRC" in + "$HOME/"|"$HOME") + RELDEST="${MACHINE_DIR}/HOME" + ;; + "/media/.secrets/"|"/media/.secrets") + 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}" + ;; + esac + + DEST="${LUKS_MOUNTPOINT}/${RELDEST}" + sudo mkdir -p "${DEST}" + + echo ">>> rsync: ${SRC} -> ${DEST}" + rsync -a --delete --progress \ + "${SRC%/}/" \ + "${DEST}/" +done + +echo ">>> All rsync backups finished." +echo ">>> Unmounting and closing LUKS (via trap)..." +# cleanup() will run automatically on script exit + diff --git a/shield-mount b/shield-mount new file mode 100755 index 0000000..9080f35 --- /dev/null +++ b/shield-mount @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -euo pipefail + +UUID="6659e638-d52a-4c32-9781-9dcedd44db35" +NAME="shield" +MNT="/mnt/shield" + +DEV_BY_UUID="/dev/disk/by-uuid/${UUID}" +MAPPER="/dev/mapper/${NAME}" + +die() { echo "Error: $*" >&2; exit 1; } + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing command: $1" +} + +resolve_blockdev() { + # Resolve the UUID symlink to an absolute /dev/... node + local target + target="$(readlink -f "$DEV_BY_UUID")" || return 1 + [[ -b "$target" ]] || return 1 + echo "$target" +} + +is_mapped() { + [[ -e "$MAPPER" ]] +} + +is_mounted() { + mountpoint -q "$MNT" +} + +show_busy() { + echo "Mount appears busy. Processes using $MNT:" + # lsof is optional but useful; show what we can. + if command -v lsof >/dev/null 2>&1; then + sudo lsof +f -- "$MNT" || true + else + echo "(Install lsof: sudo apt install lsof)" + sudo fuser -vm "$MNT" || true + fi +} + +do_open() { + need_cmd cryptsetup + need_cmd mount + need_cmd mountpoint + need_cmd readlink + + [[ -e "$DEV_BY_UUID" ]] || die "UUID device not found: $DEV_BY_UUID (is the drive plugged in?)" + + local dev + dev="$(resolve_blockdev)" || die "Could not resolve block device for UUID: $UUID" + + if is_mapped; then + echo "Mapper already exists: $MAPPER" + else + sudo cryptsetup open "$dev" "$NAME" + fi + + if [[ ! -d "$MNT" ]]; then + sudo mkdir -p "$MNT" + fi + + if is_mounted; then + echo "Already mounted: $MNT" + else + sudo mount "$MAPPER" "$MNT" + fi + + sudo sync + echo "OK: unlocked and mounted at $MNT" +} + +do_close() { + need_cmd cryptsetup + need_cmd umount + need_cmd mountpoint + need_cmd readlink + + if is_mounted; then + sudo sync + if ! sudo umount "$MNT"; then + show_busy + die "Unmount failed" + fi + fi + + if is_mapped; then + sudo cryptsetup close "$NAME" + fi + + # Best-effort power-off + if [[ -e "$DEV_BY_UUID" ]]; then + local dev + dev="$(resolve_blockdev)" || true + if [[ -n "${dev:-}" ]]; then + if command -v udisksctl >/dev/null 2>&1; then + sudo udisksctl power-off -b "$dev" || true + fi + fi + fi + + echo "OK: unmounted, closed, and powered off (if supported)" +} + +do_status() { + echo "UUID device: $DEV_BY_UUID" + if [[ -e "$DEV_BY_UUID" ]]; then + echo "Resolved: $(readlink -f "$DEV_BY_UUID")" + else + echo "Resolved: (not present)" + fi + + echo -n "Mapper: " + if is_mapped; then echo "OPEN ($MAPPER)"; else echo "CLOSED"; fi + + echo -n "Mount: " + if is_mounted; then echo "MOUNTED ($MNT)"; else echo "NOT MOUNTED"; fi +} + +usage() { + cat < +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) +BACKUP_ITEMS=( + "$HOME/.abook" + "$HOME/.config" + "$HOME/Documents" + "$HOME/dotfiles" + "$HOME/.password-store" + "$HOME/secure" + "$HOME/vimwiki" + "$HOME/.password.tomb" + "$HOME/.secrets.tomb" +) + +RSYNC_OPTS=( + -a + --delete + --no-perms + --no-owner + --no-group + --no-xattrs + --omit-dir-times + --progress +) + +LOGFILE="$HOME/backup_flash_rsync.log" + +#################### HELPERS #################### + +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 +} + +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}'" + } + fi + + if is_mapper_open; then + echo ">>> Closing LUKS mapper ${MAPPER_NAME}..." + sudo cryptsetup close "${MAPPER_NAME}" || 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 #################### + +echo ">>> Checking LUKS source exists: ${LUKS_SOURCE}" +[[ -e "${LUKS_SOURCE}" ]] || die "LUKS source not found: ${LUKS_SOURCE}" + +echo ">>> Ensuring mountpoint exists: ${MOUNTPOINT}" +sudo mkdir -p "${MOUNTPOINT}" + +is_mounted && die "Mountpoint already in use: ${MOUNTPOINT}" +is_mapper_open && die "Mapper already open: /dev/mapper/${MAPPER_NAME}" + +echo ">>> Opening LUKS2 container (prompt for passphrase)..." +sudo cryptsetup open --type luks2 "${LUKS_SOURCE}" "${MAPPER_NAME}" + +echo ">>> Mounting decrypted filesystem..." +sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}" + +echo ">>> Mounted at ${MOUNTPOINT}" +echo ">>> Logging rsync output to: ${LOGFILE}" +echo "[$(date)] Backup run started" >> "${LOGFILE}" + +#################### RSYNC LOOP #################### + +ANY_WARNING=0 + +for SRC in "${BACKUP_ITEMS[@]}"; do + [[ -e "${SRC}" ]] || die "Missing source item: ${SRC}" + + NAME="$(basename "${SRC}")" + DEST="${MOUNTPOINT}/${NAME}" + + echo ">>> rsync: ${SRC} -> ${DEST}" + echo "[$(date)] rsync: ${SRC} -> ${DEST}" >> "${LOGFILE}" + + set +e + if [[ -d "${SRC}" ]]; then + rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${DEST}/" 2>> "${LOGFILE}" + else + # file + 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}" + 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}" + ANY_WARNING=1 + else + 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}" +else + echo ">>> Backup completed OK." + echo "[$(date)] Backup completed OK" >> "${LOGFILE}" +fi + +echo ">>> Syncing..." +sync + +echo ">>> Done. (Auto-cleanup will unmount + close mapper.)" + diff --git a/usb-mount b/usb-mount new file mode 100755 index 0000000..e983360 --- /dev/null +++ b/usb-mount @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +set -euo pipefail + +UUID="bdd2a016-b6db-493d-ad0a-e8dd8a93dfbd" +NAME="usb_safe" +MNT="/mnt/usb_safe" + +DEV_BY_UUID="/dev/disk/by-uuid/${UUID}" +MAPPER="/dev/mapper/${NAME}" + +die() { echo "Error: $*" >&2; exit 1; } + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing command: $1" +} + +resolve_blockdev() { + local target + target="$(readlink -f "$DEV_BY_UUID")" || return 1 + [[ -b "$target" ]] || return 1 + echo "$target" +} + +is_mapped() { [[ -e "$MAPPER" ]]; } +is_mounted() { mountpoint -q "$MNT"; } + +show_busy() { + echo "Mount appears busy. Processes using $MNT:" + if command -v lsof >/dev/null 2>&1; then + sudo lsof +f -- "$MNT" || true + else + echo "(Install lsof: sudo apt install lsof)" + sudo fuser -vm "$MNT" || true + fi +} + +do_open() { + need_cmd cryptsetup + need_cmd mount + need_cmd mountpoint + need_cmd readlink + + [[ -e "$DEV_BY_UUID" ]] || die "UUID device not found: $DEV_BY_UUID (is the drive plugged in?)" + + local dev + dev="$(resolve_blockdev)" || die "Could not resolve block device for UUID: $UUID" + + if is_mapped; then + echo "Mapper already exists: $MAPPER" + else + sudo cryptsetup open "$dev" "$NAME" + fi + + if [[ ! -d "$MNT" ]]; then + sudo mkdir -p "$MNT" + fi + + if is_mounted; then + echo "Already mounted: $MNT" + else + sudo mount "$MAPPER" "$MNT" + fi + + sudo sync + echo "OK: unlocked and mounted at $MNT" +} + +do_close() { + need_cmd cryptsetup + need_cmd umount + need_cmd mountpoint + need_cmd readlink + + if is_mounted; then + sudo sync + if ! sudo umount "$MNT"; then + show_busy + die "Unmount failed" + fi + fi + + if is_mapped; then + sudo cryptsetup close "$NAME" + fi + + if [[ -e "$DEV_BY_UUID" ]]; then + local dev + dev="$(resolve_blockdev)" || true + if [[ -n "${dev:-}" ]] && command -v udisksctl >/dev/null 2>&1; then + sudo udisksctl power-off -b "$dev" || true + fi + fi + + echo "OK: unmounted, closed, and powered off (if supported)" +} + +do_status() { + echo "UUID device: $DEV_BY_UUID" + if [[ -e "$DEV_BY_UUID" ]]; then + echo "Resolved: $(readlink -f "$DEV_BY_UUID")" + else + echo "Resolved: (not present)" + fi + + echo -n "Mapper: " + if is_mapped; then echo "OPEN ($MAPPER)"; else echo "CLOSED"; fi + + echo -n "Mount: " + if is_mounted; then echo "MOUNTED ($MNT)"; else echo "NOT MOUNTED"; fi +} + +usage() { + cat <&2; exit 1; } - -need_cmd() { - command -v "$1" >/dev/null 2>&1 || die "Missing command: $1" -} - -resolve_blockdev() { - local target - target="$(readlink -f "$DEV_BY_UUID")" || return 1 - [[ -b "$target" ]] || return 1 - echo "$target" -} - -is_mapped() { [[ -e "$MAPPER" ]]; } -is_mounted() { mountpoint -q "$MNT"; } - -show_busy() { - echo "Mount appears busy. Processes using $MNT:" - if command -v lsof >/dev/null 2>&1; then - sudo lsof +f -- "$MNT" || true - else - echo "(Install lsof: sudo apt install lsof)" - sudo fuser -vm "$MNT" || true - fi -} - -do_open() { - need_cmd cryptsetup - need_cmd mount - need_cmd mountpoint - need_cmd readlink - - [[ -e "$DEV_BY_UUID" ]] || die "UUID device not found: $DEV_BY_UUID (is the drive plugged in?)" - - local dev - dev="$(resolve_blockdev)" || die "Could not resolve block device for UUID: $UUID" - - if is_mapped; then - echo "Mapper already exists: $MAPPER" - else - sudo cryptsetup open "$dev" "$NAME" - fi - - if [[ ! -d "$MNT" ]]; then - sudo mkdir -p "$MNT" - fi - - if is_mounted; then - echo "Already mounted: $MNT" - else - sudo mount "$MAPPER" "$MNT" - fi - - sudo sync - echo "OK: unlocked and mounted at $MNT" -} - -do_close() { - need_cmd cryptsetup - need_cmd umount - need_cmd mountpoint - need_cmd readlink - - if is_mounted; then - sudo sync - if ! sudo umount "$MNT"; then - show_busy - die "Unmount failed" - fi - fi - - if is_mapped; then - sudo cryptsetup close "$NAME" - fi - - if [[ -e "$DEV_BY_UUID" ]]; then - local dev - dev="$(resolve_blockdev)" || true - if [[ -n "${dev:-}" ]] && command -v udisksctl >/dev/null 2>&1; then - sudo udisksctl power-off -b "$dev" || true - fi - fi - - echo "OK: unmounted, closed, and powered off (if supported)" -} - -do_status() { - echo "UUID device: $DEV_BY_UUID" - if [[ -e "$DEV_BY_UUID" ]]; then - echo "Resolved: $(readlink -f "$DEV_BY_UUID")" - else - echo "Resolved: (not present)" - fi - - echo -n "Mapper: " - if is_mapped; then echo "OPEN ($MAPPER)"; else echo "CLOSED"; fi - - echo -n "Mount: " - if is_mounted; then echo "MOUNTED ($MNT)"; else echo "NOT MOUNTED"; fi -} - -usage() { - cat <