2022-08-30 14:29:44 +00:00
|
|
|
{
|
2023-06-18 00:46:31 +00:00
|
|
|
lib,
|
|
|
|
pkgs,
|
|
|
|
config,
|
|
|
|
...
|
|
|
|
}:
|
2023-06-18 03:32:15 +00:00
|
|
|
with builtins;
|
|
|
|
with lib; {
|
2022-08-30 14:29:44 +00:00
|
|
|
imports = [
|
2023-06-18 00:46:31 +00:00
|
|
|
(mkRenamedOptionModule ["wsl" "compatibility" "interopPreserveArgvZero"] ["wsl" "interop" "preserveArgvZero"])
|
2022-08-30 14:29:44 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
options.wsl.interop = with types; {
|
|
|
|
register = mkOption {
|
|
|
|
type = bool;
|
|
|
|
default = false; # Use the existing registration by default
|
|
|
|
description = "Explicitly register the binfmt_misc handler for Windows executables";
|
|
|
|
};
|
|
|
|
|
|
|
|
includePath = mkOption {
|
|
|
|
type = bool;
|
|
|
|
default = true;
|
|
|
|
description = "Include Windows PATH in WSL PATH";
|
|
|
|
};
|
|
|
|
|
|
|
|
preserveArgvZero = mkOption {
|
|
|
|
type = nullOr bool;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
Register binfmt interpreter for Windows executables with 'preserves argv[0]' flag.
|
|
|
|
|
|
|
|
Default (null): autodetect, at some performance cost.
|
|
|
|
To avoid the performance cost, set this to true for WSL Preview 0.58 and up,
|
|
|
|
or to false for any older versions, including pre-Microsoft Store and Windows 10.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-06-18 00:46:31 +00:00
|
|
|
config = let
|
|
|
|
cfg = config.wsl.interop;
|
|
|
|
in
|
2022-08-30 14:29:44 +00:00
|
|
|
mkIf config.wsl.enable {
|
|
|
|
boot.binfmt.registrations = mkIf cfg.register {
|
2023-06-18 00:46:31 +00:00
|
|
|
WSLInterop = let
|
|
|
|
compat = cfg.preserveArgvZero;
|
2022-08-30 14:29:44 +00:00
|
|
|
|
2023-06-18 00:46:31 +00:00
|
|
|
# WSL Preview 0.58 and up registers the /init binfmt interp for Windows executable
|
|
|
|
# with the "preserve argv[0]" flag, so if you run `./foo.exe`, the interp gets invoked
|
|
|
|
# as `/init foo.exe ./foo.exe`.
|
|
|
|
# argv[0] --^ ^-- actual path
|
|
|
|
#
|
|
|
|
# Older versions expect to be called without the argv[0] bit, simply as `/init ./foo.exe`.
|
|
|
|
#
|
|
|
|
# We detect that by running `/init /known-not-existing-path.exe` and checking the exit code:
|
|
|
|
# the new style interp expects at least two arguments, so exits with exit code 1,
|
|
|
|
# presumably meaning "parsing error"; the old style interp attempts to actually run
|
|
|
|
# the executable, fails to find it, and exits with 255.
|
|
|
|
compatWrapper = pkgs.writeShellScript "nixos-wsl-binfmt-hack" ''
|
|
|
|
/init /nixos-wsl-does-not-exist.exe
|
|
|
|
[ $? -eq 255 ] && shift
|
|
|
|
exec /init "$@"
|
|
|
|
'';
|
2022-08-30 14:29:44 +00:00
|
|
|
|
2023-06-18 00:46:31 +00:00
|
|
|
# use the autodetect hack if unset, otherwise call /init directly
|
|
|
|
interpreter =
|
|
|
|
if compat == null
|
|
|
|
then compatWrapper
|
|
|
|
else "/init";
|
2022-08-30 14:29:44 +00:00
|
|
|
|
2023-06-18 00:46:31 +00:00
|
|
|
# enable for the wrapper and autodetect hack
|
|
|
|
preserveArgvZero =
|
|
|
|
if compat == false
|
|
|
|
then false
|
|
|
|
else true;
|
|
|
|
in {
|
|
|
|
magicOrExtension = "MZ";
|
|
|
|
fixBinary = true;
|
|
|
|
wrapInterpreterInShell = false;
|
|
|
|
inherit interpreter preserveArgvZero;
|
|
|
|
};
|
2022-08-30 14:29:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
# Include Windows %PATH% in Linux $PATH.
|
|
|
|
environment.extraInit = mkIf cfg.includePath ''PATH="$PATH:$WSLPATH"'';
|
|
|
|
|
2023-06-18 00:46:31 +00:00
|
|
|
warnings = let
|
|
|
|
registrations = config.boot.binfmt.registrations;
|
|
|
|
in
|
2022-08-30 14:29:44 +00:00
|
|
|
optional (!(registrations ? WSLInterop) && (length (attrNames config.boot.binfmt.registrations)) != 0) "Having any binfmt registrations without re-registering WSLInterop (wsl.interop.register) will break running .exe files from WSL2";
|
|
|
|
};
|
|
|
|
}
|