@samitouri / QOSAMI-WSL / commits / 1a2ede75

Mask console-getty.service to prevent multi-distro failures (#13595) (#14490)

* Mask console-getty.service to prevent multi-distro failures (#13595) When multiple WSL distros run concurrently, /dev/tty devices are shared at the VM level. The second distro's console-getty.service fails because the tty is already held by the first, causing systemd to report failed units and triggering user@UID.service failures. Mask console-getty.service during WSL systemd unit generation, similar to the existing masking of networkd-wait-online. This service provides no value in WSL since users don't connect to the underlying tty. Fixes #13595 * format source * pr feedback --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>

Ben Hillis committed Apr 9, 2026 at 13:32 UTC 1a2ede7501a517d24fb31499381c088320b31286
3 files changed +14 -7
distributions/validate-modern.py
+1
@@ -34,6 +34,7 @@ DISCOURAGED_SYSTEM_UNITS = ['systemd-resolved.service',
34 'tmp.mount',
35 'NetworkManager.service',
36 'NetworkManager-wait-online.service',
37 + 'console-getty.service',
38 'networking.service',
39 'hypervkvpd.service']
40
src/linux/init/init.cpp
+4
@@ -336,6 +336,10 @@ int GenerateSystemdUnits(int Argc, char** Argv)
336 // Mask NetworkManager-wait-online.service for the same reason, as it causes timeouts on distros using NetworkManager.
337 THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/NetworkManager-wait-online.service", installPath).c_str()) < 0);
338
339 + // Mask console-getty.service since /dev/tty devices are shared at the VM level across all distros.
340 + // When multiple distros are running, the second distro's getty fails because the tty is already held.
341 + THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/console-getty.service", installPath).c_str()) < 0);
342 +
343 // Only create the wslg unit if both enabled in wsl.conf, and if the wslg folder actually exists.
344 if (enableGuiApps && access("/mnt/wslg/runtime-dir", F_OK) == 0)
345 {
test/windows/UnitTests.cpp
+9 -7
@@ -214,16 +214,18 @@ class UnitTests
214 VERIFY_IS_TRUE(IsSystemdRunning(L"--system"));
215
216 // Validate that systemd-networkd-wait-online.service is masked.
217 - auto [out, _] =
218 - LxsstuLaunchWslAndCaptureOutput(L"systemctl status systemd-networkd-wait-online.service | grep -iF Loaded:");
219 -
220 - VERIFY_ARE_EQUAL(out, L" Loaded: masked (Reason: Unit systemd-networkd-wait-online.service is masked.)\n");
217 + std::wstring out;
218 + std::wstring err;
219 + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(L"systemctl show -p LoadState systemd-networkd-wait-online.service");
220 + VERIFY_ARE_EQUAL(out, L"LoadState=masked\n");
221
222 // Validate that NetworkManager-wait-online.service is masked.
223 - auto [outNm, __] =
224 - LxsstuLaunchWslAndCaptureOutput(L"systemctl status NetworkManager-wait-online.service | grep -iF Loaded:");
223 + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(L"systemctl show -p LoadState NetworkManager-wait-online.service");
224 + VERIFY_ARE_EQUAL(out, L"LoadState=masked\n");
225
226 - VERIFY_ARE_EQUAL(outNm, L" Loaded: masked (Reason: Unit NetworkManager-wait-online.service is masked.)\n");
226 + // Validate that console-getty.service is masked (tty devices are shared at VM level across distros).
227 + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(L"systemctl show -p LoadState console-getty.service");
228 + VERIFY_ARE_EQUAL(out, L"LoadState=masked\n");
229 }
230
231 TEST_METHOD(SystemdUser)