#!/bin/bash # System-level state that dotsync cannot manage. # # dotsync deals in unprivileged $HOME symlinks; this covers the things that live # outside $HOME and need root: enabled units and /etc drop-ins. Kept as a plain # script rather than using the @host/@group markers, because those only apply to # files deployed into $HOME. # # Idempotent -- safe to re-run. Run after 'dotsync packages'. # # ./bootstrap set -euo pipefail hostname_id=$(uname -n) RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; } log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } if [[ $EUID -eq 0 ]]; then log_error "Run as your normal user, not root -- user units need your session." exit 1 fi # Acquire sudo up front. Without this the first privileged command fails on a # non-interactive shell and, because install_file runs as an `if` condition # (where bash disables set -e), the failure would be swallowed and the script # would report writes that never happened. if ! sudo -v; then log_error "Could not obtain sudo credentials." log_error "Run this from a real terminal -- sudo needs a tty to prompt." exit 1 fi # Write a file only if its content differs, so re-runs stay quiet and we avoid # needlessly restarting units. Aborts on failure rather than reporting a # phantom success -- see the set -e note above. install_file() { local dest="$1" content="$2" if [[ -f "$dest" ]] && [[ "$(cat "$dest")" == "$content" ]]; then return 1 fi if ! sudo mkdir -p "$(dirname "$dest")"; then log_error "Failed to create $(dirname "$dest")" exit 1 fi if ! printf '%s\n' "$content" | sudo tee "$dest" >/dev/null; then log_error "Failed to write $dest" exit 1 fi log_info "Wrote $dest" return 0 } # Units are skipped when their package isn't installed on this host -- the # package lists are per-host (tlp is jellyfish-only, for instance), so this # file is shared while the set of units that actually exist is not. enable_unit() { local unit="$1" if ! systemctl cat "$unit" &>/dev/null; then log_warn "Skipping $unit -- not installed on this host" return 0 fi if systemctl is-enabled --quiet "$unit" 2>/dev/null; then return 0 fi log_info "Enabling $unit" if ! sudo systemctl enable "$unit"; then log_error "Failed to enable $unit" exit 1 fi } enable_user_unit() { local unit="$1" if ! systemctl --user cat "$unit" &>/dev/null; then log_warn "Skipping user unit $unit -- not installed on this host" return 0 fi if systemctl --user is-enabled --quiet "$unit" 2>/dev/null; then return 0 fi log_info "Enabling (user) $unit" if ! systemctl --user enable "$unit"; then log_error "Failed to enable user unit $unit" exit 1 fi } log_info "Bootstrapping system state for $hostname_id" reload_needed=0 # --------------------------------------------------------------------------- # logind: do not kill user processes at session end. # # The default (KillUserProcesses=yes) tears down lingering processes when the # last session for a user closes. That kills etterminal, which aborts the whole # etserver -- it broke Eternal Terminal on tompot. Declared explicitly on every # host so the two machines cannot drift on it again. # # A drop-in rather than an edit to /etc/systemd/logind.conf: package upgrades # replace that file, drop-ins survive. # --------------------------------------------------------------------------- if install_file /etc/systemd/logind.conf.d/10-kill-user-processes.conf \ "[Login] KillUserProcesses=no"; then reload_needed=1 fi # --------------------------------------------------------------------------- # greetd: autologin straight into sway. # # The stock package config drops you at an agreety prompt running /bin/sh, so # without this greetd starts but never gets you into a session. Declared here # because it lives in /etc, outside what dotsync can manage -- it was previously # hand-edited on jellyfish only, which is exactly the drift this file exists to # stop. # --------------------------------------------------------------------------- if install_file /etc/greetd/config.toml \ "[terminal] vt = 1 [default_session] command = \"agreety --cmd sway\" user = \"greeter\" [initial_session] command = \"sway\" user = \"$USER\""; then log_warn "greetd config changed -- takes effect at next logout/reboot" fi # --------------------------------------------------------------------------- # iwd: shorten the stop timeout so shutdown isn't held up for 90s. # --------------------------------------------------------------------------- if install_file /etc/systemd/system/iwd.service.d/timeout.conf \ "[Service] TimeoutStopSec=5"; then reload_needed=1 fi if [[ $reload_needed -eq 1 ]]; then log_info "Reloading systemd" sudo systemctl daemon-reload fi # --------------------------------------------------------------------------- # System units # --------------------------------------------------------------------------- for unit in bluetooth.service greetd.service iwd.service \ systemd-timesyncd.service tlp.service; do enable_unit "$unit" done # --------------------------------------------------------------------------- # User units # --------------------------------------------------------------------------- for unit in wluma.service; do enable_user_unit "$unit" done log_info "Bootstrap complete" log_warn "logind changes only take full effect after a reboot or full logout."