master
h 233 lines 7.25 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #pragma once
4
5 #include "wslutil.h"
6
7 namespace wsl::windows::common {
8
9 #define THROW_HR_WITH_USER_ERROR(Result, Message) \
10 do \
11 { \
12 auto _messageWide = std::format(L"{}", Message); \
13 if (wsl::windows::common::ExecutionContext::ShouldCollectErrorMessage()) \
14 { \
15 ::wsl::windows::common::SetErrorMessage(std::wstring(_messageWide)); \
16 } \
17 THROW_HR_MSG(Result, "%ls", _messageWide.c_str()); \
18 } while (false);
19
20 #define THROW_HR_WITH_USER_ERROR_MSG(Result, Message, Format, ...) \
21 do \
22 { \
23 auto _messageWide = std::format(L"{}", Message); \
24 if (wsl::windows::common::ExecutionContext::ShouldCollectErrorMessage()) \
25 { \
26 ::wsl::windows::common::SetErrorMessage(std::wstring(_messageWide)); \
27 } \
28 THROW_HR_MSG(Result, "%ls. " Format, _messageWide.c_str(), ##__VA_ARGS__); \
29 } while (false);
30
31 #define THROW_HR_WITH_USER_ERROR_IF(Result, Message, Condition) \
32 do \
33 { \
34 if (Condition) \
35 { \
36 THROW_HR_WITH_USER_ERROR(Result, Message); \
37 } \
38 } while (false);
39
40 #define EMIT_USER_WARNING(Warning) \
41 do \
42 { \
43 if (::wsl::windows::common::ExecutionContext* context = ::wsl::windows::common::ExecutionContext::Current(); context != nullptr) \
44 { \
45 context->EmitUserWarning(Warning); \
46 } \
47 } while (false);
48
49 /* List of ExecutionContext that can be passed to ExecutionContext().
50 * Note: ExecutionContext makes the assumption that the parent context always has
51 * a lower value than its child context.
52 * (for instance RegisterDistro must be smaller than CreateInstance
53 * because RegisterDistro is always CreateInstance's parent).
54 */
55
56 enum Context : ULONGLONG
57 {
58 Empty = 0x0,
59 Wsl = 0x1,
60 Wslg = 0x2,
61 Bash = 0x4,
62 WslConfig = 0x8,
63 InstallDistro = 0x10,
64 EnumerateDistros = 0x20,
65 Service = 0x40,
66 RegisterDistro = 0x80,
67 CreateInstance = 0x100,
68 AttachDisk = 0x200,
69 DetachDisk = 0x400,
70 CreateVm = 0x800,
71 ParseConfig = 0x1000,
72 ConfigureNetworking = 0x2000,
73 ConfigureGpu = 0x4000,
74 LaunchProcess = 0x8000,
75 ConfigureDistro = 0x10000,
76 CreateLxProcess = 0x20000,
77 UnregisterDistro = 0x40000,
78 ExportDistro = 0x80000,
79 GetDistroConfiguration = 0x100000,
80 GetDistroId = 0x200000,
81 SetDefaultDistro = 0x400000,
82 SetVersion = 0x800000,
83 TerminateDistro = 0x1000000,
84 RegisterLxBus = 0x2000000,
85 MountDisk = 0x4000000,
86 Plugin = 0x8000000,
87 MoveDistro = 0x10000000,
88 GetDefaultDistro = 0x20000000,
89 DebugShell = 0x40000000,
90 HCS = 0x80000000,
91 HNS = 0x100000000,
92 CallMsi = 0x200000000,
93 Install = 0x4000000000,
94 ReadDistroConfig = 0x8000000000,
95 UpdatePackage = 0x10000000000,
96 QueryLatestGitHubRelease = 0x20000000000,
97 VerifyChecksum = 0x40000000000,
98 WslC = 0x80000000000,
99 };
100
101 DEFINE_ENUM_FLAG_OPERATORS(Context)
102
103 struct Error
104 {
105 HRESULT Code = E_UNEXPECTED;
106 ULONGLONG Context = 0;
107 std::optional<std::wstring> Message;
108 std::optional<std::wstring> Source;
109 };
110
111 /*
112 * The ExecutionContext class is a tool to automatically contextualize the errors
113 * so they are returned to the user (and optionally with a specialized error message).
114 *
115 * When an ExecutionContext is declared in a scope, it will override g_currentContext (thread-local)
116 * and keep a pointer to its parent scope (caller), if any.
117 *
118 * When an error is reported via wil (THROW_X, or RETURN_X), wil calls ExecutionContext::CollectError()
119 * which will save a record of this error with its current scope so it can be properly reported to the user.
120 */
121
122 class ExecutionContext
123 {
124 public:
125 ExecutionContext(Context context, FILE* warningsFile = nullptr) noexcept;
126 virtual ~ExecutionContext();
127
128 ExecutionContext(const ExecutionContext&) = delete;
129 ExecutionContext(ExecutionContext&&) = delete;
130
131 ExecutionContext& operator=(const ExecutionContext&) = delete;
132 ExecutionContext& operator=(ExecutionContext&&) = delete;
133
134 virtual void CollectErrorImpl(HRESULT result);
135 virtual bool CanCollectUserErrorMessage();
136 bool CanCollectUserWarnings() const;
137 void EmitUserWarning(const std::wstring& warning, const std::source_location& location = std::source_location::current());
138
139 void CollectErrorImpl(HRESULT result, ULONGLONG context, std::optional<std::wstring>&& message, std::optional<std::wstring>&& source);
140
141 const std::optional<Error>& ReportedError() const noexcept;
142
143 void SetErrorStringImpl(std::wstring&& string, std::wstring&& source);
144
145 ULONGLONG CurrentContext() const noexcept;
146
147 static void CollectError(HRESULT error);
148 static bool ShouldCollectErrorMessage();
149 static ExecutionContext* Current();
150
151 protected:
152 ExecutionContext& RootContext();
153 virtual bool CollectUserWarning(const std::wstring& warning);
154 std::optional<Error> m_error;
155 FILE* m_warningsFile = nullptr;
156
157 private:
158 ExecutionContext* m_parent = nullptr;
159 Context m_context = Context::Empty;
160 std::optional<std::wstring> m_errorString;
161 std::optional<std::wstring> m_errorSource;
162 };
163
164 class ClientExecutionContext : public ExecutionContext
165 {
166 public:
167 ClientExecutionContext(bool enableContextualizedErrors = true);
168 ~ClientExecutionContext() override;
169
170 ClientExecutionContext(const ClientExecutionContext&) = delete;
171 ClientExecutionContext(ClientExecutionContext&&) = delete;
172
173 ClientExecutionContext& operator=(const ClientExecutionContext&) = delete;
174 ClientExecutionContext& operator=(ClientExecutionContext&&) = delete;
175
176 void CollectErrorImpl(HRESULT result) override;
177
178 void FlushWarnings();
179 void EnableInteractiveWarnings();
180
181 LXSS_ERROR_INFO* OutError() noexcept;
182
183 private:
184 LXSS_ERROR_INFO m_outError = {};
185
186 wil::unique_handle m_warningsPipeWrite;
187 std::thread m_interactiveWarningsThread;
188 };
189
190 class ServiceExecutionContext : public ExecutionContext
191 {
192 public:
193 ServiceExecutionContext(LXSS_ERROR_INFO* outError) noexcept;
194 ~ServiceExecutionContext() override;
195
196 ServiceExecutionContext(const ServiceExecutionContext&) = delete;
197 ServiceExecutionContext(ServiceExecutionContext&&) = delete;
198
199 ServiceExecutionContext& operator=(const ServiceExecutionContext&) = delete;
200 ServiceExecutionContext& operator=(ServiceExecutionContext&&) = delete;
201
202 bool CanCollectUserErrorMessage() override;
203
204 protected:
205 virtual bool CollectUserWarning(const std::wstring& warning) override;
206
207 private:
208 std::optional<LXSS_ERROR_INFO*> m_outError;
209 std::optional<std::wstring> m_warningsString;
210 wil::unique_handle m_warningsPipe;
211 };
212
213 class COMServiceExecutionContext : public ExecutionContext
214 {
215
216 public:
217 NON_COPYABLE(COMServiceExecutionContext);
218 NON_MOVABLE(COMServiceExecutionContext);
219
220 COMServiceExecutionContext();
221 ~COMServiceExecutionContext() override;
222
223 bool CanCollectUserErrorMessage() override;
224 };
225
226 void EnableContextualizedErrors(bool service, bool useComErrors = false, bool enableNotifications = false);
227
228 void SetErrorMessage(std::wstring&& message, const std::source_location& source = std::source_location::current());
229 void SetErrorMessage(std::string&& message, const std::source_location& source = std::source_location::current());
230
231 void SetEventLog(HANDLE eventLog);
232
233 } // namespace wsl::windows::common