Skip to content

Commit

Permalink
feat: native systemd support (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
K900 authored Oct 30, 2022
1 parent 7bfb8f5 commit 8e3a9c1
Show file tree
Hide file tree
Showing 11 changed files with 431 additions and 127 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
result
result-*
scripts/*/target
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rust-analyzer.linkedProjects": [
"scripts/native-systemd-shim/Cargo.toml"
]
}
3 changes: 1 addition & 2 deletions configuration.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{ lib, pkgs, config, modulesPath, ... }:
{ pkgs, config, modulesPath, ... }:

with lib;
let
nixos-wsl = import ./default.nix;
in
Expand Down
13 changes: 11 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

} //
flake-utils.lib.eachSystem
(with flake-utils.lib.system; [ "x86_64-linux" "aarch64-linux" ])
[ "x86_64-linux" "aarch64-linux" ]
(system:
let
pkgs = import nixpkgs { inherit system; };
Expand All @@ -49,7 +49,16 @@
};

devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nixpkgs-fmt shfmt ];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";

nativeBuildInputs = with pkgs; [
nixpkgs-fmt
shfmt
rustc
cargo
rustfmt
clippy
];
};
}
);
Expand Down
4 changes: 1 addition & 3 deletions modules/installer.nix
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ with builtins; with lib; {
compressionExtension = ".gz";
extraArgs = "--hard-dereference";

storeContents = with pkgs; pkgs2storeContents [
installer
];
storeContents = pkgs2storeContents [ installer ];

contents = [
{ source = config.environment.etc."wsl.conf".source; target = "/etc/wsl.conf"; }
Expand Down
3 changes: 0 additions & 3 deletions modules/interop.nix
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ with builtins; with lib;
};
};

# Include Windows %PATH% in Linux $PATH.
environment.extraInit = mkIf cfg.includePath ''PATH="$PATH:$WSLPATH"'';

warnings =
let
registrations = config.boot.binfmt.registrations;
Expand Down
270 changes: 153 additions & 117 deletions modules/wsl-distro.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,144 +2,180 @@

with builtins; with lib;
{
options.wsl = with types;
let
coercedToStr = coercedTo (oneOf [ bool path int ]) (toString) str;
in
{
enable = mkEnableOption "support for running NixOS as a WSL distribution";
automountPath = mkOption {
type = str;
default = "/mnt";
description = "The path where windows drives are mounted (e.g. /mnt/c)";
};
automountOptions = mkOption {
type = str;
default = "metadata,uid=1000,gid=100";
description = "Options to use when mounting windows drives";
};
defaultUser = mkOption {
type = str;
default = "nixos";
description = "The name of the default user";
};
startMenuLaunchers = mkEnableOption "shortcuts for GUI applications in the windows start menu";
wslConf = mkOption {
type = attrsOf (attrsOf (oneOf [ string int bool ]));
description = "Entries that are added to /etc/wsl.conf";
};
options.wsl = with types; {
enable = mkEnableOption "support for running NixOS as a WSL distribution";
nativeSystemd = mkOption {
type = bool;
default = false;
description = "Use native WSL systemd support";
};
automountPath = mkOption {
type = str;
default = "/mnt";
description = "The path where windows drives are mounted (e.g. /mnt/c)";
};
automountOptions = mkOption {
type = str;
default = "metadata,uid=1000,gid=100";
description = "Options to use when mounting windows drives";
};
defaultUser = mkOption {
type = str;
default = "nixos";
description = "The name of the default user";
};
startMenuLaunchers = mkEnableOption "shortcuts for GUI applications in the windows start menu";
wslConf = mkOption {
type = attrsOf (attrsOf (oneOf [ string int bool ]));
description = "Entries that are added to /etc/wsl.conf";
};
};

config =
let
cfg = config.wsl;
syschdemd = pkgs.callPackage ../scripts/syschdemd.nix { inherit (cfg) automountPath defaultUser; };
shim = pkgs.callPackage ../scripts/native-systemd-shim/shim.nix { };

bashWrapper = pkgs.runCommand "nixos-wsl-bash-wrapper" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
makeWrapper ${pkgs.bashInteractive}/bin/sh $out/bin/sh --prefix PATH ':' ${lib.makeBinPath [pkgs.systemd pkgs.gnugrep]}
'';

bash = if cfg.nativeSystemd then bashWrapper else pkgs.bashInteractive;
in
mkIf cfg.enable {

wsl.wslConf = {
automount = {
enabled = true;
mountFsTab = true;
root = "${cfg.automountPath}/";
options = cfg.automountOptions;
mkMerge [
(mkIf cfg.enable {
wsl.wslConf = {
automount = {
enabled = true;
mountFsTab = true;
root = "${cfg.automountPath}/";
options = cfg.automountOptions;
};
network = {
generateResolvConf = mkDefault true;
generateHosts = mkDefault true;
};
};
network = {
generateResolvConf = mkDefault true;
generateHosts = mkDefault true;
};
};

# We don't need a boot loader
boot.loader.grub.enable = false;
system.build.installBootLoader = "${pkgs.coreutils}/bin/true";
boot.initrd.enable = false;
system.build.initialRamdisk = pkgs.runCommand "fake-initrd" { } ''
mkdir $out
touch $out/${config.system.boot.loader.initrdFile}
'';
system.build.initialRamdiskSecretAppender = pkgs.writeShellScriptBin "append-initrd-secrets" "";

hardware.opengl.enable = true; # Enable GPU acceleration
# We don't need a boot loader
boot.loader.grub.enable = false;
system.build.installBootLoader = "${pkgs.coreutils}/bin/true";
boot.initrd.enable = false;
system.build.initialRamdisk = pkgs.runCommand "fake-initrd" { } ''
mkdir $out
touch $out/${config.system.boot.loader.initrdFile}
'';
system.build.initialRamdiskSecretAppender = pkgs.writeShellScriptBin "append-initrd-secrets" "";

hardware.opengl.enable = true; # Enable GPU acceleration

environment = {
environment = {

etc = {
"wsl.conf".text = generators.toINI { } cfg.wslConf;
etc = {
"wsl.conf".text = generators.toINI { } cfg.wslConf;

# DNS settings are managed by WSL
hosts.enable = !config.wsl.wslConf.network.generateHosts;
"resolv.conf".enable = !config.wsl.wslConf.network.generateResolvConf;
};
# DNS settings are managed by WSL
hosts.enable = !config.wsl.wslConf.network.generateHosts;
"resolv.conf".enable = !config.wsl.wslConf.network.generateResolvConf;
};

systemPackages = [
(pkgs.runCommand "wslpath" { } ''
mkdir -p $out/bin
ln -s /init $out/bin/wslpath
'')
];
};
systemPackages = [
(pkgs.runCommand "wslpath" { } ''
mkdir -p $out/bin
ln -s /init $out/bin/wslpath
'')
];
};

networking.dhcpcd.enable = false;
networking.dhcpcd.enable = false;

users.users.${cfg.defaultUser} = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "wheel" ]; # Allow the default user to use sudo
};
users.users.${cfg.defaultUser} = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "wheel" ]; # Allow the default user to use sudo
};

users.users.root = {
shell = "${syschdemd}/bin/syschdemd";
# Otherwise WSL fails to login as root with "initgroups failed 5"
extraGroups = [ "root" ];
};
users.users.root.extraGroups = [ "root" ];

security.sudo.wheelNeedsPassword = mkDefault false; # The default user will not have a password by default

system.activationScripts = {
copy-launchers = mkIf cfg.startMenuLaunchers (
stringAfter [ ] ''
for x in applications icons; do
echo "Copying /usr/share/$x"
mkdir -p /usr/share/$x
${pkgs.rsync}/bin/rsync -ar --delete $systemConfig/sw/share/$x/. /usr/share/$x
done
''
);
populateBin = stringAfter [ ] ''
echo "setting up /bin..."
ln -sf /init /bin/wslpath
ln -sf ${bash}/bin/sh /bin/sh
ln -sf ${pkgs.util-linux}/bin/mount /bin/mount
'';
};

systemd = {
# Disable systemd units that don't make sense on WSL
services = {
"serial-getty@ttyS0".enable = false;
"serial-getty@hvc0".enable = false;
"getty@tty1".enable = false;
"autovt@".enable = false;
firewall.enable = false;
systemd-resolved.enable = false;
systemd-udevd.enable = false;
};

tmpfiles.rules = [
# Don't remove the X11 socket
"d /tmp/.X11-unix 1777 root root"
];

# Don't allow emergency mode, because we don't have a console.
enableEmergencyMode = false;
};

security.sudo = {
extraConfig = ''
warnings = (optional (config.systemd.services.systemd-resolved.enable && config.wsl.wslConf.network.generateResolvConf) "systemd-resolved is enabled, but resolv.conf is managed by WSL");
})
(mkIf (!cfg.nativeSystemd) {
users.users.root.shell = "${syschdemd}/bin/syschdemd";
security.sudo.extraConfig = ''
Defaults env_keep+=INSIDE_NAMESPACE
'';
wheelNeedsPassword = mkDefault false; # The default user will not have a password by default
};

system.activationScripts = {
copy-launchers = mkIf cfg.startMenuLaunchers (
stringAfter [ ] ''
for x in applications icons; do
echo "Copying /usr/share/$x"
mkdir -p /usr/share/$x
${pkgs.rsync}/bin/rsync -ar --delete $systemConfig/sw/share/$x/. /usr/share/$x
done
''
);
populateBin = stringAfter [ ] ''
echo "setting up /bin..."
ln -sf /init /bin/wslpath
ln -sf ${pkgs.bashInteractive}/bin/bash /bin/sh
ln -sf ${pkgs.util-linux}/bin/mount /bin/mount
'';
};

systemd = {
# Disable systemd units that don't make sense on WSL
services = {
"serial-getty@ttyS0".enable = false;
"serial-getty@hvc0".enable = false;
"getty@tty1".enable = false;
"autovt@".enable = false;
firewall.enable = false;
systemd-resolved.enable = false;
systemd-udevd.enable = false;
wsl.wslConf.users.default = "root";

# Include Windows %PATH% in Linux $PATH.
environment.extraInit = mkIf cfg.interop.includePath ''PATH="$PATH:$WSLPATH"'';
})
(mkIf cfg.nativeSystemd {
wsl.wslConf = {
user.default = cfg.defaultUser;
boot.systemd = true;
};

tmpfiles.rules = [
# Don't remove the X11 socket
"d /tmp/.X11-unix 1777 root root"
];

# Don't allow emergency mode, because we don't have a console.
enableEmergencyMode = false;
};
system.activationScripts = {
shimSystemd = stringAfter [ ] ''
echo "setting up /sbin/init shim..."
mkdir -p /sbin
ln -sf ${shim}/bin/nixos-wsl-native-systemd-shim /sbin/init
'';
};

warnings = (optional (config.systemd.services.systemd-resolved.enable && config.wsl.wslConf.network.generateResolvConf) "systemd-resolved is enabled, but resolv.conf is managed by WSL");
};
environment = {
# preserve $PATH from parent
variables.PATH = [ "$PATH" ];
extraInit = ''
export WSLPATH=$(echo "$PATH" | tr ':' '\n' | grep -E "^${cfg.automountPath}" | tr '\n' ':')
${if cfg.interop.includePath then "" else ''
export PATH=$(echo "$PATH" | tr ':' '\n' | grep -vE "^${cfg.automountPath}" | tr '\n' ':')
''}
'';
};
})
];
}
Loading

0 comments on commit 8e3a9c1

Please sign in to comment.