blob: e279d00d1677c6058702623e1a98044137e3c391 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
{ lib, pkgs, config, options, ... }:
with lib;
let
bashWrapper = pkgs.writeShellScriptBin "sh" ''
export PATH="$PATH:${lib.makeBinPath [ pkgs.systemd pkgs.gnugrep ]}"
exec ${pkgs.bashInteractive}/bin/sh "$@"
'';
cfg = config.wsl;
in
{
options.wsl = with types; {
enable = mkEnableOption "support for running NixOS as a WSL distribution";
binShPkg = mkOption {
type = lib.types.package;
internal = true;
description = "Package to be linked to /bin/sh. Mainly useful to be re-used by other modules like envfs.";
};
nativeSystemd = mkOption {
type = bool;
default = false;
description = "Use native WSL systemd support";
};
defaultUser = mkOption {
type = str;
default = "nixos";
description = "The name of the default user";
};
populateBin = mkOption {
type = bool;
default = true;
internal = true;
description = ''
Dangerous! Things might break. Use with caution!
Do not populate /bin.
This is mainfly useful if another module populates /bin like envfs.
'';
};
startMenuLaunchers = mkEnableOption "shortcuts for GUI applications in the windows start menu";
};
config =
let
syschdemd = pkgs.callPackage ../scripts/syschdemd.nix {
automountPath = cfg.wslConf.automount.root;
defaultUser = config.users.users.${cfg.defaultUser};
};
nativeUtils = pkgs.callPackage ../scripts/native-utils { };
in
mkIf cfg.enable (
mkMerge [
{
# WSL uses its own kernel and boot loader
boot = {
initrd.enable = false;
kernel.enable = false;
loader.grub.enable = false;
modprobeConfig.enable = false;
};
system.build.installBootLoader = "${pkgs.coreutils}/bin/true";
# WSL does not support virtual consoles
console.enable = false;
hardware.opengl.enable = true; # Enable GPU acceleration
environment = {
# Only set the options if the files are managed by WSL
etc = mkMerge [
(mkIf config.wsl.wslConf.network.generateHosts {
hosts.enable = false;
})
(mkIf config.wsl.wslConf.network.generateResolvConf {
"resolv.conf".enable = false;
})
];
};
# dhcp is handled by windows
networking.dhcpcd.enable = false;
users.users.${cfg.defaultUser} = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "wheel" ]; # Allow the default user to use sudo
};
# Otherwise WSL fails to login as root with "initgroups failed 5"
users.users.root.extraGroups = [ "root" ];
powerManagement.enable = false;
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 "setting up /usr/share/''${x}..."
targets=()
if [[ -d "$systemConfig/sw/share/$x" ]]; then
targets+=("$systemConfig/sw/share/$x/.")
fi
if [[ -d "/etc/profiles/per-user/${cfg.defaultUser}/share/$x" ]]; then
targets+=("/etc/profiles/per-user/${cfg.defaultUser}/share/$x/.")
fi
if (( ''${#targets[@]} != 0 )); then
mkdir -p "/usr/share/$x"
${pkgs.rsync}/bin/rsync -ar --delete-after "''${targets[@]}" "/usr/share/$x"
else
rm -rf "/usr/share/$x"
fi
done
''
);
populateBin = lib.mkIf cfg.populateBin (stringAfter [ ] ''
echo "setting up /bin..."
ln -sf /init /bin/wslpath
ln -sf ${cfg.binShPkg}/bin/sh /bin/sh
ln -sf ${pkgs.util-linux}/bin/mount /bin/mount
'');
update-entrypoint.text = ''
mkdir -p /nix/nixos-wsl
ln -sfn ${config.users.users.root.shell} /nix/nixos-wsl/entrypoint
'';
};
# 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" ];
};
# Start a systemd user session when starting a command through runuser
security.pam.services.runuser.startSession = true;
# require people to use lib.mkForce to make it harder to brick their installation
wsl = {
binShPkg = if cfg.nativeSystemd then bashWrapper else pkgs.bashInteractive;
populateBin = true;
};
warnings = flatten [
(optional (config.services.resolved.enable && config.wsl.wslConf.network.generateResolvConf)
"systemd-resolved is enabled, but resolv.conf is managed by WSL (wsl.wslConf.network.generateResolvConf)"
)
(optional ((length config.networking.nameservers) > 0 && config.wsl.wslConf.network.generateResolvConf)
"custom nameservers are set (networking.nameservers), but resolv.conf is managed by WSL (wsl.wslConf.network.generateResolvConf)"
)
(optional ((length config.networking.nameservers) == 0 && !config.services.resolved.enable && !config.wsl.wslConf.network.generateResolvConf)
"resolv.conf generation is turned off (wsl.wslConf.network.generateResolvConf), but no other nameservers are configured (networking.nameservers)"
)
];
}
(mkIf (!cfg.nativeSystemd) {
users.users.root.shell = "${syschdemd}/bin/syschdemd";
security.sudo.extraConfig = ''
Defaults env_keep+=INSIDE_NAMESPACE
'';
wsl.wslConf.user.default = "root";
# Include Windows %PATH% in Linux $PATH.
environment.extraInit = mkIf cfg.interop.includePath ''PATH="$PATH:$WSLPATH"'';
})
(mkIf cfg.nativeSystemd {
wsl.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"})
'';
};
})
# this option doesn't exist on older NixOS, so hack.
(lib.optionalAttrs (builtins.hasAttr "oomd" options.systemd) {
# systemd-oomd requires cgroup pressure info which WSL doesn't have
systemd.oomd.enable = false;
})
]);
}
|