From 2336767059314bdabb16695abe4ac3a4b9a81669 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 18 Mar 2024 02:02:24 +0000 Subject: [PATCH] port service manager to s6 still a lot of cleanup to do (e.g. support dbus service types), but it boots to a usable desktop --- hosts/common/programs/assorted.nix | 5 ++ hosts/common/programs/sway/default.nix | 64 ++++++++++++++------ hosts/common/programs/sway/sway-config | 6 +- hosts/common/programs/unl0kr/default.nix | 31 +++++++--- hosts/common/programs/xdg-desktop-portal.nix | 14 ++--- modules/users/s6-rc.nix | 43 ++++++++++++- 6 files changed, 126 insertions(+), 37 deletions(-) diff --git a/hosts/common/programs/assorted.nix b/hosts/common/programs/assorted.nix index c7f9010f..0d52c2e7 100644 --- a/hosts/common/programs/assorted.nix +++ b/hosts/common/programs/assorted.nix @@ -79,6 +79,8 @@ in "powertop" "pstree" "ripgrep" + "s6" + "s6-rc" # service manager "screen" "smartmontools" # smartctl # "socat" @@ -763,6 +765,9 @@ in rustc = {}; + s6 = {}; + s6-rc = {}; + sane-open-desktop.sandbox.enable = false; #< trivial script, and all our deps are sandboxed sane-open-desktop.suggestedPrograms = [ "gdbus" diff --git a/hosts/common/programs/sway/default.nix b/hosts/common/programs/sway/default.nix index 2238b518..5217f998 100644 --- a/hosts/common/programs/sway/default.nix +++ b/hosts/common/programs/sway/default.nix @@ -16,7 +16,10 @@ let export XDG_CURRENT_DESKTOP=sway echo "launching sway (sway.desktop)..." | ${systemd-cat} --identifier=sway - exec ${configuredSway}/bin/sway 2>&1 | ${systemd-cat} --identifier=sway + # delete DISPLAY-related vars from env before launch, else sway will try to connect to a remote display. + # (consider: nested sway sessions, where sway actually has a reason to read these) + exec env -u DISPLAY -u WAYLAND_DISPLAY \ + ${configuredSway}/bin/sway 2>&1 | ${systemd-cat} --identifier=sway ''; in pkgs.symlinkJoin { @@ -194,6 +197,7 @@ in # bind ALL of ~/.config/sway into the sandbox instead of just the individual configs. # this way `swaymsg -- reload` can work even if the fd for ~/.config/sway/config changes. ".config/sway" + ".config/s6" ]; sandbox.extraConfig = [ "--sane-sandbox-keep-namespace" "pid" @@ -218,26 +222,50 @@ in xwayland = if config.sane.programs.xwayland.enabled then "enable" else "disable"; }; - services.sway-session = { - description = "sway-session: active iff sway desktop environment is baseline operational"; - documentation = [ - "https://github.com/swaywm/sway/issues/7862" - "https://github.com/alebastr/sway-systemd" - ]; + env.XDG_CURRENT_DESKTOP = "sway"; + # TODO: don't hardcode user id! + env.SWAYSOCK = "/run/user/1000/sway-ipc.sock"; + # TODO: ensure this is reliable? might not work across sway restarts, etc. + env.DISPLAY = ":0"; + env.WAYLAND_DISPLAY = "wayland-1"; - # we'd like to start graphical-session after sway is ready, but it's marked `RefuseManualStart` because Lennart. - # instead, create `sway-session.service` which `bindsTo` `graphical-session.target`. - # we can manually start `sway-session`, and the `bindsTo` means that it will start `graphical-session`, - # and then track `graphical-session`s state (that is: it'll stop when graphical-session stops). - # - # additionally, set `ConditionEnvironment` to guard that the sway environment variables *really have* been imported into systemd. - unitConfig.ConditionEnvironment = "SWAYSOCK"; - # requiredBy = [ "graphical-session.target" ]; - before = [ "graphical-session.target" ]; - bindsTo = [ "graphical-session.target" ]; + # services.sway-session = { + # description = "sway-session: active iff sway desktop environment is baseline operational"; + # documentation = [ + # "https://github.com/swaywm/sway/issues/7862" + # "https://github.com/alebastr/sway-systemd" + # ]; + # # we'd like to start graphical-session after sway is ready, but it's marked `RefuseManualStart` because Lennart. + # # instead, create `sway-session.service` which `bindsTo` `graphical-session.target`. + # # we can manually start `sway-session`, and the `bindsTo` means that it will start `graphical-session`, + # # and then track `graphical-session`s state (that is: it'll stop when graphical-session stops). + # # + # # additionally, set `ConditionEnvironment` to guard that the sway environment variables *really have* been imported into systemd. + # unitConfig.ConditionEnvironment = "SWAYSOCK"; + # # requiredBy = [ "graphical-session.target" ]; + # before = [ "graphical-session.target" ]; + # bindsTo = [ "graphical-session.target" ]; + + # serviceConfig = { + # ExecStart = "${pkgs.coreutils}/bin/true"; + # Type = "oneshot"; + # RemainAfterExit = true; + # }; + # }; + + # services.sway-session = { + # description = "sway-session: meta-service for everything wanted by sway (after launch)"; + # wants = [ "graphical-session.target" ]; + # }; + + services.sway = { + description = "sway: launch it"; + wantedBy = [ "default.target" ]; serviceConfig = { - ExecStart = "${pkgs.coreutils}/bin/true"; + ExecStart = "${cfg.package}/bin/sway"; + # ExecStart = "${lib.getBin pkgs.glib}/bin/gio launch sway.desktop"; + # ExecStart = "${pkgs.sane-open-desktop}/bin/sane-open-desktop sway.desktop"; Type = "oneshot"; RemainAfterExit = true; }; diff --git a/hosts/common/programs/sway/sway-config b/hosts/common/programs/sway/sway-config index 7c4ded70..ff8eb84f 100644 --- a/hosts/common/programs/sway/sway-config +++ b/hosts/common/programs/sway/sway-config @@ -258,6 +258,10 @@ exec --no-startup-id dbus-update-activation-environment --systemd PATH XDG_DATA_ # signal to systemd that sway is active, # and therefore let it start any downstream services (e.g. apps that would like to auto-start) # see `systemctl --user cat sway-session` for links to docs -exec --no-startup-id systemctl start --user sway-session.service +# exec --no-startup-id systemctl start --user sway-session.service + +# TODO: remove this. likely, the variables above can/should be set *before* launching the service manager at all. +# and if need be we can replace this line with a "notify ready" signal to the service manager instead. +exec --no-startup-id s6-rc -l $HOME/.config/s6/live start graphical-session @extra_lines@ diff --git a/hosts/common/programs/unl0kr/default.nix b/hosts/common/programs/unl0kr/default.nix index 8a133878..d8e8fd0d 100644 --- a/hosts/common/programs/unl0kr/default.nix +++ b/hosts/common/programs/unl0kr/default.nix @@ -119,15 +119,28 @@ in }; }; - # lib.mkAfter so that launching the DE happens *after* any other .profile setup. - # alternatively, we could recurse: exec a new login shell with some env-var signalling to not launch the DE, - # run with `-c "{cfg.afterLogin}"` - fs.".profile".symlink.text = lib.mkAfter '' - # if already running a desktop environment, or if running from ssh, then `tty` will show /dev/pts/NN. - if [ "$(tty)" = "/dev/${tty}" ]; then - ${tryLaunchDefaultDesktop}/bin/tryLaunchDefaultDesktop - fi - ''; + fs.".profile".symlink.text = lib.mkMerge [ + (lib.mkBefore '' + primarySessionCommands=() + initPrimarySession() { + for c in "''${primarySessionCommands[@]}"; do + eval "$c" + done + } + '') + # lib.mkAfter so that launching the DE happens *after* any other .profile setup. + (lib.mkAfter '' + # if already running a desktop environment, or if running from ssh, then `tty` will show /dev/pts/NN. + if [ "$(tty)" = "/dev/${tty}" ]; then + if (( ''${#primarySessionCommands[@]} )); then + echo "launching primary session commands in ${builtins.toString cfg.config.delay}s: ''${primarySessionCommands[*]}" + # if the `sleep` call here is `Ctrl+C'd`, then it'll exit false and the desktop isn't launched. + sleep ${builtins.toString cfg.config.delay} && \ + initPrimarySession + fi + fi + '') + ]; # N.B.: this sandboxing applies to `unl0kr` itself -- the on-screen-keyboard; # NOT to the wrapper which invokes `login`. diff --git a/hosts/common/programs/xdg-desktop-portal.nix b/hosts/common/programs/xdg-desktop-portal.nix index b88d9fef..e7606836 100644 --- a/hosts/common/programs/xdg-desktop-portal.nix +++ b/hosts/common/programs/xdg-desktop-portal.nix @@ -59,19 +59,19 @@ in wantedBy = [ "graphical-session.target" ]; serviceConfig = { - ExecStart="${cfg.package}/libexec/xdg-desktop-portal"; + # tracking issue for having xdg-desktop-portal locate portals via more standard directories, obviating this var: + # - + # i can actually almost omit it today; problem is that if you don't set it it'll look for `sway-portals.conf` in ~/.config/xdg-desktop-portal + # but then will check its *own* output dir for {gtk,wlr}.portal. + # arguable if that's a packaging bug, or limitation... + ExecStart=''env XDG_DESKTOP_PORTAL_DIR="$HOME/.config/xdg-desktop-portal" ${cfg.package}/libexec/xdg-desktop-portal''; Type = "dbus"; BusName = "org.freedesktop.portal.Desktop"; Restart = "always"; RestartSec = "10s"; }; - # tracking issue for having xdg-desktop-portal locate portals via more standard directories, obviating this var: - # - - # i can actually almost omit it today; problem is that if you don't set it it'll look for `sway-portals.conf` in ~/.config/xdg-desktop-portal - # but then will check its *own* output dir for {gtk,wlr}.portal. - # arguable if that's a packaging bug, or limitation... - environment.XDG_DESKTOP_PORTAL_DIR = "%E/xdg-desktop-portal"; + # environment.XDG_DESKTOP_PORTAL_DIR = "%E/xdg-desktop-portal"; # environment.G_MESSAGES_DEBUG = "all"; #< also applies to all apps launched by the portal }; diff --git a/modules/users/s6-rc.nix b/modules/users/s6-rc.nix index 4f50cf57..572bd7b6 100644 --- a/modules/users/s6-rc.nix +++ b/modules/users/s6-rc.nix @@ -103,7 +103,7 @@ let inherit name; run = service.serviceConfig.ExecStart; depends = service.wants ++ builtins.attrNames ( - lib.filterAttrs (_: cfg: lib.elem name cfg.wantedBy) services + lib.filterAttrs (_: cfg: lib.elem name cfg.wantedBy || lib.elem "${name}.service" cfg.wantedBy) services ); }) services @@ -131,7 +131,46 @@ in sources = genServices (s6SvcsFromConfigServices (implicitServices // config.services)); in { fs.".config/s6/sources".symlink.target = sources; - fs.".config/s6/compiled".symlink.target = compileServices sources; + # N.B.: `compiled` needs to be writable (for locks -- maybe i can use symlinks to dodge this someday), + # so write this nearby and copy it over to `compiled` later + fs.".config/s6/compiled-static".symlink.target = compileServices sources; + + fs.".profile".symlink.text = '' + function startS6() { + local S6_TARGET="''${1:-default}" + + local COMPILED=$HOME/.config/s6/compiled + local LIVE=$HOME/.config/s6/live + local SCANDIR=$HOME/.config/s6/scandir + + rm -rf $SCANDIR + mkdir $SCANDIR + s6-svscan $SCANDIR & + local SVSCAN=$! + + # the scandir is just links back into the compiled dir, + # so the compiled dir therefore needs to be writable: + rm -rf $COMPILED + cp --dereference -R $COMPILED-static $COMPILED + chmod -R 0700 $COMPILED + + s6-rc-init -c $COMPILED -l $LIVE -d $SCANDIR + + # echo default: deps + # s6-rc-db -c $COMPILED contents default + # echo graphical-session: deps + # s6-rc-db -c $COMPILED contents graphical-session + + if [ -n "$S6_TARGET" ]; then + s6-rc -l $LIVE start "$S6_TARGET" + fi + + echo "s6 initialized: Ctrl+C to stop" + wait $SVSCAN + } + + primarySessionCommands+=('startS6 &') + ''; })); }; }