Skip to content

Commit 819b815

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 819b815

File tree

8 files changed

+203
-116
lines changed

8 files changed

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