master
cpp 457 lines 13.9 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 Session.cpp
8
9 Abstract:
10
11 This file contains the implementation of the WinRT wrapper for the WSLC SDK Session class.
12
13 --*/
14
15 #include "precomp.h"
16 #include "Session.h"
17 #include "AuthenticateResult.h"
18 #include "ProcessCrashInformation.h"
19 #include "SessionSettings.h"
20 #include "Microsoft.WSL.Containers.Session.g.cpp"
21
22 using namespace winrt::Windows::Foundation;
23 using namespace winrt::Windows::Foundation::Collections;
24
25 namespace winrt::Microsoft::WSL::Containers::implementation {
26
27 namespace {
28
29 HRESULT CALLBACK ImageProgressCallback(const WslcImageProgressMessage* progressMessage, PVOID context) noexcept
30 {
31 try
32 {
33 auto progress = winrt::make<implementation::ImageProgress>(progressMessage);
34 ProgressCallbackHelper<decltype(progress)>::ReportProgress(context, progress);
35 }
36 CATCH_LOG();
37 return S_OK;
38 }
39
40 } // namespace
41
42 Session::Session(winrt::Microsoft::WSL::Containers::SessionSettings const& settings) : m_settings(settings)
43 {
44 if (!m_settings)
45 {
46 throw winrt::hresult_error(E_POINTER, L"Session settings cannot be null");
47 }
48 }
49
50 void Session::Start()
51 {
52 if (m_session)
53 {
54 throw winrt::hresult_illegal_method_call(L"Session has already been started");
55 }
56
57 wil::unique_cotaskmem_string errorMessage;
58 auto hr = WslcCreateSession(GetStructPointer(m_settings), m_session.put(), errorMessage.put());
59 THROW_MSG_IF_FAILED(hr, errorMessage);
60 m_settings = nullptr;
61
62 winrt::check_hresult(WslcGetSessionTerminationEvent(m_session.get(), m_terminationEvent.put()));
63
64 m_terminationWait.reset(CreateThreadpoolWait(&Session::OnTerminated, this, nullptr));
65 THROW_LAST_ERROR_IF_NULL(m_terminationWait);
66 SetThreadpoolWait(m_terminationWait.get(), m_terminationEvent.get(), nullptr);
67
68 hr = WslcRegisterSessionCrashDumpCallback(m_session.get(), &Session::OnCrashDump, this, &m_crashDumpSubscription, errorMessage.put());
69 THROW_MSG_IF_FAILED(hr, errorMessage);
70 }
71
72 void Session::EnsureStarted() const
73 {
74 if (!m_session)
75 {
76 throw winrt::hresult_illegal_method_call(L"Session has not been started");
77 }
78 }
79
80 void Session::Terminate()
81 {
82 winrt::check_hresult(WslcTerminateSession(ToHandle()));
83 }
84
85 winrt::Microsoft::WSL::Containers::Container Session::CreateContainer(winrt::Microsoft::WSL::Containers::ContainerSettings const& containerSettings)
86 {
87 EnsureStarted();
88
89 if (!containerSettings)
90 {
91 throw winrt::hresult_error(E_POINTER, L"Container settings cannot be null");
92 }
93
94 return winrt::make<implementation::Container>(ToHandle(), containerSettings);
95 }
96
97 winrt::Microsoft::WSL::Containers::Container Session::OpenContainer(hstring const& nameOrId, winrt::Microsoft::WSL::Containers::ProcessOutputMode const& initProcessOutputMode)
98 {
99 EnsureStarted();
100
101 if (nameOrId.empty())
102 {
103 throw winrt::hresult_invalid_argument(L"nameOrId cannot be empty");
104 }
105
106 wil::unique_any<WslcContainer, decltype(&WslcReleaseContainer), &WslcReleaseContainer> containerHandle;
107 wil::unique_cotaskmem_string errorMessage;
108 auto hr = WslcOpenContainer(ToHandle(), winrt::to_string(nameOrId).c_str(), containerHandle.put(), errorMessage.put());
109 THROW_MSG_IF_FAILED(hr, errorMessage);
110
111 return winrt::make<implementation::Container>(containerHandle.release(), initProcessOutputMode);
112 }
113
114 void Session::PullImage(winrt::Microsoft::WSL::Containers::PullImageOptions const& options)
115 {
116 if (!options)
117 {
118 throw winrt::hresult_error(E_POINTER, L"Options for pull cannot be null");
119 }
120
121 EnsureStarted();
122
123 auto pullOptions = GetStruct(options);
124
125 wil::unique_cotaskmem_string errorMessage;
126 auto hr = WslcPullSessionImage(ToHandle(), &pullOptions, errorMessage.put());
127 THROW_MSG_IF_FAILED(hr, errorMessage);
128 }
129
130 IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::ImageProgress> Session::PullImageAsync(winrt::Microsoft::WSL::Containers::PullImageOptions options)
131 {
132 if (!options)
133 {
134 throw winrt::hresult_error(E_POINTER, L"Options for pull cannot be null");
135 }
136
137 EnsureStarted();
138
139 auto self = get_strong(); // keep session alive across suspension
140 co_await winrt::resume_background();
141
142 auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::ImageProgress>{co_await winrt::get_progress_token()};
143
144 auto pullOptions = GetStruct(options);
145 pullOptions.progressCallback = ImageProgressCallback;
146 pullOptions.progressCallbackContext = &context;
147
148 wil::unique_cotaskmem_string errorMessage;
149 auto hr = WslcPullSessionImage(ToHandle(), &pullOptions, errorMessage.put());
150 THROW_MSG_IF_FAILED(hr, errorMessage);
151 }
152
153 void Session::ImportImage(hstring const& path, hstring const& imageName)
154 {
155 if (path.empty())
156 {
157 throw winrt::hresult_invalid_argument(L"Path cannot be empty");
158 }
159
160 if (imageName.empty())
161 {
162 throw winrt::hresult_invalid_argument(L"Image name cannot be empty");
163 }
164
165 EnsureStarted();
166
167 auto name = winrt::to_string(imageName);
168
169 WslcImportImageOptions importOptions{};
170
171 wil::unique_cotaskmem_string errorMessage;
172 auto hr = WslcImportSessionImageFromFile(ToHandle(), name.c_str(), path.c_str(), &importOptions, errorMessage.put());
173 THROW_MSG_IF_FAILED(hr, errorMessage);
174 }
175
176 IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::ImageProgress> Session::ImportImageAsync(hstring path, hstring imageName)
177 {
178 if (path.empty())
179 {
180 throw winrt::hresult_invalid_argument(L"Path cannot be empty");
181 }
182
183 if (imageName.empty())
184 {
185 throw winrt::hresult_invalid_argument(L"Image name cannot be empty");
186 }
187
188 EnsureStarted();
189
190 auto self = get_strong(); // keep session alive across suspension
191 co_await winrt::resume_background();
192
193 auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::ImageProgress>{co_await winrt::get_progress_token()};
194
195 auto name = winrt::to_string(imageName);
196
197 WslcImportImageOptions importOptions{};
198 importOptions.progressCallback = ImageProgressCallback;
199 importOptions.progressCallbackContext = &context;
200
201 wil::unique_cotaskmem_string errorMessage;
202 auto hr = WslcImportSessionImageFromFile(ToHandle(), name.c_str(), path.c_str(), &importOptions, errorMessage.put());
203 THROW_MSG_IF_FAILED(hr, errorMessage);
204 }
205
206 void Session::LoadImage(hstring const& path)
207 {
208 if (path.empty())
209 {
210 throw winrt::hresult_invalid_argument(L"Path cannot be empty");
211 }
212
213 EnsureStarted();
214
215 WslcLoadImageOptions loadOptions{};
216
217 wil::unique_cotaskmem_string errorMessage;
218 auto hr = WslcLoadSessionImageFromFile(ToHandle(), path.c_str(), &loadOptions, errorMessage.put());
219 THROW_MSG_IF_FAILED(hr, errorMessage);
220 }
221
222 IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::ImageProgress> Session::LoadImageAsync(hstring path)
223 {
224 if (path.empty())
225 {
226 throw winrt::hresult_invalid_argument(L"Path cannot be empty");
227 }
228
229 EnsureStarted();
230
231 auto self = get_strong(); // keep session alive across suspension
232 co_await winrt::resume_background();
233
234 auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::ImageProgress>{co_await winrt::get_progress_token()};
235
236 WslcLoadImageOptions loadOptions{};
237 loadOptions.progressCallback = ImageProgressCallback;
238 loadOptions.progressCallbackContext = &context;
239
240 wil::unique_cotaskmem_string errorMessage;
241 auto hr = WslcLoadSessionImageFromFile(ToHandle(), path.c_str(), &loadOptions, errorMessage.put());
242 THROW_MSG_IF_FAILED(hr, errorMessage);
243 }
244
245 void Session::PushImage(winrt::Microsoft::WSL::Containers::PushImageOptions const& options)
246 {
247 if (!options)
248 {
249 throw winrt::hresult_error(E_POINTER, L"Options for push cannot be null");
250 }
251
252 EnsureStarted();
253
254 auto pushOptions = GetStruct(options);
255
256 wil::unique_cotaskmem_string errorMessage;
257 auto hr = WslcPushSessionImage(ToHandle(), &pushOptions, errorMessage.put());
258 THROW_MSG_IF_FAILED(hr, errorMessage);
259 }
260
261 IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::ImageProgress> Session::PushImageAsync(winrt::Microsoft::WSL::Containers::PushImageOptions options)
262 {
263 if (!options)
264 {
265 throw winrt::hresult_error(E_POINTER, L"Options for push cannot be null");
266 }
267
268 EnsureStarted();
269
270 auto self = get_strong(); // keep session alive across suspension
271 co_await winrt::resume_background();
272
273 auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::ImageProgress>{co_await winrt::get_progress_token()};
274
275 auto pushOptions = GetStruct(options);
276 pushOptions.progressCallback = ImageProgressCallback;
277 pushOptions.progressCallbackContext = &context;
278
279 wil::unique_cotaskmem_string errorMessage;
280 auto hr = WslcPushSessionImage(ToHandle(), &pushOptions, errorMessage.put());
281 THROW_MSG_IF_FAILED(hr, errorMessage);
282 }
283
284 void Session::DeleteImage(hstring const& nameOrId)
285 {
286 if (nameOrId.empty())
287 {
288 throw winrt::hresult_invalid_argument(L"Image name cannot be empty");
289 }
290
291 EnsureStarted();
292
293 wil::unique_cotaskmem_string errorMessage;
294 auto hr = WslcDeleteSessionImage(ToHandle(), winrt::to_string(nameOrId).c_str(), errorMessage.put());
295 THROW_MSG_IF_FAILED(hr, errorMessage);
296 }
297
298 void Session::TagImage(winrt::Microsoft::WSL::Containers::TagImageOptions const& options)
299 {
300 if (!options)
301 {
302 throw winrt::hresult_error(E_POINTER, L"Tag image options cannot be null");
303 }
304
305 EnsureStarted();
306
307 wil::unique_cotaskmem_string errorMessage;
308 auto hr = WslcTagSessionImage(ToHandle(), GetStructPointer(options), errorMessage.put());
309 THROW_MSG_IF_FAILED(hr, errorMessage);
310 }
311
312 void Session::CreateVhdVolume(winrt::Microsoft::WSL::Containers::VhdOptions const& options)
313 {
314 if (!options)
315 {
316 throw winrt::hresult_error(E_POINTER, L"VHD options cannot be null");
317 }
318
319 EnsureStarted();
320
321 wil::unique_cotaskmem_string errorMessage;
322 auto hr = WslcCreateSessionVhdVolume(ToHandle(), GetStructPointer(options), errorMessage.put());
323 THROW_MSG_IF_FAILED(hr, errorMessage);
324 }
325
326 void Session::DeleteVhdVolume(hstring const& name)
327 {
328 if (name.empty())
329 {
330 throw winrt::hresult_invalid_argument(L"VHD name cannot be empty");
331 }
332
333 EnsureStarted();
334
335 wil::unique_cotaskmem_string errorMessage;
336 auto hr = WslcDeleteSessionVhdVolume(ToHandle(), winrt::to_string(name).c_str(), errorMessage.put());
337 THROW_MSG_IF_FAILED(hr, errorMessage);
338 }
339
340 winrt::Microsoft::WSL::Containers::AuthenticateResult Session::Authenticate(Uri const& serverAddress, hstring const& username, hstring const& password)
341 {
342 if (!serverAddress)
343 {
344 throw winrt::hresult_invalid_argument(L"Server address cannot be null");
345 }
346
347 if (username.empty())
348 {
349 throw winrt::hresult_invalid_argument(L"Username cannot be empty");
350 }
351
352 EnsureStarted();
353
354 wil::unique_cotaskmem_string errorMessage;
355 wil::unique_cotaskmem_ansistring token;
356 WslcIdentityTokenType tokenType{};
357 auto hr = WslcSessionAuthenticate(
358 ToHandle(),
359 winrt::to_string(serverAddress.ToString()).c_str(),
360 winrt::to_string(username).c_str(),
361 winrt::to_string(password).c_str(),
362 token.put(),
363 &tokenType,
364 errorMessage.put());
365 THROW_MSG_IF_FAILED(hr, errorMessage);
366 return winrt::make<implementation::AuthenticateResult>(
367 winrt::to_hstring(token.get()), static_cast<winrt::Microsoft::WSL::Containers::IdentityTokenType>(tokenType));
368 }
369
370 winrt::event_token Session::Terminated(winrt::Microsoft::WSL::Containers::SessionTerminationHandler const& handler)
371 {
372 return m_terminatedEvent.add(handler);
373 }
374
375 void Session::Terminated(winrt::event_token const& token) noexcept
376 {
377 m_terminatedEvent.remove(token);
378 }
379
380 winrt::event_token Session::ProcessCrashed(winrt::Microsoft::WSL::Containers::ProcessCrashHandler const& handler)
381 {
382 return m_crashDumpEvent.add(handler);
383 }
384
385 void Session::ProcessCrashed(winrt::event_token const& token) noexcept
386 {
387 m_crashDumpEvent.remove(token);
388 }
389
390 IVectorView<winrt::Microsoft::WSL::Containers::ImageInfo> Session::GetImages()
391 {
392 EnsureStarted();
393
394 wil::unique_cotaskmem_array_ptr<WslcImageInfo> imagesArray;
395 winrt::check_hresult(WslcListSessionImages(ToHandle(), imagesArray.put(), imagesArray.size_address<uint32_t>()));
396
397 auto images = std::vector<winrt::Microsoft::WSL::Containers::ImageInfo>();
398 images.reserve(imagesArray.size());
399 for (uint32_t i = 0; i < imagesArray.size(); i++)
400 {
401 images.push_back(winrt::make<implementation::ImageInfo>(imagesArray[i]));
402 }
403
404 return winrt::single_threaded_vector(std::move(images)).GetView();
405 }
406
407 WslcSession Session::ToHandle()
408 {
409 EnsureStarted();
410 return m_session.get();
411 }
412
413 void Session::Close()
414 {
415 m_terminationWait.reset();
416 m_terminationEvent.reset();
417 m_crashDumpSubscription.reset();
418
419 // Methods called after Close() will fail due to EnsureStarted().
420 m_settings = nullptr;
421 m_session.reset();
422 }
423
424 void Session::final_release(std::unique_ptr<Session> self)
425 {
426 // Ensure cleanup when refcount drops to zero even if Close() was not called explicitly.
427 self->Close();
428 }
429
430 void CALLBACK Session::OnTerminated(PTP_CALLBACK_INSTANCE /* instance */, PVOID context, PTP_WAIT /* wait */, TP_WAIT_RESULT /* waitResult */) noexcept
431 {
432 try
433 {
434 auto session = static_cast<Session*>(context);
435
436 WslcSessionTerminationReason reason = WSLC_SESSION_TERMINATION_REASON_UNKNOWN;
437 LOG_IF_FAILED(WslcGetSessionTerminationReason(session->m_session.get(), &reason));
438
439 session->m_terminatedEvent(static_cast<SessionTerminationReason>(reason));
440 }
441 CATCH_LOG();
442 }
443
444 void CALLBACK Session::OnCrashDump(const WslcSessionCrashDumpInfo* info, PVOID context) noexcept
445 {
446 try
447 {
448 auto session = static_cast<Session*>(context);
449
450 auto information = winrt::make_self<implementation::ProcessCrashInformation>(info);
451
452 session->m_crashDumpEvent(*information);
453 }
454 CATCH_LOG();
455 }
456
457 } // namespace winrt::Microsoft::WSL::Containers::implementation