|
| 1 | +#!/usr/bin/env bash |
| 2 | +# MiOS -- wsl-early: WSL2 pre-sysinit fixups. |
| 3 | +# |
| 4 | +# Runs as PID 1's child with full root caps BEFORE basic.target and BEFORE |
| 5 | +# any service that creates a mount namespace. Fixes two WSL2-specific gaps |
| 6 | +# that cause cascading failures across cockpit and rootless podman: |
| 7 | +# |
| 8 | +# 1. Root mount propagation -- WSL2's /init mounts / private, which makes |
| 9 | +# systemd's per-unit `mount(NULL, "/", NULL, MS_REC|MS_SLAVE, NULL)` |
| 10 | +# return EOPNOTSUPP. That's the actual cause of `cockpit.service: |
| 11 | +# Failed to set up mount namespacing: Operation not supported` and |
| 12 | +# every "status=226/NAMESPACE" failure on WSL. Marking root rshared |
| 13 | +# lets every subsequent unit's PrivateTmp/ProtectSystem/etc. clone |
| 14 | +# a private mount namespace cleanly. |
| 15 | +# |
| 16 | +# 2. /dev/net/tun and /dev/fuse -- the WSL2 kernel builds tun + fuse |
| 17 | +# in-tree, but systemd-udevd is condition-gated off (ConditionPathIs |
| 18 | +# ReadWrite=/sys fails because /sys is RO under WSL2's /init), so |
| 19 | +# nothing auto-creates the char device nodes. Without /dev/net/tun |
| 20 | +# slirp4netns can't open its tap fd; without /dev/fuse fuse-overlayfs |
| 21 | +# reports "cannot mount: No such file or directory" and rootless |
| 22 | +# podman storage init fails entirely. We mknod them by hand here. |
| 23 | +# |
| 24 | +# Why this script and not tmpfiles.d: |
| 25 | +# * `c!` lines in tmpfiles.d would work in theory, but tmpfiles is run |
| 26 | +# after some early namespace-using units have already failed, and |
| 27 | +# because tmpfiles itself uses sandboxing on some unit paths, mknod |
| 28 | +# can return EPERM under the same propagation lock that bites cockpit. |
| 29 | +# Doing it from a no-sandbox oneshot before basic.target avoids both. |
| 30 | +set -euo pipefail |
| 31 | + |
| 32 | +_log() { logger -t mios-wsl-early "$*" 2>/dev/null || true; echo "[wsl-early] $*" >&2; } |
| 33 | + |
| 34 | +# Hard gate: only on WSL. |
| 35 | +if [[ ! -f /proc/sys/fs/binfmt_misc/WSLInterop && ! -d /run/WSL ]]; then |
| 36 | + case "$(systemd-detect-virt 2>/dev/null || echo unknown)" in |
| 37 | + wsl) ;; |
| 38 | + *) _log "not running under WSL; nothing to do"; exit 0 ;; |
| 39 | + esac |
| 40 | +fi |
| 41 | + |
| 42 | +# 1. Make / rshared so per-unit mount namespaces can MS_SLAVE-propagate. |
| 43 | +# Idempotent: a second --make-rshared on an already-shared mount is a no-op. |
| 44 | +if mount --make-rshared / 2>/dev/null; then |
| 45 | + _log "mount --make-rshared / OK" |
| 46 | +else |
| 47 | + _log "WARN: mount --make-rshared / failed (continuing -- some namespace setups may still fail)" |
| 48 | +fi |
| 49 | + |
| 50 | +# 2. Ensure /dev/net/tun (c 10:200) and /dev/fuse (c 10:229) exist. |
| 51 | +# Both are kernel-builtin on the microsoft-standard-WSL2 kernel; the |
| 52 | +# nodes only need to be present in /dev for slirp4netns and |
| 53 | +# fuse-overlayfs to open them. |
| 54 | +mkdir -p /dev/net 2>/dev/null || true |
| 55 | +if [[ ! -e /dev/net/tun ]]; then |
| 56 | + if mknod /dev/net/tun c 10 200 2>/dev/null && chmod 0666 /dev/net/tun 2>/dev/null; then |
| 57 | + _log "mknod /dev/net/tun (c 10:200) OK" |
| 58 | + else |
| 59 | + _log "WARN: /dev/net/tun mknod failed (slirp4netns may not work)" |
| 60 | + fi |
| 61 | +fi |
| 62 | +if [[ ! -e /dev/fuse ]]; then |
| 63 | + if mknod /dev/fuse c 10 229 2>/dev/null && chmod 0666 /dev/fuse 2>/dev/null; then |
| 64 | + _log "mknod /dev/fuse (c 10:229) OK" |
| 65 | + else |
| 66 | + _log "WARN: /dev/fuse mknod failed (fuse-overlayfs may not work)" |
| 67 | + fi |
| 68 | +fi |
| 69 | + |
| 70 | +_log "WSL2 early fixups complete" |
0 commit comments