Skip to content

Commit 596e8ba

Browse files
committed
feat: rewrite syschdemd
Mostly just a big pile of cleanups. Important changes: - refactor and clean up the code, fix all the shellcheck complaints - stop trying to guess paths in scripts and wrap them correctly instead - use nsfs instead of trying to figure out the right PID to copy namespaces from - clean up $WSLPATH to remove extra impure entries - don't try to restart systemd if it died - make sure the store is read-only before we do anything, so systemd can't mess with it - reformat shell scripts with shfmt
1 parent 0b29fc7 commit 596e8ba

File tree

8 files changed

+201
-116
lines changed

8 files changed

+201
-116
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
- uses: actions/download-artifact@v2
6060
with:
6161
name: installer
62-
62+
6363
- name: Generate checksums
6464
run: |
6565
for x in *.tar.gz; do

flake.nix

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@
4040
pkgs = import nixpkgs { inherit system; };
4141
in
4242
{
43-
checks.check-format = pkgs.runCommand "check-format"
44-
{
45-
buildInputs = with pkgs; [ nixpkgs-fmt ];
46-
} ''
47-
nixpkgs-fmt --check ${./.}
48-
mkdir $out # success
49-
'';
43+
checks = {
44+
check-format = pkgs.runCommand "check-format" { nativeBuildInputs = with pkgs; [ nixpkgs-fmt shfmt ]; } ''
45+
nixpkgs-fmt --check ${./.}
46+
shfmt -i 2 -d ${./scripts}/*.sh
47+
mkdir $out # success
48+
'';
49+
};
5050

5151
devShell = pkgs.mkShell {
52-
nativeBuildInputs = with pkgs; [ nixpkgs-fmt ];
52+
nativeBuildInputs = with pkgs; [ nixpkgs-fmt shfmt ];
5353
};
5454
}
5555
);

modules/wsl-distro.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ with builtins; with lib;
3333
config =
3434
let
3535
cfg = config.wsl;
36-
syschdemd = import ../syschdemd.nix { inherit lib pkgs config; inherit (cfg) automountPath defaultUser; defaultUserHome = config.users.users.${cfg.defaultUser}.home; };
36+
syschdemd = pkgs.callPackage ../scripts/syschdemd.nix { inherit (cfg) automountPath defaultUser; };
3737
in
3838
mkIf cfg.enable {
3939

scripts/syschdemd.nix

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{ runCommand
2+
, makeWrapper
3+
, lib
4+
, coreutils
5+
, daemonize
6+
, glibc
7+
, gnugrep
8+
, systemd
9+
, util-linux
10+
, defaultUser
11+
, automountPath
12+
,
13+
}:
14+
let
15+
writeShellScript =
16+
{ name
17+
, src
18+
, path
19+
, ...
20+
} @ args:
21+
runCommand name ({ nativeBuildInputs = [ makeWrapper ]; } // args) ''
22+
install -Dm755 ${src} $out/bin/${name}
23+
patchShebangs $out/bin/${name}
24+
substituteAllInPlace $out/bin/${name}
25+
wrapProgram $out/bin/${name} --prefix PATH ':' ${lib.escapeShellArg path}
26+
'';
27+
28+
wrapper = writeShellScript {
29+
name = "nixos-wsl-systemd-wrapper";
30+
src = ./wrapper.sh;
31+
path = lib.makeSearchPath "" [
32+
"/run/wrappers/bin" # mount
33+
"${systemd}/lib/systemd" # systemd
34+
];
35+
};
36+
in
37+
writeShellScript {
38+
name = "syschdemd";
39+
src = ./syschdemd.sh;
40+
path = lib.makeBinPath [
41+
"/run/wrappers" # mount
42+
coreutils
43+
daemonize
44+
glibc # getent
45+
gnugrep
46+
systemd # machinectl
47+
util-linux # nsenter
48+
wrapper
49+
];
50+
inherit defaultUser automountPath;
51+
}

scripts/syschdemd.sh

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
[ "${NIXOS_WSL_DEBUG:-}" == "1" ] && set -x
5+
6+
rundir="/run/nixos-wsl"
7+
pidfile="$rundir/unshare.pid"
8+
9+
ensure_root() {
10+
if [ $EUID -ne 0 ]; then
11+
echo "[ERROR] Requires root! :( Make sure the WSL default user is set to root" >&2
12+
exit 1
13+
fi
14+
}
15+
16+
activate() {
17+
mount --bind -o ro /nix/store /nix/store
18+
LANG="C.UTF-8" /nix/var/nix/profiles/system/activate
19+
}
20+
21+
create_rundir() {
22+
if [ ! -d $rundir ]; then
23+
mkdir -p $rundir/ns
24+
touch $rundir/ns/{pid,mount}
25+
fi
26+
}
27+
28+
is_unshare_alive() {
29+
[ -e $pidfile ] && [ -d "/proc/$(<$pidfile)" ]
30+
}
31+
32+
run_in_namespace() {
33+
nsenter \
34+
--pid=$rundir/ns/pid \
35+
--mount=$rundir/ns/mount \
36+
-- "$@"
37+
}
38+
39+
start_systemd() {
40+
daemonize \
41+
-o $rundir/stdout \
42+
-e $rundir/stderr \
43+
-l $rundir/systemd.lock \
44+
-p $pidfile \
45+
-E LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \
46+
"$(command -v unshare)" \
47+
--fork \
48+
--pid=$rundir/ns/pid \
49+
--mount=$rundir/ns/mount \
50+
--mount-proc=/proc \
51+
nixos-wsl-systemd-wrapper
52+
53+
# Wait for systemd to start
54+
while ! (run_in_namespace systemctl is-system-running -q --wait) &>/dev/null; do
55+
sleep 1
56+
57+
if ! is_unshare_alive; then
58+
echo "[ERROR] systemd startup failed!"
59+
60+
echo "[ERROR] stderr:"
61+
cat $rundir/stderr
62+
63+
echo "[ERROR] stdout:"
64+
cat $rundir/stdout
65+
66+
exit 1
67+
fi
68+
done
69+
}
70+
71+
get_shell() {
72+
getent passwd "$1" | cut -d: -f7
73+
}
74+
75+
get_home() {
76+
getent passwd "$1" | cut -d: -f6
77+
}
78+
79+
is_in_container() {
80+
[ "${INSIDE_NAMESPACE:-}" == "true" ]
81+
}
82+
83+
clean_wslpath() {
84+
echo "$PATH" | tr ':' '\n' | grep -E "^@automountPath@" | tr '\n' ':'
85+
}
86+
87+
main() {
88+
ensure_root
89+
90+
if [ ! -e "/run/current-system" ]; then
91+
activate
92+
fi
93+
94+
if [ ! -e "$rundir" ]; then
95+
create_rundir
96+
fi
97+
98+
if ! is_in_container && ! is_unshare_alive; then
99+
start_systemd
100+
fi
101+
102+
if [ $# -gt 0 ]; then
103+
# wsl seems to prefix with "-c"
104+
shift
105+
command="$*"
106+
else
107+
command=$(get_shell @defaultUser@)
108+
fi
109+
110+
# If we're executed from inside the container, e.g. sudo
111+
if is_in_container; then
112+
exec $command
113+
fi
114+
115+
# If we are currently in /root, this is probably because the directory that WSL was started is inaccessible
116+
# cd to the user's home to prevent a warning about permission being denied on /root
117+
if [ "$PWD" == "/root" ]; then
118+
cd "$(get_home @defaultUser@)"
119+
fi
120+
121+
# Pass external environment but filter variables specific to root user.
122+
exportCmd="$(export -p | grep -vE ' (HOME|LOGNAME|SHELL|USER)=')"
123+
124+
run_in_namespace \
125+
machinectl \
126+
--quiet \
127+
--uid=@defaultUser@ \
128+
--setenv=INSIDE_NAMESPACE=true \
129+
--setenv=WSLPATH="$(clean_wslpath)" \
130+
shell .host \
131+
/bin/sh -c "cd \"$PWD\"; $exportCmd; source /etc/set-environment; exec $command"
132+
}
133+
134+
main "$@"

scripts/wrapper.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
set -euxo pipefail
3+
4+
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
5+
6+
exec systemd

syschdemd.nix

Lines changed: 0 additions & 28 deletions
This file was deleted.

syschdemd.sh

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)