master
cpp 761 lines 24 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 PluginManager.cpp
8
9 Abstract:
10
11 This file contains the PluginManager helper class implementation.
12
13 --*/
14
15 #include "precomp.h"
16 #include "install.h"
17 #include "PluginManager.h"
18 #include "WslPluginApi.h"
19 #include "LxssUserSessionFactory.h"
20 #include "WSLCSessionManager.h"
21
22 using wsl::windows::common::Context;
23 using wsl::windows::common::ExecutionContext;
24 using wsl::windows::service::PluginManager;
25
26 constexpr auto c_pluginPath = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Lxss\\Plugins";
27
28 constexpr WSLVersion Version = {wsl::shared::VersionMajor, wsl::shared::VersionMinor, wsl::shared::VersionRevision};
29
30 thread_local std::optional<std::wstring> g_pluginErrorMessage;
31 thread_local bool g_inWslcPluginNotification = false;
32
33 // Plugin-originated calls into the WSLC plugin API never acquire a VM lease: a plugin is a side
34 // effect of the session's own activity, never a reason to bring a VM up. The call is served by
35 // whatever VM is already running -- including one committed to stopping, which is what lets a plugin
36 // do last-minute work from its OnWslcVmStopping handler without deadlocking against the teardown it
37 // is blocking -- and is rejected with WSLC_E_VM_NOT_RUNNING when there is no VM.
38 constexpr BOOL c_pluginAcquireVmLease = FALSE;
39
40 class WslcPluginNotificationContext
41 {
42 public:
43 WslcPluginNotificationContext() : m_previous(std::exchange(g_inWslcPluginNotification, true))
44 {
45 }
46
47 ~WslcPluginNotificationContext()
48 {
49 g_inWslcPluginNotification = m_previous;
50 }
51
52 private:
53 ExecutionContext m_executionContext{Context::Plugin};
54 bool m_previous;
55 };
56
57 extern "C" {
58 HRESULT MountFolder(WSLSessionId Session, LPCWSTR WindowsPath, LPCWSTR LinuxPath, BOOL ReadOnly, LPCWSTR Name)
59 try
60 {
61 const auto session = FindSessionByCookie(Session);
62 RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
63
64 auto result = session->MountRootNamespaceFolder(WindowsPath, LinuxPath, ReadOnly, Name);
65
66 WSL_LOG(
67 "PluginMountFolderCall",
68 TraceLoggingValue(WindowsPath, "WindowsPath"),
69 TraceLoggingValue(LinuxPath, "LinuxPath"),
70 TraceLoggingValue(ReadOnly, "ReadOnly"),
71 TraceLoggingValue(Name, "Name"),
72 TraceLoggingValue(result, "Result"));
73
74 return result;
75 }
76 CATCH_RETURN();
77
78 HRESULT ExecuteBinary(WSLSessionId Session, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket)
79 try
80 {
81
82 const auto session = FindSessionByCookie(Session);
83 RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
84
85 auto result = session->CreateLinuxProcess(nullptr, Path, Arguments, Socket);
86
87 WSL_LOG("PluginExecuteBinaryCall", TraceLoggingValue(Path, "Path"), TraceLoggingValue(result, "Result"));
88 return result;
89 }
90 CATCH_RETURN();
91
92 HRESULT PluginError(LPCWSTR UserMessage)
93 try
94 {
95 const auto* context = ExecutionContext::Current();
96 THROW_HR_IF(E_INVALIDARG, UserMessage == nullptr);
97 THROW_HR_IF_MSG(
98 E_ILLEGAL_METHOD_CALL, context == nullptr || WI_IsFlagClear(context->CurrentContext(), Context::Plugin), "Message: %ls", UserMessage);
99
100 // Logs when a WSL plugin hits an error and what that error message is
101 WSL_LOG_TELEMETRY("PluginError", PDT_ProductAndServicePerformance, TraceLoggingValue(UserMessage, "Message"));
102
103 THROW_HR_IF(E_ILLEGAL_STATE_CHANGE, g_pluginErrorMessage.has_value());
104
105 g_pluginErrorMessage.emplace(UserMessage);
106
107 return S_OK;
108 }
109 CATCH_RETURN();
110
111 HRESULT ExecuteBinaryInDistribution(WSLSessionId Session, const GUID* Distro, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket)
112 try
113 {
114 THROW_HR_IF(E_INVALIDARG, Distro == nullptr);
115
116 const auto session = FindSessionByCookie(Session);
117 RETURN_HR_IF(RPC_E_DISCONNECTED, !session);
118
119 auto result = session->CreateLinuxProcess(Distro, Path, Arguments, Socket);
120
121 WSL_LOG("PluginExecuteBinaryInDistributionCall", TraceLoggingValue(Path, "Path"), TraceLoggingValue(result, "Result"));
122
123 return result;
124 }
125 CATCH_RETURN();
126 }
127
128 namespace {
129
130 // Opaque wrapper around IWSLCProcess, handed out as WSLCProcessHandle to plugins.
131 struct WslcProcessWrapper
132 {
133 wil::com_ptr<IWSLCProcess> Process;
134 };
135
136 wil::com_ptr<IWSLCSession> ResolveWslcSession(WSLCSessionId Session)
137 {
138 auto* mgr = wsl::windows::service::wslc::WSLCSessionManagerImpl::Instance();
139 THROW_HR_IF(RPC_E_DISCONNECTED, mgr == nullptr);
140
141 return mgr->FindSession(static_cast<ULONG>(Session));
142 }
143
144 } // namespace
145
146 extern "C" {
147
148 HRESULT WSLCMountFolder(WSLCSessionId Session, LPCWSTR WindowsPath, LPCSTR Mountpoint, BOOL ReadOnly)
149 try
150 {
151 // TODO: Once plugins are out of proc, add logic to validate that the mountpoint isn't in use by another plugin.
152 RETURN_HR_IF(E_POINTER, WindowsPath == nullptr || Mountpoint == nullptr);
153
154 auto session = ResolveWslcSession(Session);
155 auto result = session->MountWindowsFolder(WindowsPath, Mountpoint, ReadOnly, c_pluginAcquireVmLease);
156
157 WSL_LOG(
158 "WslcPluginMountFolderCall",
159 TraceLoggingValue(Session, "SessionId"),
160 TraceLoggingValue(WindowsPath, "WindowsPath"),
161 TraceLoggingValue(Mountpoint, "Mountpoint"),
162 TraceLoggingValue(ReadOnly, "ReadOnly"),
163 TraceLoggingValue(result, "Result"));
164
165 return result;
166 }
167 CATCH_RETURN();
168
169 HRESULT WSLCUnmountFolder(WSLCSessionId Session, LPCSTR Mountpoint)
170 try
171 {
172 // TODO: Once plugins are out of proc, add logic to validate that the mountpoint is actually owned by the plugin.
173 RETURN_HR_IF(E_POINTER, Mountpoint == nullptr);
174
175 auto session = ResolveWslcSession(Session);
176
177 auto result = session->UnmountWindowsFolder(Mountpoint, c_pluginAcquireVmLease);
178
179 WSL_LOG(
180 "WslcPluginUnmountFolderCall",
181 TraceLoggingValue(Session, "SessionId"),
182 TraceLoggingValue(Mountpoint, "Mountpoint"),
183 TraceLoggingValue(result, "Result"));
184
185 return result;
186 }
187 CATCH_RETURN();
188
189 HRESULT WSLCCreateProcess(WSLCSessionId Session, LPCSTR Executable, LPCSTR* Arguments, LPCSTR* Env, WSLCProcessHandle* Process, int* Errno)
190 try
191 {
192 RETURN_HR_IF(E_POINTER, Executable == nullptr || Process == nullptr);
193
194 *Process = nullptr;
195 if (Errno != nullptr)
196 {
197 *Errno = 0;
198 }
199
200 auto session = ResolveWslcSession(Session);
201
202 // Count NULL-terminated arrays.
203 auto countArray = [](LPCSTR* arr) -> ULONG {
204 if (arr == nullptr)
205 {
206 return 0;
207 }
208 ULONG count = 0;
209 while (arr[count] != nullptr)
210 {
211 ++count;
212 }
213 return count;
214 };
215
216 WSLCProcessOptions options{};
217 options.CommandLine.Values = Arguments;
218 options.CommandLine.Count = countArray(Arguments);
219 options.Environment.Values = Env;
220 options.Environment.Count = countArray(Env);
221 options.Flags = WSLCProcessFlagsStdin;
222
223 wil::com_ptr<IWSLCProcess> process;
224 int errnoValue = 0;
225 auto result = session->CreateRootNamespaceProcess(Executable, &options, 0, 0, c_pluginAcquireVmLease, &process, &errnoValue);
226
227 if (Errno != nullptr)
228 {
229 *Errno = errnoValue;
230 }
231
232 if (FAILED(result))
233 {
234 WSL_LOG(
235 "WslcPluginCreateProcessCall",
236 TraceLoggingValue(Session, "SessionId"),
237 TraceLoggingValue(Executable, "Executable"),
238 TraceLoggingValue(result, "Result"),
239 TraceLoggingValue(errnoValue, "Errno"));
240 return result;
241 }
242
243 auto wrapper = std::make_unique<WslcProcessWrapper>();
244 wrapper->Process = std::move(process);
245 *Process = wrapper.release();
246
247 WSL_LOG(
248 "WslcPluginCreateProcessCall",
249 TraceLoggingValue(Session, "SessionId"),
250 TraceLoggingValue(Executable, "Executable"),
251 TraceLoggingValue(*Process, "Process"),
252 TraceLoggingValue(S_OK, "Result"));
253
254 return S_OK;
255 }
256 CATCH_RETURN();
257
258 HRESULT WSLCProcessGetFd(WSLCProcessHandle Process, WSLCProcessFd Fd, HANDLE* Handle)
259 try
260 {
261 RETURN_HR_IF(E_POINTER, Process == nullptr || Handle == nullptr);
262
263 *Handle = nullptr;
264
265 auto* wrapper = static_cast<WslcProcessWrapper*>(Process);
266
267 WSLCFD wslcFd{};
268 switch (Fd)
269 {
270 case WSLCProcessFdStdin:
271 wslcFd = WSLCFDStdin;
272 break;
273 case WSLCProcessFdStdout:
274 wslcFd = WSLCFDStdout;
275 break;
276 case WSLCProcessFdStderr:
277 wslcFd = WSLCFDStderr;
278 break;
279 default:
280 WSL_LOG(
281 "WslcPluginProcessGetFd", TraceLoggingValue(static_cast<int>(Fd), "Fd"), TraceLoggingValue(E_INVALIDARG, "Result"));
282 return E_INVALIDARG;
283 }
284
285 WSLCHandle handle{};
286 auto result = wrapper->Process->GetStdHandle(wslcFd, &handle);
287
288 WSL_LOG(
289 "WslcPluginProcessGetFd",
290 TraceLoggingValue(static_cast<int>(Fd), "Fd"),
291 TraceLoggingValue(handle.Handle.Socket, "Handle"),
292 TraceLoggingValue(result, "Result"));
293
294 RETURN_IF_FAILED(result);
295 WI_ASSERT(handle.Type == WSLCHandleTypeSocket);
296
297 *Handle = handle.Handle.Socket;
298 return S_OK;
299 }
300 CATCH_RETURN();
301
302 HRESULT WSLCProcessGetExitEvent(WSLCProcessHandle Process, HANDLE* ExitEvent)
303 try
304 {
305 RETURN_HR_IF(E_POINTER, Process == nullptr || ExitEvent == nullptr);
306
307 *ExitEvent = nullptr;
308
309 auto* wrapper = static_cast<WslcProcessWrapper*>(Process);
310 auto result = wrapper->Process->GetExitEvent(ExitEvent);
311
312 WSL_LOG("WslcPluginProcessGetExitEvent", TraceLoggingValue(*ExitEvent, "ExitEvent"), TraceLoggingValue(result, "Result"));
313
314 return result;
315 }
316 CATCH_RETURN();
317
318 HRESULT WSLCProcessGetExitCode(WSLCProcessHandle Process, int* ExitCode)
319 try
320 {
321 RETURN_HR_IF(E_POINTER, Process == nullptr || ExitCode == nullptr);
322
323 *ExitCode = -1;
324 auto* wrapper = static_cast<WslcProcessWrapper*>(Process);
325
326 WSLCProcessState state{};
327 auto result = wrapper->Process->GetState(&state, ExitCode);
328
329 if (SUCCEEDED(result) && state != WslcProcessStateExited && state != WslcProcessStateSignalled)
330 {
331 result = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
332 }
333
334 WSL_LOG(
335 "WslcPluginProcessGetExitCode",
336 TraceLoggingValue(*ExitCode, "ExitCode"),
337 TraceLoggingValue(static_cast<int>(state), "State"),
338 TraceLoggingValue(result, "Result"));
339
340 return result;
341 }
342 CATCH_RETURN();
343
344 void WSLCReleaseProcess(WSLCProcessHandle Process)
345 {
346 if (Process != nullptr)
347 {
348 WSL_LOG("WslcPluginReleaseProcess", TraceLoggingValue(Process, "Process"));
349 delete static_cast<WslcProcessWrapper*>(Process);
350 }
351 }
352
353 } // extern "C"
354
355 static constexpr WSLPluginAPIV1 ApiV1 = {
356 Version,
357 &MountFolder,
358 &ExecuteBinary,
359 &PluginError,
360 &ExecuteBinaryInDistribution,
361 &WSLCMountFolder,
362 &WSLCUnmountFolder,
363 &WSLCCreateProcess,
364 &WSLCProcessGetFd,
365 &WSLCProcessGetExitEvent,
366 &WSLCProcessGetExitCode,
367 &WSLCReleaseProcess};
368
369 void PluginManager::LoadPlugins()
370 {
371 ExecutionContext context(Context::Plugin);
372
373 const auto key = common::registry::CreateKey(HKEY_LOCAL_MACHINE, c_pluginPath, KEY_READ);
374 const auto values = common::registry::EnumValues(key.get());
375
376 std::set<std::wstring, wsl::shared::string::CaseInsensitiveCompare> loaded;
377 for (const auto& e : values)
378 {
379 if (e.second != REG_SZ)
380 {
381 LOG_HR_MSG(E_UNEXPECTED, "Plugin value: '%ls' has incorrect type: %lu, skipping", e.first.c_str(), e.second);
382 continue;
383 }
384
385 auto path = common::registry::ReadString(key.get(), nullptr, e.first.c_str());
386
387 if (!loaded.insert(path).second)
388 {
389 LOG_HR_MSG(E_UNEXPECTED, "Module '%ls' has already been loaded, skipping plugin '%ls'", path.c_str(), e.first.c_str());
390 continue;
391 }
392
393 auto loadResult = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&]() { LoadPlugin(e.first.c_str(), path.c_str()); });
394
395 // Logs when a WSL plugin is loaded, used for evaluating plugin populations
396 WSL_LOG_TELEMETRY(
397 "PluginLoad",
398 PDT_ProductAndServiceUsage,
399 TraceLoggingValue(e.first.c_str(), "Name"),
400 TraceLoggingValue(path.c_str(), "Path"),
401 TraceLoggingValue(loadResult, "Result"));
402
403 if (FAILED(loadResult))
404 {
405 // If this plugin reported an error, record it to display it to the user
406 m_pluginError.emplace(PluginError{e.first, loadResult});
407 }
408 }
409 }
410
411 void PluginManager::LoadPlugin(LPCWSTR Name, LPCWSTR ModulePath)
412 {
413 // Validate the plugin signature before loading it.
414 // The handle to the module is kept open after validating the signature so the file can't be written to
415 // after the signature check.
416 wil::unique_hfile pluginHandle;
417 if constexpr (wsl::shared::OfficialBuild)
418 {
419 pluginHandle = wsl::windows::common::install::ValidateFileSignature(ModulePath);
420 WI_ASSERT(pluginHandle.is_valid());
421 }
422
423 LoadedPlugin plugin{};
424 plugin.name = Name;
425
426 plugin.module.reset(LoadLibrary(ModulePath));
427 THROW_LAST_ERROR_IF_NULL(plugin.module);
428
429 const WSLPluginAPI_EntryPointV1 entryPoint =
430 reinterpret_cast<WSLPluginAPI_EntryPointV1>(GetProcAddress(plugin.module.get(), GSL_STRINGIFY(WSLPLUGINAPI_ENTRYPOINTV1)));
431
432 THROW_LAST_ERROR_IF_NULL(entryPoint);
433 THROW_IF_FAILED_MSG(entryPoint(&ApiV1, &plugin.hooks), "Error returned by plugin: '%ls'", ModulePath);
434
435 m_plugins.emplace_back(std::move(plugin));
436 }
437
438 void PluginManager::OnVmStarted(const WSLSessionInformation* Session, const WSLVmCreationSettings* Settings)
439 {
440 ExecutionContext context(Context::Plugin);
441
442 for (const auto& e : m_plugins)
443 {
444 if (e.hooks.OnVMStarted != nullptr)
445 {
446 WSL_LOG(
447 "PluginOnVmStartedCall", TraceLoggingValue(e.name.c_str(), "Plugin"), TraceLoggingValue(Session->UserSid, "Sid"));
448
449 SlowOperationWatcher slowOperation{"PluginOnVmStarted"};
450 ThrowIfPluginError(e.hooks.OnVMStarted(Session, Settings), e.name.c_str());
451 }
452 }
453 }
454
455 void PluginManager::OnVmStopping(const WSLSessionInformation* Session) const
456 {
457 ExecutionContext context(Context::Plugin);
458
459 for (const auto& e : m_plugins)
460 {
461 if (e.hooks.OnVMStopping != nullptr)
462 {
463 WSL_LOG(
464 "PluginOnVmStoppingCall", TraceLoggingValue(e.name.c_str(), "Plugin"), TraceLoggingValue(Session->UserSid, "Sid"));
465
466 const auto result = e.hooks.OnVMStopping(Session);
467 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
468 }
469 }
470 }
471
472 void PluginManager::OnDistributionStarted(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution)
473 {
474 ExecutionContext context(Context::Plugin);
475
476 for (const auto& e : m_plugins)
477 {
478 if (e.hooks.OnDistributionStarted != nullptr)
479 {
480 WSL_LOG(
481 "PluginOnDistroStartedCall",
482 TraceLoggingValue(e.name.c_str(), "Plugin"),
483 TraceLoggingValue(Session->UserSid, "Sid"),
484 TraceLoggingValue(Distribution->Id, "DistributionId"));
485
486 SlowOperationWatcher slowOperation{"PluginOnDistributionStarted"};
487 ThrowIfPluginError(e.hooks.OnDistributionStarted(Session, Distribution), e.name.c_str());
488 }
489 }
490 }
491
492 void PluginManager::OnDistributionStopping(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution) const
493 {
494 ExecutionContext context(Context::Plugin);
495
496 for (const auto& e : m_plugins)
497 {
498 if (e.hooks.OnDistributionStopping != nullptr)
499 {
500 WSL_LOG(
501 "PluginOnDistroStoppingCall",
502 TraceLoggingValue(e.name.c_str(), "Plugin"),
503 TraceLoggingValue(Session->UserSid, "Sid"),
504 TraceLoggingValue(Distribution->Id, "DistributionId"));
505
506 const auto result = e.hooks.OnDistributionStopping(Session, Distribution);
507 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
508 }
509 }
510 }
511
512 void PluginManager::OnDistributionRegistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution) const
513 {
514 ExecutionContext context(Context::Plugin);
515
516 for (const auto& e : m_plugins)
517 {
518 if (e.hooks.OnDistributionRegistered != nullptr)
519 {
520 WSL_LOG(
521 "PluginOnDistributionRegisteredCall",
522 TraceLoggingValue(e.name.c_str(), "Plugin"),
523 TraceLoggingValue(Session->UserSid, "Sid"),
524 TraceLoggingValue(Distribution->Id, "DistributionId"));
525
526 const auto result = e.hooks.OnDistributionRegistered(Session, Distribution);
527 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
528 }
529 }
530 }
531
532 void PluginManager::OnDistributionUnregistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution) const
533 {
534 ExecutionContext context(Context::Plugin);
535
536 for (const auto& e : m_plugins)
537 {
538 if (e.hooks.OnDistributionUnregistered != nullptr)
539 {
540 WSL_LOG(
541 "PluginOnDistributionUnregisteredCall",
542 TraceLoggingValue(e.name.c_str(), "Plugin"),
543 TraceLoggingValue(Session->UserSid, "Sid"),
544 TraceLoggingValue(Distribution->Id, "DistributionId"));
545
546 const auto result = e.hooks.OnDistributionUnregistered(Session, Distribution);
547 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
548 }
549 }
550 }
551
552 void PluginManager::ThrowIfPluginError(HRESULT Result, LPCWSTR Plugin)
553 {
554 const auto message = std::move(g_pluginErrorMessage);
555 g_pluginErrorMessage.reset(); // std::move() doesn't clear the previous std::optional
556
557 if (FAILED(Result))
558 {
559 if (message.has_value())
560 {
561 THROW_HR_WITH_USER_ERROR(Result, wsl::shared::Localization::MessageFatalPluginErrorWithMessage(Plugin, message->c_str()));
562 }
563 else
564 {
565 THROW_HR_WITH_USER_ERROR(Result, wsl::shared::Localization::MessageFatalPluginError(Plugin));
566 }
567 }
568 else if (message.has_value())
569 {
570 THROW_HR_MSG(E_ILLEGAL_STATE_CHANGE, "Plugin '%ls' emitted an error message but returned success", Plugin);
571 }
572 }
573
574 void PluginManager::ThrowIfFatalPluginError() const
575 {
576 ExecutionContext context(Context::Plugin);
577
578 if (!m_pluginError.has_value())
579 {
580 return;
581 }
582 else if (m_pluginError->error == WSL_E_PLUGIN_REQUIRES_UPDATE)
583 {
584 THROW_HR_WITH_USER_ERROR(
585 WSL_E_PLUGIN_REQUIRES_UPDATE, wsl::shared::Localization::MessagePluginRequiresUpdate(m_pluginError->plugin));
586 }
587 else
588 {
589 THROW_HR_WITH_USER_ERROR(m_pluginError->error, wsl::shared::Localization::MessageFatalPluginError(m_pluginError->plugin));
590 }
591 }
592
593 bool PluginManager::IsInWslcNotification() noexcept
594 {
595 return g_inWslcPluginNotification;
596 }
597
598 void PluginManager::OnWslcSessionCreated(const WSLCSessionInformation* Session)
599 {
600 WslcPluginNotificationContext context;
601
602 for (const auto& e : m_plugins)
603 {
604 if (e.hooks.OnSessionCreated != nullptr)
605 {
606 auto result = e.hooks.OnSessionCreated(Session);
607 WSL_LOG(
608 "PluginOnWslcSessionCreatedCall",
609 TraceLoggingValue(e.name.c_str(), "Plugin"),
610 TraceLoggingValue(Session->SessionId, "SessionId"),
611 TraceLoggingValue(Session->DisplayName, "DisplayName"),
612 TraceLoggingValue(result, "Result"));
613
614 ThrowIfPluginError(result, e.name.c_str());
615 }
616 }
617 }
618
619 void PluginManager::OnWslcSessionStopping(const WSLCSessionInformation* Session) const
620 {
621 WslcPluginNotificationContext context;
622
623 for (const auto& e : m_plugins)
624 {
625 if (e.hooks.OnSessionStopping != nullptr)
626 {
627 const auto result = e.hooks.OnSessionStopping(Session);
628 WSL_LOG(
629 "PluginOnWslcSessionStoppingCall",
630 TraceLoggingValue(e.name.c_str(), "Plugin"),
631 TraceLoggingValue(Session->SessionId, "SessionId"),
632 TraceLoggingValue(result, "Result"));
633
634 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
635 }
636 }
637 }
638
639 HRESULT PluginManager::OnWslcContainerStarted(const WSLCSessionInformation* Session, LPCSTR InspectJson) const
640 try
641 {
642 WslcPluginNotificationContext context;
643
644 for (const auto& e : m_plugins)
645 {
646 if (e.hooks.ContainerStarted != nullptr)
647 {
648 // Failure here aborts the container creation. Surface the first error.
649 const auto result = e.hooks.ContainerStarted(Session, InspectJson);
650 WSL_LOG(
651 "PluginOnWslcContainerStartedCall",
652 TraceLoggingValue(e.name.c_str(), "Plugin"),
653 TraceLoggingValue(Session->SessionId, "SessionId"),
654 TraceLoggingValue(result, "Result"));
655
656 ThrowIfPluginError(result, e.name.c_str());
657 }
658 }
659 return S_OK;
660 }
661 CATCH_RETURN()
662
663 void PluginManager::OnWslcContainerStopping(const WSLCSessionInformation* Session, LPCSTR ContainerId) const
664 {
665 WslcPluginNotificationContext context;
666
667 for (const auto& e : m_plugins)
668 {
669 if (e.hooks.ContainerStopping != nullptr)
670 {
671
672 const auto result = e.hooks.ContainerStopping(Session, ContainerId);
673 WSL_LOG(
674 "PluginOnWslcContainerStoppingCall",
675 TraceLoggingValue(e.name.c_str(), "Plugin"),
676 TraceLoggingValue(Session->SessionId, "SessionId"),
677 TraceLoggingValue(ContainerId, "ContainerId"),
678 TraceLoggingValue(result, "Result"));
679
680 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
681 }
682 }
683 }
684
685 void PluginManager::OnWslcImageCreated(const WSLCSessionInformation* Session, LPCSTR InspectJson) const
686 {
687 WslcPluginNotificationContext context;
688
689 for (const auto& e : m_plugins)
690 {
691 if (e.hooks.ImageCreated != nullptr)
692 {
693 const auto result = e.hooks.ImageCreated(Session, InspectJson);
694 WSL_LOG(
695 "PluginOnWslcImageCreatedCall",
696 TraceLoggingValue(e.name.c_str(), "Plugin"),
697 TraceLoggingValue(Session->SessionId, "SessionId"),
698 TraceLoggingValue(result, "Result"));
699
700 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
701 }
702 }
703 }
704
705 void PluginManager::OnWslcImageDeleted(const WSLCSessionInformation* Session, LPCSTR ImageId) const
706 {
707 WslcPluginNotificationContext context;
708
709 for (const auto& e : m_plugins)
710 {
711 if (e.hooks.ImageDeleted != nullptr)
712 {
713 const auto result = e.hooks.ImageDeleted(Session, ImageId);
714 WSL_LOG(
715 "PluginOnWslcImageDeletedCall",
716 TraceLoggingValue(e.name.c_str(), "Plugin"),
717 TraceLoggingValue(Session->SessionId, "SessionId"),
718 TraceLoggingValue(ImageId, "ImageId"),
719 TraceLoggingValue(result, "Result"));
720 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
721 }
722 }
723 }
724
725 void PluginManager::OnWslcVmStarted(const WSLCSessionInformation* Session) const
726 {
727 WslcPluginNotificationContext context;
728
729 for (const auto& e : m_plugins)
730 {
731 if (e.hooks.WslcVmStarted != nullptr)
732 {
733 const auto result = e.hooks.WslcVmStarted(Session);
734 WSL_LOG(
735 "PluginOnWslcVmStartedCall",
736 TraceLoggingValue(e.name.c_str(), "Plugin"),
737 TraceLoggingValue(Session->SessionId, "SessionId"),
738 TraceLoggingValue(result, "Result"));
739 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
740 }
741 }
742 }
743
744 void PluginManager::OnWslcVmStopping(const WSLCSessionInformation* Session) const
745 {
746 WslcPluginNotificationContext context;
747
748 for (const auto& e : m_plugins)
749 {
750 if (e.hooks.WslcVmStopping != nullptr)
751 {
752 const auto result = e.hooks.WslcVmStopping(Session);
753 WSL_LOG(
754 "PluginOnWslcVmStoppingCall",
755 TraceLoggingValue(e.name.c_str(), "Plugin"),
756 TraceLoggingValue(Session->SessionId, "SessionId"),
757 TraceLoggingValue(result, "Result"));
758 LOG_IF_FAILED_MSG(result, "Error thrown from plugin: '%ls'", e.name.c_str());
759 }
760 }
761 }