diff --git a/README.md b/README.md index 215b3f9..8153d16 100644 --- a/README.md +++ b/README.md @@ -84,11 +84,10 @@ runs `home-manager switch`; what it sets up is listed under [Set up by install.sh](#set-up-by-installsh). Everything works in the running session right away, except two settings read only at a process start: the [keyboard layout](docs/session.md#keyboard-layout) (next Steam session start) -and the [SteamVR dashboard patches](docs/steamvr-debugger.md) (VR pet, the VR -keyboard's suggestion strip, window curvature and the other patches of -SteamVR's dashboard; next SteamVR start). If you use them, the installer ends -with "Restart once": reboot once. Re-running it just switches -again (`--yes` answers every question). Afterwards edit +and the [SteamVR dashboard patches](docs/steamvr-debugger.md) (VR pet, VR +keyboard suggestions, window curvature, ...; next SteamVR start). If they +are waiting, the installer ends with "Restart once": reboot once. Re-running +it just switches again (`--yes` answers every question). Afterwards edit `~/nix-config/home.nix` and switch (see [Usage](#usage)). Your own config in a git repository: `--clone ` clones it into @@ -501,12 +500,11 @@ stops Home Manager's user services (reverting the UI patches), runs `cleanup --all`, runs `home-manager uninstall`, then removes Nix and the per-user Nix state (see [Set up by install.sh](#set-up-by-installsh)). If SteamVR is running, its key is restored when SteamVR stops (the closing -message says so). Programs started from the Nix store keep `/nix` busy: -before removing Nix, `uninstall` lists them and waits until you close them -(with `--yes`, it stops instead); close them or run it right after a reboot. -If Nix's own uninstaller fails anyway, the rest still runs (the `~/.config/home-manager` link goes) except -the per-user Nix state, and the closing message says to reboot and run -`uninstall` again. Your configuration, `*.hm-backup-*` files, app data and +message says so). Nix can't be removed while programs started from the Nix +store run: `uninstall` lists them and waits; close them or run it right +after a reboot. If Nix stays (in use, `--yes`, or its uninstaller failed), +the rest still runs (the `~/.config/home-manager` link goes) except the +per-user Nix state, and the closing message says to run `uninstall` again. Your configuration, `*.hm-backup-*` files, app data and Flatpaks stay. To drop steam-frame-nix from a Home Manager configuration you keep, first diff --git a/docs/cleanup.md b/docs/cleanup.md index c94f9ed..6a8aa58 100644 --- a/docs/cleanup.md +++ b/docs/cleanup.md @@ -45,6 +45,21 @@ What `install.sh install` sets up is listed in the README under desktop can't reach it with its own environment) and uses the installed `home-manager` if there is one, else Home Manager's `master`. +`install --clone` takes SteamOS' `git`, else `nix run nixpkgs#git`. An +existing directory is reused only if it is the top of a clone whose +`origin` is the same repository: URLs are compared as lower-case +host/path without `.git` (so `git@host:o/r`, `ssh://git@host/o/r` and +`https://host/o/r.git` match), and with `--ref` it must be on that branch. + +`restart-check` (run by the session module's activation after +`steamFrameUserServices`, and by `install` at its end) compares the +keyboard layout drop-in on `gamescope-session.service` with +`XKB_DEFAULT_*` in the environment of the first readable process in that +unit's cgroup (gamescope's own isn't readable), and, while SteamVR runs +with the debugger drop-in, looks for a listener on port 8087 in +`/proc/net/tcp*`. Tests use `STEAM_FRAME_NIX_PROC` and +`STEAM_FRAME_NIX_CGROUP` (section F of `modules/cleanup/check.nix`). + `uninstall` removes Nix with nix-installer, which fails when it can't unmount `/nix` (`systemctl stop nix.mount`). Before that it scans `/proc` for processes using `/nix`: their `exe`, `cwd`, `root` or an `fd` links into @@ -56,6 +71,6 @@ the script itself, its subshells (descendants) and its process group (the `uninstall` from another terminal. Nothing is killed: it asks to close them and re-checks on Enter, or (`--yes`, no terminal) stops before Nix. A bash from the Nix store re-executes the script with `/usr/bin/bash` first, and -nix-installer runs from a copy outside `/nix`. The check uses +nix-installer runs from a root-owned copy in `/tmp`. The check uses `STEAM_FRAME_NIX_PROC` as a fake `/proc` (section H of `modules/cleanup/check.nix`). diff --git a/install.sh b/install.sh index 84f0268..c0e753e 100755 --- a/install.sh +++ b/install.sh @@ -234,17 +234,19 @@ install_nix() { } uninstall_nix() { - local rc=0 bin + local rc=0 bin= need_sudo ro_unlock step "Uninstalling Nix" - # From a copy: the uninstaller running from /nix would keep /nix busy. - bin="$(mktemp)" - if ! { cp "$NIX_INSTALLER_BIN" "$bin" && chmod +x "$bin" && "$bin" --version >/dev/null 2>&1; }; then - rm -f "$bin"; bin=$NIX_INSTALLER_BIN + # From a root-owned copy: the uninstaller running from /nix would keep + # /nix busy. + if ! bin="$(sudo mktemp)" || ! sudo cp "$NIX_INSTALLER_BIN" "$bin" \ + || ! sudo chmod 700 "$bin" || ! sudo "$bin" --version >/dev/null 2>&1; then + [[ -n $bin ]] && sudo rm -f "$bin" + bin=$NIX_INSTALLER_BIN fi sudo "$bin" uninstall --no-confirm || rc=$? - [[ $bin == "$NIX_INSTALLER_BIN" ]] || rm -f "$bin" + [[ $bin == "$NIX_INSTALLER_BIN" ]] || sudo rm -f "$bin" ro_relock return "$rc" } @@ -1056,7 +1058,6 @@ session_xkb() { # LAYOUT|VARIANT # Whether something listens on 127.0.0.1/::1/any : (/proc/net/tcp*). port_listening() { # port - local hex local hex f files=() printf -v hex '%04X' "$1" for f in "$SFN_PROC/net/tcp" "$SFN_PROC/net/tcp6"; do [[ -r $f ]] && files+=("$f"); done @@ -1178,7 +1179,8 @@ nix_users() { grep -ls '[[:space:]]/nix/' "$SFN_PROC"/[0-9]*/maps || true } | while IFS= read -r p; do p=${p#"$SFN_PROC"/}; echo "${p%%/*}"; done | sort -un \ | while read -r pid; do - [[ $pid != "$self" ]] && { read -r name <"$SFN_PROC/$pid/comm"; } 2>/dev/null || continue + [[ $pid != "$self" ]] || continue + { read -r name <"$SFN_PROC/$pid/comm"; } 2>/dev/null || continue # exited # argv[0]'s name says more than comm (often a thread name) argv0=''; { IFS= read -r -d '' argv0 <"$SFN_PROC/$pid/cmdline"; } 2>/dev/null || true [[ ${argv0##*/} == '' || ${argv0##*/} == exe ]] || name=${argv0##*/} @@ -1274,9 +1276,9 @@ cmd_uninstall() { cmd_cleanup --all remove_hm - # A failed Nix uninstall (e.g. /nix still busy) must not skip the rest: - # Home Manager is gone by now, so its config link goes regardless; the - # per-user Nix files stay for a Nix that may still be there. + # Nix still in use or a failed Nix uninstall must not skip the rest: Home + # Manager is gone by now, so its config link goes regardless; the per-user + # Nix files stay for the Nix that is still there. local nix_failed='' if (( ! keep_nix )); then if [[ -x $NIX_INSTALLER_BIN ]]; then diff --git a/modules/session.nix b/modules/session.nix index 3427629..7904f4c 100644 --- a/modules/session.nix +++ b/modules/session.nix @@ -98,12 +98,6 @@ in { # In a subshell: the outer session's environment must not leak into # activation steps that run later. - # Read-only; a warning, never a failed switch. - config.home.activation.steamFrameRestartCheck = lib.hm.dag.entryAfter [ "steamFrameUserServices" ] '' - STEAM_FRAME_NIX_RUNTIME_DIR=${lib.escapeShellArg cfg.runtimeDir} XDG_CONFIG_HOME=${lib.escapeShellArg config.xdg.configHome} \ - ${lib.getExe restartCheck} || true - ''; - config.home.activation.steamFrameUserServices = lib.hm.dag.entryAfter [ "reloadSystemd" ] ('' ( export XDG_RUNTIME_DIR=${lib.escapeShellArg cfg.runtimeDir} DBUS_SESSION_BUS_ADDRESS=${lib.escapeShellArg cfg.bus} @@ -121,4 +115,10 @@ in { '' + '' ) ''); + + # Read-only; a warning, never a failed switch. + config.home.activation.steamFrameRestartCheck = lib.hm.dag.entryAfter [ "steamFrameUserServices" ] '' + STEAM_FRAME_NIX_RUNTIME_DIR=${lib.escapeShellArg cfg.runtimeDir} XDG_CONFIG_HOME=${lib.escapeShellArg config.xdg.configHome} \ + ${lib.getExe restartCheck} || true + ''; }