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