Switch `wslinfo --vm-id` to not rely on presence of VM ID environment variable (#13318)
* Switch WSLg to use wslinfo --vm-id instead of relying on environment variable * DO NOT MERGE: bad WSLg nuget * dead code removal * always send response to LxInitMessageQueryVmId message * add back invalid WslInfoMode error * remove unneeded wsl2 check * use temporary workaround until WSLg update is ready * unit test update * Update string compare Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Ben Hillis committed
Aug 5, 2025 at 14:36 UTC
642331364dda7a3d88bf64acb87e7056918a5fc9
9 files changed
+113
-33
src/linux/init/WslDistributionConfig.h
+1
@@ -79,6 +79,7 @@ struct WslDistributionConfig
79
bool GuiAppsEnabled = false;
80
std::optional<int> FeatureFlags = 0;
81
std::optional<LX_MINI_INIT_NETWORKING_MODE> NetworkingMode = LxMiniInitNetworkingModeNone;
82
+ std::optional<std::string> VmId;
83
84
//
85
// Global state for boot state. The socket is used to delay-start the distro init process
src/linux/init/config.cpp
+12
-11
@@ -436,6 +436,18 @@ try
436
ResponseChannel.SendResultMessage<uint8_t>(static_cast<uint8_t>(Config.NetworkingMode.value()));
437
break;
438
439
+ case LxInitMessageQueryVmId:
440
+ {
441
+ wsl::shared::MessageWriter<LX_INIT_QUERY_VM_ID> Response(LxInitMessageQueryVmId);
442
+ if (Config.VmId.has_value())
443
+ {
444
+ Response.WriteString(Config.VmId.value());
445
+ }
446
+
447
+ ResponseChannel.SendMessage<LX_INIT_QUERY_VM_ID>(Response.Span());
448
+ break;
449
+ }
450
+
451
default:
452
LOG_ERROR("unexpected message {}", Header->MessageType);
453
break;
@@ -1662,17 +1674,6 @@ Return Value:
1674
ConfigAppendNtPath(Environment, Buffer);
1675
}
1676
1665
- //
1666
- // If the VM ID environment variable is present, add it to the environment
1667
- // block.
1668
- //
1669
-
1670
- auto VmId = getenv(LX_WSL2_VM_ID_ENV);
1671
- if (VmId)
1672
- {
1673
- Environment.AddVariable(LX_WSL2_VM_ID_ENV, VmId);
1674
- }
1675
-
1677
return Environment;
1678
}
1679
src/linux/init/init.cpp
+15
-1
@@ -2239,13 +2239,27 @@ Return Value:
2239
unsetenv(LX_WSL2_DISTRO_READ_ONLY_ENV);
2240
}
2241
2242
- const auto Value = getenv(LX_WSL2_NETWORKING_MODE_ENV);
2242
+ auto Value = getenv(LX_WSL2_NETWORKING_MODE_ENV);
2243
if (Value != nullptr)
2244
{
2245
Config.NetworkingMode = static_cast<LX_MINI_INIT_NETWORKING_MODE>(std::atoi(Value));
2246
unsetenv(LX_WSL2_NETWORKING_MODE_ENV);
2247
}
2248
2249
+ Value = getenv(LX_WSL2_VM_ID_ENV);
2250
+ if (Value != nullptr)
2251
+ {
2252
+ Config.VmId = Value;
2253
+
2254
+ // Unset the environment variable for user distros.
2255
+ // TODO: this can be removed when WSLg is updated to use `wslinfo --vm-id` instead of the environment variable.
2256
+ Value = getenv(LX_WSL2_SYSTEM_DISTRO);
2257
+ if (!Value || strcmp(Value, "1") != 0)
2258
+ {
2259
+ unsetenv(LX_WSL2_VM_ID_ENV);
2260
+ }
2261
+ }
2262
+
2263
//
2264
// If the boot.systemd option is specified in /etc/wsl.conf, launch the distro init process as pid 1.
2265
// WSL init and session leaders continue as children of the distro init process.
src/linux/init/main.cpp
+5
@@ -1827,6 +1827,11 @@ try
1827
AddEnvironmentVariable(LX_WSL2_USER_PROFILE, UserProfile);
1828
AddEnvironmentVariable(LX_WSL2_NETWORKING_MODE_ENV, std::to_string(static_cast<int>(Config.NetworkingMode)).c_str());
1829
1830
+ if (UserProfile)
1831
+ {
1832
+ AddEnvironmentVariable(LX_WSL2_SYSTEM_DISTRO, "1");
1833
+ }
1834
+
1835
if (DistroInitPid.has_value())
1836
{
1837
AddEnvironmentVariable(LX_WSL2_DISTRO_INIT_PID, std::to_string(static_cast<int>(DistroInitPid.value())).c_str());
src/linux/init/util.cpp
+36
-7
@@ -1201,8 +1201,7 @@ std::optional<LX_MINI_INIT_NETWORKING_MODE> UtilGetNetworkingMode(void)
1201
1202
Routine Description:
1203
1204
- This routine gets the feature flags, either directly, from an environment
1205
- variable, or by querying it from the init process.
1204
+ This routine queries the networking mode from the init process.
1205
1206
Arguments:
1207
@@ -1210,16 +1209,12 @@ Arguments:
1209
1210
Return Value:
1211
1213
- The feature flags.
1212
+ The networking mode if successful, std::nullopt otherwise.
1213
1214
--*/
1215
1216
try
1217
{
1219
- //
1220
- // Query init for the value.
1221
- //
1222
-
1218
wsl::shared::SocketChannel channel{UtilConnectUnix(WSL_INIT_INTEROP_SOCKET), "wslinfo"};
1219
THROW_LAST_ERROR_IF(channel.Socket() < 0);
1220
@@ -1297,6 +1292,40 @@ Return Value:
1292
return Result;
1293
}
1294
1295
+std::string UtilGetVmId(void)
1296
+
1297
+/*++
1298
+
1299
+Routine Description:
1300
+
1301
+ This routine queries the VM ID from the init process.
1302
+
1303
+Arguments:
1304
+
1305
+ None.
1306
+
1307
+Return Value:
1308
+
1309
+ The VM ID if successful, an empty string otherwise.
1310
+
1311
+--*/
1312
+
1313
+try
1314
+{
1315
+ wsl::shared::SocketChannel channel{UtilConnectUnix(WSL_INIT_INTEROP_SOCKET), "wslinfo"};
1316
+ THROW_LAST_ERROR_IF(channel.Socket() < 0);
1317
+
1318
+ wsl::shared::MessageWriter<LX_INIT_QUERY_VM_ID> Message(LxInitMessageQueryVmId);
1319
+ channel.SendMessage<LX_INIT_QUERY_VM_ID>(Message.Span());
1320
+
1321
+ return channel.ReceiveMessage<LX_INIT_QUERY_VM_ID>().Buffer;
1322
+}
1323
+catch (...)
1324
+{
1325
+ LOG_CAUGHT_EXCEPTION();
1326
+ return {};
1327
+}
1328
+
1329
void UtilInitGroups(const char* User, gid_t Gid)
1330
1331
/*++
src/linux/init/util.h
+2
@@ -227,6 +227,8 @@ std::optional<LX_MINI_INIT_NETWORKING_MODE> UtilGetNetworkingMode(void);
227
228
pid_t UtilGetPpid(pid_t Pid);
229
230
+std::string UtilGetVmId(void);
231
+
232
void UtilInitGroups(const char* User, gid_t Gid);
233
234
void UtilInitializeMessageBuffer(std::vector<gsl::byte>& Buffer);
src/linux/init/wslinfo.cpp
+13
-6
@@ -149,14 +149,21 @@ Return Value:
149
}
150
else if (Mode.value() == WslInfoMode::VMId)
151
{
152
- auto value = UtilGetEnvironmentVariable(LX_WSL2_VM_ID_ENV);
153
- if (value.empty())
152
+ if (UtilIsUtilityVm())
153
{
155
- std::cerr << Localization::MessageNoValueFound() << "\n";
156
- return 1;
157
- }
154
+ auto vmId = UtilGetVmId();
155
+ if (vmId.empty())
156
+ {
157
+ std::cerr << Localization::MessageNoValueFound() << "\n";
158
+ return 1;
159
+ }
160
159
- std::cout << value;
161
+ std::cout << vmId;
162
+ }
163
+ else
164
+ {
165
+ std::cout << "wsl1";
166
+ }
167
}
168
else
169
{
src/shared/inc/lxinitshared.h
+12
@@ -255,6 +255,7 @@ Abstract:
255
#define LX_WSL2_SHARED_MEMORY_OB_DIRECTORY "WSL2_SHARED_MEMORY_OB_DIRECTORY"
256
#define LX_WSL2_INSTALL_PATH "WSL2_INSTALL_PATH"
257
#define LX_WSL2_SAFE_MODE "WSL2_SAFE_MODE"
258
+#define LX_WSL2_SYSTEM_DISTRO "WSL2_SYSTEM_DISTRO"
259
#define LX_WSL2_USER_PROFILE "WSL2_USER_PROFILE"
260
#define LX_WSL2_VM_ID_ENV "WSL2_VM_ID"
261
#define LX_WSL_PID_ENV "WSL2_PID"
@@ -304,6 +305,7 @@ typedef enum _LX_MESSAGE_TYPE
305
LxInitMessageCreateLoginSession,
306
LxInitMessageStopPlan9Server,
307
LxInitMessageQueryNetworkingMode,
308
+ LxInitMessageQueryVmId,
309
LxInitCreateProcess,
310
LxInitOobeResult,
311
LxMiniInitMessageLaunchInit,
@@ -1496,6 +1498,16 @@ typedef struct _LX_INIT_OOBE_RESULT
1498
PRETTY_PRINT(FIELD(Header), FIELD(Result), FIELD(DefaultUid));
1499
} LX_INIT_OOBE_RESULT, *PLX_INIT_OOBE_RESULT;
1500
1501
+typedef struct _LX_INIT_QUERY_VM_ID
1502
+{
1503
+ static inline auto Type = LxInitMessageQueryVmId;
1504
+
1505
+ MESSAGE_HEADER Header;
1506
+ char Buffer[];
1507
+
1508
+ PRETTY_PRINT(FIELD(Header), FIELD(Buffer));
1509
+} LX_INIT_QUERY_VM_ID, *PLX_INIT_QUERY_VM_ID;
1510
+
1511
template <>
1512
struct std::formatter<LX_MESSAGE_TYPE, char>
1513
{
test/windows/UnitTests.cpp
+17
-8
@@ -843,16 +843,25 @@ class UnitTests
843
L"arguments.\n");
844
}
845
846
- if (LxsstuVmMode())
846
{
848
- // Get the VM ID from the distro and validate that it not null.
849
- auto [vmId, vmIdErr] = LxsstuLaunchWslAndCaptureOutput(L"env | grep 'WSL2_VM_ID' | awk -F= '{print $2}'");
850
- VERIFY_ARE_NOT_EQUAL(vmId, L"");
851
-
852
- // Ensure that the response from wslinfo matches the VM id from the distros environment
853
- auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L"wslinfo --vm-id");
854
- VERIFY_ARE_EQUAL(out, std::format(L"{}", vmId));
847
+ auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L"wslinfo --vm-id -n");
848
VERIFY_ARE_EQUAL(err, L"");
849
+ if (LxsstuVmMode())
850
+ {
851
+ // Ensure that the response from wslinfo has the VM ID.
852
+ auto guid = wsl::shared::string::ToGuid(out);
853
+ VERIFY_IS_TRUE(guid.has_value());
854
+ VERIFY_IS_FALSE(IsEqualGUID(guid.value(), GUID_NULL));
855
+
856
+ // Validate that the VM ID is not propagated to user commands.
857
+ std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(L"echo -n \"$WSL2_VM_ID\"");
858
+ VERIFY_ARE_EQUAL(out, L"");
859
+ VERIFY_ARE_EQUAL(err, L"");
860
+ }
861
+ else
862
+ {
863
+ VERIFY_ARE_EQUAL(out, L"wsl1");
864
+ }
865
}
866
}
867