ask where the ballast goes and how big it is
Three questions instead of one, because the two the original never asked are the
two that decide whether the thing is useful.
1. Do you want one, with the explanation first.
2. Where. Home (easiest to find again months from now), beside Officer, or a
path typed in. This is not tidiness: the checker measures its own directory,
so a ballast only protects the filesystem it sits on. Choosing where it goes
is choosing which mount is covered.
3. How much, as 5/10/20% — with the actual numbers, and with what would be LEFT
rather than only what is taken:
[1] 5% — reserves 2.9GB leaving 54.3GB free
[2] 10% — reserves 5.8GB leaving 51.4GB free
[3] 20% — reserves 11.5GB leaving 45.7GB free
A percentage on its own is unanswerable. The number that decides it is the
one on the right: the reserve has to be big enough to matter and small
enough not to be the thing that filled the disk.
The size is computed against the filesystem the chosen path lands on, after the
location is known, so the percentages are of the right disk. ballast_free_kb
walks up to a directory that exists, since nothing has created the target yet.
An existing ballast in either default location is found and left alone rather
than a second one being made beside it.
Verified end to end in a temp home: created at the chosen path, 2.9G for 5% of
57.1GB, checker installed and reporting it present.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -430,34 +430,108 @@ step "Emergency disk ballast"
|
||||
if ! skip; then
|
||||
echo ""
|
||||
info "Emergency disk ballast — a reserve you can burn when the disk fills up"
|
||||
echo " A junk file holding no data, sized at 10% of free disk. A root cron"
|
||||
echo " checks every 10 minutes and deletes it if free space drops below"
|
||||
echo " ${BALLAST_THRESHOLD}%, so you get room to log in and clean up instead of meeting a"
|
||||
echo " wedged machine — Docker, journald and postgres all misbehave badly"
|
||||
echo " at 100% full, and not all of them recover on their own."
|
||||
echo " It is a one-shot valve: once spent, run this again to recreate it."
|
||||
echo " A file holding nothing, whose only job is to be deleted. A root cron"
|
||||
echo " checks every 10 minutes and removes it if free space drops below"
|
||||
echo " ${BALLAST_THRESHOLD}%, which buys you room to log in and clean up rather than"
|
||||
echo " meeting a wedged machine — Docker, journald and postgres all"
|
||||
echo " misbehave badly at 100% full, and not all of them recover on their"
|
||||
echo " own."
|
||||
echo ""
|
||||
echo " It is a one-shot valve: once spent, run this again to recreate it."
|
||||
if is_server; then
|
||||
echo " recommended for ${MACHINE_ROLE} — a full disk on an unattended box is the bad case"
|
||||
echo " Worth having on ${MACHINE_ROLE} — a full disk on an unattended box is the bad case."
|
||||
else
|
||||
echo " less useful on ${MACHINE_ROLE} — you are sitting at this machine and will notice"
|
||||
echo " Less useful on ${MACHINE_ROLE} — you are sitting at this machine and will notice."
|
||||
fi
|
||||
|
||||
if [[ -f "${USER_HOME}/${BALLAST_NAME}" ]]; then BALLAST_FILE="${USER_HOME}/${BALLAST_NAME}"; fi
|
||||
[[ -f "${OFFICER_ROOT}/${BALLAST_NAME}" ]] && BALLAST_FILE="${OFFICER_ROOT}/${BALLAST_NAME}"
|
||||
|
||||
if ballast_exists; then
|
||||
echo ""
|
||||
echo " already present: $(ballast_size_human) at ${BALLAST_FILE}"
|
||||
SUMMARY+=("Disk ballast: already present ($(ballast_size_human))")
|
||||
elif ! confirm "Create one?"; then
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("Disk ballast: SKIPPED by request")
|
||||
else
|
||||
BALLAST_MB="$(ballast_size_mb)"
|
||||
echo " to create: ${BALLAST_MB}M at ${BALLAST_FILE}"
|
||||
if confirm "Create it?"; then
|
||||
ballast_create "$BALLAST_MB"
|
||||
ballast_install_checker
|
||||
ok "ballast $(ballast_size_human), checker at ${BALLAST_CHECKER}"
|
||||
SUMMARY+=("Disk ballast: $(ballast_size_human), checked every 10 min")
|
||||
else
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("Disk ballast: SKIPPED by request")
|
||||
fi
|
||||
# ── where ──
|
||||
#
|
||||
# A ballast only protects the filesystem it sits on, because the checker
|
||||
# measures its own directory. So this is also a choice of which mount is
|
||||
# being protected, not just where the file is tidiest.
|
||||
echo ""
|
||||
info "Where should it go?"
|
||||
echo " [1] ${USER_HOME}/${BALLAST_NAME}"
|
||||
echo " your home — easiest to find again months from now"
|
||||
echo " [2] ${OFFICER_ROOT}/${BALLAST_NAME}"
|
||||
echo " beside Officer — same place as everything else it owns"
|
||||
echo " [3] somewhere else, typed in"
|
||||
echo ""
|
||||
|
||||
BALLAST_FILE=""
|
||||
while [[ -z "$BALLAST_FILE" ]]; do
|
||||
if ! read -rp " Which one? (1/2/3) [1]: " BALLAST_WHERE; then
|
||||
echo ""
|
||||
fail "No answer."
|
||||
fi
|
||||
case "${BALLAST_WHERE:-1}" in
|
||||
1) BALLAST_FILE="${USER_HOME}/${BALLAST_NAME}" ;;
|
||||
2) BALLAST_FILE="${OFFICER_ROOT}/${BALLAST_NAME}" ;;
|
||||
3)
|
||||
read -rp " Full path to the file: " BALLAST_TYPED || fail "No answer."
|
||||
BALLAST_TYPED="${BALLAST_TYPED/#\~/$USER_HOME}"
|
||||
if [[ "$BALLAST_TYPED" == /* ]]; then
|
||||
BALLAST_FILE="$BALLAST_TYPED"
|
||||
else
|
||||
warn "That needs to be an absolute path, starting with /"
|
||||
fi
|
||||
;;
|
||||
*) warn "Pick 1, 2 or 3." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── how much ──
|
||||
#
|
||||
# Percentages mean nothing without the numbers behind them, and the number
|
||||
# that matters is what is LEFT — the point of the reserve is to be big enough
|
||||
# to matter and small enough not to be the thing that filled the disk.
|
||||
BALLAST_FREE_KB="$(ballast_free_kb "$(dirname "$BALLAST_FILE")")"
|
||||
|
||||
echo ""
|
||||
info "How much? ${BALLAST_FILE} sits on a filesystem with $(human_bytes $((BALLAST_FREE_KB * 1024))) free."
|
||||
for i in 1 2 3; do
|
||||
case $i in
|
||||
1) BP=5 ;;
|
||||
2) BP=10 ;;
|
||||
3) BP=20 ;;
|
||||
esac
|
||||
BSZ=$((BALLAST_FREE_KB * BP / 100))
|
||||
printf ' [%d] %3d%% — reserves %-8s leaving %s free\n' \
|
||||
"$i" "$BP" "$(human_bytes $((BSZ * 1024)))" "$(human_bytes $(((BALLAST_FREE_KB - BSZ) * 1024)))"
|
||||
done
|
||||
echo ""
|
||||
|
||||
BALLAST_PCT=""
|
||||
while [[ -z "$BALLAST_PCT" ]]; do
|
||||
if ! read -rp " Which one? (1/2/3) [2]: " BALLAST_SIZE_CHOICE; then
|
||||
echo ""
|
||||
fail "No answer."
|
||||
fi
|
||||
case "${BALLAST_SIZE_CHOICE:-2}" in
|
||||
1) BALLAST_PCT=5 ;;
|
||||
2) BALLAST_PCT=10 ;;
|
||||
3) BALLAST_PCT=20 ;;
|
||||
*) warn "Pick 1, 2 or 3." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
BALLAST_MB=$((BALLAST_FREE_KB * BALLAST_PCT / 100 / 1024))
|
||||
ballast_create "$BALLAST_MB"
|
||||
ballast_install_checker
|
||||
ok "ballast $(ballast_size_human) at ${BALLAST_FILE}, checked every 10 minutes"
|
||||
ok "status any time: ${BALLAST_CHECKER} --status"
|
||||
SUMMARY+=("Disk ballast: $(ballast_size_human) at ${BALLAST_FILE} (${BALLAST_PCT}%)")
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user