master
cpp 3,629 lines 103 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 init.c
8
9 Abstract:
10
11 This file contains the lx init implementation.
12
13 --*/
14
15 #include <cassert>
16 #include <sys/eventfd.h>
17 #include <sys/mount.h>
18 #include <sys/prctl.h>
19 #include <sys/signalfd.h>
20 #include <sys/wait.h>
21 #include <sys/epoll.h>
22 #include <sys/syscall.h>
23 #include <linux/filter.h>
24 #include <pty.h>
25 #include <utmp.h>
26 #include <libgen.h>
27 #include <grp.h>
28 #include <sysexits.h>
29 #include <iostream>
30 #include <cstddef>
31 #include <lxbusapi.h>
32 #include "p9tracelogging.h"
33 #include "common.h"
34 #include "config.h"
35 #include "util.h"
36 #include "timezone.h"
37 #include "binfmt.h"
38 #include "wslpath.h"
39 #include "wslinfo.h"
40 #include "drvfs.h"
41 #include "plan9.h"
42 #include "localhost.h"
43 #include "telemetry.h"
44 #include "GnsEngine.h"
45 #include "lxinitshared.h"
46 #include "message.h"
47 #include "configfile.h"
48 #include "CommandLine.h"
49
50 static_assert(EX_NOUSER == LX_INIT_USER_NOT_FOUND);
51 static_assert(EUSERS == LX_INIT_TTY_LIMIT);
52
53 #define DEFAULT_SHELL "/bin/sh"
54 #define DEFAULT_SHELL_ARGS 4
55 #define HOME_ENV "HOME"
56 #define LOGNAME_ENV "LOGNAME"
57 #define SHELL_ENV "SHELL"
58 #define SHELL_PATH "/bin/sh"
59 #define USER_ENV "USER"
60
61 using namespace wsl::shared;
62
63 typedef struct _CREATE_PROCESS_PARSED_COMMON
64 {
65 const char* Filename;
66 std::string CurrentWorkingDirectory;
67 std::vector<const char*> CommandLine;
68 EnvironmentBlock Environment;
69 uid_t Uid;
70 CREATE_PROCESS_SHELL_OPTIONS ShellOptions;
71 bool AllowOOBE;
72 } CREATE_PROCESS_PARSED_COMMON, *PCREATE_PROCESS_PARSED_COMMON;
73
74 typedef struct _CREATE_PROCESS_PARSED
75 {
76 CREATE_PROCESS_PARSED_COMMON Common;
77 wil::unique_fd EventFd;
78 wil::unique_fd StdFd[LX_INIT_STD_FD_COUNT];
79 wil::unique_fd ServiceFd;
80 } CREATE_PROCESS_PARSED, *PCREATE_PROCESS_PARSED;
81
82 struct sigaction g_SavedSignalActions[_NSIG];
83
84 //
85 // Best effort to put all processes launched within a session into the same
86 // process group. This is how a shell like /bin/bash would typically launch
87 // grouped commands (e.g. 'find' and 'less' from: find . -iname "*.txt" | less)
88 //
89
90 volatile pid_t g_SessionGroup = -1;
91
92 //
93 // Fallback passwd struct to use in case the /etc/passwd file is missing or
94 // corrupt.
95 //
96
97 constexpr passwd c_defaultPasswordEntry = {
98 const_cast<char*>("root"), NULL, ROOT_UID, ROOT_GID, NULL, const_cast<char*>("/"), const_cast<char*>(DEFAULT_SHELL)};
99
100 int CaptureCrash(int Argc, char** Argv);
101
102 void CreateProcess(PCREATE_PROCESS_PARSED Parsed, int TtyFd, const wsl::linux::WslDistributionConfig& Config);
103
104 void CreateProcessCommon(PCREATE_PROCESS_PARSED_COMMON Common, int TtyFd, int ServiceSocketFd, const wsl::linux::WslDistributionConfig&);
105
106 CREATE_PROCESS_PARSED CreateProcessParse(gsl::span<gsl::byte> Buffer, int MessageFd, const wsl::linux::WslDistributionConfig& Config);
107
108 int CreateProcessParseCommon(PCREATE_PROCESS_PARSED_COMMON Parsed, gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);
109
110 int CreateProcessReplyToServer(PCREATE_PROCESS_PARSED Parsed, pid_t CreateProcessPid, int MessageFd);
111
112 void CreateWslSystemdUnits(const wsl::linux::WslDistributionConfig& Config);
113
114 int InitConnectToServer(int LxBusFd, bool WaitForServer);
115
116 int InitCreateProcessUtilityVm(
117 gsl::span<gsl::byte> Message,
118 const LX_INIT_CREATE_PROCESS_UTILITY_VM& Header,
119 wsl::shared::Transaction& Transaction,
120 const wsl::linux::WslDistributionConfig& Config);
121
122 int InitCreateSessionLeader(
123 gsl::span<gsl::byte> Buffer,
124 wsl::shared::SocketChannel& Channel,
125 const std::function<void(LX_INIT_CREATE_SESSION_RESPONSE&)>& SendResponse,
126 int LxBusFd,
127 wsl::linux::WslDistributionConfig& Config);
128
129 void InitEntry(int Argc, char* Argv[]);
130
131 void InitEntryWsl(wsl::linux::WslDistributionConfig& Config);
132
133 void InitEntryUtilityVm(wsl::linux::WslDistributionConfig& Config);
134
135 void InitTerminateInstance(gsl::span<gsl::byte> Buffer, const std::function<void(bool)>& SendResult, wsl::linux::WslDistributionConfig& Config);
136
137 void InitTerminateInstanceInternal(const wsl::linux::WslDistributionConfig& Config);
138
139 void InstallSystemdUnit(const char* Path, const std::string& Name, const char* Content);
140
141 void LockBinfmtStatusReadOnly();
142
143 int GenerateSystemdUnits(int Argc, char** Argv);
144
145 int GenerateUserSystemdUnits(int Argc, char** Argv);
146
147 void HardenMirroredNetworkingSettingsAgainstSystemd();
148
149 void PostProcessImportedDistribution(wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT>& Message, const char* ExtractedPath);
150
151 void SessionLeaderCreateProcess(gsl::span<gsl::byte> Buffer, int MessageFd, int TtyFd);
152
153 void SessionLeaderEntry(int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config);
154
155 void SessionLeaderEntryUtilityVm(wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config);
156
157 unsigned int StartPlan9(int Argc, char** Argv);
158
159 unsigned int StartGns(int Argc, char** Argv);
160
161 void WaitForBootProcess(wsl::linux::WslDistributionConfig& Config);
162
163 wil::unique_fd UnmarshalConsoleFromServer(int MessageFd, LXBUS_IPC_CONSOLE_ID ConsoleId);
164
165 int WslInitWatcher(int Argc, char** Argv);
166
167 int WslcGpuHookEntry();
168
169 int WslEntryPoint(int Argc, char* Argv[])
170 {
171 //
172 // Determine if the binary is being launched in init daemon mode by
173 // checking the pid and Argc.
174 //
175 // N.B. Using the pid is not enough because this process might be running
176 // in a docker container. See: https://github.com/microsoft/WSL/issues/10883.
177 //
178 // If not in init daemon mode, differentiate between various functionality by checking Argv[0].
179 //
180
181 char* BaseName = basename(Argv[0]);
182 int ExitCode = -1;
183 pid_t Pid = getpid();
184
185 if (Pid == 1 && strcmp(BaseName, "init") == 0 && Argc <= 1)
186 {
187 InitEntry(Argc, Argv);
188 }
189 else
190 {
191 if (strcmp(BaseName, WSLPATH_NAME) == 0)
192 {
193 ExitCode = WslPathEntry(Argc, Argv);
194 }
195 else if (strcmp(BaseName, MOUNT_DRVFS_NAME) == 0)
196 {
197 ExitCode = MountDrvfsEntry(Argc, Argv);
198 }
199 else if (strcmp(BaseName, LX_INIT_LOCALHOST_RELAY) == 0)
200 {
201 ExitCode = RunPortTracker(Argc, Argv);
202 }
203 else if (strcmp(BaseName, LX_INIT_TELEMETRY_AGENT) == 0)
204 {
205 ExitCode = StartTelemetryAgent();
206 }
207 else if (strcmp(BaseName, LX_INIT_GNS) == 0)
208 {
209 ExitCode = StartGns(Argc, Argv);
210 }
211 else if (strcmp(BaseName, LX_INIT_PLAN9) == 0)
212 {
213 ExitCode = StartPlan9(Argc, Argv);
214 }
215 else if (strcmp(BaseName, WSLINFO_NAME) == 0)
216 {
217 ExitCode = WslInfoEntry(Argc, Argv);
218 }
219 else if (strcmp(BaseName, LX_INIT_WSL_CAPTURE_CRASH) == 0)
220 {
221 ExitCode = CaptureCrash(Argc, Argv);
222 }
223 else if (strcmp(BaseName, LX_INIT_WSL_GENERATOR) == 0)
224 {
225 ExitCode = GenerateSystemdUnits(Argc, Argv);
226 }
227 else if (strcmp(BaseName, LX_INIT_WSL_USER_GENERATOR) == 0)
228 {
229 ExitCode = GenerateUserSystemdUnits(Argc, Argv);
230 }
231 else if (strcmp(BaseName, LX_INIT_WSL_INIT_WATCHER) == 0)
232 {
233 ExitCode = WslInitWatcher(Argc, Argv);
234 }
235 else if (strcmp(BaseName, LX_INIT_WSLC_GPU_HOOK) == 0)
236 {
237 ExitCode = WslcGpuHookEntry();
238 }
239 else
240 {
241 // Handle the special case for import result messages, everything else is sent to the binfmt interpreter.
242 if (Pid == 1 && strcmp(BaseName, "init") == 0 && Argc == 3 && strcmp(Argv[1], LX_INIT_IMPORT_MESSAGE_ARG) == 0)
243 {
244 try
245 {
246 wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT> message;
247 PostProcessImportedDistribution(message, Argv[2]);
248 UtilWriteBuffer(STDOUT_FILENO, message.Span());
249 char buffer[1];
250 read(STDIN_FILENO, buffer, sizeof(buffer));
251 exit(0);
252 }
253 CATCH_RETURN_ERRNO()
254 }
255
256 ExitCode = CreateNtProcess(Argc - 1, &Argv[1]);
257 }
258 }
259
260 return ExitCode;
261 }
262
263 int GenerateUserSystemdUnits(int Argc, char** Argv)
264 {
265 if (Argc < 2)
266 {
267 LOG_ERROR("Unit folder missing");
268 return 1;
269 }
270
271 const auto* installPath = Argv[1];
272
273 try
274 {
275 std::string automountRoot = "/mnt";
276 wil::unique_file File{fopen("/etc/wsl.conf", "r")};
277 if (File)
278 {
279 std::vector<ConfigKey> ConfigKeys = {
280 ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),
281
282 };
283 ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));
284 File.reset();
285 }
286
287 // TODO: handle quotes in path
288
289 auto unitContent = std::format(
290 R"(# Note: This file is generated by WSL to configure wslg.
291
292 [Unit]
293 Description=WSLg user service
294 DefaultDependencies=no
295
296 [Service]
297 Type=oneshot
298 Environment=WSLG_RUNTIME_DIR={}/{}/{}
299 ExecStart=/bin/sh -c 'mkdir -p -m 00755 "$XDG_RUNTIME_DIR/pulse"'
300 ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/wayland-0" "$XDG_RUNTIME_DIR/wayland-0"'
301 ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/wayland-0.lock" "$XDG_RUNTIME_DIR/wayland-0.lock"'
302 ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/pulse/native" "$XDG_RUNTIME_DIR/pulse/native"'
303 ExecStart=/bin/sh -c 'ln -sf "$WSLG_RUNTIME_DIR/pulse/pid" "$XDG_RUNTIME_DIR/pulse/pid"'
304 )",
305 automountRoot,
306 WSLG_SHARED_FOLDER,
307 WAYLAND_RUNTIME_DIR);
308
309 InstallSystemdUnit(installPath, "wslg-session", unitContent.c_str());
310
311 return 0;
312 }
313 CATCH_LOG()
314
315 return 1;
316 }
317
318 int GenerateSystemdUnits(int Argc, char** Argv)
319 {
320 if (Argc < 2)
321 {
322 LOG_ERROR("Unit folder missing");
323 return 1;
324 }
325
326 try
327 {
328 const auto* installPath = Argv[1];
329
330 LOG_INFO("Generating WSL systemd units in {}", installPath);
331
332 bool enableGuiApps = true;
333 std::string automountRoot = "/mnt";
334
335 wil::unique_file File{fopen("/etc/wsl.conf", "r")};
336 if (File)
337 {
338 std::vector<ConfigKey> ConfigKeys = {
339 ConfigKey(wsl::linux::c_ConfigEnableGuiAppsOption, enableGuiApps),
340 ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),
341
342 };
343 ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));
344 File.reset();
345 }
346
347 if (automountRoot.empty() || automountRoot.back() != '/')
348 {
349 automountRoot += '/';
350 }
351
352 // Mask systemd-networkd-wait-online.service since WSL always ensures that networking is configured during boot.
353 // That unit can cause systemd boot timeouts since WSL's network interface is unmanaged by systemd.
354 THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/systemd-networkd-wait-online.service", installPath).c_str()) < 0);
355
356 // Mask NetworkManager-wait-online.service for the same reason, as it causes timeouts on distros using NetworkManager.
357 THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/NetworkManager-wait-online.service", installPath).c_str()) < 0);
358
359 // Mask console-getty.service since /dev/tty devices are shared at the VM level across all distros.
360 // When multiple distros are running, the second distro's getty fails because the tty is already held.
361 THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/console-getty.service", installPath).c_str()) < 0);
362
363 const auto sharedMountPath = automountRoot + "wsl";
364 const auto mountGuardUnitContent = std::format(
365 R"(# Note: This file is generated by WSL to prevent shutdown unmounts from propagating to other distributions.
366
367 [Unit]
368 Description=WSL Cross-Distribution Mount Guard
369 After=local-fs.target remote-fs.target
370 ConditionPathIsMountPoint={}
371
372 [Service]
373 Type=oneshot
374 RemainAfterExit=yes
375 ExecStart=/bin/true
376 ExecStop=-/bin/mount --make-rslave {})",
377 sharedMountPath,
378 sharedMountPath);
379 InstallSystemdUnit(installPath, "wsl-mnt-guard", mountGuardUnitContent.c_str());
380
381 // Only create the wslg unit if both enabled in wsl.conf, and if the wslg folder actually exists.
382 if (enableGuiApps && access("/mnt/wslg/runtime-dir", F_OK) == 0)
383 {
384 // Note: It's not possible to use a mount unit because systemd will not mount /tmp/.X11-unix
385 // if /proc/mount says it's already mounted.
386
387 constexpr auto* x11UnitContent = R"(# Note: This file is generated by WSL to prevent tmp.mount from hiding /tmp/.X11-unix
388
389 [Unit]
390 Description=WSLg Remount Service
391 DefaultDependencies=no
392 After=systemd-tmpfiles-setup.service tmp.mount
393 ConditionPathExists=/mnt/wslg/.X11-unix
394 ConditionPathExists=!/tmp/.X11-unix/X0
395
396 [Service]
397 Type=oneshot
398 ExecStart=/bin/mount -o bind,ro,X-mount.mkdir -t none /mnt/wslg/.X11-unix /tmp/.X11-unix)";
399 InstallSystemdUnit(installPath, "wslg", x11UnitContent);
400 }
401
402 return 0;
403 }
404 CATCH_LOG()
405
406 return 1;
407 }
408
409 int CaptureCrash(int Argc, char** Argv)
410 try
411 {
412 UtilSetThreadName("CaptureCrash");
413
414 if (Argc < 5)
415 {
416 std::cerr << "Usage: " << Argv[0] << "<time> <executable> <pid> <signal>" << std::endl;
417 return 1;
418 }
419
420 InitializeLogging(false);
421
422 LOG_INFO("Capturing crash for pid: {}, executable: {}, signal: {}, port: {}", Argv[3], Argv[2], Argv[4], LX_INIT_UTILITY_VM_CRASH_DUMP_PORT);
423
424 wsl::shared::SocketChannel channel(UtilConnectVsock(LX_INIT_UTILITY_VM_CRASH_DUMP_PORT, true), "crash-dump");
425
426 wsl::shared::MessageWriter<LX_PROCESS_CRASH> message(LxProcessCrash);
427 message.WriteString(Argv[2]);
428 message->Timestamp = std::strtoull(Argv[1], nullptr, 10);
429 message->Signal = std::strtoul(Argv[4], nullptr, 10);
430 message->Pid = std::strtoul(Argv[3], nullptr, 10);
431
432 auto result = channel.Transaction<LX_PROCESS_CRASH>(message.Span()).Result;
433 if (result != 0)
434 {
435 LOG_ERROR("Received error while trying to capture crash dump: {}", result);
436 }
437
438 std::vector<char> buffer(LX_RELAY_BUFFER_SIZE);
439
440 int bytes = -1;
441 while ((bytes = TEMP_FAILURE_RETRY(read(STDIN_FILENO, buffer.data(), buffer.size()))) > 0)
442 {
443 if (UtilWriteBuffer(channel.Socket(), buffer.data(), bytes) < 0)
444 {
445 LOG_ERROR("Error while trying read write dump, {}", errno);
446 return 1;
447 }
448 }
449
450 if (bytes != 0)
451 {
452 LOG_ERROR("Error while trying read crash dump from stdin, {}", errno);
453 return 1;
454 }
455
456 return 0;
457 }
458 CATCH_RETURN_ERRNO()
459
460 void CreateProcess(PCREATE_PROCESS_PARSED Parsed, int TtyFd, const wsl::linux::WslDistributionConfig& Config)
461
462 /*++
463
464 Routine Description:
465
466 This routine is the entry point for the create process.
467
468 Arguments:
469
470 Parsed - Supplies a pointer to a create process parsed structure.
471
472 Config - Supplies the distribution configuration.
473
474 Return Value:
475
476 None.
477
478 --*/
479
480 {
481 ssize_t BytesRead;
482 uint64_t EventFdData;
483 int StdFdIndex;
484
485 //
486 // Initialize the new process and wait until the session leader signals
487 // to execvpe.
488 //
489
490 for (StdFdIndex = 0; StdFdIndex < LX_INIT_STD_FD_COUNT; StdFdIndex += 1)
491 {
492 //
493 // If a standard file descriptor is not set, use the TTY file descriptor.
494 //
495
496 if (dup2(Parsed->StdFd[StdFdIndex] ? Parsed->StdFd[StdFdIndex].get() : TtyFd, StdFdIndex) < 0)
497 {
498 FATAL_ERROR("dup2 failed {}", errno);
499 }
500
501 Parsed->StdFd[StdFdIndex].reset();
502 }
503
504 //
505 // Read the eventfd data from the wsl service.
506 //
507
508 BytesRead = TEMP_FAILURE_RETRY(read(Parsed->EventFd.get(), &EventFdData, sizeof(EventFdData)));
509 if (BytesRead != sizeof(EventFdData))
510 {
511 FATAL_ERROR("Failed to read (size {}) EventFd {}", BytesRead, errno);
512 }
513
514 //
515 // Launch the process.
516 //
517
518 CreateProcessCommon(&Parsed->Common, TtyFd, Parsed->ServiceFd.get(), Config);
519 return;
520 }
521
522 void CreateProcessCommon(PCREATE_PROCESS_PARSED_COMMON Common, int TtyFd, int ServiceSocket, const wsl::linux::WslDistributionConfig& Config)
523
524 /*++
525
526 Routine Description:
527
528 This routine is entry point for the common create process functionality.
529
530 Arguments:
531
532 Common - Supplies a pointer to the common create process parsed data.
533
534 TtyFd - Supplies the file descriptor representing the process's terminal.
535 This method takes ownership of this file descriptor.
536
537 ServiceSocket - Supplies the file descriptor to a socket connected to the Service.
538
539 Config - Supplies the distribution configuration
540
541 Return Value:
542
543 None.
544
545 --*/
546
547 try
548 {
549 //
550 // Print any errors that occurred.
551 //
552
553 for (const auto& e : wsl::shared::string::Split<char>(wil::ScopedWarningsCollector::ConsumeWarnings(), '\n'))
554 {
555 if (!e.empty())
556 {
557 fprintf(stderr, "wsl: %s\n", e.c_str());
558 }
559 }
560
561 //
562 // Restore default signal dispositions and clear the signal mask for the child process.
563 //
564
565 THROW_LAST_ERROR_IF(UtilSetSignalHandlers(g_SavedSignalActions, false) < 0);
566
567 sigset_t SignalMask;
568 sigemptyset(&SignalMask);
569 THROW_LAST_ERROR_IF(sigprocmask(SIG_SETMASK, &SignalMask, NULL) < 0);
570
571 auto AddEnvironmentVariable = [&](const char* Name) {
572 const auto Value = UtilGetEnvironmentVariable(Name);
573 if (!Value.empty())
574 {
575 Common->Environment.AddVariable(Name, Value.c_str());
576 }
577 };
578
579 AddEnvironmentVariable(NAME_ENV);
580 AddEnvironmentVariable(WSL_DISTRO_NAME_ENV);
581
582 //
583 // Get the password entry for the user. (root if the distribution is being installed)
584 //
585
586 const passwd* PasswordEntry{};
587
588 auto ConfigureUid = [&](uint32_t Uid) {
589 PasswordEntry = getpwuid(Uid);
590 if (PasswordEntry == nullptr)
591 {
592 LOG_ERROR("getpwuid({}) failed {}", Uid, errno);
593 PasswordEntry = const_cast<passwd*>(&c_defaultPasswordEntry);
594 }
595
596 //
597 // Add environment variables to the environment block.
598 //
599
600 Common->Environment.AddVariable(HOME_ENV, PasswordEntry->pw_dir);
601 Common->Environment.AddVariable(USER_ENV, PasswordEntry->pw_name);
602 Common->Environment.AddVariable(LOGNAME_ENV, PasswordEntry->pw_name);
603 Common->Environment.AddVariable(SHELL_ENV, PasswordEntry->pw_shell);
604 };
605
606 //
607 // Set the $LANG environment variable.
608 //
609 // N.B. Failure to update $LANG environment variable is non-fatal.
610 //
611
612 ConfigUpdateLanguage(Common->Environment);
613
614 //
615 // Launch the OOBE command, if any
616 //
617
618 if (Common->AllowOOBE)
619 {
620 assert(ServiceSocket != -1);
621
622 wsl::shared::SocketChannel channel(wil::unique_fd{ServiceSocket}, "OOBE");
623
624 std::string OobeCommand{};
625 int defaultUid = 0;
626 ConfigKeyPresence defaultUidPresent{};
627 std::vector<ConfigKey> keys = {ConfigKey("oobe.command", OobeCommand), ConfigKey("oobe.defaultUid", defaultUid, &defaultUidPresent)};
628
629 {
630 wil::unique_file File{fopen(WSL_DISTRIBUTION_CONF, "r")};
631 ParseConfigFile(keys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));
632 }
633
634 int32_t OobeResult = 0;
635 if (!OobeCommand.empty())
636 {
637 auto Pid = UtilCreateChildProcess("OOBE", [&OobeCommand, &Common, &ConfigureUid]() {
638 ConfigureUid(0);
639 execle("/bin/sh", "sh", "-c", OobeCommand.c_str(), nullptr, const_cast<char**>(Common->Environment.Variables().data()));
640 LOG_ERROR("execle() failed, {}", errno);
641 });
642
643 int Status = -1;
644 if (TEMP_FAILURE_RETRY(waitpid(Pid, &Status, 0)) < 0)
645 {
646 LOG_ERROR("Waiting for child '{}' failed, waitpid failed {}", OobeCommand.c_str(), errno);
647 _exit(1);
648 }
649
650 if (UtilProcessChildExitCode(Status, OobeCommand.c_str(), 0, false) < 0)
651 {
652 OobeResult = -1;
653 fprintf(stderr, "OOBE command \"%s\" failed, exiting\n", OobeCommand.c_str());
654 }
655 }
656
657 LX_INIT_OOBE_RESULT result{};
658 result.Header.MessageType = LxInitOobeResult;
659 result.Header.MessageSize = sizeof(result);
660 result.Result = OobeResult;
661 result.DefaultUid = defaultUidPresent == ConfigKeyPresence::Present ? defaultUid : -1;
662
663 channel.SendMessage(result);
664
665 if (OobeResult != 0)
666 {
667 _exit(1);
668 }
669
670 ConfigureUid(defaultUidPresent == ConfigKeyPresence::Present ? defaultUid : Common->Uid);
671 }
672 else
673 {
674 ConfigureUid(Common->Uid);
675 }
676
677 //
678 // Ensure that a login session has been created for the user and set expected
679 // environment variables.
680 //
681
682 if (Config.InitPid.has_value())
683 {
684 wsl::shared::SocketChannel InteropChannel{UtilConnectToInteropServer(Config.InitPid.value()), "InteropClient"};
685 THROW_LAST_ERROR_IF(InteropChannel.Socket() < 0);
686
687 wsl::shared::MessageWriter<LX_INIT_CREATE_LOGIN_SESSION> CreateSession(LxInitMessageCreateLoginSession);
688 CreateSession->Uid = PasswordEntry->pw_uid;
689 CreateSession->Gid = PasswordEntry->pw_gid;
690 CreateSession.WriteString(PasswordEntry->pw_name);
691
692 auto result = InteropChannel.Transaction<LX_INIT_CREATE_LOGIN_SESSION>(CreateSession.Span());
693
694 if (!result.Result)
695 {
696 fprintf(stderr, "wsl: %s\n", wsl::shared::Localization::MessageSystemdUserSessionFailed(PasswordEntry->pw_name).c_str());
697 }
698
699 Common->Environment.AddVariable("DBUS_SESSION_BUS_ADDRESS", std::format("unix:path=/run/user/{}/bus", PasswordEntry->pw_uid));
700 Common->Environment.AddVariable(XDG_RUNTIME_DIR_ENV, std::format("/run/user/{}", PasswordEntry->pw_uid));
701 }
702
703 //
704 // If a filename was provided, use the filename and command line as-is.
705 // Otherwise, use the user's default shell. If the user's default shell is
706 // is empty, fall back to using /bin/sh.
707 //
708
709 std::string Argv0;
710 std::vector<const char*> CommandLine = Common->CommandLine;
711 char* Filename = const_cast<char*>(Common->Filename);
712 if (strlen(Filename) == 0)
713 {
714 Filename = const_cast<char*>(SHELL_PATH);
715 auto Size = sizeof(SHELL_PATH) - 1;
716 if (PasswordEntry->pw_shell != NULL)
717 {
718 Size = strlen(PasswordEntry->pw_shell);
719 if (Size != 0)
720 {
721 Filename = PasswordEntry->pw_shell;
722 }
723 }
724
725 if ((Common->ShellOptions & ShellOptionsLogin) != 0)
726 {
727 //
728 // Construct the name of the shell as the last path element
729 // prepended with a '-' and use this as Argv[0].
730 //
731 // N.B. This is the same behavior as the login binary.
732 //
733
734 auto Shell = strrchr(Filename, '/');
735 if (Shell != nullptr)
736 {
737 Shell = Shell + 1;
738 }
739 else
740 {
741 Shell = Filename;
742 }
743
744 Argv0 = "-";
745 Argv0 += Shell;
746 }
747 else
748 {
749 Argv0 = Filename;
750 }
751
752 CommandLine.insert(CommandLine.begin(), Argv0.c_str());
753 }
754
755 //
756 // Set the owner of the tty device.
757 //
758
759 if (TtyFd != -1)
760 {
761 if (fchown(TtyFd, PasswordEntry->pw_uid, TTY_GID) < 0)
762 {
763 LOG_ERROR("fchown failed {}", errno);
764 }
765
766 CLOSE(TtyFd);
767 TtyFd = -1;
768 }
769
770 //
771 // Set the supplemental groups, gid, uid, and current working directory.
772 //
773
774 UtilInitGroups(PasswordEntry->pw_name, PasswordEntry->pw_gid);
775 THROW_LAST_ERROR_IF(setgid(PasswordEntry->pw_gid) < 0);
776 THROW_LAST_ERROR_IF(setuid(PasswordEntry->pw_uid) < 0);
777
778 //
779 // If the provided current working directory is empty, use the user's home
780 // path as the current working directory.
781 //
782 // N.B. Failures to set the current working directory are non-fatal.
783 //
784
785 std::string Directory;
786 if (Common->CurrentWorkingDirectory.empty())
787 {
788 Directory = PasswordEntry->pw_dir;
789 }
790 else
791 {
792 Directory = Common->CurrentWorkingDirectory;
793 if (Directory[0] == '~')
794 {
795 Directory = PasswordEntry->pw_dir;
796 if (Common->CurrentWorkingDirectory.size() > 1)
797 {
798 Directory += &Common->CurrentWorkingDirectory[1];
799 }
800 }
801 }
802
803 if (chdir(Directory.c_str()) < 0)
804 {
805 LOG_ERROR("chdir({}) failed {}", Directory, errno);
806 }
807
808 //
809 // Launch the process.
810 //
811
812 execvpe(Filename, const_cast<char**>(CommandLine.data()), const_cast<char**>(Common->Environment.Variables().data()));
813 FATAL_ERROR("execvpe({}) failed: {}", Filename, strerror(errno));
814
815 return;
816 }
817 catch (...)
818 {
819 LOG_CAUGHT_EXCEPTION();
820 FATAL_ERROR("Create process failed");
821 }
822
823 CREATE_PROCESS_PARSED CreateProcessParse(gsl::span<gsl::byte> Buffer, int MessageFd, const wsl::linux::WslDistributionConfig& Config)
824
825 /*++
826
827 Routine Description:
828
829 This routine parses a create process message.
830
831 Arguments:
832
833 Buffer - Supplies the create process message.
834
835 MessageFd - Supplies a message port file descriptor.
836
837 Config - Supplies the distribution configuration.
838
839 Return Value:
840
841 The create process parameters.
842
843 --*/
844
845 {
846 //
847 // Validate the message size.
848 //
849
850 auto* Message = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS>(Buffer);
851 THROW_ERRNO_IF(EINVAL, !Message);
852
853 //
854 // Parse common create process information.
855 //
856
857 CREATE_PROCESS_PARSED Parsed{};
858 int Result = CreateProcessParseCommon(&Parsed.Common, Buffer.subspan(offsetof(LX_INIT_CREATE_PROCESS, Common)), Config);
859 THROW_ERRNO_IF(EINVAL, Result < 0);
860
861 //
862 // Create the eventfd.
863 //
864
865 Parsed.EventFd = eventfd(0, EFD_CLOEXEC);
866 THROW_LAST_ERROR_IF(!Parsed.EventFd);
867
868 //
869 // Set up the standard handles for the process.
870 //
871 //
872
873 for (unsigned short Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)
874 {
875 if (Message->StdFdIds[Index] != LX_INIT_CREATE_PROCESS_USE_CONSOLE)
876 {
877 LXBUS_IPC_MESSAGE_UNMARSHAL_HANDLE_PARAMETERS UnmarshalHandle{};
878 UnmarshalHandle.Input.HandleId = Message->StdFdIds[Index];
879 Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_HANDLE, &UnmarshalHandle));
880 THROW_LAST_ERROR_IF(Result < 0);
881
882 Parsed.StdFd[Index] = UnmarshalHandle.Output.FileDescriptor;
883 }
884 }
885
886 //
887 // Unmarshal the fork token.
888 //
889
890 LXBUS_IPC_MESSAGE_UNMARSHAL_FORK_TOKEN_PARAMETERS UnmarshalForkToken{};
891 UnmarshalForkToken.Input.ForkTokenId = Message->ForkTokenId;
892 Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_FORK_TOKEN, &UnmarshalForkToken));
893 THROW_LAST_ERROR_IF(Result < 0);
894
895 //
896 // Unmarshal the ipc server.
897 //
898
899 if (Message->IpcServerId != LXBUS_IPC_SERVER_ID_INVALID)
900 {
901 LXBUS_IPC_MESSAGE_UNMARSHAL_SERVER_PARAMETERS UnmarshalServer{};
902 UnmarshalServer.Input.ServerId = Message->IpcServerId;
903 Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_SERVER, &UnmarshalServer));
904 THROW_LAST_ERROR_IF(Result < 0);
905
906 if (Parsed.Common.AllowOOBE)
907 {
908 wil::unique_fd LxBusFd{TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR))};
909 THROW_LAST_ERROR_IF(!LxBusFd);
910
911 LXBUS_CONNECT_SERVER_PARAMETERS ConnectParams{};
912 ConnectParams.Input.Flags = LXBUS_IPC_CONNECT_FLAG_UNNAMED_SERVER;
913 ConnectParams.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;
914 Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd.get(), LXBUS_IOCTL_CONNECT_SERVER, &ConnectParams));
915 THROW_LAST_ERROR_IF(Result < 0);
916
917 Parsed.ServiceFd = ConnectParams.Output.MessagePort;
918 }
919 }
920
921 return Parsed;
922 }
923
924 int CreateProcessParseCommon(PCREATE_PROCESS_PARSED_COMMON Parsed, gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config)
925
926 /*++
927
928 Routine Description:
929
930 This routine parses a create process message.
931
932 Arguments:
933
934 Parsed - Supplies a buffer to store the common create process parameters.
935
936 Buffer - Supplies the common create process message data.
937
938 Config - Supplies the distribution configuration.
939
940 Return Value:
941
942 0 on success, -1 on failure.
943
944 --*/
945
946 try
947 {
948 auto* Common = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS_COMMON>(Buffer);
949 if (!Common)
950 {
951 LOG_ERROR("Invalid message size {}", Buffer.size());
952 return -1;
953 }
954
955 //
956 // Populate the current working directory. If the path does not begin with a
957 // UNIX path separator or `~`, it is translated.
958 //
959 // N.B. Failure to translate the current working directory is non-fatal.
960 //
961
962 auto* Path = wsl::shared::string::FromSpan(Buffer, Common->CurrentWorkingDirectoryOffset);
963 if ((*Path == '/') || (*Path == '~'))
964 {
965 Parsed->CurrentWorkingDirectory = Path;
966 }
967 else if (*Path != '\0')
968 {
969 Parsed->CurrentWorkingDirectory = WslPathTranslate(const_cast<char*>(Path), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX);
970 if (Parsed->CurrentWorkingDirectory.empty() && Config.AutoMount)
971 {
972 EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToTranslate(Path));
973 }
974 }
975
976 //
977 // Initialize the command line will a null-terminator.
978 //
979
980 auto CommandLine = Buffer.subspan(Common->CommandLineOffset);
981 for (unsigned short Index = 0; Index < Common->CommandLineCount; Index += 1)
982 {
983 std::string_view Argument{wsl::shared::string::FromSpan(CommandLine)};
984 Parsed->CommandLine.emplace_back(Argument.data());
985 CommandLine = CommandLine.subspan(Argument.size() + 1);
986 }
987
988 //
989 // If a username was provided, get the password entry for the specified username.
990 // If no username was provided use the one specified in /etc/wsl.conf.
991 // Otherwise, use the default UID from the registry.
992 //
993
994 struct passwd* PasswordEntry = nullptr;
995 auto Username = wsl::shared::string::FromSpan(Buffer, Common->UsernameOffset);
996 if (strlen(Username) != 0)
997 {
998 PasswordEntry = getpwnam(Username);
999 if (PasswordEntry == nullptr)
1000 {
1001 FATAL_ERROR_EX(EX_NOUSER, "getpwnam({}) failed {}", Username, errno);
1002 }
1003 }
1004 else if (Config.DefaultUser.has_value())
1005 {
1006 PasswordEntry = getpwnam(Config.DefaultUser->c_str());
1007 if (PasswordEntry == nullptr)
1008 {
1009 LOG_ERROR("getpwnam({}) failed {}", Config.DefaultUser->c_str(), errno);
1010 }
1011 }
1012
1013 if (PasswordEntry == nullptr)
1014 {
1015 PasswordEntry = getpwuid(Common->DefaultUid);
1016 if (PasswordEntry == nullptr)
1017 {
1018 LOG_ERROR("getpwuid({}) failed {}", Common->DefaultUid, errno);
1019 }
1020 }
1021
1022 Parsed->CommandLine.emplace_back(nullptr);
1023 Parsed->Environment = ConfigCreateEnvironmentBlock(Common, Config);
1024 Parsed->Filename = wsl::shared::string::FromSpan(Buffer, Common->FilenameOffset);
1025 Parsed->ShellOptions = static_cast<CREATE_PROCESS_SHELL_OPTIONS>(Common->ShellOptions);
1026 Parsed->Uid = PasswordEntry ? PasswordEntry->pw_uid : ROOT_UID; // If the default user was not found, fall back to root.
1027 Parsed->AllowOOBE = WI_IsFlagSet(Common->Flags, LxInitCreateProcessFlagAllowOOBE);
1028 return 0;
1029 }
1030 CATCH_RETURN_ERRNO()
1031
1032 int CreateProcessReplyToServer(PCREATE_PROCESS_PARSED Parsed, pid_t CreateProcessPid, int MessageFd)
1033
1034 /*++
1035
1036 Routine Description:
1037
1038 This routine replies to the server for a create process message.
1039
1040 Arguments:
1041
1042 Parsed - Supplies a pointer to a create process parsed structure.
1043
1044 CreateProcessPid - Supplies the pid of a newly created child process.
1045
1046 MessageFd - Supplies a message port file descriptor.
1047
1048 Return Value:
1049
1050 0 on success, -1 on failure.
1051
1052 N.B. On failure, this routine will terminate the child process.
1053
1054 --*/
1055
1056 {
1057 auto terminateChild = wil::scope_exit([CreateProcessPid]() {
1058 if (kill(CreateProcessPid, SIGKILL) < 0)
1059 {
1060 FATAL_ERROR("Failed to kill child process {}", errno);
1061 }
1062 });
1063
1064 //
1065 // Marshal the pid of the new child process and send a message
1066 // indicating that the child was created.
1067 //
1068
1069 LXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS MarshalProcess{};
1070 MarshalProcess.Input.Process = CreateProcessPid;
1071 if (TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_PROCESS, &MarshalProcess)) < 0)
1072 {
1073 LOG_ERROR("Failed to marshal pid {}", errno);
1074 return -1;
1075 }
1076
1077 auto Bytes = UtilWriteBuffer(MessageFd, &MarshalProcess.Output.ProcessId, sizeof(MarshalProcess.Output.ProcessId));
1078 if (Bytes < 0)
1079 {
1080 LOG_ERROR("Failed to write ProcessId {}", errno);
1081 return -1;
1082 }
1083
1084 //
1085 // Wait for the server to indicate that the process can be continued or
1086 // it needs to be terminated.
1087 //
1088
1089 Bytes = TEMP_FAILURE_RETRY(read(MessageFd, &MarshalProcess.Output.ProcessId, sizeof(MarshalProcess.Output.ProcessId)));
1090 if (Bytes != sizeof(MarshalProcess.Output.ProcessId))
1091 {
1092 LOG_ERROR("Failed to read (size {}) ProcessId {}", Bytes, errno);
1093 return -1;
1094 }
1095
1096 if (MarshalProcess.Output.ProcessId == 0)
1097 {
1098 LOG_ERROR("Server replied with failure");
1099 return -1;
1100 }
1101
1102 uint64_t EventFdData = 1;
1103 Bytes = UtilWriteBuffer(Parsed->EventFd.get(), &EventFdData, sizeof(EventFdData));
1104 if (Bytes < 0)
1105 {
1106 LOG_ERROR("Failed to write EventFd {}", errno);
1107 return -1;
1108 }
1109
1110 terminateChild.release();
1111 return 0;
1112 }
1113
1114 int InitCreateSessionLeader(
1115 gsl::span<gsl::byte> Buffer,
1116 wsl::shared::SocketChannel& Channel,
1117 const std::function<void(LX_INIT_CREATE_SESSION_RESPONSE&)>& SendResponse,
1118 int LxBusFd,
1119 wsl::linux::WslDistributionConfig& Config)
1120
1121 /*++
1122
1123 Routine Description:
1124
1125 This routine creates a session leader from the init process.
1126
1127 Arguments:
1128
1129 Buffer - Supplies the message buffer.
1130
1131 Channel - Supplies a message channel
1132
1133 LxBusFd - Supplies an LxBus file descriptor (WSL1 only).
1134
1135 Config - Supplies the distribution configuration.
1136
1137 Return Value:
1138
1139 0 on success, -1 on failure.
1140
1141 --*/
1142 try
1143 {
1144 int Result = -1;
1145 pid_t SessionLeader = -1;
1146 wil::unique_fd SessionLeaderFd;
1147 struct sockaddr_vm SocketAddress;
1148
1149 //
1150 // N.B. FATAL_ERROR will exit the init process, which will also terminate
1151 // any previously created sessions that are still running. On failure,
1152 // the calling function may choose to continue, in order to preserve
1153 // these previous sessions. The FATAL_ERROR macro should be used with
1154 // care here.
1155 //
1156
1157 //
1158 // Validate input parameters.
1159 //
1160
1161 auto* CreateSession = gslhelpers::try_get_struct<LX_INIT_CREATE_SESSION>(Buffer);
1162 if (!CreateSession)
1163 {
1164 FATAL_ERROR("Unexpected create session size {}", Buffer.size());
1165 }
1166
1167 //
1168 // Connect to the Windows server for the new session leader and create the
1169 // new session leader process.
1170 //
1171
1172 if (LxBusFd >= 0)
1173 {
1174 //
1175 // Unmarshal the console for the session leader.
1176 //
1177
1178 if (CreateSession->ConsoleId == LX_INIT_NO_CONSOLE)
1179 {
1180 FATAL_ERROR("Console required for session leader");
1181 }
1182
1183 auto TtyFd = UnmarshalConsoleFromServer(Channel.Socket(), CreateSession->ConsoleId);
1184 if (!TtyFd)
1185 {
1186 Result = -1;
1187 LOG_ERROR("UnmarshalConsoleFromServer failed");
1188 goto InitCreateSessionLeaderExit;
1189 }
1190
1191 SessionLeaderFd = InitConnectToServer(LxBusFd, false);
1192 if (!SessionLeaderFd)
1193 {
1194 Result = -1;
1195 goto InitCreateSessionLeaderExit;
1196 }
1197
1198 SessionLeader = UtilCreateChildProcess(
1199 "SessionLeader",
1200 [SessionLeaderFd = std::move(SessionLeaderFd), TtyFd = std::move(TtyFd), &Channel, &Config]() mutable {
1201 umask(Config.Umask);
1202 Channel.Close();
1203
1204 THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);
1205
1206 SessionLeaderEntry(SessionLeaderFd.get(), TtyFd.get(), Config);
1207 },
1208 {},
1209 Config.CgroupPath);
1210 }
1211 else
1212 {
1213 WaitForBootProcess(Config);
1214
1215 //
1216 // Ensure the /etc/resolv.conf symlink is present.
1217 //
1218
1219 ConfigCreateResolvConfSymlink(Config);
1220
1221 //
1222 // Create a listening socket for the service to connect to and tell the
1223 // service which port to use.
1224 //
1225 // N.B. If creating the socket fails, a message with invalid port number
1226 // should be sent to unblock the wsl service.
1227 //
1228
1229 wil::unique_fd ListenSocket{UtilListenVsockAnyPort(&SocketAddress, 1)};
1230 if (!ListenSocket)
1231 {
1232 SocketAddress.svm_port = -1;
1233 }
1234
1235 LX_INIT_CREATE_SESSION_RESPONSE Response{};
1236 Response.Header.MessageType = LxInitMessageCreateSessionResponse;
1237 Response.Header.MessageSize = sizeof(Response);
1238 Response.Port = SocketAddress.svm_port;
1239 SendResponse(Response);
1240
1241 if (!ListenSocket)
1242 {
1243 Result = -1;
1244 goto InitCreateSessionLeaderExit;
1245 }
1246
1247 // Note: The call to accept() must be done in the child because if accept() takes a long time, it can block the creation
1248 // of other session leaders. See https://github.com/microsoft/WSL/issues/9114.
1249
1250 SessionLeader = UtilCreateChildProcess(
1251 "SessionLeader",
1252 [ListenSocket = std::move(ListenSocket), &Channel, &Config, Mask = Config.Umask, SocketAddress]() {
1253 umask(Mask);
1254 Channel.Close();
1255
1256 THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);
1257
1258 wsl::shared::SocketChannel channel{
1259 {UtilAcceptVsock(ListenSocket.get(), SocketAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS)}, "SessionLeader"};
1260 if (channel.Socket() < 0)
1261 {
1262 LOG_ERROR("UtilAcceptVsock() failed for session leader {}", errno);
1263 _exit(1);
1264 }
1265
1266 SessionLeaderEntryUtilityVm(channel, Config);
1267 },
1268 {},
1269 Config.CgroupPath);
1270 }
1271
1272 if (SessionLeader < 0)
1273 {
1274 Result = -1;
1275 goto InitCreateSessionLeaderExit;
1276 }
1277
1278 Result = 0;
1279
1280 InitCreateSessionLeaderExit:
1281 return Result;
1282 }
1283 CATCH_RETURN_ERRNO();
1284
1285 int InitConnectToServer(int LxBusFd, bool WaitForServer)
1286
1287 /*++
1288
1289 Routine Description:
1290
1291 This routine connects the init process to the lxbus server.
1292
1293 Arguments:
1294
1295 LxBusFd - Supplies an LxBus file descriptor.
1296
1297 WaitForServer - Supplies true to wait for the server, false otherwise.
1298
1299 Return Value:
1300
1301 A message port file descriptor on success, -1 on failure.
1302
1303 --*/
1304
1305 {
1306 LXBUS_CONNECT_SERVER_PARAMETERS Connection;
1307 int MessageFd;
1308 int Result;
1309
1310 MessageFd = -1;
1311 memset(&Connection, 0, sizeof(Connection));
1312
1313 //
1314 // Connect to the server and set the CLOEXEC flag.
1315 //
1316
1317 Connection.Input.ServerName = LX_INIT_SERVER_NAME;
1318 Connection.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;
1319 if (WaitForServer != false)
1320 {
1321 Connection.Input.Flags = LXBUS_IPC_CONNECT_FLAG_WAIT_FOR_SERVER_REGISTRATION;
1322 }
1323
1324 Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd, LXBUS_IOCTL_CONNECT_SERVER, &Connection));
1325 if (Result < 0)
1326 {
1327 FATAL_ERROR("Failed to connect to server {}", errno);
1328 }
1329
1330 MessageFd = Connection.Output.MessagePort;
1331 Result = fcntl(MessageFd, F_SETFD, FD_CLOEXEC);
1332 if (Result < 0)
1333 {
1334 FATAL_ERROR("fcntl failed {}", errno);
1335 }
1336
1337 return MessageFd;
1338 }
1339
1340 int InitCreateProcessUtilityVm(
1341 gsl::span<gsl::byte> Span,
1342 const LX_INIT_CREATE_PROCESS_UTILITY_VM& CreateProcess,
1343 wsl::shared::Transaction& Transaction,
1344 const wsl::linux::WslDistributionConfig& Config)
1345
1346 /*++
1347
1348 Routine Description:
1349
1350 This routine creates a process from init.
1351
1352 Arguments:
1353
1354 Span - Supplies the message buffer.
1355
1356 CreateProcess - Supplies the message
1357
1358 Channel - Supplies the channel.
1359
1360 Config - Supplies the distribution configuration.
1361
1362 Return Value:
1363
1364 0 on success, -1 on failure.
1365
1366 --*/
1367
1368 {
1369 std::vector<gsl::byte> Buffer;
1370 ssize_t BytesRead;
1371 ssize_t BytesWritten;
1372 pid_t ChildPid;
1373 wsl::shared::SocketChannel ControlChannel;
1374
1375 LX_INIT_PROCESS_EXIT_STATUS ExitStatus{};
1376 unsigned int Index;
1377 bool InteropEnabled;
1378 InteropServer InteropServer;
1379 int ListenSocket = -1;
1380 int Master = -1;
1381 CREATE_PROCESS_PARSED_COMMON Parsed = {nullptr};
1382 std::vector<gsl::byte> PendingStdin;
1383 struct pollfd PollDescriptors[7];
1384 pid_t RelayPid = -1;
1385 int Result;
1386 int SignalFd = -1;
1387 struct signalfd_siginfo SignalInfo;
1388 sigset_t SignalMask;
1389 struct sockaddr_vm SocketAddress;
1390 std::vector<wil::unique_fd> Sockets(LX_INIT_UTILITY_VM_CREATE_PROCESS_SOCKET_COUNT);
1391 int Status;
1392 wil::unique_pipe StdErrPipe;
1393 int StdIn = -1;
1394 wil::unique_pipe StdInPipe;
1395 wil::unique_pipe StdOutPipe;
1396 wsl::shared::SocketChannel TerminalControlChannel;
1397
1398 int TtyFd = -1;
1399 struct winsize WindowSize;
1400
1401 //
1402 // Connect an extra socket for OOBE, if requested.
1403 //
1404
1405 if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagAllowOOBE))
1406 {
1407 Sockets.push_back(wil::unique_fd{});
1408 }
1409
1410 //
1411 // Create a listening socket to accept connections for stdin, stdout,
1412 // stderr, and the control channel.
1413 //
1414 // N.B. If creating the socket fails, a message with invalid port number
1415 // should be sent to unblock the wsl service.
1416 //
1417
1418 ListenSocket = UtilListenVsockAnyPort(&SocketAddress, Sockets.size());
1419 if (ListenSocket < 0)
1420 {
1421 SocketAddress.svm_port = -1;
1422 }
1423
1424 //
1425 // Tell the service which sockets ports to connect to.
1426 //
1427
1428 Transaction.SendResultMessage<uint32_t>(SocketAddress.svm_port);
1429
1430 //
1431 // Exit if creating the listening socket failed.
1432 //
1433
1434 if (ListenSocket < 0)
1435 {
1436 Result = -1;
1437 goto CreateProcessUtilityVmEnd;
1438 }
1439
1440 //
1441 // Create a process to relay input and output via sockets. The parent
1442 // returns to continue processing messages.
1443 //
1444
1445 RelayPid = fork();
1446 if (RelayPid < 0)
1447 {
1448 FATAL_ERROR("fork failed for child process {}", errno);
1449 }
1450
1451 if (RelayPid > 0)
1452 {
1453 Result = 0;
1454 goto CreateProcessUtilityVmEnd;
1455 }
1456
1457 UtilSetThreadName("Relay");
1458
1459 //
1460 // Move to the correct mount namespace to create the child in.
1461 //
1462
1463 if (ConfigSetMountNamespace(WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsElevated)) < 0)
1464 {
1465 Result = -1;
1466 goto CreateProcessUtilityVmEnd;
1467 }
1468
1469 //
1470 // Accept connections from the wsl service.
1471 //
1472
1473 for (auto& Socket : Sockets)
1474 {
1475 Socket.reset(UtilAcceptVsock(ListenSocket, SocketAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS));
1476 if (Socket.get() < 0)
1477 {
1478 Result = -1;
1479 goto CreateProcessUtilityVmEnd;
1480 }
1481 }
1482
1483 //
1484 // Close the listening socket.
1485 //
1486
1487 CLOSE(ListenSocket);
1488 ListenSocket = -1;
1489
1490 //
1491 // Initialize interop.
1492 //
1493
1494 InteropEnabled = WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsInteropEnabled) && Config.InteropEnabled;
1495 if (InteropEnabled)
1496 {
1497 Result = InteropServer.Create();
1498 if (Result < 0)
1499 {
1500 goto CreateProcessUtilityVmEnd;
1501 }
1502 }
1503
1504 //
1505 // For any of the standard handles that are not consoles, create pipes.
1506 //
1507
1508 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))
1509 {
1510 try
1511 {
1512 StdInPipe = wil::unique_pipe::create(O_CLOEXEC);
1513 }
1514 catch (...)
1515 {
1516 LOG_CAUGHT_EXCEPTION_MSG("pipe failed");
1517 Result = -1;
1518 goto CreateProcessUtilityVmEnd;
1519 }
1520 }
1521
1522 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))
1523 {
1524 try
1525 {
1526 StdOutPipe = wil::unique_pipe::create(O_CLOEXEC);
1527 }
1528 catch (...)
1529 {
1530 LOG_CAUGHT_EXCEPTION_MSG("pipe failed");
1531 Result = -1;
1532 goto CreateProcessUtilityVmEnd;
1533 }
1534 }
1535
1536 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))
1537 {
1538 try
1539 {
1540 StdErrPipe = wil::unique_pipe::create(O_CLOEXEC);
1541 }
1542 catch (...)
1543 {
1544 LOG_CAUGHT_EXCEPTION_MSG("pipe failed");
1545 Result = -1;
1546 goto CreateProcessUtilityVmEnd;
1547 }
1548 }
1549
1550 //
1551 // Mark the relay process as a subreaper.
1552 //
1553
1554 Result = prctl(PR_SET_CHILD_SUBREAPER, 1);
1555 if (Result < 0)
1556 {
1557 LOG_ERROR("prctl failed {}", errno);
1558 goto CreateProcessUtilityVmEnd;
1559 }
1560
1561 //
1562 // Block SIGCHLD.
1563 // N.B. This needs to be done before forking so SIGCHILD isn't missed
1564 // in case the child exits before the relay masks the signal.
1565 //
1566
1567 sigemptyset(&SignalMask);
1568 sigaddset(&SignalMask, SIGCHLD);
1569 Result = sigprocmask(SIG_BLOCK, &SignalMask, nullptr);
1570 if (Result < 0)
1571 {
1572 LOG_ERROR("sigprocmask failed {}", errno);
1573 goto CreateProcessUtilityVmEnd;
1574 }
1575
1576 //
1577 // Create a pseudoterminal and child process.
1578 //
1579
1580 memset(&WindowSize, 0, sizeof(WindowSize));
1581 WindowSize.ws_col = CreateProcess.Columns;
1582 WindowSize.ws_row = CreateProcess.Rows;
1583 Result = forkpty(&Master, NULL, NULL, &WindowSize);
1584 if (Result < 0)
1585 {
1586 LOG_ERROR("forkpty failed {}", errno);
1587 goto CreateProcessUtilityVmEnd;
1588 }
1589
1590 if (Result == 0)
1591 {
1592 //
1593 // Reset the signal masks.
1594 //
1595
1596 sigemptyset(&SignalMask);
1597 Result = sigprocmask(SIG_SETMASK, &SignalMask, nullptr);
1598 if (Result < 0)
1599 {
1600 LOG_ERROR("sigprocmask failed {}", errno);
1601 goto CreateProcessUtilityVmEnd;
1602 }
1603
1604 //
1605 // Duplicate stdin to get a file descriptor representing the controlling
1606 // terminal.
1607 //
1608
1609 TtyFd = dup(STDIN_FILENO);
1610 if (TtyFd < 0)
1611 {
1612 LOG_ERROR("dup failed {}", errno);
1613 goto CreateProcessUtilityVmEnd;
1614 }
1615
1616 //
1617 // Replace any standard file descriptor that is not a console with a
1618 // pipe.
1619 //
1620
1621 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))
1622 {
1623 Result = dup2(StdInPipe.read().get(), STDIN_FILENO);
1624 if (Result < 0)
1625 {
1626 LOG_ERROR("dup2 failed {}", errno);
1627 goto CreateProcessUtilityVmEnd;
1628 }
1629 }
1630
1631 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))
1632 {
1633 Result = dup2(StdOutPipe.write().get(), STDOUT_FILENO);
1634 if (Result < 0)
1635 {
1636 LOG_ERROR("dup2 failed {}", errno);
1637 goto CreateProcessUtilityVmEnd;
1638 }
1639 }
1640
1641 if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))
1642 {
1643 Result = dup2(StdErrPipe.write().get(), STDERR_FILENO);
1644 if (Result < 0)
1645 {
1646 LOG_ERROR("dup2 failed {}", errno);
1647 goto CreateProcessUtilityVmEnd;
1648 }
1649 }
1650
1651 //
1652 // Parse common create process information and create the process in
1653 // the child.
1654 //
1655
1656 Result = CreateProcessParseCommon(&Parsed, Span.subspan(offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common)), Config);
1657 if (Result < 0)
1658 {
1659 goto CreateProcessUtilityVmEnd;
1660 }
1661
1662 //
1663 // Set the unique interop socket name as an environment variable.
1664 //
1665
1666 if (InteropEnabled)
1667 {
1668 Result = Parsed.Environment.AddVariableNoThrow(WSL_INTEROP_ENV, InteropServer.Path());
1669 if (Result < 0)
1670 {
1671 goto CreateProcessUtilityVmEnd;
1672 }
1673 }
1674
1675 CreateProcessCommon(&Parsed, TtyFd, Sockets.size() >= 6 ? Sockets[5].get() : -1, Config);
1676 TtyFd = -1;
1677 goto CreateProcessUtilityVmEnd;
1678 }
1679
1680 //
1681 // Parent...
1682 //
1683
1684 ChildPid = Result;
1685
1686 if (Sockets.size() >= 6)
1687 {
1688 Sockets[5].reset();
1689 }
1690
1691 //
1692 // Add the child pid to the thread name for convenience.
1693 //
1694
1695 UtilSetThreadName(std::format("Relay({})", ChildPid).c_str());
1696
1697 //
1698 // Close the unneeded ends of the std pipes.
1699 //
1700
1701 StdInPipe.read().reset();
1702 StdOutPipe.write().reset();
1703 StdErrPipe.write().reset();
1704
1705 //
1706 // Create a signalfd to detect when the child process exits.
1707 //
1708
1709 SignalFd = signalfd(-1, &SignalMask, 0);
1710 if (SignalFd < 0)
1711 {
1712 Result = -1;
1713 LOG_ERROR("signalfd failed {}", errno);
1714 goto CreateProcessUtilityVmEnd;
1715 }
1716
1717 //
1718 // Duplicate the stdin file descriptor.
1719 //
1720
1721 if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))
1722 {
1723 StdIn = dup(Master);
1724 }
1725 else
1726 {
1727 StdIn = dup(StdInPipe.write().get());
1728 StdInPipe.write().reset();
1729 }
1730
1731 if (StdIn < 0)
1732 {
1733 Result = -1;
1734 LOG_ERROR("dup failed {}", errno);
1735 goto CreateProcessUtilityVmEnd;
1736 }
1737
1738 THROW_LAST_ERROR_IF(fcntl(StdIn, F_SETFL, O_NONBLOCK) < 0);
1739
1740 //
1741 // Fill the poll descriptors.
1742 //
1743 // N.B. Any files descriptors that are -1 are ignored by poll.
1744 //
1745
1746 PollDescriptors[0].fd = Sockets[0].get();
1747 PollDescriptors[0].events = POLLIN;
1748 PollDescriptors[1].fd = StdOutPipe.read().get();
1749 PollDescriptors[1].events = POLLIN;
1750 PollDescriptors[2].fd = StdErrPipe.read().get();
1751 PollDescriptors[2].events = POLLIN;
1752 PollDescriptors[3].fd = Master;
1753 PollDescriptors[3].events = POLLIN;
1754 PollDescriptors[4].fd = InteropServer.Socket();
1755 PollDescriptors[4].events = POLLIN;
1756 PollDescriptors[5].fd = SignalFd;
1757 PollDescriptors[5].events = POLLIN;
1758 PollDescriptors[6].fd = Sockets[3].get();
1759 PollDescriptors[6].events = POLLIN;
1760
1761 TerminalControlChannel = {{Sockets[3].get()}, "TerminalControl"};
1762
1763 //
1764 // This is required because sequence numbers can be reset during handover from wsl.exe to wslhost.exe.
1765 //
1766
1767 TerminalControlChannel.IgnoreSequenceNumbers();
1768
1769 ControlChannel = {{Sockets[4].get()}, "Control"};
1770
1771 //
1772 // Begin relaying data from the stdin socket to stdin file descriptor and
1773 // from the master PTY endpoint and output pipes to the stdout and stderr
1774 // sockets.
1775 //
1776
1777 for (;;)
1778 {
1779 BytesWritten = 0;
1780
1781 Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), PendingStdin.empty() ? -1 : 100);
1782 if (!PendingStdin.empty())
1783 {
1784 BytesWritten = write(StdIn, PendingStdin.data(), PendingStdin.size());
1785 if (BytesWritten < 0)
1786 {
1787 if (errno != EAGAIN && errno != EWOULDBLOCK)
1788 {
1789 LOG_ERROR("delayed stdin write failed {}, ChildPid={}", errno, ChildPid);
1790 }
1791 }
1792 else if (BytesWritten <= PendingStdin.size()) // Partial or complete write
1793 {
1794 PendingStdin.erase(PendingStdin.begin(), PendingStdin.begin() + BytesWritten);
1795 }
1796 else
1797 {
1798 LOG_ERROR("Unexpected write result {}, pending={}", BytesWritten, PendingStdin.size());
1799 }
1800 }
1801
1802 if (Result < 0)
1803 {
1804 LOG_ERROR("poll failed {}", errno);
1805 break;
1806 }
1807
1808 //
1809 // Relay input from the stdin socket to the stdin file descriptor.
1810 //
1811
1812 if (PollDescriptors[0].revents & (POLLIN | POLLHUP | POLLERR) && PendingStdin.empty())
1813 {
1814 BytesRead = UtilReadBuffer(Sockets[0].get(), Buffer);
1815 if (BytesRead < 0)
1816 {
1817 LOG_ERROR("read failed {}", errno);
1818 break;
1819 }
1820
1821 //
1822 // A zero-byte read means that the stdin socket has closed. Close
1823 // the corresponding stdin file descriptor and remove the stdin
1824 // socket from the poll descriptors list.
1825 //
1826
1827 if (BytesRead == 0)
1828 {
1829 CLOSE(StdIn);
1830 StdIn = -1;
1831 PollDescriptors[0].fd = -1;
1832
1833 //
1834 // If stdin is a console, close the pseudoterminal master.
1835 //
1836
1837 if ((WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole)) && (Master != -1))
1838 {
1839 CLOSE(Master);
1840 Master = -1;
1841 PollDescriptors[3].fd = -1;
1842 }
1843 }
1844 else
1845 {
1846 BytesWritten = write(StdIn, Buffer.data(), BytesRead);
1847 if (BytesWritten < 0)
1848 {
1849 //
1850 // If writing on stdin's pipe would block, mark the write as pending and continue.
1851 // This is required blocking on the write() could lead to a deadlock if the child process
1852 // is blocking trying to write on stderr / stdout while the relay tries to write stdin.
1853 //
1854
1855 if (errno == EWOULDBLOCK || errno == EAGAIN)
1856 {
1857 assert(PendingStdin.empty());
1858 PendingStdin.assign(Buffer.begin(), Buffer.begin() + BytesRead);
1859 }
1860 else
1861 {
1862 LOG_ERROR("write failed {}", errno);
1863 break;
1864 }
1865 }
1866 else if (BytesWritten < BytesRead)
1867 {
1868 assert(PendingStdin.empty());
1869 PendingStdin.assign(Buffer.begin() + BytesWritten, Buffer.begin() + BytesRead);
1870 }
1871 }
1872 }
1873
1874 //
1875 // Relay output from the stdout and stderr pipes.
1876 //
1877
1878 for (Index = 1; Index < 3; Index += 1)
1879 {
1880 if (PollDescriptors[Index].revents & (POLLIN | POLLHUP | POLLERR))
1881 {
1882 BytesRead = UtilReadBuffer(PollDescriptors[Index].fd, Buffer);
1883 if (BytesRead <= 0)
1884 {
1885 if (BytesRead < 0)
1886 {
1887 LOG_ERROR("read failed {} {}", BytesRead, errno);
1888 }
1889
1890 PollDescriptors[Index].fd = -1;
1891 UtilSocketShutdown(Sockets[Index].get(), SHUT_WR);
1892 continue;
1893 }
1894
1895 BytesWritten = UtilWriteBuffer(Sockets[Index].get(), Buffer.data(), BytesRead);
1896 if (BytesWritten < 0)
1897 {
1898 if (errno == EPIPE)
1899 {
1900 CLOSE(PollDescriptors[Index].fd);
1901 PollDescriptors[Index].fd = -1;
1902
1903 if (Index == 1)
1904 {
1905 StdOutPipe.read().reset();
1906 }
1907 else if (Index == 2)
1908 {
1909 StdErrPipe.read().reset();
1910 }
1911 }
1912 else
1913 {
1914 LOG_ERROR("write failed {}, index={}, ChildPid={}, fd={}", errno, Index, ChildPid, Sockets[Index].get());
1915 }
1916 }
1917 }
1918 }
1919
1920 //
1921 // Relay output from the PTY master to the stdout or stderr socket.
1922 //
1923
1924 if (PollDescriptors[3].revents & (POLLIN | POLLHUP | POLLERR))
1925 {
1926 BytesRead = UtilReadBuffer(Master, Buffer);
1927
1928 //
1929 // N.B. The pty will fail with EIO on read on hangup instead of
1930 // indicating EOF.
1931 //
1932
1933 if (BytesRead == 0 || (BytesRead < 0 && errno == EIO))
1934 {
1935 PollDescriptors[3].fd = -1;
1936 if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))
1937 {
1938 UtilSocketShutdown(Sockets[1].get(), SHUT_WR);
1939 }
1940
1941 if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))
1942 {
1943 UtilSocketShutdown(Sockets[2].get(), SHUT_WR);
1944 }
1945 }
1946 else if (BytesRead < 0)
1947 {
1948 LOG_ERROR("read failed {} {}", BytesRead, errno);
1949 break;
1950 }
1951 else
1952 {
1953 if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))
1954 {
1955 BytesWritten = UtilWriteBuffer(Sockets[1].get(), Buffer.data(), BytesRead);
1956 }
1957 else if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))
1958 {
1959 BytesWritten = UtilWriteBuffer(Sockets[2].get(), Buffer.data(), BytesRead);
1960 }
1961 else
1962 {
1963 LOG_ERROR("Unexpected output from PTY master");
1964 }
1965
1966 if (BytesWritten < 0)
1967 {
1968 LOG_ERROR("write failed {}", errno);
1969 break;
1970 }
1971 }
1972 }
1973
1974 //
1975 // Ensure all data has been written.
1976 //
1977
1978 if (BytesWritten > 0)
1979 {
1980 continue;
1981 }
1982
1983 //
1984 // Handle interop requests by relaying create process messages from
1985 // children over the control channel.
1986 //
1987
1988 if (PollDescriptors[4].revents & POLLIN)
1989 {
1990
1991 wsl::shared::SocketChannel channel(InteropServer.Accept(), "InteropRelay");
1992 if (channel.Socket() < 0)
1993 {
1994 continue;
1995 }
1996
1997 auto transaction = channel.ReceiveTransaction();
1998 auto [Header, Span] = transaction.ReceiveOrClosed<MESSAGE_HEADER>();
1999 if (Header != nullptr)
2000 {
2001 try
2002 {
2003 ConfigHandleInteropMessage(
2004 transaction, ControlChannel, WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsElevated), Span, Header, Config);
2005 }
2006 CATCH_LOG();
2007 }
2008 }
2009
2010 //
2011 // Handle signalfd.
2012 //
2013
2014 if (PollDescriptors[5].revents & POLLIN)
2015 {
2016 BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[5].fd, &SignalInfo, sizeof(SignalInfo)));
2017 if (BytesRead != sizeof(SignalInfo))
2018 {
2019 LOG_ERROR("read failed {} {}", BytesRead, errno);
2020 break;
2021 }
2022
2023 if (SignalInfo.ssi_signo != SIGCHLD)
2024 {
2025 LOG_ERROR("Unexpected signal {}", SignalInfo.ssi_signo);
2026 break;
2027 }
2028
2029 //
2030 // Reap any zombie child processes.
2031 //
2032
2033 for (;;)
2034 {
2035 Result = waitpid(-1, &Status, WNOHANG);
2036 if (Result <= 0)
2037 {
2038 break;
2039 }
2040
2041 //
2042 // If the child process exits, write the exit status message
2043 // via the control channel and shut down the stdin / stdout /
2044 // stderr sockets.
2045 //
2046
2047 if (ChildPid == Result)
2048 {
2049 if (WIFEXITED(Status))
2050 {
2051 Status = WEXITSTATUS(Status);
2052 }
2053
2054 try
2055 {
2056
2057 ExitStatus.Header.MessageType = LxInitMessageExitStatus;
2058 ExitStatus.Header.MessageSize = sizeof(ExitStatus);
2059 ExitStatus.ExitCode = Status;
2060 ControlChannel.SendMessage(ExitStatus);
2061
2062 // The result is purposefully ignored here.
2063 ControlChannel.ReceiveMessage<LX_INIT_PROCESS_EXIT_STATUS>();
2064 }
2065 catch (...)
2066 {
2067 Result = -1;
2068 LOG_ERROR("Failed to write exit status {}", errno);
2069 break;
2070 }
2071
2072 ChildPid = -1;
2073 UtilSocketShutdown(Sockets[0].get(), SHUT_RD);
2074 UtilSocketShutdown(Sockets[1].get(), SHUT_WR);
2075 UtilSocketShutdown(Sockets[2].get(), SHUT_WR);
2076 PollDescriptors[6].fd = -1;
2077 }
2078 }
2079
2080 //
2081 // Exit the relay if no more children exist.
2082 //
2083
2084 if (Result < 0)
2085 {
2086 if (errno != ECHILD)
2087 {
2088 LOG_ERROR("waitpid failed {}", errno);
2089 }
2090
2091 break;
2092 }
2093 }
2094
2095 //
2096 // Process messages from wsl.exe / wslhost.exe.
2097 //
2098
2099 if (PollDescriptors[6].revents & POLLIN)
2100 {
2101 auto [Message, _] = TerminalControlChannel.ReceiveMessageOrClosed<LX_INIT_WINDOW_SIZE_CHANGED>();
2102
2103 //
2104 // A zero-byte read means that the control channel has been closed
2105 // and that the relay process should exit.
2106 //
2107
2108 if (Message == nullptr)
2109 {
2110 break;
2111 }
2112
2113 memset(&WindowSize, 0, sizeof(WindowSize));
2114 WindowSize.ws_col = Message->Columns;
2115 WindowSize.ws_row = Message->Rows;
2116 Result = ioctl(Master, TIOCSWINSZ, &WindowSize);
2117 if (Result < 0)
2118 {
2119 LOG_ERROR("ioctl(TIOCSWINSZ) failed {}", errno);
2120 }
2121 }
2122 }
2123
2124 //
2125 // Cleanly shut down the sockets.
2126 //
2127 // N.B. If the socket has already been shut down, this is a no-op.
2128 //
2129
2130 UtilSocketShutdown(Sockets[0].get(), SHUT_RD);
2131 UtilSocketShutdown(Sockets[1].get(), SHUT_WR);
2132 UtilSocketShutdown(Sockets[2].get(), SHUT_WR);
2133 UtilSocketShutdown(Sockets[3].get(), SHUT_RD);
2134 UtilSocketShutdown(Sockets[4].get(), SHUT_WR);
2135 Result = 0;
2136
2137 CreateProcessUtilityVmEnd:
2138 if (ListenSocket != -1)
2139 {
2140 CLOSE(ListenSocket);
2141 }
2142
2143 if (Master != -1)
2144 {
2145 CLOSE(Master);
2146 }
2147
2148 if (SignalFd != -1)
2149 {
2150 CLOSE(SignalFd);
2151 }
2152
2153 if (StdIn != -1)
2154 {
2155 CLOSE(StdIn);
2156 }
2157
2158 if (TtyFd != -1)
2159 {
2160 CLOSE(TtyFd);
2161 }
2162
2163 //
2164 // The interop server needs to be manually reset so it deletes
2165 // its interop socket. See https://github.com/microsoft/WSL/issues/7506.
2166 //
2167
2168 InteropServer.Reset();
2169
2170 //
2171 // The relay process should always exit.
2172 //
2173
2174 if (RelayPid == 0)
2175 {
2176 _exit(Result);
2177 }
2178
2179 return Result;
2180 }
2181
2182 void InitEntry(int Argc, char* Argv[])
2183
2184 /*++
2185
2186 Routine Description:
2187
2188 This routine is the entry point for the init process.
2189
2190 Arguments:
2191
2192 Argc - Supplies command line argument count.
2193
2194 Argv - Supplies command line arguments.
2195
2196 Return Value:
2197
2198 None.
2199
2200 --*/
2201
2202 {
2203 //
2204 // Initialize the startup environment.
2205 //
2206
2207 try
2208 {
2209 wil::ScopedWarningsCollector collector;
2210
2211 auto config = ConfigInitializeCommon(g_SavedSignalActions);
2212
2213 //
2214 // Check if the binary is being run on WSL or in a Utility VM.
2215 //
2216
2217 if (!UtilIsUtilityVm())
2218 {
2219 InitEntryWsl(config);
2220 }
2221 else
2222 {
2223 InitEntryUtilityVm(config);
2224 }
2225 }
2226 catch (...)
2227 {
2228 LOG_CAUGHT_EXCEPTION();
2229 }
2230
2231 FATAL_ERROR("Init not expected to exit");
2232 return;
2233 }
2234
2235 void InitEntryUtilityVm(wsl::linux::WslDistributionConfig& Config)
2236
2237 /*++
2238
2239 Routine Description:
2240
2241 This routine is the entry point for the init process when running inside
2242 a utility VM.
2243
2244 Arguments:
2245
2246 Config - Supplies the distribution configuration.
2247
2248 Return Value:
2249
2250 None.
2251
2252 --*/
2253
2254 {
2255 UtilSetThreadName("init-distro");
2256
2257 //
2258 // Set the close-on-exec flag on the socket file descriptor inherited from mini_init.
2259 //
2260
2261 wsl::shared::SocketChannel channel{wil::unique_fd{LX_INIT_UTILITY_VM_INIT_SOCKET_FD}, "init"};
2262 if (fcntl(channel.Socket(), F_SETFD, FD_CLOEXEC) < 0)
2263 {
2264 FATAL_ERROR("fcntl failed {}", errno);
2265 return;
2266 }
2267
2268 if (getenv(LX_WSL2_DISTRO_READ_ONLY_ENV) != nullptr)
2269 {
2270 EMIT_USER_WARNING(wsl::shared::Localization::MessageReadOnlyDistro());
2271 unsetenv(LX_WSL2_DISTRO_READ_ONLY_ENV);
2272 }
2273
2274 auto Value = getenv(LX_WSL2_NETWORKING_MODE_ENV);
2275 if (Value != nullptr)
2276 {
2277 Config.NetworkingMode = static_cast<LX_MINI_INIT_NETWORKING_MODE>(std::atoi(Value));
2278 unsetenv(LX_WSL2_NETWORKING_MODE_ENV);
2279 }
2280
2281 Value = getenv(LX_WSL2_VM_ID_ENV);
2282 if (Value != nullptr)
2283 {
2284 Config.VmId = Value;
2285
2286 //
2287 // Unset the environment variable for user distros.
2288 //
2289
2290 Value = getenv(LX_WSL2_SHARED_MEMORY_OB_DIRECTORY);
2291 if (!Value)
2292 {
2293 unsetenv(LX_WSL2_VM_ID_ENV);
2294 }
2295 }
2296
2297 //
2298 // If the boot.systemd option is specified in /etc/wsl.conf, launch the distro init process as pid 1.
2299 // WSL init and session leaders continue as children of the distro init process.
2300 //
2301
2302 const auto pid = getenv(LX_WSL_PID_ENV);
2303 assert(pid != nullptr);
2304 unsetenv(LX_WSL_PID_ENV);
2305
2306 //
2307 // Send the create instance result to the service.
2308 //
2309
2310 wsl::shared::MessageWriter<LX_MINI_INIT_CREATE_INSTANCE_RESULT> message;
2311 message->Pid = std::stoul(pid);
2312 message->Result = 0;
2313
2314 auto Warnings = wil::ScopedWarningsCollector::ConsumeWarnings();
2315 if (!Warnings.empty())
2316 {
2317 message.WriteString(message->WarningsOffset, Warnings);
2318 }
2319
2320 channel.SendMessage<LX_MINI_INIT_CREATE_INSTANCE_RESULT>(message.Span());
2321
2322 std::optional<pid_t> distroInitPid;
2323 const auto distroInitPidString = getenv(LX_WSL2_DISTRO_INIT_PID);
2324 if (distroInitPidString != nullptr)
2325 {
2326 distroInitPid = std::stoul(distroInitPidString);
2327 unsetenv(LX_WSL2_DISTRO_INIT_PID);
2328 }
2329
2330 //
2331 // Get the per-distro cgroup path.
2332 //
2333
2334 const auto DistroCgroupPath = getenv(LX_WSL2_DISTRO_CGROUP_PATH);
2335 if (DistroCgroupPath != nullptr)
2336 {
2337 if (access(DistroCgroupPath, F_OK) == 0)
2338 {
2339 Config.CgroupPath = DistroCgroupPath;
2340 }
2341 else
2342 {
2343 LOG_ERROR("Cgroup path {} does not exist", DistroCgroupPath);
2344 }
2345 unsetenv(LX_WSL2_DISTRO_CGROUP_PATH);
2346 }
2347
2348 std::vector<gsl::byte> Buffer;
2349 if (Config.BootInit)
2350 {
2351 int SocketPair[2];
2352 if (socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, SocketPair) < 0)
2353 {
2354 FATAL_ERROR("socketpair failed {}", errno);
2355 }
2356
2357 wil::unique_fd BootStartReadSocket{SocketPair[0]};
2358 Config.BootStartWriteSocket = SocketPair[1];
2359
2360 const int ChildPid = fork();
2361 if (ChildPid < 0)
2362 {
2363 FATAL_ERROR("fork failed {}", errno);
2364 }
2365 else if (ChildPid != 0)
2366 {
2367 UtilSetThreadName("init-systemd");
2368
2369 //
2370 // Wait to boot the distro init process until the first session leader has been created.
2371 // This ensures that the entire boot is not done when a distro is trigger-started by accessing \\wsl.localhost.
2372 //
2373
2374 auto Message = wsl::shared::socket::RecvMessage(BootStartReadSocket.get(), Buffer);
2375 if (Message.empty())
2376 {
2377 FATAL_ERROR("recv failed {}", errno);
2378 }
2379
2380 auto* StartMessage = gslhelpers::get_struct<MESSAGE_HEADER>(Message);
2381 if (StartMessage->MessageType != LxInitMessageStartDistroInit)
2382 {
2383 FATAL_ERROR("unexpected Messagetype {}", StartMessage->MessageType);
2384 }
2385
2386 //
2387 // Initialize distro init arguments and environment.
2388 //
2389
2390 auto InitializeStringVector = [&](std::vector<const char*>& PointerVector,
2391 std::vector<std::string>& StringVector,
2392 const std::optional<std::string>& String) {
2393 if (String.has_value())
2394 {
2395 std::string_view StringView{String.value()};
2396 while (!StringView.empty())
2397 {
2398 StringVector.emplace_back(UtilStringNextToken(StringView, " "));
2399 }
2400
2401 for (const auto& TokenString : StringVector)
2402 {
2403 PointerVector.push_back(TokenString.c_str());
2404 }
2405 }
2406
2407 PointerVector.push_back(nullptr);
2408 };
2409
2410 // The wipe at systemd shutdown clears entries for every distro in
2411 // the VM, not just the terminating one, so install the protection
2412 // regardless of this distro's own InteropEnabled setting.
2413 if (Config.BootProtectBinfmt)
2414 {
2415 LockBinfmtStatusReadOnly();
2416 }
2417
2418 CreateWslSystemdUnits(Config);
2419
2420 if (Config.CgroupPath.has_value())
2421 {
2422 UtilTryMoveSelfToDistroCgroup(Config.CgroupPath.value(), true, "systemd");
2423 }
2424
2425 const char* Argv[] = {INIT_PATH, nullptr};
2426 std::vector<const char*> Env;
2427 std::vector<std::string> Environment;
2428 InitializeStringVector(
2429 Env, Environment, "container=wsl container_host_id=windows container_host_version_id=" WSL_PACKAGE_VERSION);
2430
2431 execvpe(INIT_PATH, const_cast<char**>(Argv), const_cast<char**>(Env.data()));
2432 LOG_ERROR("execvpe({}) failed {}", INIT_PATH, errno);
2433 _exit(1);
2434 }
2435
2436 //
2437 // Fork a watcher process that monitors WSL init and tears down
2438 // the PID namespace if it exits unexpectedly.
2439 //
2440
2441 UtilCreateChildProcess(LX_INIT_WSL_INIT_WATCHER, [&]() {
2442 execl(LX_INIT_PATH, LX_INIT_WSL_INIT_WATCHER, static_cast<char*>(nullptr));
2443 LOG_ERROR("execl({}) failed {}", LX_INIT_WSL_INIT_WATCHER, errno);
2444 });
2445
2446 //
2447 // Keep track of the new pid for WSL init.
2448 //
2449
2450 Config.InitPid = getpid();
2451 }
2452
2453 //
2454 // Loop waiting on the socket for requests from the Windows server.
2455 // A zero-byte read means that the connection to the wsl has been closed and the init daemon should shut down.
2456 //
2457
2458 wil::unique_fd SignalFd;
2459 std::vector<pollfd> PollDescriptors(1);
2460 PollDescriptors[0].fd = channel.Socket();
2461 PollDescriptors[0].events = POLLIN;
2462
2463 //
2464 // If a distro init pid was passed, set up a signalfd to watch it so the distribution can be terminated
2465 // when that process exits.
2466 //
2467
2468 if (distroInitPid.has_value())
2469 {
2470 // Reset sigchld so we get notified when children exit.
2471 signal(SIGCHLD, SIG_DFL);
2472
2473 sigset_t SignalMask;
2474 sigemptyset(&SignalMask);
2475 sigaddset(&SignalMask, SIGCHLD);
2476 if (UtilSaveBlockedSignals(SignalMask) < 0)
2477 {
2478 FATAL_ERROR("sigprocmask failed {}", errno);
2479 }
2480
2481 SignalFd = {signalfd(-1, &SignalMask, SFD_CLOEXEC)};
2482 if (!SignalFd)
2483 {
2484 FATAL_ERROR("signalfd failed {}", errno);
2485 }
2486
2487 // Handle the case where the child already exited before signalfd was set up.
2488 int Status{};
2489 auto WaitResult = waitpid(distroInitPid.value(), &Status, WNOHANG);
2490 if (WaitResult > 0 || (WaitResult < 0 && errno == ECHILD))
2491 {
2492 LOG_ERROR("Init has exited. Terminating distribution");
2493 InitTerminateInstanceInternal(Config);
2494 return;
2495 }
2496
2497 PollDescriptors.resize(2);
2498 PollDescriptors[1].fd = SignalFd.get();
2499 PollDescriptors[1].events = POLLIN;
2500 }
2501
2502 for (;;)
2503 {
2504 auto Result = poll(PollDescriptors.data(), PollDescriptors.size(), -1);
2505 if (Result < 0)
2506 {
2507 FATAL_ERROR("poll failed {}", errno);
2508 }
2509
2510 if (PollDescriptors[0].revents & (POLLHUP | POLLERR))
2511 {
2512 break;
2513 }
2514 else if (PollDescriptors[0].revents & POLLIN)
2515 {
2516 auto transaction = channel.ReceiveTransaction();
2517 auto [Header, Span] = transaction.ReceiveOrClosed<MESSAGE_HEADER>();
2518 if (Header == nullptr)
2519 {
2520 break;
2521 }
2522
2523 switch (Header->MessageType)
2524 {
2525 case LxInitMessageCreateSession:
2526 {
2527 auto SendResponse = [&](LX_INIT_CREATE_SESSION_RESPONSE& response) { transaction.Send(response); };
2528 if (InitCreateSessionLeader(Span, channel, SendResponse, -1, Config) < 0)
2529 {
2530 FATAL_ERROR("InitCreateSessionLeader failed");
2531 }
2532 }
2533 break;
2534
2535 case LxInitMessageInitialize:
2536 {
2537 auto SendResponse = [&](const gsl::span<gsl::byte>& span) {
2538 transaction.Send<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(span);
2539 };
2540 ConfigInitializeInstance(SendResponse, Span, Config);
2541 }
2542 break;
2543
2544 case LxInitMessageTimezoneInformation:
2545 UpdateTimezone(Span, Config);
2546 break;
2547
2548 case LxInitMessageRemountDrvfs:
2549
2550 //
2551 // If systemd is enabled, some units (like snapd) might be in the process
2552 // of creating mountpoints.
2553 // Because these mountpoints should be available in both namespaces, the elevated
2554 // and non-elevated namespaces shouldn't fork until systemd is done initializing.
2555 //
2556
2557 WaitForBootProcess(Config);
2558 ConfigRemountDrvFs(Span, transaction, Config);
2559 break;
2560
2561 case LxInitMessageTerminateInstance:
2562 {
2563 auto SendResult = [&](bool result) { transaction.SendResultMessage<bool>(result); };
2564 InitTerminateInstance(Span, SendResult, Config);
2565 }
2566 break;
2567
2568 case LxInitCreateProcess:
2569 ProcessCreateProcessMessage(transaction, Span, Config.CgroupPath);
2570 break;
2571
2572 default:
2573 FATAL_ERROR("Unexpected message {}", Header->MessageType);
2574 }
2575 }
2576
2577 if (distroInitPid.has_value() && PollDescriptors[1].revents & POLLIN)
2578 {
2579 signalfd_siginfo SignalInfo{};
2580 auto BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[1].fd, &SignalInfo, sizeof(SignalInfo)));
2581 if (BytesRead != sizeof(SignalInfo))
2582 {
2583 FATAL_ERROR("read failed {} {}", BytesRead, errno);
2584 }
2585
2586 if (SignalInfo.ssi_signo != SIGCHLD)
2587 {
2588 LOG_ERROR("Unexpected signal {}", SignalInfo.ssi_signo);
2589 continue;
2590 }
2591
2592 bool distroInitExited = false;
2593 for (;;)
2594 {
2595 int Status{};
2596 auto Pid = waitpid(-1, &Status, WNOHANG);
2597 if (Pid == 0)
2598 {
2599 break;
2600 }
2601 else if (Pid > 0)
2602 {
2603 distroInitExited |= (Pid == distroInitPid.value());
2604 }
2605 else if (errno == ECHILD)
2606 {
2607 break;
2608 }
2609 else
2610 {
2611 FATAL_ERROR("waitpid failed {}", errno);
2612 }
2613 }
2614
2615 if (distroInitExited)
2616 {
2617 LOG_ERROR("Init has exited. Terminating distribution");
2618 break;
2619 }
2620 }
2621 }
2622
2623 InitTerminateInstanceInternal(Config);
2624 return;
2625 }
2626
2627 void InitEntryWsl(wsl::linux::WslDistributionConfig& Config)
2628
2629 /*++
2630
2631 Routine Description:
2632
2633 This routine is the entry point for the init process when running inside
2634 WSL.
2635
2636 Arguments:
2637
2638 Config - Supplies the distribution configuration.
2639
2640 Return Value:
2641
2642 None.
2643
2644 --*/
2645
2646 {
2647 auto Warnings = wil::ScopedWarningsCollector::ConsumeWarnings();
2648 if (!Warnings.empty())
2649 {
2650 LOG_ERROR("{}", Warnings.c_str());
2651 }
2652
2653 //
2654 // Connect to the windows server.
2655 //
2656
2657 wil::unique_fd LxBusFd = TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR | O_CLOEXEC));
2658 if (!LxBusFd)
2659 {
2660 FATAL_ERROR("open({}) failed {}", LXBUS_DEVICE_NAME, errno);
2661 return;
2662 }
2663
2664 wsl::shared::SocketChannel Channel{wil::unique_fd{InitConnectToServer(LxBusFd.get(), true)}, "init"};
2665 if (Channel.Socket() < 0)
2666 {
2667 return;
2668 }
2669
2670 //
2671 // Loop waiting on the message port for requests from the Windows server.
2672 //
2673
2674 std::vector<gsl::byte> Buffer;
2675 ssize_t BytesRead;
2676 for (;;)
2677 {
2678 BytesRead = UtilReadMessageLxBus(Channel.Socket(), Buffer, true);
2679 if (BytesRead < 0)
2680 {
2681 return;
2682 }
2683
2684 auto Message = gsl::make_span(Buffer.data(), BytesRead);
2685 auto* Header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Message);
2686 if (!Header)
2687 {
2688 FATAL_ERROR("Invalid message size {}", Message.size());
2689 }
2690
2691 switch (Header->MessageType)
2692 {
2693 case LxInitMessageCreateSession:
2694 {
2695 auto SendResponse = [&](LX_INIT_CREATE_SESSION_RESPONSE& response) { Channel.SendMessage(response); };
2696 if (InitCreateSessionLeader(Message, Channel, SendResponse, LxBusFd.get(), Config) < 0)
2697 {
2698 //
2699 // If this distro has no children, exit on failure.
2700 //
2701
2702 int status;
2703 if (waitpid(-1, &status, WNOHANG) == -1 && (errno == ECHILD))
2704 {
2705 FATAL_ERROR("InitCreateSessionLeader failed");
2706 }
2707
2708 LOG_ERROR("InitCreateSessionLeader failed");
2709 }
2710 }
2711 break;
2712
2713 case LxInitMessageNetworkInformation:
2714 ConfigUpdateNetworkInformation(Message, Config);
2715 break;
2716
2717 case LxInitMessageInitialize:
2718 {
2719 auto SendResponse = [&](const gsl::span<gsl::byte>& span) {
2720 Channel.SendMessage<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(span);
2721 };
2722 ConfigInitializeInstance(SendResponse, Message, Config);
2723 }
2724 break;
2725
2726 case LxInitMessageTimezoneInformation:
2727 UpdateTimezone(Message, Config);
2728 break;
2729
2730 case LxInitMessageTerminateInstance:
2731 {
2732 auto SendResult = [&](bool result) { Channel.SendResultMessage<bool>(result); };
2733 InitTerminateInstance(Message, SendResult, Config);
2734 }
2735 break;
2736
2737 default:
2738 FATAL_ERROR("Unexpected message {}", Header->MessageType);
2739 }
2740 }
2741
2742 return;
2743 }
2744
2745 void InitTerminateInstance(gsl::span<gsl::byte> Buffer, const std::function<void(bool)>& SendResult, wsl::linux::WslDistributionConfig& Config)
2746
2747 /*++
2748
2749 Routine Description:
2750
2751 This routine processes a terminate instance request from the service.
2752
2753 Arguments:
2754
2755 Buffer - Supplies the message buffer.
2756
2757 SendResult - Supplies a function to send the response.
2758
2759 Config - Supplies the distribution config.
2760
2761 Return Value:
2762
2763 None.
2764
2765 --*/
2766 try
2767 {
2768
2769 auto* Message = gslhelpers::try_get_struct<LX_INIT_TERMINATE_INSTANCE>(Buffer);
2770 if (!Message)
2771 {
2772 FATAL_ERROR("Invalid message size {}", Buffer.size());
2773 }
2774
2775 //
2776 // Attempt to stop the plan9 server, if it is not able to be stopped because of an
2777 // in-use file, reply to the service that the instance could not be terminated.
2778 //
2779
2780 if (!StopPlan9Server(Message->Force, Config))
2781 {
2782 SendResult(false);
2783 return;
2784 }
2785
2786 InitTerminateInstanceInternal(Config);
2787 }
2788 CATCH_LOG();
2789
2790 void InitTerminateInstanceInternal(const wsl::linux::WslDistributionConfig& Config)
2791
2792 /*++
2793
2794 Routine Description:
2795
2796 This routine attempts to cleanly terminate the instance.
2797
2798 Arguments:
2799
2800 Config - Supplies the distribution config.
2801
2802 Return Value:
2803
2804 None.
2805
2806 --*/
2807 try
2808 {
2809 //
2810 // If systemd is enabled, attempt to poweroff the instance via systemctl.
2811 //
2812
2813 if (Config.BootInit && !Config.BootStartWriteSocket)
2814 {
2815 THROW_LAST_ERROR_IF(UtilSetSignalHandlers(g_SavedSignalActions, false) < 0);
2816
2817 //
2818 // systemctl poweroff is normally async but can block in rare cases.
2819 // Run it in a thread so a stuck invocation can't prevent the fallback timeout below.
2820 //
2821 std::thread([]() { UtilExecCommandLine("systemctl poweroff", nullptr); }).detach();
2822
2823 std::this_thread::sleep_for(std::chrono::milliseconds(Config.BootInitTimeout));
2824 LOG_ERROR("systemctl poweroff did not terminate the instance in {} ms, calling reboot(RB_POWER_OFF)", Config.BootInitTimeout);
2825 }
2826
2827 reboot(RB_POWER_OFF);
2828 FATAL_ERROR("reboot(RB_POWER_OFF) failed {}", errno);
2829 }
2830 CATCH_LOG();
2831
2832 void InstallSystemdUnit(const char* Path, const std::string& Name, const char* Content)
2833 try
2834 {
2835 std::string target = std::format("{}/{}.service", Path, Name);
2836 std::string defaultTarget = std::format("{}/default.target.wants", Path);
2837 THROW_LAST_ERROR_IF(UtilMkdirPath(Path, 0755) < 0);
2838 THROW_LAST_ERROR_IF(WriteToFile(target.c_str(), Content) < 0);
2839 THROW_LAST_ERROR_IF(UtilMkdirPath(defaultTarget.c_str(), 0755) < 0);
2840
2841 std::string symlinkPath = std::format("{}/{}.service", defaultTarget, Name);
2842 THROW_LAST_ERROR_IF(symlink(target.c_str(), symlinkPath.c_str()) < 0);
2843 }
2844 CATCH_LOG();
2845
2846 void CreateWslSystemdUnits(const wsl::linux::WslDistributionConfig& Config)
2847
2848 /*++
2849
2850 Routine Description:
2851
2852 This method creates systemd unit files to protect WSL functionality from being disabled by systemd.
2853
2854 Arguments:
2855
2856 Config - Supplies the distribution configuration.
2857
2858 Return Value:
2859
2860 None.
2861
2862 --*/
2863
2864 try
2865 {
2866 if (Config.NetworkingMode == LxMiniInitNetworkingModeMirrored)
2867 {
2868 HardenMirroredNetworkingSettingsAgainstSystemd();
2869 }
2870
2871 constexpr auto folder = "/run/systemd/system-generators";
2872
2873 THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);
2874 THROW_LAST_ERROR_IF(symlink("/init", std::format("{}/{}", folder, LX_INIT_WSL_GENERATOR).c_str()));
2875
2876 if (Config.GuiAppsEnabled)
2877 {
2878 constexpr auto folder = "/run/systemd/user-generators";
2879
2880 THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);
2881 THROW_LAST_ERROR_IF(symlink("/init", std::format("{}/{}", folder, LX_INIT_WSL_USER_GENERATOR).c_str()));
2882 }
2883 }
2884 CATCH_LOG();
2885
2886 void LockBinfmtStatusReadOnly()
2887
2888 /*++
2889
2890 Routine Description:
2891
2892 Bind-mounts a read-only file over /proc/sys/fs/binfmt_misc/status so that
2893 systemd-shutdown's disable_binfmt() can't wipe the kernel-global binfmt
2894 registry when this distro terminates. Without this, terminating any
2895 systemd-enabled distro would clear WSLInterop in every other running
2896 distro and break Windows interop VM-wide.
2897
2898 Arguments:
2899
2900 None.
2901
2902 Return Value:
2903
2904 None. Failures are logged; this is a best-effort hardening step.
2905
2906 --*/
2907
2908 try
2909 {
2910 constexpr auto* lockFile = "/run/wsl/binfmt-status-lock";
2911 constexpr auto* statusFile = BINFMT_MISC_MOUNT_TARGET "/status";
2912 constexpr std::string_view content{"enabled\n"};
2913
2914 THROW_LAST_ERROR_IF(UtilMkdirPath("/run/wsl", 0755) < 0);
2915
2916 const wil::unique_fd fd{TEMP_FAILURE_RETRY(open(lockFile, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644))};
2917 THROW_LAST_ERROR_IF(!fd);
2918 THROW_LAST_ERROR_IF(write(fd.get(), content.data(), content.size()) != static_cast<ssize_t>(content.size()));
2919
2920 THROW_LAST_ERROR_IF(mount(lockFile, statusFile, nullptr, MS_BIND, nullptr) < 0);
2921
2922 // If the remount fails, tear down the bind-mount so /status either reflects
2923 // the real binfmt_misc control file or is correctly read-only. A writable
2924 // shadow would silently swallow writes that callers expect to reach the
2925 // kernel (e.g. "echo -1 > /status").
2926 auto unmountOnFailure = wil::scope_exit([&]() { umount2(statusFile, MNT_DETACH); });
2927 THROW_LAST_ERROR_IF(mount(nullptr, statusFile, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr) < 0);
2928 unmountOnFailure.release();
2929 }
2930 CATCH_LOG();
2931
2932 void HardenMirroredNetworkingSettingsAgainstSystemd()
2933
2934 /*++
2935
2936 Routine Description:
2937
2938 This routine writes configuration required for the mirrored networking mode loopback datapath to
2939 a .conf file applied by systemd. Some distros come with default .conf files only applied when
2940 systemd is enabled, and some of these contain network configurations that conflict with mirrored
2941 networking mode. By writing to a .conf file that has higher precedence, we can prevent these
2942 conflicting settings from being applied.
2943
2944 Arguments:
2945
2946 None.
2947
2948 Return Value:
2949
2950 None.
2951
2952 --*/
2953
2954 try
2955 {
2956 const char* NetworkingConfigFileDirectory = "/run/sysctl.d";
2957 const char* NetworkingConfigFileName = "wsl-networking.conf";
2958 const std::string NetworkingConfigFilePath = std::format("{}/{}", NetworkingConfigFileDirectory, NetworkingConfigFileName);
2959 constexpr auto NetworkingConfig =
2960 "# Note: This file is generated by WSL to prevent default .conf files applied by systemd from overwriting critical "
2961 "networking settings\n"
2962 "net.ipv4.conf.all.rp_filter=0\n"
2963 "net.ipv4.conf." LX_INIT_LOOPBACK_DEVICE_NAME ".rp_filter=0\n";
2964
2965 THROW_LAST_ERROR_IF(UtilMkdirPath(NetworkingConfigFileDirectory, 0755) < 0);
2966 THROW_LAST_ERROR_IF(WriteToFile(NetworkingConfigFilePath.c_str(), NetworkingConfig) < 0);
2967 }
2968 CATCH_LOG();
2969
2970 void SessionLeaderCreateProcess(gsl::span<gsl::byte> Buffer, int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config)
2971
2972 /*++
2973
2974 Routine Description:
2975
2976 This routine creates a process from a session leader.
2977
2978 Arguments:
2979
2980 Buffer - Supplies the message buffer.
2981
2982 MessageFd - Supplies a message port file descriptor.
2983
2984 TtyFd - Supplies a Tty file descriptor.
2985
2986 Config - Supplies the distribution configuration.
2987
2988 Return Value:
2989
2990 None.
2991
2992 --*/
2993
2994 {
2995 //
2996 // Parse the create process message buffer and create the new child process.
2997 //
2998
2999 CREATE_PROCESS_PARSED Parsed = CreateProcessParse(Buffer, MessageFd, Config);
3000 auto CreateProcessPid = fork();
3001 THROW_LAST_ERROR_IF(CreateProcessPid < 0);
3002
3003 if (CreateProcessPid > 0)
3004 {
3005 //
3006 // Remember the current process group.
3007 //
3008
3009 if (g_SessionGroup == -1)
3010 {
3011 g_SessionGroup = CreateProcessPid;
3012 }
3013
3014 //
3015 // Reply with pid of the child process.
3016 //
3017
3018 THROW_LAST_ERROR_IF(CreateProcessReplyToServer(&Parsed, CreateProcessPid, MessageFd) < 0);
3019
3020 return;
3021 }
3022
3023 //
3024 // Child...
3025 //
3026 // The child process should be part of a separate foreground process group.
3027 // If a separate foreground process group does not exist, create one here.
3028 //
3029
3030 int Result = 0;
3031 if (g_SessionGroup != -1)
3032 {
3033 //
3034 // Attempt to join an existing foreground process group.
3035 //
3036
3037 Result = setpgid(0, g_SessionGroup);
3038 }
3039
3040 if ((g_SessionGroup == -1) || (Result < 0))
3041 {
3042 //
3043 // Create a new process group.
3044 //
3045
3046 THROW_LAST_ERROR_IF(setpgid(0, 0) < 0);
3047 }
3048
3049 //
3050 // Always bring the process group to the foreground. This will give
3051 // the newly launched process access to the terminal. In cases where
3052 // multiple processes are being launched in the same session due to
3053 // piping commands together (e.g. bash.exe -c ls | bash.exe -c less)
3054 // or calling bash.exe from within a running WSL instance (inception),
3055 // there may be issues when this process terminates, as restoring the
3056 // foreground group does not happen by default. If the launcher is a
3057 // shell program like /bin/bash, then it typically assumes that the
3058 // foreground needs to be restored and all will work well.
3059 //
3060
3061 //
3062 // N.B. SIGTTOU along with most other signals are blocked. Otherwise,
3063 // this could generate a signal with the default behavior of
3064 // stopping the process (waiting for SIGCONT to continue).
3065 //
3066
3067 if (tcsetpgrp(TtyFd, getpgid(0)) < 0)
3068 {
3069 LOG_ERROR("tcsetpgrp failed {}", errno);
3070 }
3071
3072 //
3073 // Exec the new process.
3074 //
3075
3076 //
3077 // Resources are not released for the child process because it will call execv.
3078 //
3079 // N.B. CreateProcess does not return.
3080 //
3081
3082 CreateProcess(&Parsed, TtyFd, Config);
3083 FATAL_ERROR("CreateProcess not expected to return");
3084 }
3085
3086 void SessionLeaderSigchldHandler(__attribute__((unused)) int Signal, __attribute__((unused)) siginfo_t* SigInfo, __attribute__((unused)) void* UContext)
3087
3088 /*++
3089
3090 Routine Description:
3091
3092 This routine determines if the process group assigned to processes launched
3093 by the session leader has terminated.
3094
3095 Arguments:
3096
3097 Signal - Supplies the signal that was received.
3098
3099 SigInfo - Supplies additional information about the signal.
3100
3101 UContext - Supplies the scheduling context from the process before the
3102 signal handler was invoked.
3103
3104 Return Value:
3105
3106 None.
3107
3108 --*/
3109
3110 {
3111 pid_t child;
3112 int status;
3113
3114 while ((child = waitpid(-1, &status, WNOHANG)) > 0)
3115 {
3116 if (child == g_SessionGroup)
3117 {
3118 g_SessionGroup = -1;
3119 }
3120 }
3121
3122 return;
3123 }
3124
3125 void SessionLeaderEntryUtilityVm(wsl::shared::SocketChannel& channel, const wsl::linux::WslDistributionConfig& Config)
3126
3127 /*++
3128
3129 Routine Description:
3130
3131 This routine is the entry point for the session leader process.
3132
3133 Arguments:
3134
3135 channel - Supplies a message channel
3136
3137 Return Value:
3138
3139 None.
3140
3141 --*/
3142
3143 {
3144 std::vector<gsl::byte> Buffer;
3145 struct sigaction SignalAction;
3146
3147 //
3148 // Create a new session.
3149 //
3150
3151 if (setsid() < 0)
3152 {
3153 FATAL_ERROR("setsid failed {}", errno);
3154 }
3155
3156 //
3157 // Set up a signal handler to reap child processes and track session
3158 // leader.
3159 //
3160
3161 memset(&SignalAction, 0, sizeof(SignalAction));
3162 SignalAction.sa_flags = SA_SIGINFO;
3163 SignalAction.sa_sigaction = SessionLeaderSigchldHandler;
3164 if (sigaction(SIGCHLD, &SignalAction, NULL) < 0)
3165 {
3166 FATAL_ERROR("sigaction SIGCHLD failed {}", errno);
3167 }
3168
3169 //
3170 // Loop waiting on the socket for requests from the Windows server. A zero-byte read means that there is no
3171 // longer any active console applications for this session and that the session leader should exit.
3172 //
3173
3174 for (;;)
3175 {
3176 auto transaction = channel.ReceiveTransaction();
3177 auto [Message, Span] = transaction.ReceiveOrClosed<LX_INIT_CREATE_PROCESS_UTILITY_VM>();
3178 if (Message == nullptr)
3179 {
3180 _exit(0);
3181 }
3182
3183 switch (Message->Header.MessageType)
3184 {
3185 case LxInitMessageCreateProcessUtilityVm:
3186 if (InitCreateProcessUtilityVm(Span, *Message, transaction, Config) < 0)
3187 {
3188 FATAL_ERROR("InitCreateProcessUtilityVm failed");
3189 }
3190
3191 break;
3192
3193 default:
3194 FATAL_ERROR("Unexpected message {}", Message->Header.MessageType);
3195 }
3196 }
3197
3198 FATAL_ERROR("Session leader not expected to exit");
3199 return;
3200 }
3201
3202 void SessionLeaderEntry(int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config)
3203
3204 /*++
3205
3206 Routine Description:
3207
3208 This routine is the entry point for the session leader process.
3209
3210 Arguments:
3211
3212 MessageFd - Supplies a message port file descriptor.
3213
3214 TtyFd - Supplies a Tty file descriptor.
3215
3216 Config - Supplies the distribution configuration.
3217
3218 Return Value:
3219
3220 None.
3221
3222 --*/
3223
3224 {
3225 std::vector<gsl::byte> Buffer;
3226 ssize_t BytesRead;
3227 struct sigaction SignalAction;
3228
3229 //
3230 // Create a new session and set the controlling session on the Tty device.
3231 //
3232
3233 if (setsid() < 0)
3234 {
3235 FATAL_ERROR("setsid failed {}", errno);
3236 }
3237
3238 if (TEMP_FAILURE_RETRY(ioctl(TtyFd, TIOCSCTTY, NULL)) < 0)
3239 {
3240 FATAL_ERROR("ioctl failed for TIOCSCTTY {}", errno);
3241 }
3242
3243 //
3244 // Set up a signal handler to reap child processes and track session
3245 // leader.
3246 //
3247
3248 memset(&SignalAction, 0, sizeof(SignalAction));
3249 SignalAction.sa_flags = SA_SIGINFO;
3250 SignalAction.sa_sigaction = SessionLeaderSigchldHandler;
3251 if (sigaction(SIGCHLD, &SignalAction, NULL) < 0)
3252 {
3253 FATAL_ERROR("sigaction SIGCHLD failed {}", errno);
3254 }
3255
3256 //
3257 // Loop waiting on the message port for requests from the Windows server.
3258 //
3259
3260 for (;;)
3261 {
3262 BytesRead = UtilReadMessageLxBus(MessageFd, Buffer, false);
3263 if (BytesRead < 0)
3264 {
3265 FATAL_ERROR("read failed {}", errno);
3266 }
3267
3268 auto Message = gsl::make_span(Buffer.data(), BytesRead);
3269 auto* Header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Message);
3270 if (!Header)
3271 {
3272 FATAL_ERROR("Invalid message size {}", Message.size());
3273 }
3274
3275 if (Header->MessageType == LxInitMessageCreateProcess)
3276 {
3277 SessionLeaderCreateProcess(Message, MessageFd, TtyFd, Config);
3278 }
3279 else
3280 {
3281 FATAL_ERROR("Unexpected message {}", Header->MessageType);
3282 }
3283 }
3284
3285 FATAL_ERROR("Session leader not expected to exit");
3286 return;
3287 }
3288
3289 bool StopPlan9Server(bool Force, wsl::linux::WslDistributionConfig& Config)
3290 {
3291 if (Config.Plan9ControlChannel.Socket() < 0)
3292 {
3293 return true;
3294 }
3295
3296 LX_INIT_STOP_PLAN9_SERVER Message{};
3297 Message.Header.MessageType = LxInitMessageStopPlan9Server;
3298 Message.Header.MessageSize = sizeof(Message);
3299 Message.Force = Force;
3300
3301 const auto& Response = Config.Plan9ControlChannel.Transaction(Message);
3302
3303 if (Response.Result)
3304 {
3305 // The plan9 server is terminated, release the socket.
3306 Config.Plan9ControlChannel.Close();
3307 }
3308
3309 return Response.Result;
3310 }
3311
3312 wil::unique_fd UnmarshalConsoleFromServer(int MessageFd, LXBUS_IPC_CONSOLE_ID ConsoleId)
3313
3314 /*++
3315
3316 Routine Description:
3317
3318 This routine unmarshals a console.
3319
3320 Arguments:
3321
3322 MessageFd - Supplies a message port file descriptor.
3323
3324 ConsoleId - Supplies a console ID.
3325
3326 TtyFd - Supplies a buffer to store a Tty file descriptor.
3327
3328 Return Value:
3329
3330 0 on success, -1 on failure.
3331
3332 --*/
3333
3334 {
3335 LXBUS_IPC_MESSAGE_UNMARSHAL_CONSOLE_PARAMETERS UnmarshalConsole{};
3336
3337 //
3338 // N.B. Failures to unmarshall the console are non-fatal.
3339 //
3340
3341 UnmarshalConsole.Input.ConsoleId = ConsoleId;
3342
3343 if (TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_CONSOLE, &UnmarshalConsole)))
3344 {
3345 LOG_ERROR("Failed to unmarshal console {}", errno);
3346 return {};
3347 }
3348
3349 return UnmarshalConsole.Output.FileDescriptor;
3350 }
3351
3352 unsigned int StartPlan9(int Argc, char** Argv)
3353 {
3354 constexpr auto* Usage = "Usage: plan9 " LX_INIT_PLAN9_CONTROL_SOCKET_ARG " fd " LX_INIT_PLAN9_SOCKET_PATH_ARG
3355 " path " LX_INIT_PLAN9_SERVER_FD_ARG " fd " LX_INIT_PLAN9_LOG_FILE_ARG
3356 " log-file " LX_INIT_PLAN9_LOG_LEVEL_ARG " level " LX_INIT_PLAN9_PIPE_FD_ARG " fd [--log-truncate]\n";
3357
3358 bool LogTruncate = false;
3359 int LogLevel = TRACE_LEVEL_INFORMATION;
3360 wil::unique_fd PipeFd;
3361 const char* SocketPath{};
3362 const char* LogFile{};
3363 wil::unique_fd ControlSocket;
3364 wil::unique_fd ServerFd;
3365
3366 ArgumentParser parser(Argc, Argv);
3367 parser.AddArgument(UniqueFd{ControlSocket}, LX_INIT_PLAN9_CONTROL_SOCKET_ARG);
3368 parser.AddArgument(SocketPath, LX_INIT_PLAN9_SOCKET_PATH_ARG);
3369 parser.AddArgument(UniqueFd{ServerFd}, LX_INIT_PLAN9_SERVER_FD_ARG);
3370 parser.AddArgument(LogFile, LX_INIT_PLAN9_LOG_FILE_ARG);
3371 parser.AddArgument(Integer{LogLevel}, LX_INIT_PLAN9_LOG_LEVEL_ARG);
3372 parser.AddArgument(UniqueFd{PipeFd}, LX_INIT_PLAN9_PIPE_FD_ARG);
3373 parser.AddArgument(LogTruncate, LX_INIT_PLAN9_TRUNCATE_LOG_ARG);
3374
3375 try
3376 {
3377 parser.Parse();
3378 }
3379 catch (const wil::ExceptionWithUserMessage& e)
3380 {
3381 std::cerr << e.what() << "\n" << Usage;
3382 return 1;
3383 }
3384
3385 RunPlan9Server(SocketPath, LogFile, LogLevel, LogTruncate, ControlSocket.get(), ServerFd.get(), PipeFd);
3386
3387 return 0;
3388 }
3389
3390 unsigned int StartGns(int Argc, char** Argv)
3391 {
3392 constexpr auto* Usage =
3393 "Usage: gns [" LX_INIT_GNS_SOCKET_ARG " fd] [" LX_INIT_GNS_DNS_SOCKET_ARG " fd] [" LX_INIT_GNS_ADAPTER_ARG
3394 " guid] [" LX_INIT_GNS_MESSAGE_TYPE_ARG " int] [" LX_INIT_GNS_DNS_TUNNELING_IP " ip]\n";
3395
3396 UtilSetThreadName("GNS");
3397
3398 // Initialize error and telemetry logging.
3399 InitializeLogging(false);
3400
3401 // hvsocket file descriptor used for DNS tunneling
3402 std::optional<int> DnsFd;
3403 std::optional<GUID> AdapterId;
3404 std::optional<LX_MESSAGE_TYPE> MessageType;
3405 std::string DnsTunnelingIp;
3406 wil::unique_fd Socket;
3407
3408 ArgumentParser parser(Argc, Argv);
3409 parser.AddArgument(UniqueFd{Socket}, LX_INIT_GNS_SOCKET_ARG);
3410 parser.AddArgument(Integer{DnsFd}, LX_INIT_GNS_DNS_SOCKET_ARG);
3411 parser.AddArgument(AdapterId, LX_INIT_GNS_ADAPTER_ARG);
3412 parser.AddArgument(Integer{MessageType}, LX_INIT_GNS_MESSAGE_TYPE_ARG);
3413 parser.AddArgument(DnsTunnelingIp, LX_INIT_GNS_DNS_TUNNELING_IP);
3414
3415 try
3416 {
3417 parser.Parse();
3418 }
3419 catch (const wil::ExceptionWithUserMessage& e)
3420 {
3421 std::cerr << e.what() << "\n" << Usage;
3422 return 1;
3423 }
3424
3425 wsl::shared::SocketChannel channel{std::move(Socket), "GNS"};
3426
3427 GnsEngine::NotificationRoutine readNotification;
3428 GnsEngine::StatusRoutine returnStatus;
3429
3430 // returns the most recent error when init is created for unit tests (i.e. Fd == -1)
3431 int exitCode = 0;
3432
3433 if (channel.Socket() == -1)
3434 {
3435 readNotification = [&](wsl::shared::Transaction&) -> std::optional<GnsEngine::Message> {
3436 std::string content{std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>()};
3437 if (content.empty())
3438 {
3439 return {};
3440 }
3441 if (MessageType.has_value())
3442 {
3443 return {{MessageType.value(), content, AdapterId}};
3444 }
3445
3446 return {{AdapterId.has_value() ? LxGnsMessageNotification : LxGnsMessageInterfaceConfiguration, content, AdapterId}};
3447 };
3448
3449 returnStatus = [&](int Result, const std::string& Error, wsl::shared::Transaction&) {
3450 GNS_LOG_INFO("Returning LxGnsMessageResult (no output fd) [{} - {}]", Result, Error.c_str());
3451 // exitCode keeps the most recent error in the test path
3452 if (Result != 0)
3453 {
3454 exitCode = Result;
3455 }
3456 return true;
3457 };
3458 }
3459 else
3460 {
3461 readNotification = [&](wsl::shared::Transaction& transaction) -> std::optional<GnsEngine::Message> {
3462 std::vector<gsl::byte> Buffer;
3463 auto [Message, Span] = transaction.ReceiveOrClosed<MESSAGE_HEADER>();
3464 if (Message == nullptr)
3465 {
3466 return {};
3467 }
3468
3469 auto type = Message->MessageType;
3470 GNS_LOG_INFO("Processing LX_MESSAGE_TYPE {}", ToString(type));
3471 switch (type)
3472 {
3473 case LxGnsMessageNoOp:
3474 case LxGnsMessageGlobalNetFilter:
3475 {
3476 return {{type, {}, {}}};
3477 }
3478 case LxGnsMessageInterfaceConfiguration:
3479 {
3480 auto size = Span.size() - offsetof(LX_GNS_INTERFACE_CONFIGURATION, Content) - 1;
3481 assert(size > 0);
3482
3483 std::string Content{reinterpret_cast<PLX_GNS_INTERFACE_CONFIGURATION>(Span.data())->Content, size};
3484
3485 return {{type, Content, {}}};
3486 }
3487
3488 case LxGnsMessageNotification:
3489 {
3490 auto size = Span.size() - offsetof(LX_GNS_NOTIFICATION, Content) - 1;
3491 assert(size > 0);
3492
3493 const auto* NotificationMessage = reinterpret_cast<PLX_GNS_NOTIFICATION>(Span.data());
3494 std::string Content{NotificationMessage->Content, size};
3495 return {{type, Content, {NotificationMessage->AdapterId}}};
3496 }
3497
3498 case LxGnsMessageVmNicCreatedNotification:
3499 case LxGnsMessageCreateDeviceRequest:
3500 case LxGnsMessageModifyGuestDeviceSettingRequest:
3501 case LxGnsMessageLoopbackRoutesRequest:
3502 case LxGnsMessageInitialIpConfigurationNotification:
3503 case LxGnsMessageInterfaceNetFilter:
3504 case LxGnsMessageDeviceSettingRequest:
3505 case LxGnsMessageSetupIpv6:
3506 case LxGnsMessageConnectTestRequest:
3507 {
3508 auto size = Span.size() - offsetof(LX_GNS_JSON_MESSAGE, Content) - 1;
3509 if (size == 0)
3510 {
3511 throw RuntimeErrorWithSourceLocation(
3512 std::format("Failed to find content for LX_MESSAGE_TYPE : {}", static_cast<int>(type)));
3513 }
3514
3515 std::string Content{reinterpret_cast<PLX_GNS_JSON_MESSAGE>(Span.data())->Content, size};
3516 return {{type, Content, {}}};
3517 }
3518
3519 default:
3520 {
3521 throw RuntimeErrorWithSourceLocation(std::format("Unexpected LX_MESSAGE_TYPE : {}", static_cast<int>(type)));
3522 }
3523 }
3524 };
3525
3526 returnStatus = [&](int Result, const std::string& Error, wsl::shared::Transaction& transaction) {
3527 std::vector<gsl::byte> Buffer(sizeof(LX_GNS_RESULT) + Error.size() + 1);
3528
3529 GNS_LOG_INFO("Returning LxGnsMessageResult [{} - {}]", Result, Error.c_str());
3530
3531 wsl::shared::MessageWriter<LX_GNS_RESULT> response(LX_GNS_RESULT::Type);
3532 response->Result = Result;
3533 if (!Error.empty())
3534 {
3535 response.WriteString(Error);
3536 }
3537
3538 return transaction.Send<LX_GNS_RESULT>(response.Span());
3539 };
3540 }
3541
3542 RoutingTable routingTable(RT_TABLE_MAIN);
3543 NetworkManager manager(routingTable);
3544 GnsEngine engine(channel, readNotification, returnStatus, manager, DnsFd, DnsTunnelingIp);
3545
3546 engine.run();
3547
3548 GNS_LOG_INFO("StartGns returning {} (GNS Socket {}, MessageType {})", exitCode, channel.Socket(), MessageType.value_or(LxMiniInitMessageAny));
3549 return exitCode;
3550 }
3551
3552 void WaitForBootProcess(wsl::linux::WslDistributionConfig& Config)
3553 {
3554 if (!Config.BootStartWriteSocket)
3555 {
3556 return;
3557 }
3558
3559 //
3560 // Launch the boot process wait for it to finish booting.
3561 //
3562
3563 MESSAGE_HEADER Message{};
3564 Message.MessageType = LxInitMessageStartDistroInit;
3565 Message.MessageSize = sizeof(Message);
3566 if (UtilWriteBuffer(Config.BootStartWriteSocket.get(), gslhelpers::struct_as_bytes(Message)) < 0)
3567 {
3568 LOG_ERROR("write failed {}", errno);
3569 }
3570
3571 Config.BootStartWriteSocket.reset();
3572 if (Config.BootInitTimeout > 0)
3573 {
3574 try
3575 {
3576 //
3577 // N.B. Init needs to not ignore SIGCHLD so it can wait for the child process.
3578 //
3579
3580 signal(SIGCHLD, SIG_DFL);
3581 auto restoreDisposition = wil::scope_exit([]() { signal(SIGCHLD, SIG_IGN); });
3582 wsl::shared::retry::RetryWithTimeout<void>(
3583 [&]() {
3584 std::string Output;
3585 THROW_LAST_ERROR_IF(
3586 UtilExecCommandLine("systemctl is-system-running | grep -E \"running|degraded\"", &Output, 0, false) < 0);
3587 },
3588 std::chrono::milliseconds{250},
3589 std::chrono::milliseconds{Config.BootInitTimeout});
3590 }
3591 catch (...)
3592 {
3593 LOG_ERROR("{} failed to start within {}ms", INIT_PATH, Config.BootInitTimeout);
3594 }
3595 }
3596 }
3597
3598 int WslInitWatcher(int Argc, char** Argv)
3599 {
3600 // Ignore log initialization failure. Not critical.
3601 InitializeLogging(false);
3602
3603 UtilSetThreadName(LX_INIT_WSL_INIT_WATCHER);
3604
3605 const pid_t wslInitPid = getppid();
3606 const int pidfd = syscall(SYS_pidfd_open, wslInitPid, 0);
3607 if (pidfd < 0)
3608 {
3609 LOG_ERROR("pidfd_open failed {}", errno);
3610 _exit(1);
3611 }
3612
3613 pollfd pfd{pidfd, POLLIN, 0};
3614 int rc;
3615 while ((rc = poll(&pfd, 1, -1)) < 0 && errno == EINTR)
3616 {
3617 }
3618 if (rc <= 0 || (pfd.revents & POLLIN) == 0)
3619 {
3620 LOG_ERROR("poll failed {} {}", rc, errno);
3621 _exit(1);
3622 }
3623
3624 LOG_ERROR("wsl init has exited, shutting down the distro");
3625
3626 // Teardown the current PID namespace. Not shutting down the VM.
3627 reboot(RB_POWER_OFF);
3628 _exit(1);
3629 }