diff options
| author | nzbr <mail@nzbr.de> | 2023-09-12 12:19:14 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-12 12:19:14 +0200 |
| commit | ff57c8dc58f707299379fd538c6b6ec77980f7cf (patch) | |
| tree | 1e29af88c893020fdd1dcb694c9303c6bf43d78b /modules/systemd | |
| parent | 212e2d6b0d820fc9f1e79f7b5feeea2824db51bb (diff) | |
Refactor (#291)
* move module imports to default.nix
* move docker modules to subdirectory
* add an otion for adding files to /bin. Fixes #279
* move recovery script to own module
* reorder options
* move systemd related code to separate modules
* move utils to repo root
* devShell -> devShells.default
* fix utils imports
* fix bashWrapper
Diffstat (limited to 'modules/systemd')
| -rw-r--r-- | modules/systemd/default.nix | 48 | ||||
| -rw-r--r-- | modules/systemd/native/default.nix | 46 | ||||
| -rw-r--r-- | modules/systemd/syschdemd/default.nix | 41 | ||||
| -rw-r--r-- | modules/systemd/syschdemd/syschdemd.nix | 55 | ||||
| -rw-r--r-- | modules/systemd/syschdemd/syschdemd.sh | 140 | ||||
| -rw-r--r-- | modules/systemd/syschdemd/wrapper.sh | 8 |
6 files changed, 338 insertions, 0 deletions
diff --git a/modules/systemd/default.nix b/modules/systemd/default.nix new file mode 100644 index 0000000..c6c2337 --- /dev/null +++ b/modules/systemd/default.nix @@ -0,0 +1,48 @@ +{ config, pkgs, lib, ... }: +with lib; { + + imports = [ + ./native + ./syschdemd + ]; + + options.wsl = with types; { + nativeSystemd = mkOption { + type = bool; + default = false; + description = "Use native WSL systemd support"; + }; + }; + + config = + let + cfg = config.wsl; + in + mkIf (cfg.enable) { + + # systemd-oomd requires cgroup pressure info which WSL doesn't have + systemd.oomd.enable = false; + + # useful for usbip but adds a dependency on various firmwares which are combined over 300 MB big + services.udev.enable = lib.mkDefault false; + + systemd = { + # Disable systemd units that don't make sense on WSL + services = { + firewall.enable = false; + systemd-resolved.enable = lib.mkDefault false; + # systemd-timesyncd actually works in WSL and without it the clock can drift + systemd-timesyncd.unitConfig.ConditionVirtualization = ""; + }; + + # Don't allow emergency mode, because we don't have a console. + enableEmergencyMode = false; + + # Link the X11 socket into place. This is a no-op on a normal setup, + # but helps if /tmp is a tmpfs or mounted from some other location. + tmpfiles.rules = [ "L /tmp/.X11-unix - - - - ${cfg.wslConf.automount.root}/wslg/.X11-unix" ]; + }; + + }; + +} diff --git a/modules/systemd/native/default.nix b/modules/systemd/native/default.nix new file mode 100644 index 0000000..f641580 --- /dev/null +++ b/modules/systemd/native/default.nix @@ -0,0 +1,46 @@ +{ config, pkgs, lib, ... }: +with lib; { + + config = + let + cfg = config.wsl; + nativeUtils = pkgs.callPackage ../../../utils { }; + + bashWrapper = pkgs.writeShellScriptBin "sh" '' + export PATH="$PATH:${lib.makeBinPath [ pkgs.systemd pkgs.gnugrep ]}" + exec ${pkgs.bashInteractive}/bin/sh "$@" + ''; + in + mkIf (cfg.enable && cfg.nativeSystemd) { + + wsl = { + binShPkg = bashWrapper; + wslConf = { + user.default = config.users.users.${cfg.defaultUser}.name; + boot.systemd = true; + }; + }; + + system.activationScripts = { + shimSystemd = stringAfter [ ] '' + echo "setting up /sbin/init shim..." + mkdir -p /sbin + ln -sf ${nativeUtils}/bin/systemd-shim /sbin/init + ''; + setupLogin = lib.mkIf cfg.populateBin (stringAfter [ ] '' + echo "setting up /bin/login..." + mkdir -p /bin + ln -sf ${pkgs.shadow}/bin/login /bin/login + ''); + }; + + environment = { + # preserve $PATH from parent + variables.PATH = [ "$PATH" ]; + extraInit = '' + eval $(${nativeUtils}/bin/split-path --automount-root="${cfg.wslConf.automount.root}" ${lib.optionalString cfg.interop.includePath "--include-interop"}) + ''; + }; + }; + +} diff --git a/modules/systemd/syschdemd/default.nix b/modules/systemd/syschdemd/default.nix new file mode 100644 index 0000000..cdea1ed --- /dev/null +++ b/modules/systemd/syschdemd/default.nix @@ -0,0 +1,41 @@ +{ config, pkgs, lib, ... }: +with lib; { + + options = { }; + + config = + let + cfg = config.wsl; + + syschdemd = pkgs.callPackage ./syschdemd.nix { + automountPath = cfg.wslConf.automount.root; + defaultUser = config.users.users.${cfg.defaultUser}; + }; + in + mkIf (cfg.enable && !cfg.nativeSystemd) { + + wsl = { + binShPkg = pkgs.bashInteractive; + wslConf.user.default = "root"; + }; + + users.users.root.shell = "${syschdemd}/bin/syschdemd"; + security.sudo.extraConfig = '' + Defaults env_keep+=INSIDE_NAMESPACE + ''; + + # Start a systemd user session when starting a command through runuser + security.pam.services.runuser.startSession = true; + + # Include Windows %PATH% in Linux $PATH. + environment.extraInit = mkIf cfg.interop.includePath ''PATH="$PATH:$WSLPATH"''; + environment.systemPackages = [ + (pkgs.runCommand "wslpath" { } '' + mkdir -p $out/bin + ln -s /init $out/bin/wslpath + '') + ]; + + }; + +} diff --git a/modules/systemd/syschdemd/syschdemd.nix b/modules/systemd/syschdemd/syschdemd.nix new file mode 100644 index 0000000..06ba653 --- /dev/null +++ b/modules/systemd/syschdemd/syschdemd.nix @@ -0,0 +1,55 @@ +{ runCommand +, makeWrapper +, lib +, coreutils +, daemonize +, getent +, gnugrep +, systemd +, util-linux +, which +, defaultUser +, automountPath +, ... +}: +let + mkWrappedScript = + { name + , src + , path + , ... + } @ args: + runCommand name ({ nativeBuildInputs = [ makeWrapper ]; } // args) '' + install -Dm755 ${src} $out/bin/${name} + patchShebangs $out/bin/${name} + substituteAllInPlace $out/bin/${name} + wrapProgram $out/bin/${name} --prefix PATH ':' ${lib.escapeShellArg path} + ''; + + wrapper = mkWrappedScript { + name = "nixos-wsl-systemd-wrapper"; + src = ./wrapper.sh; + path = lib.makeSearchPath "" [ + "/run/wrappers/bin" # mount + "${gnugrep}/bin" # grep + "${systemd}/lib/systemd" # systemd + ]; + }; +in +mkWrappedScript { + name = "syschdemd"; + src = ./syschdemd.sh; + path = lib.makeBinPath [ + "/run/wrappers" # mount + coreutils + daemonize + getent + gnugrep + systemd # machinectl + util-linux # nsenter, runuser + which + wrapper + ]; + username = defaultUser.name; + inherit automountPath; +} diff --git a/modules/systemd/syschdemd/syschdemd.sh b/modules/systemd/syschdemd/syschdemd.sh new file mode 100644 index 0000000..fa0ffe5 --- /dev/null +++ b/modules/systemd/syschdemd/syschdemd.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +set -euo pipefail + +[ "${NIXOS_WSL_DEBUG:-}" == "1" ] && set -x + +rundir="/run/nixos-wsl" +pidfile="$rundir/unshare.pid" + +ensure_root() { + if [ $EUID -ne 0 ]; then + echo "[ERROR] Requires root! :( Make sure the WSL default user is set to root" >&2 + exit 1 + fi +} + +activate() { + mount --bind -o ro /nix/store /nix/store + + LANG="C.UTF-8" /nix/var/nix/profiles/system/activate +} + +create_rundir() { + if [ ! -d $rundir ]; then + mkdir -p $rundir/ns + touch $rundir/ns/{pid,mount} + fi +} + +is_unshare_alive() { + [ -e $pidfile ] && [ -d "/proc/$(<$pidfile)" ] +} + +run_in_namespace() { + nsenter \ + --pid=$rundir/ns/pid \ + --mount=$rundir/ns/mount \ + -- "$@" +} + +start_systemd() { + daemonize \ + -o $rundir/stdout \ + -e $rundir/stderr \ + -l $rundir/systemd.lock \ + -p $pidfile \ + -E LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \ + "$(command -v unshare)" \ + --fork \ + --pid=$rundir/ns/pid \ + --mount=$rundir/ns/mount \ + --mount-proc=/proc \ + --propagation=unchanged \ + nixos-wsl-systemd-wrapper + + # Wait for systemd to start + while ! (run_in_namespace systemctl is-system-running -q --wait) &>/dev/null; do + sleep 1 + + if ! is_unshare_alive; then + echo "[ERROR] systemd startup failed!" + + echo "[ERROR] stderr:" + cat $rundir/stderr + + echo "[ERROR] stdout:" + cat $rundir/stdout + + exit 1 + fi + done +} + +get_shell() { + getent passwd "$1" | cut -d: -f7 +} + +get_home() { + getent passwd "$1" | cut -d: -f6 +} + +is_in_container() { + [ "${INSIDE_NAMESPACE:-}" == "true" ] +} + +clean_wslpath() { + echo "$PATH" | tr ':' '\n' | grep -E "^@automountPath@" | tr '\n' ':' +} + +main() { + ensure_root + + if [ ! -e "/run/current-system" ]; then + activate + fi + + if [ ! -e "$rundir" ]; then + create_rundir + fi + + if ! is_in_container && ! is_unshare_alive; then + start_systemd + fi + + if [ $# -gt 1 ]; then # Ignore just -c without a command + # wsl seems to prefix with "-c" + shift + command="$(get_shell @username@) -l -c \"$*\"" + else + command="$(get_shell @username@) -l" + fi + + # If we're executed from inside the container, e.g. sudo + if is_in_container; then + eval $command + fi + + # If we are currently in /root, this is probably because the directory that WSL was started is inaccessible + # cd to the user's home to prevent a warning about permission being denied on /root + if [ "$PWD" == "/root" ]; then + cd "$(get_home @username@)" + fi + + # Pass external environment but filter variables specific to root user. + exportCmd="$(export -p | grep -vE ' (HOME|LOGNAME|SHELL|USER)=')" + + run_in_namespace \ + systemd-run \ + --quiet \ + --collect \ + --wait \ + --pty \ + --service-type=exec \ + --setenv=INSIDE_NAMESPACE=true \ + --setenv=WSLPATH="$(clean_wslpath)" \ + --working-directory="$PWD" \ + --machine=.host \ + "$(which runuser)" --pty -u @username@ -- /bin/sh -c "$exportCmd; source /etc/set-environment; exec $command" +} + +main "$@" diff --git a/modules/systemd/syschdemd/wrapper.sh b/modules/systemd/syschdemd/wrapper.sh new file mode 100644 index 0000000..15b24be --- /dev/null +++ b/modules/systemd/syschdemd/wrapper.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euxo pipefail + +if grep -q binfmt_misc /proc/filesystems; then + mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc +fi + +exec systemd |
