Fix various issues with systemd user sessions (#13101)

* Save state * Save state * Add test coverage * Remove useless condition * Update localized strings * Update test distro

Blue committed Jun 13, 2025 at 16:31 UTC 02ca6d3d2d5bc137b76637ffa87ab8915831668d
9 files changed +273 -114
localization/strings/en-US/Resources.resw
+4
@@ -787,6 +787,10 @@ For information please visit https://aka.ms/wslinstall</value>
787 <data name="MessageWarningDuringStartup" xml:space="preserve">
788 <value>Errors occurred during WSL startup</value>
789 </data>
790 + <data name="MessageSystemdUserSessionFailed" xml:space="preserve">
791 + <value>Failed to start the systemd user session for '{}'. See journalctl for more details.</value>
792 + <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
793 + </data>
794 <data name="MessageOpenEventViewer" xml:space="preserve">
795 <value>Open EventViewer</value>
796 </data>
packages.config
+1 -1
@@ -23,7 +23,7 @@
23 <package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
24 <package id="Microsoft.WSL.LxUtil.amd64fre" version="10.0.26100.1-240331-1435.ge-release" />
25 <package id="Microsoft.WSL.LxUtil.arm64fre" version="10.0.26100.1-240331-1435.ge-release" />
26 - <package id="Microsoft.WSL.TestDistro" version="2.4.8-116" />
26 + <package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />
27 <package id="Microsoft.WSLg" version="1.0.66" />
28 <package id="Microsoft.Xaml.Behaviors.WinUI.Managed" version="3.0.0" />
29 <package id="StrawberryPerl" version="5.28.0.1" />
src/linux/init/WslDistributionConfig.cpp
+1 -1
@@ -24,7 +24,7 @@ WslDistributionConfig::WslDistributionConfig(const char* configFilePath)
24
25 std::vector<ConfigKey> keys = {
26 ConfigKey(c_ConfigAutoMountOption, AutoMount),
27 - ConfigKey("automount.root", DrvFsPrefix),
27 + ConfigKey(c_ConfigAutoMountRoot, DrvFsPrefix),
28 ConfigKey("automount.options", DrvFsOptions),
29 ConfigKey(c_ConfigMountFsTabOption, MountFsTab),
30 ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs),
src/linux/init/WslDistributionConfig.h
+1
@@ -36,6 +36,7 @@ constexpr auto c_ConfigPlan9EnabledOption = "fileServer.enabled";
36 constexpr auto c_ConfigAppendGpuLibPathOption = "gpu.appendLibPath";
37 constexpr auto c_ConfigGpuEnabledOption = "gpu.enabled";
38 constexpr auto c_ConfigLinkOsLibsOption = "automount.ldconfig";
39 +constexpr auto c_ConfigAutoMountRoot = "automount.root";
40
41 struct WslDistributionConfig
42 {
src/linux/init/config.cpp
+105 -68
@@ -75,6 +75,8 @@ Abstract:
75
76 static void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config);
77
78 +static bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid);
79 +
80 class RemoveMountAndEnvironmentOnScopeExit
81 {
82 public:
@@ -414,81 +416,18 @@ try
416 return;
417 }
418
419 + bool success = false;
420 + auto sendResponse = wil::scope_exit([&]() { ResponseChannel.SendResultMessage<bool>(success); });
421 +
422 if (!Config.BootInit || Config.InitPid.value_or(0) != getpid())
423 {
424 LOG_ERROR("Unexpected LxInitMessageCreateLoginSession message");
420 - return;
421 - }
422 -
423 - static std::mutex LoginSessionsLock;
424 - static std::map<uid_t, int> LoginSessions;
425 -
426 - // Keep track of login sessions that have been created.
427 - LoginSessionsLock.lock();
428 - auto Unlock = wil::scope_exit([&]() { LoginSessionsLock.unlock(); });
429 - if (LoginSessions.contains(CreateSession->Uid))
430 - {
431 - return;
432 - }
433 -
434 - // Symlink the content of the WSLG XDG runtime dir onto the user's runtime path and
435 - // create a login session to initialize PAM for the user.
436 - if (Config.GuiAppsEnabled)
437 - {
438 - auto* RuntimeDir = getenv(XDG_RUNTIME_DIR_ENV);
439 - if (RuntimeDir)
440 - {
441 - // Create a tmpfs mount point for the user directory.
442 - auto userFolder = std::format("/run/user/{}", CreateSession->Uid);
443 - UtilMount("tmpfs", userFolder.c_str(), "tmpfs", (MS_NOSUID | MS_NODEV | MS_NOEXEC), "mode=755");
444 -
445 - // Create the directory structure for wslg's symlinks.
446 - for (const auto* e : {"/", "/dbus-1", "/dbus-1/service", "/pulse"})
447 - {
448 - auto target = userFolder + e;
449 - UtilMkdir(target.c_str(), 0777);
450 - if (chown(target.c_str(), CreateSession->Uid, CreateSession->Gid) < 0)
451 - {
452 - LOG_ERROR("chown({}, {}, {}) failed {}", target, CreateSession->Uid, CreateSession->Gid, errno);
453 - }
454 - }
455 -
456 - // Create the actual symlinks.
457 - for (const auto* e : {"wayland-0", "wayland-0.lock", "pulse/native", "pulse/pid"})
458 - {
459 - auto link = std::format("{}/{}", userFolder, e);
460 - if (unlink(link.c_str()) < 0 && errno != ENOENT)
461 - {
462 - LOG_ERROR("unlink({}) failed {}", link, errno);
463 - }
464 -
465 - auto target = RuntimeDir + std::string("/") + e;
466 - if (symlink(target.c_str(), link.c_str()) < 0)
467 - {
468 - LOG_ERROR("symlink({}, {}) failed {}", target, link, errno);
469 - }
470 - }
471 - }
472 - else
473 - {
474 - LOG_ERROR("getenv({}) failed {}", XDG_RUNTIME_DIR_ENV, errno);
475 - }
476 - }
477 -
478 - int LoginLeader;
479 - const int Result = forkpty(&LoginLeader, nullptr, nullptr, nullptr);
480 - if (Result < 0)
481 - {
482 - LOG_ERROR("forkpty failed {}", errno);
483 - return;
425 }
485 - else if (Result == 0)
426 + else
427 {
487 - Unlock.reset();
488 - _exit(execl("/bin/login", "/bin/login", "-f", CreateSession->Buffer, nullptr));
428 + success = CreateLoginSession(Config, CreateSession->Buffer, CreateSession->Uid);
429 }
430
491 - LoginSessions.emplace(CreateSession->Uid, LoginLeader);
431 break;
432 }
433
@@ -576,6 +515,11 @@ Return Value:
515
516 wsl::linux::WslDistributionConfig Config{CONFIG_FILE};
517
518 + if (getenv(LX_WSL2_SYSTEM_DISTRO_SHARE_ENV) != nullptr)
519 + {
520 + Config.GuiAppsEnabled = true;
521 + }
522 +
523 //
524 // Initialize the static entries.
525 //
@@ -2703,3 +2647,96 @@ try
2647 }
2648 }
2649 CATCH_LOG()
2650 +
2651 +bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid)
2652 +/*++
2653 +
2654 +Routine Description:
2655 +
2656 + Create a systemd login session for the given user.
2657 +
2658 +Arguments:
2659 +
2660 + Config - Supplies the WSL distribution configuration.
2661 +
2662 + Username - Supplies session username.
2663 +
2664 + Uid - Supplies the session UID.
2665 +
2666 +Return Value:
2667 +
2668 + true on success, false on failure.
2669 +
2670 +--*/
2671 +try
2672 +{
2673 + static std::mutex LoginSessionsLock;
2674 + static std::map<uid_t, int> LoginSessions;
2675 +
2676 + // Keep track of login sessions that have been created.
2677 + LoginSessionsLock.lock();
2678 + auto Unlock = wil::scope_exit([&]() { LoginSessionsLock.unlock(); });
2679 + if (LoginSessions.contains(Uid))
2680 + {
2681 + return true;
2682 + }
2683 +
2684 + int LoginLeader;
2685 + const int Result = forkpty(&LoginLeader, nullptr, nullptr, nullptr);
2686 + if (Result < 0)
2687 + {
2688 + LOG_ERROR("forkpty failed {}", errno);
2689 + return false;
2690 + }
2691 + else if (Result == 0)
2692 + {
2693 + Unlock.reset();
2694 + _exit(execl("/bin/login", "/bin/login", "-f", Username, nullptr));
2695 + }
2696 +
2697 + LoginSessions.emplace(Uid, LoginLeader);
2698 +
2699 + //
2700 + // N.B. Init needs to not ignore SIGCHLD so it can wait for the child process.
2701 + //
2702 + signal(SIGCHLD, SIG_DFL);
2703 + auto restoreDisposition = wil::scope_exit([]() { signal(SIGCHLD, SIG_IGN); });
2704 +
2705 + if (Config.BootInitTimeout > 0)
2706 + {
2707 + auto cmd = std::format("/usr/bin/systemctl is-active user@{}.service", Uid);
2708 + try
2709 + {
2710 + return wsl::shared::retry::RetryWithTimeout<bool>(
2711 + [&]() {
2712 + std::string Output;
2713 + auto exitCode = UtilExecCommandLine(cmd.c_str(), &Output, 0, false);
2714 + if (exitCode == 0) // is-active returns 0 if the unit is active.
2715 + {
2716 + return true;
2717 + }
2718 + else if (Output == "failed\n")
2719 + {
2720 + LOG_ERROR("{} returned: {}", cmd, Output);
2721 + return false;
2722 + }
2723 +
2724 + THROW_ERRNO(EAGAIN);
2725 + },
2726 + std::chrono::milliseconds{250},
2727 + std::chrono::milliseconds{Config.BootInitTimeout});
2728 + }
2729 + catch (...)
2730 + {
2731 + LOG_ERROR("Timed out waiting for user session for uid={}", Uid);
2732 + return false;
2733 + }
2734 + }
2735 +
2736 + return true;
2737 +}
2738 +catch (...)
2739 +{
2740 + LOG_CAUGHT_EXCEPTION();
2741 + return false;
2742 +}
\ No newline at end of file
src/linux/init/init.cpp
+78 -1
@@ -138,6 +138,8 @@ void InstallSystemdUnit(const char* Path, const std::string& Name, const char* C
138
139 int GenerateSystemdUnits(int Argc, char** Argv);
140
141 +int GenerateUserSystemdUnits(int Argc, char** Argv);
142 +
143 void HardenMirroredNetworkingSettingsAgainstSystemd();
144
145 void PostProcessImportedDistribution(wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT>& Message, const char* ExtractedPath);
@@ -214,6 +216,10 @@ int WslEntryPoint(int Argc, char* Argv[])
216 {
217 ExitCode = GenerateSystemdUnits(Argc, Argv);
218 }
219 + else if (strcmp(BaseName, LX_INIT_WSL_USER_GENERATOR) == 0)
220 + {
221 + ExitCode = GenerateUserSystemdUnits(Argc, Argv);
222 + }
223 else
224 {
225 // Handle the special case for import result messages, everything else is sent to the binfmt interpreter.
@@ -236,6 +242,61 @@ int WslEntryPoint(int Argc, char* Argv[])
242 return ExitCode;
243 }
244
245 +int GenerateUserSystemdUnits(int Argc, char** Argv)
246 +{
247 + if (Argc < 2)
248 + {
249 + LOG_ERROR("Unit folder missing");
250 + return 1;
251 + }
252 +
253 + const auto* installPath = Argv[1];
254 +
255 + try
256 + {
257 + std::string automountRoot = "/mnt";
258 + wil::unique_file File{fopen("/etc/wsl.conf", "r")};
259 + if (File)
260 + {
261 + std::vector<ConfigKey> ConfigKeys = {
262 + ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),
263 +
264 + };
265 + ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));
266 + File.reset();
267 + }
268 +
269 + // TODO: handle quotes in path
270 +
271 + auto unitContent = std::format(
272 + R"(# Note: This file is generated by WSL to configure wslg.
273 +
274 +[Unit]
275 +Description=WSLg user service
276 +DefaultDependencies=no
277 +
278 +[Service]
279 +Type=oneshot
280 +Environment=WSLG_RUNTIME_DIR={}/{}/{}
281 +ExecStart=/bin/sh -c 'mkdir -p -m 00755 "$XDG_RUNTIME_DIR/pulse"'
282 +ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/wayland-0" "$XDG_RUNTIME_DIR/wayland-0"'
283 +ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/wayland-0.lock" "$XDG_RUNTIME_DIR/wayland-0.lock"'
284 +ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/pulse/native" "$XDG_RUNTIME_DIR/pulse/native"'
285 +ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/pulse/pid" "$XDG_RUNTIME_DIR/pulse/pid"'
286 + )",
287 + automountRoot,
288 + WSLG_SHARED_FOLDER,
289 + WAYLAND_RUNTIME_DIR);
290 +
291 + InstallSystemdUnit(installPath, "wslg-session", unitContent.c_str());
292 +
293 + return 0;
294 + }
295 + CATCH_LOG()
296 +
297 + return 1;
298 +}
299 +
300 int GenerateSystemdUnits(int Argc, char** Argv)
301 {
302 if (Argc < 2)
@@ -253,6 +314,8 @@ int GenerateSystemdUnits(int Argc, char** Argv)
314 bool enableGuiApps = true;
315 bool protectBinfmt = true;
316 bool interopEnabled = true;
317 + std::string automountRoot = "/mnt";
318 +
319 wil::unique_file File{fopen("/etc/wsl.conf", "r")};
320 if (File)
321 {
@@ -260,6 +323,7 @@ int GenerateSystemdUnits(int Argc, char** Argv)
323 ConfigKey(wsl::linux::c_ConfigEnableGuiAppsOption, enableGuiApps),
324 ConfigKey(wsl::linux::c_ConfigBootProtectBinfmtOption, protectBinfmt),
325 ConfigKey(wsl::linux::c_ConfigInteropEnabledOption, interopEnabled),
326 + ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),
327
328 };
329 ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));
@@ -616,7 +680,12 @@ try
680 CreateSession->Gid = PasswordEntry->pw_gid;
681 CreateSession.WriteString(PasswordEntry->pw_name);
682
619 - InteropChannel.SendMessage<LX_INIT_CREATE_LOGIN_SESSION>(CreateSession.Span());
683 + auto result = InteropChannel.Transaction<LX_INIT_CREATE_LOGIN_SESSION>(CreateSession.Span());
684 +
685 + if (!result.Result)
686 + {
687 + fprintf(stderr, "wsl: %s\n", wsl::shared::Localization::MessageSystemdUserSessionFailed(PasswordEntry->pw_name).c_str());
688 + }
689
690 Common->Environment.AddVariable("DBUS_SESSION_BUS_ADDRESS", std::format("unix:path=/run/user/{}/bus", PasswordEntry->pw_uid));
691 Common->Environment.AddVariable(XDG_RUNTIME_DIR_ENV, std::format("/run/user/{}/", PasswordEntry->pw_uid));
@@ -2791,6 +2860,14 @@ try
2860
2861 THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);
2862 THROW_LAST_ERROR_IF(symlink("/init", std::format("{}/{}", folder, LX_INIT_WSL_GENERATOR).c_str()));
2863 +
2864 + if (Config.GuiAppsEnabled)
2865 + {
2866 + constexpr auto folder = "/run/systemd/user-generators";
2867 +
2868 + THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);
2869 + THROW_LAST_ERROR_IF(symlink("/init", std::format("{}/{}", folder, LX_INIT_WSL_USER_GENERATOR).c_str()));
2870 + }
2871 }
2872 CATCH_LOG();
2873
src/shared/inc/lxinitshared.h
+3
@@ -239,6 +239,8 @@ Abstract:
239
240 #define LX_INIT_WSL_GENERATOR "wsl-generator"
241
242 +#define LX_INIT_WSL_USER_GENERATOR "wsl-user-generator"
243 +
244 //
245 // WSL2-specific environment variables.
246 //
@@ -681,6 +683,7 @@ typedef struct _LX_INIT_NETWORK_INFORMATION
683 typedef struct _LX_INIT_CREATE_LOGIN_SESSION
684 {
685 static inline auto Type = LxInitMessageCreateLoginSession;
686 + using TResponse = RESULT_MESSAGE<bool>;
687
688 MESSAGE_HEADER Header;
689 unsigned int Uid;
test/windows/UnitTests.cpp
+70 -33
@@ -202,8 +202,6 @@ class UnitTests
202 {
203 WSL2_TEST_ONLY();
204
205 - SKIP_TEST_UNSTABLE(); // TODO: Re-enable when this issue is solved in main.
206 -
205 auto cleanup = wil::scope_exit([] {
206 // clean up wsl.conf file
207 const std::wstring disableSystemdCmd(LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE);
@@ -219,8 +217,6 @@ class UnitTests
217 {
218 WSL2_TEST_ONLY();
219
222 - SKIP_TEST_UNSTABLE(); // TODO: Re-enable when this issue is solved in main.
223 -
220 // enable systemd before creating the user.
221 // if not called first, the runtime directories needed for --user will not have been created
222 auto cleanup = EnableSystemd();
@@ -231,45 +227,79 @@ class UnitTests
227 CreateUser(LXSST_TEST_USERNAME, &TestUid, &TestGid);
228 auto userCleanup = wil::scope_exit([]() { LxsstuLaunchWsl(L"userdel " LXSST_TEST_USERNAME); });
229
234 - // verify that the user service is running
235 - const std::wstring isServiceActiveCmd = std::format(L"-u {} systemctl is-active user@{}.service", LXSST_TEST_USERNAME, TestUid);
236 - std::wstring out;
237 - std::wstring err;
230 + auto validateUserSesssion = [&]() {
231 + // verify that the user service is running
232 + const std::wstring isServiceActiveCmd =
233 + std::format(L"-u {} systemctl is-active user@{}.service ; exit 0", LXSST_TEST_USERNAME, TestUid);
234 + std::wstring out;
235 + std::wstring err;
236
239 - try
237 + try
238 + {
239 + std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(isServiceActiveCmd.data());
240 + }
241 + CATCH_LOG();
242 +
243 + Trim(out);
244 +
245 + if (out.compare(L"active") != 0)
246 + {
247 + LogError(
248 + "Unexpected output from systemd: %ls. Stderr: %ls, cmd: %ls", out.c_str(), err.c_str(), isServiceActiveCmd.c_str());
249 + VERIFY_FAIL();
250 + }
251 +
252 + // Verify that /run/user/<uid> is a writable tmpfs mount visible in both mount namespaces.
253 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"touch /run/user/" + std::to_wstring(TestUid) + L"/dummy-test-file"), 0u);
254 + auto command = L"mount | grep -iF 'tmpfs on /run/user/" + std::to_wstring(TestUid) + L" type tmpfs (rw'";
255 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command), 0u);
256 +
257 + const auto nonElevatedToken = GetNonElevatedToken();
258 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command, nullptr, nullptr, nullptr, nonElevatedToken.get()), 0u);
259 + };
260 +
261 + // Validate user sessions state with gui apps disabled.
262 {
241 - std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(isServiceActiveCmd.data());
242 - }
243 - CATCH_LOG();
263 + validateUserSesssion();
264
245 - Trim(out);
265 + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"echo $DISPLAY", LXSST_TEST_USERNAME));
266 + VERIFY_ARE_EQUAL(out, L"\n");
267 + }
268
247 - if (out.compare(L"active") != 0)
269 + // Validate user sessions state with gui apps enabled.
270 {
249 - LogError("Unexpected output from systemd: %ls. Stderr: %ls, cmd: %ls", out.c_str(), err.c_str(), isServiceActiveCmd.c_str());
250 - VERIFY_FAIL();
271 + WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true}));
272 +
273 + validateUserSesssion();
274 + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"echo $DISPLAY", LXSST_TEST_USERNAME));
275 + VERIFY_ARE_EQUAL(out, L":0\n");
276 }
277
253 - // Verify that /run/user/<uid> is a writable tmpfs mount visible in both mount namespaces.
254 - VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"touch /run/user/" + std::to_wstring(TestUid) + L"/dummy-test-file"), 0u);
255 - auto command = L"mount | grep -iF 'tmpfs on /run/user/" + std::to_wstring(TestUid) + L" type tmpfs (rw'";
256 - VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command), 0u);
278 + // Create a 'broken' /run/user and validate that the warning is correctly displayed.
279 + {
280 + TerminateDistribution();
281
258 - const auto nonElevatedToken = GetNonElevatedToken();
259 - VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command, nullptr, nullptr, nullptr, nonElevatedToken.get()), 0u);
282 + VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"chmod 000 /run/user"), 0L);
283 +
284 + auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-u {} echo OK", LXSST_TEST_USERNAME));
285 +
286 + VERIFY_ARE_EQUAL(out, L"OK\n");
287 + VERIFY_ARE_EQUAL(
288 + err, L"wsl: Failed to start the systemd user session for 'kerneltest'. See journalctl for more details.\n");
289 + }
290 }
291
292 static bool IsSystemdRunning(const std::wstring& SystemdScope, int ExpectedExitCode = 0)
293 {
294 // run and check the output of systemctl --system
265 - const std::wstring systemctlCmd(L"systemctl " + SystemdScope + L" is-system-running");
295 + const auto systemctlCmd = std::format(L"systemctl '{}' is-system-running ; exit 0", SystemdScope);
296 std::wstring out;
297 std::wstring error;
298
299 // capture the output of systemctl and trim for good measure
300 try
301 {
272 - std::tie(out, error) = LxsstuLaunchWslAndCaptureOutput(systemctlCmd.data(), ExpectedExitCode);
302 + std::tie(out, error) = LxsstuLaunchWslAndCaptureOutput(systemctlCmd.c_str(), ExpectedExitCode);
303 }
304 CATCH_LOG()
305 Trim(out);
@@ -809,7 +839,8 @@ class UnitTests
839 VERIFY_ARE_EQUAL(out, L"");
840 VERIFY_ARE_EQUAL(
841 err,
812 - L"Invalid command line argument: --invalid\nPlease use 'wslinfo --help' to get a list of supported arguments.\n");
842 + L"Invalid command line argument: --invalid\nPlease use 'wslinfo --help' to get a list of supported "
843 + L"arguments.\n");
844 }
845 }
846
@@ -1217,7 +1248,8 @@ class UnitTests
1248
1249 ValidateErrorMessage(
1250 L"-d DummyBrokenDistro",
1220 - L"Failed to attach disk 'C:\\DoesNotExit\\ext4.vhdx' to WSL2: The system cannot find the path specified. ",
1251 + L"Failed to attach disk 'C:\\DoesNotExit\\ext4.vhdx' to WSL2: The system cannot find the path "
1252 + L"specified. ",
1253 L"Wsl/Service/CreateInstance/MountDisk/HCS/ERROR_PATH_NOT_FOUND");
1254
1255 // Purposefully set an incorrect value type to validate registry error handling.
@@ -1230,7 +1262,8 @@ class UnitTests
1262 ValidateErrorMessage(
1263 L"-d DummyBrokenDistro",
1264 L"An error occurred accessing the registry. Path: '\\REGISTRY\\USER\\" + Sid +
1233 - L"\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\{baa405ef-1822-4bbe-84e2-30e4c6330d42}\\Version'."
1265 + L"\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\{baa405ef-1822-4bbe-84e2-30e4c6330d42}"
1266 + L"\\Version'."
1267 L" "
1268 L"Error: Data of this type is not supported. ",
1269 L"Wsl/Service/ReadDistroConfig/ERROR_UNSUPPORTED_TYPE",
@@ -2234,7 +2267,8 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND
2267 // Keys that are created by the optional component and the service.
2268 const std::vector<LPCWSTR> inboxKeys{
2269 L"SOFTWARE\\Classes\\CLSID\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}",
2237 - L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}",
2270 + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{B2B4A4D1-2754-4140-A2EB-"
2271 + L"9A76D9D7CDC6}",
2272 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSL",
2273 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSLLegacy",
2274 L"SOFTWARE\\Classes\\Directory\\shell\\WSL",
@@ -2782,7 +2816,8 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND
2816
2817 VERIFY_ARE_EQUAL(
2818 out,
2785 - L"The supplied install location is already in use.\r\nError code: Wsl/Service/MoveDistro/ERROR_FILE_EXISTS\r\n");
2819 + L"The supplied install location is already in use.\r\nError code: "
2820 + L"Wsl/Service/MoveDistro/ERROR_FILE_EXISTS\r\n");
2821 // Validate that the distribution still starts and that the vhd hasn't moved.
2822 validateDistro();
2823 VERIFY_IS_TRUE(std::filesystem::exists(std::format(L"{}\\ext4.vhdx", absolutePath)));
@@ -2837,7 +2872,8 @@ Error code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND
2872 WslKeepAlive keepAlive;
2873 auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L"--manage test_distro --resize 1500GB", -1);
2874 VERIFY_ARE_EQUAL(
2840 - L"The operation could not be completed because the vhdx is currently in use. To force WSL to stop use: wsl.exe "
2875 + L"The operation could not be completed because the vhdx is currently in use. To force WSL to stop use: "
2876 + L"wsl.exe "
2877 L"--shutdown\r\nError code: Wsl/Service/WSL_E_DISTRO_NOT_STOPPED\r\n",
2878 out);
2879 }
@@ -3400,8 +3436,8 @@ localhostForwarding=true
3436 }
3437
3438 {
3403 - // This test verifies removal of a setting from the .wslconfig when a default value for the particular setting is set.
3404 - // This gives wsl control over the default value.
3439 + // This test verifies removal of a setting from the .wslconfig when a default value for the particular setting is
3440 + // set. This gives wsl control over the default value.
3441 std::wstring customWslConfigContentOut{
3442 LR"(
3443 [wsl2]
@@ -4517,7 +4553,8 @@ Error code: Wsl/Service/RegisterDistro/E_INVALIDARG\r\n";
4553 std::format(L"--name {}", distroName).c_str(),
4554 0,
4555 nullptr,
4520 - L"wsl: Failed to parse terminal profile while registering distribution: [json.exception.parse_error.101] parse "
4556 + L"wsl: Failed to parse terminal profile while registering distribution: [json.exception.parse_error.101] "
4557 + L"parse "
4558 L"error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'b'\r\n");
4559
4560 ValidateDistributionStarts(distroName);
tools/test/build-test-distro.ps1
+10 -10
@@ -1,14 +1,14 @@
1 <#
2 .SYNOPSIS
3 Takes in an exported distribution, installs dependencies for testing, cleans unnecessary components, and exports it for later use.
4 -.PARAMETER InputTarPath
5 - Path to the .tar/.tar.gz to build the test distro from.
4 +.PARAMETER Base
5 + Base distribution to use.
6 .PARAMETER OutputTarPath
7 Path to write the test distro .tar to.
8 #>
9
10 [CmdletBinding()]
11 -Param ($InputTarPath)
11 +Param ($Base)
12
13 $ErrorActionPreference = "Stop"
14 Set-StrictMode -Version Latest
@@ -37,20 +37,20 @@ $version = "$($git_version[0])-$($git_version[1])"
37
38 echo "Building test_distro version: $version"
39
40 -Run { wsl.exe --import test_distro . $InputTarPath --version 2 }
40 +Run { wsl.exe --install $Base --name test_distro --version 2 --no-launch }
41
42 RunInDistro("apt update")
43 -RunInDistro("apt install daemonize libmount-dev genisoimage dosfstools make gcc socat systemd libpam-systemd dnsutils xz-utils bzip2 -f -y --no-install-recommends")
44 -RunInDistro("apt purge cpio isc-dhcp-client isc-dhcp-common nftables rsyslog vim whiptail xxd init genisoimage tasksel -y -f --allow-remove-essential")
43 +RunInDistro("apt install daemonize libmount-dev genisoimage dosfstools make gcc socat systemd libpam-systemd bind9-dnsutils xz-utils bzip2 -f -y --no-install-recommends")
44 +RunInDistro("apt purge cpio isc-dhcp-client isc-dhcp-common nftables rsyslog vim vim-tiny vim-common whiptail xxd init genisoimage tasksel kmod mawk udev cron -y -f --allow-remove-essential")
45 RunInDistro("apt-get autopurge -y")
46 RunInDistro("apt clean")
47 RunInDistro("umount /usr/lib/wsl/drivers")
48 RunInDistro("umount /usr/lib/wsl/lib")
49 -RunInDistro("rm -rf /etc/wsl-distribution.conf /etc/wsl.conf /usr/share/doc/* /var/lib/apt/lists/* /var/log/* /var/cache/debconf/* /var/cache/ldconfig/* /usr/lib/wsl")
49 +RunInDistro("rm -rf /etc/wsl-distribution.conf /etc/wsl.conf /usr/share/doc/* /var/lib/apt/lists/* /var/log/* /var/cache/debconf/* /var/cache/ldconfig/* /usr/lib/wsl /usr/share/{gdb,vim,zsh,man}")
50 +RunInDistro('rm -rf -- $(ls /usr/share/locale ^| grep -vE "en|locale.alias")')
51 RunInDistro("rm /usr/lib/systemd/user/{systemd-tmpfiles-setup.service,systemd-tmpfiles-clean.timer,systemd-tmpfiles-clean.service}")
51 -RunInDistro("rm /usr/lib/systemd/system/{systemd-tmpfiles-setup-dev.service,systemd-tmpfiles-setup.service,systemd-tmpfiles-clean.timer,systemd-tmpfiles-clean.service}")
52 -RunInDistro("rm /usr/lib/systemd/system/user-runtime-dir@.service")
52 +RunInDistro("rm /usr/lib/systemd/system/{systemd-tmpfiles-setup-dev.service,systemd-tmpfiles-setup.service,systemd-tmpfiles-clean.timer,systemd-tmpfiles-clean.service} /lib/systemd/system/{kmod-static-nodes.service,kmod.service,sysinit.target.wants/kmod-static-nodes.service}")
53 Run { wsl.exe --export test_distro "test_distro.tar" }
54 -Run { wsl.exe xz -9 "test_distro.tar" }
54 +Run { wsl.exe xz -e9 "test_distro.tar" }
55
56 & "$PSScriptRoot/../../_deps/nuget.exe" pack Microsoft.WSL.TestDistro.nuspec -Properties version=$version
\ No newline at end of file