mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-14 00:00:47 +01:00
Follow-up to9085a4e5, which recorded services_units.sh at 84.51% because its remaining branches sit behind [[ -f /etc/systemd/system/*.service ]] -- absolute root-owned paths that exist on this machine, so the else-branch never runs and no PATH shim can redirect it. All 87 such paths across the seven check libs now go through $SYSROOT, read from SERVICES_ROOT. The prefix is deliberately NOT defaulted. Three commands in hosts_fix_resolved run outside `run` and so are not stopped by --dry-run (chattr -i on resolved.conf.d, find -delete over its *.conf, and an append to resolved.conf); with an empty default, a test that forgot to set SERVICES_ROOT would really delete this machine's resolved.conf drop-ins. Unset is therefore a hard error and only an explicitly empty value means the real filesystem. The harness now owns PATH outright: $FAKE_BIN for shims and staged fake apps, $REAL_BIN for a curated set of symlinks to genuine coreutils. The machine's /usr/bin is deliberately absent -- a really-installed discord and chromium otherwise make the "no target apps present" branches untestable, which is exactly what happened until these tests caught it. Coverage, measured against run_all.sh because two test files jointly cover services_common.sh after the 250-cap split: common 55/55, report 27/27, units 72/72 (was 84.51%), wrappers 100/100, apps 66/66, browser 79/79 -- seven libs at 100%. services_hosts.sh reads 86.05% and services_hosts_fix.sh 97.30%, and those 14 lines are a kcov attribution artifact rather than a gap. Falsified rather than assumed: the suite prints "51 passed, 0 failed" when run under kcov, so no group is skipped, and every supposedly-uncovered line is an issues+=("...") whose string can only reach stdout through the print loop below it -- with a passing assertion matching that exact string. Two falsification checks, both re-run after the rewrite: `--status --dry-run` as root stays byte-identical to rev6691cb26modulo the Date: line, and the sha256sums of /etc/hosts, /etc/nsswitch.conf and /etc/systemd/resolved.conf plus the contents of resolved.conf.d are unchanged across a full suite run. The second one is what makes a missed prefix site detectable instead of a matter of inspection: the repair paths really do write. 220 assertions across 8 test files. Also fixes a bug in the harness's own lsattr shim, where printf with a leading-dash format string parsed the dashes as options and silently produced nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3PzGc75gBcdxP3PkHXGcV
141 lines
6.4 KiB
Bash
Executable File
141 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib/services_hosts_fix.sh — the repair half of check_hosts.
|
|
#
|
|
# Split out of services_hosts.sh so both stay under the 250-line cap. The
|
|
# detection half decides WHETHER to repair; everything here assumes that
|
|
# decision is already made and only does the repairing.
|
|
#
|
|
# All three helpers go through `run`, so --dry-run prints instead of acting.
|
|
# They increment FIXES_APPLIED and may call err_missing_script, exactly as they
|
|
# did inline. hosts_repair_all returns 0 when the post-fix re-verify passes, so
|
|
# the caller can promote its status to "ok" without reading a global.
|
|
|
|
# Put 'files' back on the nsswitch.conf hosts line. Without it every /etc/hosts
|
|
# entry is bypassed outright, which makes this the highest-severity repair here
|
|
# — the blocking stack is silently inert until it is fixed.
|
|
# Absolute paths the checks below probe are prefixed with $SYSROOT, which is
|
|
# empty in production and a fixture tree under test. It is deliberately NOT
|
|
# defaulted here: several repairs in this family write outside `run` (chattr,
|
|
# find -delete, an append to resolved.conf), so a test that forgot to set it
|
|
# would edit the real /etc. Unset is a hard error; empty is the real filesystem.
|
|
SYSROOT="${SERVICES_ROOT?SERVICES_ROOT must be set (empty = the real filesystem)}"
|
|
|
|
hosts_fix_nsswitch() {
|
|
[[ -f "${SYSROOT}/etc/nsswitch.conf" ]] || return 0
|
|
local line
|
|
line=$(grep '^hosts:' "${SYSROOT}/etc/nsswitch.conf" 2>/dev/null || echo "")
|
|
[[ -n $line ]] || return 0
|
|
if echo "$line" | grep -qw 'files'; then
|
|
return 0
|
|
fi
|
|
note "Fixing nsswitch.conf — adding 'files' to hosts line..."
|
|
# 'files' must precede whichever resolver is present, or the resolver
|
|
# answers first and /etc/hosts never gets consulted.
|
|
if echo "$line" | grep -qw 'resolve'; then
|
|
run sed -i 's/^hosts:\(.*\)resolve/hosts: files\1resolve/' "${SYSROOT}/etc/nsswitch.conf"
|
|
elif echo "$line" | grep -qw 'dns'; then
|
|
run sed -i 's/^hosts:\(.*\)dns/hosts:\1files dns/' "${SYSROOT}/etc/nsswitch.conf"
|
|
else
|
|
run sed -i 's/^hosts:/hosts: files/' "${SYSROOT}/etc/nsswitch.conf"
|
|
fi
|
|
((FIXES_APPLIED++)) || true
|
|
msg "nsswitch.conf fixed: $(grep '^hosts:' "${SYSROOT}/etc/nsswitch.conf")"
|
|
}
|
|
|
|
# Repair the three systemd-resolved settings that can bypass /etc/hosts:
|
|
# ReadEtcHosts=no, DNSOverTLS enabled, and drop-in overrides of either. The
|
|
# file carries the immutable attribute, so each edit is bracketed by chattr.
|
|
hosts_fix_resolved() {
|
|
[[ -f "${SYSROOT}/etc/systemd/resolved.conf" ]] || return 0
|
|
local read_etc_hosts
|
|
read_etc_hosts=$(grep -E '^\s*ReadEtcHosts\s*=' "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null |
|
|
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]')
|
|
if [[ "$read_etc_hosts" != "yes" ]]; then
|
|
note "Fixing resolved.conf — setting ReadEtcHosts=yes..."
|
|
chattr -i "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null || true
|
|
if grep -qE '^\s*ReadEtcHosts\s*=' "${SYSROOT}/etc/systemd/resolved.conf"; then
|
|
run sed -i -E 's/^\s*ReadEtcHosts\s*=.*/ReadEtcHosts=yes/' "${SYSROOT}/etc/systemd/resolved.conf"
|
|
elif grep -q '^\[Resolve\]' "${SYSROOT}/etc/systemd/resolved.conf"; then
|
|
run sed -i '/^\[Resolve\]/a ReadEtcHosts=yes' "${SYSROOT}/etc/systemd/resolved.conf"
|
|
else
|
|
printf '\n[Resolve]\nReadEtcHosts=yes\n' >>"${SYSROOT}/etc/systemd/resolved.conf"
|
|
fi
|
|
chattr +i "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null || true
|
|
run systemctl restart systemd-resolved
|
|
((FIXES_APPLIED++)) || true
|
|
msg "resolved.conf ReadEtcHosts fixed"
|
|
fi
|
|
|
|
local dns_over_tls
|
|
dns_over_tls=$(grep -E '^\s*DNSOverTLS\s*=' "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null |
|
|
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]') || true
|
|
if [[ -n "$dns_over_tls" && "$dns_over_tls" != "no" ]]; then
|
|
note "Fixing resolved.conf — disabling DNSOverTLS..."
|
|
chattr -i "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null || true
|
|
run sed -i -E 's/^\s*DNSOverTLS\s*=.*/#DNSOverTLS=no/' "${SYSROOT}/etc/systemd/resolved.conf"
|
|
chattr +i "${SYSROOT}/etc/systemd/resolved.conf" 2>/dev/null || true
|
|
run systemctl restart systemd-resolved
|
|
((FIXES_APPLIED++)) || true
|
|
msg "resolved.conf DNSOverTLS disabled"
|
|
fi
|
|
|
|
# A drop-in can re-enable either setting without touching resolved.conf, so
|
|
# the two repairs above are not durable while any drop-in survives.
|
|
if [[ -d "${SYSROOT}/etc/systemd/resolved.conf.d" ]]; then
|
|
local dropin_count
|
|
dropin_count=$(find "${SYSROOT}/etc/systemd/resolved.conf.d" -name '*.conf' -type f 2>/dev/null | wc -l)
|
|
if [[ "$dropin_count" -gt 0 ]]; then
|
|
note "Removing $dropin_count resolved.conf drop-in override(s)..."
|
|
chattr -i "${SYSROOT}/etc/systemd/resolved.conf.d" 2>/dev/null || true
|
|
find "${SYSROOT}/etc/systemd/resolved.conf.d" -name '*.conf' -type f -delete
|
|
chattr +i "${SYSROOT}/etc/systemd/resolved.conf.d" 2>/dev/null || true
|
|
run systemctl restart systemd-resolved
|
|
((FIXES_APPLIED++)) || true
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Reinstall the hosts file and re-run the guard-lib migration when needed, then
|
|
# re-verify. Returns 0 only if all four guard-lib facts hold afterwards, which
|
|
# is what lets the caller promote its status to "ok".
|
|
#
|
|
# Under --dry-run nothing was actually changed, so re-verifying would report on
|
|
# the unrepaired machine; return 1 there instead of claiming a fix landed.
|
|
hosts_repair_all() {
|
|
# Order matters and matches the pre-split script: resolver config first, so
|
|
# that a hosts file installed below is actually consulted once it lands.
|
|
hosts_fix_nsswitch
|
|
hosts_fix_resolved
|
|
|
|
if [[ ! -f "${SYSROOT}/etc/hosts" ]] || [[ $(wc -l <"${SYSROOT}/etc/hosts") -lt 100 ]]; then
|
|
note "Installing hosts file..."
|
|
if [[ -f $HOSTS_INSTALL_SCRIPT ]]; then
|
|
run bash "$HOSTS_INSTALL_SCRIPT"
|
|
((FIXES_APPLIED++)) || true
|
|
else
|
|
err_missing_script "Hosts install script not found: $HOSTS_INSTALL_SCRIPT"
|
|
fi
|
|
fi
|
|
|
|
# The migration script documents itself as idempotent, so re-running it
|
|
# against an already-migrated healthy machine is a no-op.
|
|
if ! guard_lib_instance_healthy hosts ||
|
|
! guard_lib_instance_healthy nsswitch ||
|
|
! guard_lib_instance_healthy resolved ||
|
|
! hosts_pacman_hooks_installed; then
|
|
note "Repairing guard-lib hosts/nsswitch/resolved instances..."
|
|
if [[ -f $GUARD_LIB_MIGRATE_SCRIPT ]]; then
|
|
run bash "$GUARD_LIB_MIGRATE_SCRIPT"
|
|
((FIXES_APPLIED++)) || true
|
|
else
|
|
err_missing_script "Guard-lib migration script not found: $GUARD_LIB_MIGRATE_SCRIPT"
|
|
fi
|
|
fi
|
|
|
|
[[ $DRY_RUN -eq 0 ]] || return 1
|
|
guard_lib_instance_healthy hosts &&
|
|
guard_lib_instance_healthy nsswitch &&
|
|
guard_lib_instance_healthy resolved &&
|
|
hosts_pacman_hooks_installed
|
|
}
|