fix(setup): handle unterminated passwd, make shell change non-fatal

shadow's commonio_open() refuses to modify a user/group database whose
last line has no trailing newline, failing with a misleading
"cannot open /etc/passwd". Under 'set -e' this aborted the whole setup
at the fish step (and would later break docker's groupadd/usermod too).

- add ensure_db_trailing_newlines() to normalize /etc/passwd, /etc/group
  and /etc/shadow before any user/group modification
- switch default shell via 'usermod -s' with a 'chsh' fallback, warning
  instead of aborting the script on failure
This commit is contained in:
FlintyLemming
2026-07-23 13:56:20 +08:00
parent 7e7d94cc6b
commit 8cdd0fb0b1
+25 -3
View File
@@ -300,13 +300,16 @@ setup_fish() {
info "$FISH_PATH already in /etc/shells" info "$FISH_PATH already in /etc/shells"
fi fi
# Change default shell only if not already fish # Change default shell only if not already fish.
# Prefer usermod (script-friendly, no TTY/PAM quirks); fall back to chsh.
# Never abort the whole setup if this fails — just warn and move on.
current_shell="$(getent passwd "$USER" | cut -d: -f7)" current_shell="$(getent passwd "$USER" | cut -d: -f7)"
if [ "$current_shell" = "$FISH_PATH" ]; then if [ "$current_shell" = "$FISH_PATH" ]; then
info "fish is already the default shell" info "fish is already the default shell"
else elif psudo usermod -s "$FISH_PATH" "$USER" || psudo chsh -s "$FISH_PATH" "$USER"; then
psudo chsh -s "$FISH_PATH" "$USER"
success "Default shell changed to fish ($FISH_PATH)" success "Default shell changed to fish ($FISH_PATH)"
else
warn "Could not change default shell to fish — run manually: sudo usermod -s $FISH_PATH $USER"
fi fi
# For brew-based systems: add brew shellenv to fish config # For brew-based systems: add brew shellenv to fish config
@@ -749,6 +752,24 @@ setup_passwordless_sudo() {
esac esac
} }
# ─── Normalize user/group databases ──────────────────────────────────────────
# shadow's commonio_open() refuses to modify a database whose last line is not
# newline-terminated, failing with a misleading "cannot open /etc/passwd".
# Some installers/tools leave these files unterminated, which then breaks chsh,
# usermod and groupadd alike — so make sure they end with a newline first.
ensure_db_trailing_newlines() {
local f
for f in /etc/passwd /etc/group /etc/shadow; do
[ -f "$f" ] || continue
# `tail -c 1` yields the last byte; command substitution strips a
# trailing newline, so non-empty output means the file is unterminated.
if [ -n "$(psudo tail -c 1 "$f")" ]; then
psudo sh -c "printf '\\n' >> '$f'"
warn "Added missing trailing newline to $f (required by chsh/usermod)"
fi
done
}
# ─── Main ───────────────────────────────────────────────────────────────────── # ─── Main ─────────────────────────────────────────────────────────────────────
main() { main() {
echo -e "${BOLD}${CYAN}" echo -e "${BOLD}${CYAN}"
@@ -759,6 +780,7 @@ main() {
detect_os detect_os
ensure_sudo ensure_sudo
ensure_db_trailing_newlines
setup_passwordless_sudo setup_passwordless_sudo
set_hostname set_hostname