Foto ulang tahun tersebar di ponsel setiap anggota keluarga, dokumen penting tersimpan di laptop yang dipakai bergantian, dan satu-satunya salinan resep warisan ibu ada di harddisk eksternal yang bunyinya sudah tidak wajar. Data keluarga biasanya hidup begitu saja — tersebar, tanpa pemilik yang jelas, dan sering tanpa satu pun salinan cadangan. Proyek ini mengubah satu perangkat hemat di rumah menjadi home server yang menjawab tiga kebutuhan itu sekaligus: berbagi file, backup otomatis yang terjadwal, dan laporan status yang bisa dibaca kapan saja. Seluruh layanan dibangun sebagai unit systemd yang dikelola rapi — dijalankan oleh user khusus non-root, dijadwalkan lewat timer, dan diperiksa kesehatannya lewat skrip — bukan proses yang ditempel di background lalu dilupakan.
Perangkat yang kita pakai sengaja yang paling murah dan paling sering terlupakan: mini PC bekas kantor, laptop tua yang layarnya rusak, atau Raspberry Pi yang menganggur di laci. Ketiganya justru cocok karena tugasnya sederhana — bagaimana memilih dan menghidupkan perangkat semacam ini sudah kita bahas di section Linux untuk Perangkat Lama .
Layanan yang kita bangun dalam proyek ini:
- berbagi file keluarga — folder
/srv/berbagiyang bisa dijangkau semua perangkat di jaringan rumah, dilayani oleh unitberbagi-file.serviceyang menjalankanpython3 -m http.serverpada port 8765, - backup harian terjadwal — unit
backup-keluarga.timeryang memicubackup-keluarga.servicesetiap hari jam 02:30, menjalankan pola rsync+tar dari proyek Skrip Backup Otomatis , - dashboard status — skrip
status-home-server.shyang memeriksa tiga hal (layanan jalan, disk data, umur backup terakhir) dari snapshot beku, lalu melaporkannya dengan kode keluar yang bisa dibaca mesin, - peta layanan nyata — Syncthing, Jellyfin, dan pi-hole dipetakan fungsinya supaya kita tahu kapan waktunya naik kelas dari layanan mini ke layanan sungguhan.
Tampilan Program Akhir #
Sesi berikut menunjukkan hasil akhir proyek pada data contoh dengan tanggal acuan dikunci ke 2026-02-15. Perintah pertama menjalankan skrip status dari folder kerja yang berisi home-server.conf dan snapshot di data/; perintah kedua memvalidasi ketiga unit file sekaligus — systemd-analyze verify tidak mencetak apa pun saat semuanya valid:
$ HARI_INI=2026-02-15 bash deploy/status-home-server.sh
================================================================
STATUS HOME SERVER
Tanggal acuan : 2026-02-15
================================================================
layanan berbagi : JALAN (port 8765)
disk data : /srv/data 72% (ambang 85%)
backup terakhir : backup-20260214 (umur 1 hari, maksimal 2)
----------------------------------------------------------------
temuan : tidak ada — server sehat
$ systemd-analyze verify config/berbagi-file.service config/backup-keluarga.service config/backup-keluarga.timer
$ echo $?
0
Laporan itu ringkas karena memang ditujukan untuk dibaca sekilas: tiga baris status dan satu baris kesimpulan. Di baliknya ada keputusan desain yang menjadi inti proyek — snapshot dibekukan supaya hasil bisa diulang, setiap temuan menyala sebagai bit pada exit code, dan setiap unit divalidasi strukturnya sebelum dipasang.
Arsitektur Proyek #
Seluruh keputusan di atas bertemu dalam satu gambar berikut:
Perangkat keluarga (laptop, ponsel, TV)
│ salin / ambil file
▼
┌──────────────────────────────────────────────┐
│ HOME SERVER — mini PC / laptop tua │
│ │
│ /srv/berbagi folder file keluarga │
│ /srv/foto-keluarga foto dari semua ponsel │
│ /srv/data disk data (fstab nofail)│
│ /srv/backup tujuan backup harian │
│ │
│ berbagi-file.service │
│ └─ python3 -m http.server 8765 │
│ (user berbagi, hardening unit) │
│ │
│ backup-keluarga.timer ── tiap hari 02:30 ──┐ │
│ └─ backup-keluarga.service (oneshot) │ │
│ └─ rsync + tar (Proyek Skrip Backup)│ │
│ │ │
│ status-home-server.sh ─ laporan + log temuan│ │
└──────────────────────────────────────────────┘Empat folder di bawah /srv dipisah perannya: data hidup di berbagi dan foto-keluarga, disk data besar termuat di data, dan hasil backup menumpuk di backup. Tiga unit systemd mengurus hidup matinya layanan, dan skrip status menutupnya dengan laporan — semuanya akan kita bangun dan uji satu per satu.
Prasyarat #
Proyek ini merangkai kembali materi dari beberapa section. Chapter yang paling sering dirujuk:
- Menulis Service Unit — section Unit, Service, dan Install beserta directive dasar,
- Scheduling Scripts — cron versus systemd timer dan alasan timer lebih cocok untuk server,
- Backup Strategies — aturan 3-2-1 dan skema retensi yang melatari timer backup kita.
Tujuan Pembelajaran #
Setelah menyelesaikan proyek ini, kita mampu:
- menyiapkan fondasi server yang layak huni: OS tanpa GUI, IP stabil lewat DHCP reservation, SSH, disk data terpisah dengan
nofail, dan struktur/srvyang jelas perannya, - menjelaskan mengapa layanan berjalan sebagai user sistem non-root dan menyiapkan folder dengan permission yang sesuai,
- menulis unit service dan timer systemd yang lolos
systemd-analyze verifysebelum dipasang, - membuktikan sebuah layanan benar-benar bekerja lewat
curldanss— bukan sekadar percaya statusnya, - menerapkan hardening unit (
User=,ProtectSystem=,PrivateTmp=) dan membedah artinya, - membangun laporan status deterministik dari snapshot beku dengan alert berupa log dan exit code ber-bitmask,
- membuktikan seluruh klaim lewat test suite bash yang menjalankan layanan sungguhan.
Tahapan Pengerjaan #
Proyek dikerjakan dalam tiga bagian berurutan, dan tiap bagian diakhiri dengan keadaan yang bisa diperiksa:
- Bagian 1: Persiapan Home Server — peran server, OS tanpa GUI, IP stabil, SSH, disk data dengan fstab
nofail, struktur/srv, user layanan, dan skrip cek kesiapan. - Bagian 2: Menjalankan Layanan — unit
berbagi-file.servicedan pasanganbackup-keluarga.service/.timerditulis dan divalidasi, layanan mini dijalankan dan dibuktikan, lalu di-hardening. - Bagian 3: Monitoring dan Backup — snapshot beku, skrip status dengan laporan dan kode keluar ber-bitmask, alert sederhana, serta test suite yang mengunci semuanya.
Mulailah dari Bagian 1 karena setiap keputusan di sana — struktur folder, user layanan, mount point — dipakai terus oleh unit dan skrip di dua bagian berikutnya. Berkas proyek (config/ berisi tiga unit systemd dan home-server.conf, deploy/ berisi cek-kesiapan.sh dan status-home-server.sh, tests/run-tests.sh, serta docs/README.txt) tersedia pada bagian Berkas Sumber di bawah halaman setiap bagian, sehingga hasil pekerjaan bisa dibandingkan dengan versi referensi kapan saja.
Rancang peran server, instalasi dasar, dan penyimpanan yang dipakai bersama.
Buka → # Bagian 2: Menjalankan LayananPasang dan konfigurasikan layanan inti sebagai unit yang terkelola rapi.
Buka → # Bagian 3: Monitoring dan BackupPantau kesehatan server dan jadwalkan backup otomatis untuk data keluarga.
Buka →Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
deploy/cek-kesiapan.sh
Unduh berkas#!/usr/bin/env bash
# cek-kesiapan.sh — periksa kesiapan struktur home server.
#
# Memeriksa folder layanan di bawah SRV_ROOT, permission dan
# kepemilikan folder berbagi, serta entri fstab untuk disk data.
# Semua target bisa digeser lewat variabel lingkungan sehingga
# pemeriksaan bisa dilatih pada fixture:
# SRV_ROOT akar struktur layanan (default: /srv)
# FSTAB_FILE berkas fstab yang diperiksa (default: /etc/fstab)
# LAYANAN_USER user pemilik folder berbagi (default: user saat ini)
# DATA_MOUNT mount point disk data (default: /srv/data)
#
# Kode keluar: 0 = siap dijalankan; selain itu = jumlah temuan.
set -u
SRV_ROOT="${SRV_ROOT:-/srv}"
FSTAB_FILE="${FSTAB_FILE:-/etc/fstab}"
LAYANAN_USER="${LAYANAN_USER:-$(id -un)}"
DATA_MOUNT="${DATA_MOUNT:-/srv/data}"
temuan=0
lolos() {
printf 'OK %s\n' "$1"
}
gagal() {
printf 'GAGAL %s\n' "$1"
temuan=$((temuan + 1))
}
# --- folder berbagi: ada, mode 750, dimiliki user layanan --------------
if [ -d "$SRV_ROOT/berbagi" ]; then
mode=$(stat -c %a "$SRV_ROOT/berbagi")
if [ "$mode" = "750" ]; then
lolos "folder $SRV_ROOT/berbagi (mode $mode)"
else
gagal "folder $SRV_ROOT/berbagi mode $mode, harapan 750"
fi
pemilik=$(stat -c %U "$SRV_ROOT/berbagi")
if [ "$pemilik" = "$LAYANAN_USER" ]; then
lolos "pemilik $SRV_ROOT/berbagi sesuai user layanan"
else
gagal "pemilik $SRV_ROOT/berbagi bukan user layanan"
fi
else
gagal "folder $SRV_ROOT/berbagi tidak ditemukan"
# permission dan kepemilikan tidak bisa diperiksa bila foldernya
# pun tidak ada; dua temuan cukup mewakili satu folder hilang.
fi
# --- folder data keluarga -------------------------------------------------
for d in foto-keluarga backup; do
if [ -d "$SRV_ROOT/$d" ]; then
lolos "folder $SRV_ROOT/$d"
else
gagal "folder $SRV_ROOT/$d tidak ditemukan"
fi
done
# --- entri fstab disk data: nofail wajib ----------------------------------
# $2 mount point, $4 opsi; nofail dicocokkan sebagai satu opsi utuh
# sehingga opsi seperti "mynofailx" tidak lolos mengaku nofail.
if awk -v mnt="$DATA_MOUNT" '
$0 !~ /^[[:space:]]*#/ && $2 == mnt &&
$4 ~ /(^|,)nofail(,|$)/ { found = 1 }
END { exit found ? 0 : 1 }' "$FSTAB_FILE" 2> /dev/null; then
lolos "entri fstab untuk $DATA_MOUNT memuat nofail"
else
gagal "fstab belum memuat entri nofail untuk $DATA_MOUNT"
fi
# --- ringkasan ------------------------------------------------------------
if [ "$temuan" -eq 0 ]; then
printf 'server siap dijalankan: 0 temuan\n'
else
printf 'server belum siap: %d temuan\n' "$temuan"
fi
exit "$temuan"
deploy/status-home-server.sh
Unduh berkas#!/usr/bin/env bash
# status-home-server.sh — dashboard status home server.
#
# Membaca snapshot beku dari folder data/ (ss.txt: hasil `ss -tln`,
# df.txt: hasil `df -h`) dan daftar run backup (backup-YYYYMMDD) di
# folder BACKUP_DIR, lalu menulis laporan status ke stdout sekaligus
# baris temuan ke berkas log. Pola snapshot membuat hasilnya bisa
# diulang dan diuji — pola yang sama dipakai Proyek Otomasi
# Pemeliharaan Sistem.
#
# Variabel lingkungan (berguna untuk pengujian):
# CONF berkas konfigurasi yang dibaca (default: home-server.conf)
# DATA_DIR folder snapshot (default: data)
# HARI_INI tanggal acuan umur backup (default: hari ini, YYYY-MM-DD)
#
# Kode keluar (temuan bisa gabungan — jumlahkan nilainya):
# 0 sehat 1 disk data melewati ambang
# 2 layanan berbagi tidak jalan 4 backup terlalu tua / belum ada
set -u
CONF="${CONF:-home-server.conf}"
DATA_DIR="${DATA_DIR:-data}"
HARI_INI="${HARI_INI:-$(date +%F)}"
if [ -f "$CONF" ]; then
# shellcheck source=/dev/null
. "$CONF"
fi
PORT_BERBAGI="${PORT_BERBAGI:-8765}"
DATA_MOUNT="${DATA_MOUNT:-/srv/data}"
BACKUP_DIR="${BACKUP_DIR:-/srv/backup/run}"
BACKUP_MAX_UMUR_HARI="${BACKUP_MAX_UMUR_HARI:-2}"
DISK_AMBANG_PCT="${DISK_AMBANG_PCT:-85}"
LOG_FILE="${LOG_FILE:-status-home-server.log}"
GARIS='----------------------------------------------------------------'
# --- 1. Layanan berbagi: cari baris LISTEN pada port dari snapshot ----
# Kolom ke-4 output ss adalah "alamat:port", jadi port dicocokkan
# sebagai akhiran kolom — bentuk [::]:8765 pun lolos pola yang sama.
layanan="MATI"
if awk -v port="$PORT_BERBAGI" '
$1 == "LISTEN" && $4 ~ ":" port "$" { ketemu = 1 }
END { exit ketemu ? 0 : 1 }' "$DATA_DIR/ss.txt" 2> /dev/null; then
layanan="JALAN"
fi
# --- 2. Disk data: persentase pemakaian dari snapshot df --------------
# Kolom ke-5 df adalah Use% ("+0" membuang tanda %), kolom ke-6
# mount point. Mount yang tidak ada berarti disk data gagal termuat.
disk_pct=$(awk -v mnt="$DATA_MOUNT" '
$6 == mnt { print $5 + 0; exit }' "$DATA_DIR/df.txt" 2> /dev/null)
# --- 3. Backup terakhir: umur dihitung dari nama folder run -----------
backup_terakhir=$(ls -1 "$BACKUP_DIR" 2> /dev/null |
grep -E '^backup-[0-9]{8}$' | sort | tail -n 1)
umur=""
if [ -n "$backup_terakhir" ]; then
tgl="${backup_terakhir#backup-}"
tgl_iso="${tgl:0:4}-${tgl:4:2}-${tgl:6:2}"
umur=$(( ( $(date -d "$HARI_INI" +%s) -
$(date -d "$tgl_iso" +%s) ) / 86400 ))
fi
# --- 4. Kumpulkan temuan ------------------------------------------------
temuan=0
n_temuan=0
daftar_temuan=""
catat() {
n_temuan=$((n_temuan + 1))
daftar_temuan="$daftar_temuan
- $1"
}
if [ "$layanan" = "MATI" ]; then
temuan=$((temuan + 2))
catat "layanan berbagi tidak jalan (port $PORT_BERBAGI tidak mendengarkan)"
fi
if [ -z "$disk_pct" ]; then
temuan=$((temuan + 1))
catat "disk data $DATA_MOUNT tidak termuat — periksa fstab"
elif [ "$disk_pct" -ge "$DISK_AMBANG_PCT" ]; then
temuan=$((temuan + 1))
catat "disk data melewati ambang: ${disk_pct}% dari ${DISK_AMBANG_PCT}%"
fi
if [ -z "$backup_terakhir" ]; then
temuan=$((temuan + 4))
catat "belum ada run backup di $BACKUP_DIR"
elif [ "$umur" -gt "$BACKUP_MAX_UMUR_HARI" ]; then
temuan=$((temuan + 4))
catat "backup terlalu tua: $umur hari (maksimal $BACKUP_MAX_UMUR_HARI)"
fi
# --- 5. Tulis laporan ke stdout dan temuan ke log -----------------------
printf '================================================================\n'
printf 'STATUS HOME SERVER\n'
printf 'Tanggal acuan : %s\n' "$HARI_INI"
printf '================================================================\n'
if [ "$layanan" = "JALAN" ]; then
printf 'layanan berbagi : JALAN (port %s)\n' "$PORT_BERBAGI"
else
printf 'layanan berbagi : MATI (port %s tidak mendengarkan)\n' "$PORT_BERBAGI"
fi
if [ -n "$disk_pct" ]; then
if [ "$disk_pct" -ge "$DISK_AMBANG_PCT" ]; then
printf 'disk data : %s %s%% MELEWATI AMBANG %s%%\n' \
"$DATA_MOUNT" "$disk_pct" "$DISK_AMBANG_PCT"
else
printf 'disk data : %s %s%% (ambang %s%%)\n' \
"$DATA_MOUNT" "$disk_pct" "$DISK_AMBANG_PCT"
fi
else
printf 'disk data : %s tidak ada di snapshot df\n' "$DATA_MOUNT"
fi
if [ -n "$backup_terakhir" ]; then
printf 'backup terakhir : %s (umur %s hari, maksimal %s)\n' \
"$backup_terakhir" "$umur" "$BACKUP_MAX_UMUR_HARI"
else
printf 'backup terakhir : belum ada run backup\n'
fi
printf '%s\n' "$GARIS"
if [ "$temuan" -eq 0 ]; then
printf 'temuan : tidak ada — server sehat\n'
else
printf 'temuan : %d\n' "$n_temuan"
# ${var#$'\n'} membuang baris kosong pertama dari daftar.
printf '%s\n' "${daftar_temuan#$'\n'}"
# Alert sederhana: satu baris per temuan ke berkas log.
mkdir -p "$(dirname "$LOG_FILE")"
printf '%s\n' "${daftar_temuan#$'\n'}" |
while IFS= read -r baris; do
printf '[%s] TEMUAN: %s\n' "$HARI_INI" "${baris#- }" >> "$LOG_FILE"
done
printf 'temuan dicatat ke: %s\n' "$LOG_FILE"
fi
exit "$temuan"
tests/run-tests.sh
Unduh berkas#!/usr/bin/env bash
# run-tests.sh — test suite untuk paket layanan home server.
#
# Suite menyiapkan folder kerja terisolasi (mktemp), menyalin unit
# systemd dari config/ dan kedua skrip dari deploy/, lalu menjalankan:
# 1. validasi unit lewat systemd-analyze verify,
# 2. pemeriksaan directive penting lewat grep,
# 3. layanan mini yang benar-benar mendengarkan dan menjawab HTTP,
# 4. skrip kesiapan pada fixture siap dan rusak,
# 5. skrip status pada fixture sehat dan kombinasi temuan.
# Jalankan dari mana saja: bash tests/run-tests.sh
set -u
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")
PASS=0
FAIL=0
# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
if [ "$1" = "$2" ]; then
PASS=$((PASS + 1))
printf 'OK %s\n' "$3"
else
FAIL=$((FAIL + 1))
printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
fi
}
# --- siapkan folder kerja terisolasi ------------------------------------
WORK=$(mktemp -d)
cp "$PROJ_DIR/config/berbagi-file.service" \
"$PROJ_DIR/config/backup-keluarga.service" \
"$PROJ_DIR/config/backup-keluarga.timer" \
"$PROJ_DIR/config/home-server.conf" \
"$PROJ_DIR/deploy/status-home-server.sh" \
"$PROJ_DIR/deploy/cek-kesiapan.sh" "$WORK/"
cd "$WORK" || exit 1
server_pid=""
cleanup() {
if [ -n "$server_pid" ]; then
kill "$server_pid" 2> /dev/null
wait "$server_pid" 2> /dev/null
fi
}
trap cleanup EXIT
# --- uji 1: unit valid menurut systemd-analyze verify -------------------
systemd-analyze verify berbagi-file.service
assert_eq 0 "$?" "berbagi-file.service lolos systemd-analyze verify"
systemd-analyze verify backup-keluarga.service backup-keluarga.timer
assert_eq 0 "$?" "unit backup lolos systemd-analyze verify"
# --- uji 2: directive penting ada di tempatnya ----------------------------
grep -q '^User=berbagi$' berbagi-file.service
assert_eq 0 "$?" "unit berbagi berjalan sebagai user layanan non-root"
grep -q '^WorkingDirectory=/srv/berbagi$' berbagi-file.service
assert_eq 0 "$?" "WorkingDirectory mengarah ke folder berbagi"
grep -q '^ExecStart=/usr/bin/python3 -m http.server 8765' \
berbagi-file.service
assert_eq 0 "$?" "ExecStart menjalankan layanan pada port 8765"
grep -q '^ProtectSystem=' berbagi-file.service
grep -q '^PrivateTmp=true$' berbagi-file.service
assert_eq 0 "$?" "hardening ProtectSystem dan PrivateTmp aktif"
grep -q '^OnCalendar=' backup-keluarga.timer
assert_eq 0 "$?" "timer memuat OnCalendar"
grep -q '^Persistent=true$' backup-keluarga.timer
assert_eq 0 "$?" "timer Persistent mengejar jadwal yang terlewat"
# --- uji 3: layanan mini benar-benar menjawab ------------------------------
mkdir -p srv/berbagi
printf 'jadwal piknik keluarga\n' > srv/berbagi/jadwal-piknik.txt
python3 -m http.server 8765 --directory srv/berbagi >/dev/null 2>&1 &
server_pid=$!
siap=1
for i in 1 2 3 4 5 6 7 8 9 10; do
curl -s -o /dev/null http://127.0.0.1:8765/ && { siap=0; break; }
sleep 0.3
done
assert_eq 0 "$siap" "layanan mini siap menerima permintaan"
kode=$(curl -s -o /dev/null -w '%{http_code}' \
http://127.0.0.1:8765/jadwal-piknik.txt)
assert_eq 200 "$kode" "berkas dijawab HTTP 200"
isi=$(curl -s http://127.0.0.1:8765/jadwal-piknik.txt)
assert_eq "jadwal piknik keluarga" "$isi" "isi berkas utuh sampai klien"
ss -tln | awk -v port="8765" '$1 == "LISTEN" && $4 ~ ":" port "$"' \
| grep -q .
assert_eq 0 "$?" "ss melihat port 8765 dalam keadaan LISTEN"
# --- uji 4: skrip kesiapan pada fixture -------------------------------------
mkdir -p srv/foto-keluarga srv/backup srv/backup/run
chmod 750 srv/berbagi
printf 'UUID=5c1b2a3d-1111-4222-8333-444455556666 /srv/data ext4 defaults,nofail 0 2\n' > fstab.data
SRV_ROOT=srv FSTAB_FILE=fstab.data bash cek-kesiapan.sh > cek-siap.txt 2>&1
assert_eq 0 "$?" "layout siap lolos pemeriksaan"
grep -q 'server siap dijalankan: 0 temuan' cek-siap.txt
assert_eq 0 "$?" "laporan kesiapan menyatakan siap"
chmod 755 srv/berbagi
sed 's/,nofail//' fstab.data > fstab.rusak
SRV_ROOT=srv FSTAB_FILE=fstab.rusak bash cek-kesiapan.sh > /dev/null 2>&1
assert_eq 2 "$?" "mode salah dan nofail hilang terdeteksi (exit 2)"
chmod 750 srv/berbagi
# --- uji 5: skrip status pada fixture sehat ---------------------------------
# Snapshot ss diambil saat layanan masih jalan (uji 3), lalu layanan
# dihentikan supaya suite tidak meninggalkan proses.
mkdir -p data
ss -tln > data/ss.txt
kill "$server_pid"
wait "$server_pid" 2> /dev/null
server_pid=""
ss -tln | awk -v port="8765" '$1 == "LISTEN" && $4 ~ ":" port "$"' \
| wc -l | grep -qx 0
assert_eq 0 "$?" "port 8765 bebas setelah layanan berhenti"
cat > data/df.txt <<'EOF'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 20G 28G 41% /
/dev/sdb1 200G 143G 47G 72% /srv/data
EOF
mkdir -p srv/backup/run/backup-20260214
HARI_INI=2026-02-15 bash status-home-server.sh > laporan-sehat.txt
assert_eq 0 "$?" "fixture sehat keluar 0"
grep -q '^layanan berbagi : JALAN' laporan-sehat.txt
assert_eq 0 "$?" "laporan menyatakan layanan JALAN"
grep -q '72% (ambang 85%)' laporan-sehat.txt
assert_eq 0 "$?" "disk data terbaca 72% dari snapshot"
grep -q 'backup-20260214 (umur 1 hari, maksimal 2)' laporan-sehat.txt
assert_eq 0 "$?" "umur backup terhitung dari tanggal acuan"
# --- uji 6: setiap temuan menyalakan bit kode keluar sendiri -----------------
# Tanggal acuan 2026-02-15: backup-20260214 berumur 1 hari (segar),
# backup-20260120 berumur 26 hari (terlalu tua).
mv data/ss.txt data/ss-jalan.txt
ss -tln | grep -v ':8765 ' > data/ss.txt || true
HARI_INI=2026-02-15 bash status-home-server.sh > /dev/null 2>&1
assert_eq 2 "$?" "layanan mati menyalakan bit 2"
mv data/ss-jalan.txt data/ss.txt
sed 's/ 72% / 91% /' data/df.txt > data/df-91.txt
mv data/df-91.txt data/df.txt
HARI_INI=2026-02-15 bash status-home-server.sh > /dev/null 2>&1
assert_eq 1 "$?" "disk 91% melewati ambang menyalakan bit 1"
rm -rf srv/backup/run/backup-20260214
mkdir -p srv/backup/run/backup-20260120
HARI_INI=2026-02-15 bash status-home-server.sh > /dev/null 2>&1
assert_eq 5 "$?" "disk 91% dan backup tua menyalakan bit 1+4"
ss -tln | grep -v ':8765 ' > data/ss.txt || true
HARI_INI=2026-02-15 bash status-home-server.sh > laporan-temuan.txt 2>&1
assert_eq 7 "$?" "tiga temuan menyalakan bit 1+2+4"
grep -Eq '^temuan[[:space:]]+: 3$' laporan-temuan.txt
assert_eq 0 "$?" "laporan menyebut tiga temuan"
grep -q 'backup terlalu tua: 26 hari' laporan-temuan.txt
assert_eq 0 "$?" "umur backup tua dihitung benar"
grep -q 'TEMUAN: layanan berbagi tidak jalan' \
srv/backup/status-home-server.log
assert_eq 0 "$?" "temuan tercatat ke berkas log"
# --- ringkasan --------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
printf 'FAILED: %d/%d test lulus\n' "$PASS" "$((PASS + FAIL))"
fi
cd / && rm -rf "$WORK"
[ "$FAIL" -eq 0 ]