summaryrefslogtreecommitdiff
path: root/modules/systemd
diff options
context:
space:
mode:
authornzbr <mail@nzbr.de>2023-09-12 12:19:14 +0200
committerGitHub <noreply@github.com>2023-09-12 12:19:14 +0200
commitff57c8dc58f707299379fd538c6b6ec77980f7cf (patch)
tree1e29af88c893020fdd1dcb694c9303c6bf43d78b /modules/systemd
parent212e2d6b0d820fc9f1e79f7b5feeea2824db51bb (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.nix48
-rw-r--r--modules/systemd/native/default.nix46
-rw-r--r--modules/systemd/syschdemd/default.nix41
-rw-r--r--modules/systemd/syschdemd/syschdemd.nix55
-rw-r--r--modules/systemd/syschdemd/syschdemd.sh140
-rw-r--r--modules/systemd/syschdemd/wrapper.sh8
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