mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-11 13:27:41 +01:00
Cap --restore at 23:00 so it is a reward, not an off switch
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
--restore exists so the workout bonus can push shutdown later, and it had no upper bound at all: `sudo adjust_shutdown_schedule.sh --restore 24 24 0` from any shell undid the entire ratchet, with nothing written down. It is now clamped to a ceiling and every restore is recorded. Clamped rather than refused, so the reward path keeps working: the bonus asks for up to 24 and simply lands on 23. Ceiling chosen by kuhy as today's earned state -- base is 21, live was 23. Two corrections to the previous commit, both found by reading the callers: - It tightened validation from 0-24 to 0-23 on the grounds that the error message said 0-23. That was backwards. 24 is a real, documented value: _adjust_shutdown_time_by caps at 24 to mean midnight, and day-specific-shutdown-check.sh catches it through the morning-window condition. The message was what was wrong, so the message is what changed. - The audit line does NOT go in /etc/shutdown-schedule-overrides.conf, which was the original plan. That file is parsed by day-specific-shutdown-check.sh as start|end|created|reason, and a line covering the current time makes it exit 0 and SKIP the shutdown. An audit trail written there would have suppressed the very curfew it documented. It goes to /var/log/shutdown-restore.log, and a test asserts the conf stays empty. The log write says so on stderr when it fails, per this repo's no-silent- failures rule; it is not fatal, because refusing an earned reward over an unwritable log file would be the worse failure. 29 shell cases and 1146 python tests pass. Verified in a vmbox sandbox as root against a real /etc and a real chattr: 24 clamps to 23 and is logged, the overrides conf is untouched, and 25 is still refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5jMviyk15MKw1PqW1pzKr
This commit is contained in:
parent
2bc31cf587
commit
5d0c4d0e3a
@ -15,6 +15,11 @@
|
||||
# shutdown window. Comparing it with `-gt`, as the two evening hours do, would
|
||||
# ratchet it backwards and quietly allow the window to be shortened.
|
||||
#
|
||||
# With --restore the schedule may be loosened -- that is how the workout reward
|
||||
# pushes shutdown later -- but never past RESTORE_CEILING, and every restore is
|
||||
# recorded. Without the ceiling, `--restore 24 24 0` is an off switch for the
|
||||
# whole ratchet.
|
||||
#
|
||||
# Add to /etc/sudoers.d/workout-locker:
|
||||
# <username> ALL=(root) NOPASSWD: /home/kuhy/screen-locker/screen_locker/adjust_shutdown_schedule.sh
|
||||
|
||||
@ -27,19 +32,32 @@ CONFIG_FILE="${SHUTDOWN_CONFIG_FILE:-/etc/shutdown-schedule.conf}"
|
||||
GUARD_NAME="${SHUTDOWN_GUARD_NAME:-shutdown-schedule}"
|
||||
readonly CONFIG_FILE GUARD_NAME
|
||||
|
||||
readonly MAX_HOUR=23
|
||||
# 24 is a real, documented value here: _shutdown.py's extra-workout bonus uses
|
||||
# it to mean midnight, and day-specific-shutdown-check.sh catches it via the
|
||||
# morning-window condition. The old error message claimed 0-23 while the code
|
||||
# allowed 24; the message was the thing that was wrong.
|
||||
readonly MAX_HOUR=24
|
||||
|
||||
# The latest the machine may ever shut down, whatever has been earned. --restore
|
||||
# exists so the workout reward can loosen the schedule, and without a ceiling it
|
||||
# is simply an off switch for the ratchet: `--restore 24 24 0` from any shell
|
||||
# undoes everything. Bonuses above this are clamped, not refused, so the reward
|
||||
# path keeps working.
|
||||
readonly RESTORE_CEILING=23
|
||||
readonly RESTORE_LOG="${SHUTDOWN_RESTORE_LOG:-/var/log/shutdown-restore.log}"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [--restore] <mon_wed_hour> <thu_sun_hour> <morning_end_hour>" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Validate that every argument is an hour in [0, 23].
|
||||
# Validate that every argument is an hour in [0, 24]; 24 means midnight.
|
||||
validate_hours() {
|
||||
local hour
|
||||
for hour in "$@"; do
|
||||
if ! [[ "$hour" =~ ^[0-9]+$ ]] || [[ "$hour" -gt $MAX_HOUR ]]; then
|
||||
echo "Error: Hours must be integers between 0 and ${MAX_HOUR}" >&2
|
||||
echo "Error: Hours must be integers between 0 and ${MAX_HOUR}" \
|
||||
"(24 means midnight)" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
@ -87,6 +105,35 @@ check_stricter_only() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Clamp a restore to the ceiling and record that it happened.
|
||||
#
|
||||
# Deliberately NOT written to /etc/shutdown-schedule-overrides.conf: that file
|
||||
# is parsed by day-specific-shutdown-check.sh as start|end|created|reason, and a
|
||||
# line matching the current time makes it exit 0 and skip the shutdown outright.
|
||||
# An "audit trail" written there would suppress the very curfew it documents.
|
||||
clamp_restore() {
|
||||
local hour="$1" label="$2"
|
||||
if [[ "$hour" -gt $RESTORE_CEILING ]]; then
|
||||
echo "Note: ${label} ${hour}:00 clamped to the ${RESTORE_CEILING}:00 ceiling" >&2
|
||||
printf '%s' "$RESTORE_CEILING"
|
||||
return 0
|
||||
fi
|
||||
printf '%s' "$hour"
|
||||
}
|
||||
|
||||
# A restore that is not recorded is a loosening nobody can audit, so a failure
|
||||
# to write the log says so on stderr rather than being swallowed. It is not
|
||||
# fatal: refusing the reward because a log file is unwritable would be worse
|
||||
# than the missing line.
|
||||
log_restore() {
|
||||
local line
|
||||
line="$(date -Is) | RESTORE | mon_wed=$1 thu_sun=$2 morning_end=$3 | by=${SUDO_USER:-${USER:-unknown}}"
|
||||
if ! printf '%s\n' "$line" >>"$RESTORE_LOG" 2>/dev/null; then
|
||||
echo "Warning: could not record this restore in ${RESTORE_LOG};" \
|
||||
"the schedule was still loosened" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# Resolve the guard-lib canonical copy, failing loudly when the guard is absent.
|
||||
resolve_canonical() {
|
||||
local canonical
|
||||
@ -133,6 +180,11 @@ main() {
|
||||
|
||||
if [[ "$restore_mode" == false ]]; then
|
||||
check_stricter_only "$mon_wed" "$thu_sun" "$morning_end" || exit 1
|
||||
else
|
||||
# A restore may loosen, but never past the ceiling, and never silently.
|
||||
mon_wed="$(clamp_restore "$mon_wed" "Mon-Wed")"
|
||||
thu_sun="$(clamp_restore "$thu_sun" "Thu-Sun")"
|
||||
log_restore "$mon_wed" "$thu_sun" "$morning_end"
|
||||
fi
|
||||
|
||||
local canonical body
|
||||
|
||||
@ -50,6 +50,15 @@ _t_blocks() {
|
||||
fi
|
||||
}
|
||||
|
||||
_t_eq() {
|
||||
local want="$1" got="$2" what="$3"
|
||||
if [[ "$got" == "$want" ]]; then
|
||||
_t_pass "$what"
|
||||
else
|
||||
_t_fail "$what (want '${want}', got '${got}')"
|
||||
fi
|
||||
}
|
||||
|
||||
_t_has() {
|
||||
local haystack="$1" needle="$2" what="$3"
|
||||
if [[ "$haystack" == *"$needle"* ]]; then
|
||||
@ -76,6 +85,7 @@ chmod +x "$TMP/bin/chattr" "$TMP/bin/guardctl"
|
||||
export PATH="$TMP/bin:$PATH"
|
||||
|
||||
export SHUTDOWN_CONFIG_FILE="$TMP/shutdown-schedule.conf"
|
||||
export SHUTDOWN_RESTORE_LOG="$TMP/restore.log"
|
||||
export FAKE_CANONICAL="$TMP/canonical"
|
||||
|
||||
write_config() {
|
||||
@ -91,8 +101,6 @@ source "$TARGET"
|
||||
|
||||
echo "validate_hours"
|
||||
_t_allows "accepts 0 and 23" validate_hours 0 23 5
|
||||
_t_blocks "rejects 24 (the message says 0-23, and now so does the check)" \
|
||||
validate_hours 24 23 5
|
||||
_t_blocks "rejects a non-integer" validate_hours 21 abc 5
|
||||
_t_blocks "rejects a negative (the minus makes it non-numeric)" \
|
||||
validate_hours -1 21 5
|
||||
@ -156,6 +164,37 @@ else
|
||||
_t_fail "--restore still loosens (deliberately unchanged)"
|
||||
fi
|
||||
|
||||
echo "validate_hours: 24 means midnight"
|
||||
# _shutdown.py's extra-workout bonus caps at 24 deliberately, and
|
||||
# day-specific-shutdown-check.sh catches it via the morning window. Rejecting
|
||||
# 24 would break that path; the old 0-23 error message was what was wrong.
|
||||
_t_allows "accepts 24 (midnight), which the bonus path relies on" \
|
||||
validate_hours 24 24 5
|
||||
_t_blocks "still rejects 25" validate_hours 25 21 5
|
||||
|
||||
echo "--restore ceiling"
|
||||
_t_eq "23" "$(clamp_restore 24 Mon-Wed)" "clamps 24 down to the ceiling"
|
||||
_t_eq "23" "$(clamp_restore 23 Mon-Wed)" "leaves the ceiling itself alone"
|
||||
_t_eq "21" "$(clamp_restore 21 Mon-Wed)" "leaves an earlier hour alone"
|
||||
out="$(clamp_restore 24 Mon-Wed 2>&1 >/dev/null)"
|
||||
_t_has "$out" "clamped" "says so rather than clamping silently"
|
||||
|
||||
write_config 21 21 5
|
||||
if (main --restore 24 24 5 >/dev/null 2>&1); then
|
||||
_t_pass "a restore above the ceiling still succeeds"
|
||||
else
|
||||
_t_fail "a restore above the ceiling still succeeds"
|
||||
fi
|
||||
_t_has "$(cat "$SHUTDOWN_CONFIG_FILE")" "MON_WED_HOUR=23" \
|
||||
"the written value is the ceiling, not 24"
|
||||
_t_has "$(cat "$SHUTDOWN_RESTORE_LOG")" "RESTORE" "the restore was recorded"
|
||||
|
||||
# The overrides CONF is parsed by day-specific-shutdown-check.sh as
|
||||
# start|end|created|reason, and a matching line makes it exit 0 and skip the
|
||||
# shutdown. Audit lines must never go there.
|
||||
_t_eq "0" "$(grep -c . "$TMP/shutdown-schedule-overrides.conf" 2>/dev/null || echo 0)" \
|
||||
"nothing was written to the overrides conf"
|
||||
|
||||
echo
|
||||
printf 'passed: %d, failed: %d\n' "$PASS" "$FAIL"
|
||||
[[ $FAIL -eq 0 ]]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user