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)