bootstrap 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/bin/bash
  2. # System-level state that dotsync cannot manage.
  3. #
  4. # dotsync deals in unprivileged $HOME symlinks; this covers the things that live
  5. # outside $HOME and need root: enabled units and /etc drop-ins. Kept as a plain
  6. # script rather than using the @host/@group markers, because those only apply to
  7. # files deployed into $HOME.
  8. #
  9. # Idempotent -- safe to re-run. Run after 'dotsync packages'.
  10. #
  11. # ./bootstrap
  12. set -euo pipefail
  13. hostname_id=$(uname -n)
  14. RED='\033[0;31m'
  15. GREEN='\033[0;32m'
  16. YELLOW='\033[1;33m'
  17. NC='\033[0m'
  18. log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
  19. log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
  20. log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
  21. if [[ $EUID -eq 0 ]]; then
  22. log_error "Run as your normal user, not root -- user units need your session."
  23. exit 1
  24. fi
  25. # Acquire sudo up front. Without this the first privileged command fails on a
  26. # non-interactive shell and, because install_file runs as an `if` condition
  27. # (where bash disables set -e), the failure would be swallowed and the script
  28. # would report writes that never happened.
  29. if ! sudo -v; then
  30. log_error "Could not obtain sudo credentials."
  31. log_error "Run this from a real terminal -- sudo needs a tty to prompt."
  32. exit 1
  33. fi
  34. # Write a file only if its content differs, so re-runs stay quiet and we avoid
  35. # needlessly restarting units. Aborts on failure rather than reporting a
  36. # phantom success -- see the set -e note above.
  37. install_file() {
  38. local dest="$1" content="$2"
  39. if [[ -f "$dest" ]] && [[ "$(cat "$dest")" == "$content" ]]; then
  40. return 1
  41. fi
  42. if ! sudo mkdir -p "$(dirname "$dest")"; then
  43. log_error "Failed to create $(dirname "$dest")"
  44. exit 1
  45. fi
  46. if ! printf '%s\n' "$content" | sudo tee "$dest" >/dev/null; then
  47. log_error "Failed to write $dest"
  48. exit 1
  49. fi
  50. log_info "Wrote $dest"
  51. return 0
  52. }
  53. # Units are skipped when their package isn't installed on this host -- the
  54. # package lists are per-host (tlp is jellyfish-only, for instance), so this
  55. # file is shared while the set of units that actually exist is not.
  56. enable_unit() {
  57. local unit="$1"
  58. if ! systemctl cat "$unit" &>/dev/null; then
  59. log_warn "Skipping $unit -- not installed on this host"
  60. return 0
  61. fi
  62. if systemctl is-enabled --quiet "$unit" 2>/dev/null; then
  63. return 0
  64. fi
  65. log_info "Enabling $unit"
  66. if ! sudo systemctl enable "$unit"; then
  67. log_error "Failed to enable $unit"
  68. exit 1
  69. fi
  70. }
  71. enable_user_unit() {
  72. local unit="$1"
  73. if ! systemctl --user cat "$unit" &>/dev/null; then
  74. log_warn "Skipping user unit $unit -- not installed on this host"
  75. return 0
  76. fi
  77. if systemctl --user is-enabled --quiet "$unit" 2>/dev/null; then
  78. return 0
  79. fi
  80. log_info "Enabling (user) $unit"
  81. if ! systemctl --user enable "$unit"; then
  82. log_error "Failed to enable user unit $unit"
  83. exit 1
  84. fi
  85. }
  86. log_info "Bootstrapping system state for $hostname_id"
  87. reload_needed=0
  88. # ---------------------------------------------------------------------------
  89. # logind: do not kill user processes at session end.
  90. #
  91. # The default (KillUserProcesses=yes) tears down lingering processes when the
  92. # last session for a user closes. That kills etterminal, which aborts the whole
  93. # etserver -- it broke Eternal Terminal on tompot. Declared explicitly on every
  94. # host so the two machines cannot drift on it again.
  95. #
  96. # A drop-in rather than an edit to /etc/systemd/logind.conf: package upgrades
  97. # replace that file, drop-ins survive.
  98. # ---------------------------------------------------------------------------
  99. if install_file /etc/systemd/logind.conf.d/10-kill-user-processes.conf \
  100. "[Login]
  101. KillUserProcesses=no"; then
  102. reload_needed=1
  103. fi
  104. # ---------------------------------------------------------------------------
  105. # greetd: autologin straight into sway.
  106. #
  107. # The stock package config drops you at an agreety prompt running /bin/sh, so
  108. # without this greetd starts but never gets you into a session. Declared here
  109. # because it lives in /etc, outside what dotsync can manage -- it was previously
  110. # hand-edited on jellyfish only, which is exactly the drift this file exists to
  111. # stop.
  112. # ---------------------------------------------------------------------------
  113. if install_file /etc/greetd/config.toml \
  114. "[terminal]
  115. vt = 1
  116. [default_session]
  117. command = \"agreety --cmd sway\"
  118. user = \"greeter\"
  119. [initial_session]
  120. command = \"sway\"
  121. user = \"$USER\""; then
  122. log_warn "greetd config changed -- takes effect at next logout/reboot"
  123. fi
  124. # ---------------------------------------------------------------------------
  125. # iwd: shorten the stop timeout so shutdown isn't held up for 90s.
  126. # ---------------------------------------------------------------------------
  127. if install_file /etc/systemd/system/iwd.service.d/timeout.conf \
  128. "[Service]
  129. TimeoutStopSec=5"; then
  130. reload_needed=1
  131. fi
  132. if [[ $reload_needed -eq 1 ]]; then
  133. log_info "Reloading systemd"
  134. sudo systemctl daemon-reload
  135. fi
  136. # ---------------------------------------------------------------------------
  137. # System units
  138. # ---------------------------------------------------------------------------
  139. for unit in bluetooth.service greetd.service iwd.service \
  140. systemd-timesyncd.service tlp.service; do
  141. enable_unit "$unit"
  142. done
  143. # ---------------------------------------------------------------------------
  144. # User units
  145. # ---------------------------------------------------------------------------
  146. for unit in wluma.service; do
  147. enable_user_unit "$unit"
  148. done
  149. log_info "Bootstrap complete"
  150. log_warn "logind changes only take full effect after a reboot or full logout."