365 Days of Code - Day 047

Project Status

ProjectLanguageStatusDue DateLatest Update
Personal WebsiteHugoOngoingNoneThe site is live. There are some TODOs. Need to work on categorization, tagging, and layout improvements.
Laravel From ScratchLaravel (PHP)In-Progress2026-03-31Episode 8
PRMLaravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Client Website (J.L.)Laravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Project EulerCOngoingNoneWorking on P25. BigInt (AI gen) was a waste of time, need to rewrite
Practice JavaJavaPausedNoneInstalled, need to find a good project.
Practice PythonPythonPausedNoneInstalled, need to find a good project.
Learn GoGoPausedNoneInstalled, work on LDAP Injector from ippsec.
Learn RustRustHaven’t StartedNoneInstalled, will try network protocols after finishing in C and Zig.
Learn ElixirElixirHaven’t StartedNoneInstalled, need a good tutorial project.
Learn HaskellHaskellHaven’t StartedNoneInstalled, need a good tutorial project.
Learn ZigZigHaven’t StartedNoneInstalled, will try network protocols after finishing in C.
Linux+N/AIn-Progress2026-03-31Reading Chapter 4.
Cyber Quest 2026N/AIn-Progress2026-02-28Finished quiz 1 with 75%.
Operating SystemsN/AIn-Progress2026-03-31Reading Chapter 4: Abstraction
Grey-Hat HackingVariousIn-Progress2026-03-31Reading Chapter 8: Threat Hunting Lab
PHP Time TrackerPHPBeta FinishedNoneWorking on a basic level. Could use a couple more updates to make it fully functional.
HTTP Status Code ReaderCComplete2026-02-18Complete. Could potentially upgrade for more advanced functions or follow redirects.
ZSH Configurationbash/zshCompleteNoneSort of an ongoing process, but complete for now. Works good.
Network ProtocolsCIn-ProgressNoneV2 complete. Moving to V3, refactoring again.
Discinox WebsiteHTML, CSS, JSComplete2026-03-04The site is live.
DiroffTech WebsiteHTML, CSS, JSComplete2026-03-05The site is live. git-lfs needs to be initialized for images.
Automate BackupsbashComplete2026-03-08Backups done.

Archive Day

Today is archive day. I take the backups we created yesterday, and copy those to a remote archive server. The primary complication is that we don’t automatically know what the names of the backups are. They will have unique dates and times. Despite executing on a set schedule, we should be able to detect the file name to copy. We also need to include the checksum file, and run the checksum to validate the integrity of the backup. Lastly, the backups are stored while being owned by root.

A new user and SSH key was added for the archiving purposes on the web server. The script below runs on the local archive server.

bash
#!/usr/bin/env bash
set -Eeuo pipefail

REMOTE_USER="archivepull"
REMOTE_HOST="webserver.example.com"
REMOTE_DIR="/var/backups/local"
SSH_KEY="/home/user/.ssh/archive_pull"
LOCAL_DIR="/srv/archive/webserver"
LOCK_FILE="/var/lock/pull_web_backup.lock"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
}

warn() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $*" >&2
}

error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
}

require_command() {
    local cmd="$1"
    command -v "${cmd}" >/dev/null 2>&1 || {
        error "Required command not found: ${cmd}"
        exit 1
    }
}

create_lock() {
    mkdir -p "$(dirname "${LOCK_FILE}")"
    exec 9>"${LOCK_FILE}"
    flock -n 9 || {
        error "Another archive pull job is already running"
        exit 1
    }
}

main() {
    local latest_archive
    local latest_file
    local latest_checksum

    require_command ssh
    require_command rsync
    require_command sha256sum
    require_command find
    require_command sort
    require_command tail
    require_command basename

    mkdir -p "${LOCAL_DIR}"
    create_lock

    log "Locating newest remote backup"

    latest_archive="$(ssh -i "${SSH_KEY}" -o BatchMode=yes "${REMOTE_USER}@${REMOTE_HOST}" \
        "find '${REMOTE_DIR}' -maxdepth 1 -type f -name 'backup_*.tar.gz' | sort | tail -n 1")"

    if [[ -z "${latest_archive}" ]]; then
        error "No remote backup archive found"
        exit 1
    fi

    latest_file="$(basename "${latest_archive}")"
    latest_checksum="${latest_file}.sha256"

    log "Newest backup identified: ${latest_file}"

    if [[ -f "${LOCAL_DIR}/${latest_file}" && -f "${LOCAL_DIR}/${latest_checksum}" ]]; then
        log "Latest backup already exists locally, validating checksum"
        (
            cd "${LOCAL_DIR}"
            sha256sum -c "${latest_checksum}" >/dev/null
        )
        log "Backup already archived and verified; skipping copy"
        exit 0
    fi

    log "Copying backup and checksum from remote server"
    rsync -av --partial -e "ssh -i ${SSH_KEY} -o BatchMode=yes" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${latest_file}" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${latest_checksum}" \
        "${LOCAL_DIR}/"

    log "Validating checksum"
    (
        cd "${LOCAL_DIR}"
        sha256sum -c "${latest_checksum}" >/dev/null
    )

    log "Archive pull completed successfully"
}

main "$@"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env bash
set -Eeuo pipefail

REMOTE_USER="archivepull"
REMOTE_HOST="webserver.example.com"
REMOTE_DIR="/var/backups/local"
SSH_KEY="/home/user/.ssh/archive_pull"
LOCAL_DIR="/srv/archive/webserver"
LOCK_FILE="/var/lock/pull_web_backup.lock"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
}

warn() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $*" >&2
}

error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
}

require_command() {
    local cmd="$1"
    command -v "${cmd}" >/dev/null 2>&1 || {
        error "Required command not found: ${cmd}"
        exit 1
    }
}

create_lock() {
    mkdir -p "$(dirname "${LOCK_FILE}")"
    exec 9>"${LOCK_FILE}"
    flock -n 9 || {
        error "Another archive pull job is already running"
        exit 1
    }
}

main() {
    local latest_archive
    local latest_file
    local latest_checksum

    require_command ssh
    require_command rsync
    require_command sha256sum
    require_command find
    require_command sort
    require_command tail
    require_command basename

    mkdir -p "${LOCAL_DIR}"
    create_lock

    log "Locating newest remote backup"

    latest_archive="$(ssh -i "${SSH_KEY}" -o BatchMode=yes "${REMOTE_USER}@${REMOTE_HOST}" \
        "find '${REMOTE_DIR}' -maxdepth 1 -type f -name 'backup_*.tar.gz' | sort | tail -n 1")"

    if [[ -z "${latest_archive}" ]]; then
        error "No remote backup archive found"
        exit 1
    fi

    latest_file="$(basename "${latest_archive}")"
    latest_checksum="${latest_file}.sha256"

    log "Newest backup identified: ${latest_file}"

    if [[ -f "${LOCAL_DIR}/${latest_file}" && -f "${LOCAL_DIR}/${latest_checksum}" ]]; then
        log "Latest backup already exists locally, validating checksum"
        (
            cd "${LOCAL_DIR}"
            sha256sum -c "${latest_checksum}" >/dev/null
        )
        log "Backup already archived and verified; skipping copy"
        exit 0
    fi

    log "Copying backup and checksum from remote server"
    rsync -av --partial -e "ssh -i ${SSH_KEY} -o BatchMode=yes" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${latest_file}" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${latest_checksum}" \
        "${LOCAL_DIR}/"

    log "Validating checksum"
    (
        cd "${LOCAL_DIR}"
        sha256sum -c "${latest_checksum}" >/dev/null
    )

    log "Archive pull completed successfully"
}

main "$@"

Related content