master
cpp 295 lines 8.03 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 Process.cpp
8
9 Abstract:
10
11 This file contains the implementation of the WinRT wrapper for the WSLC SDK Process class.
12
13 --*/
14
15 #include "precomp.h"
16 #include "Process.h"
17 #include "Streams.h"
18 #include "Microsoft.WSL.Containers.Process.g.cpp"
19
20 namespace winrt::Microsoft::WSL::Containers::implementation {
21
22 Process::Process(winrt::Microsoft::WSL::Containers::Container const& container, winrt::Microsoft::WSL::Containers::ProcessSettings const& settings) :
23 Process(settings)
24 {
25 m_container = container;
26 }
27
28 Process::Process(winrt::Microsoft::WSL::Containers::ProcessSettings const& settings) : m_settings(settings)
29 {
30 if (m_settings)
31 {
32 m_outputMode = GetImplementation(m_settings)->OutputMode();
33 ApplyCallbacksToSettings();
34 }
35 }
36
37 Process::Process(winrt::Microsoft::WSL::Containers::ProcessOutputMode outputMode) : m_outputMode(outputMode)
38 {
39 // No ProcessSettings; used for opened-container paths where output mode is set independently.
40 // For Event mode, callers are responsible for registering callbacks via
41 // WslcSetContainerInitProcessIOCallbacks using the OutputCallback/ExitCallback statics.
42 }
43
44 void Process::ApplyCallbacksToSettings()
45 {
46 // Callbacks are only used with OutputMode::Event.
47 // Stream and Discard modes use the exit event path (StartWaitingForExitAsync).
48 if (m_outputMode != ProcessOutputMode::Event)
49 {
50 return;
51 }
52
53 auto settingsPtr = GetStructPointer(m_settings);
54
55 WslcProcessCallbacks callbacks = GetEventCallbacks();
56 winrt::check_hresult(WslcSetProcessSettingsCallbacks(settingsPtr, &callbacks, this));
57 }
58
59 void Process::StartWaitingForExit()
60 {
61 m_waitForExitAction = StartWaitingForExitAsync();
62 }
63
64 winrt::Windows::Foundation::IAsyncAction Process::StartWaitingForExitAsync()
65 {
66 // Event mode uses the exit callback set in ApplyCallbacksToSettings; no need to wait here.
67 if (m_outputMode == ProcessOutputMode::Event)
68 {
69 co_return;
70 }
71
72 wil::unique_handle exitEventHandle;
73 winrt::check_hresult(WslcGetProcessExitEvent(ToHandle(), exitEventHandle.put()));
74
75 // Allow the wait to be cancelled even if suspended for resume_on_signal.
76 auto cancellation = co_await winrt::get_cancellation_token();
77 cancellation.enable_propagation();
78
79 auto weak_this = get_weak();
80 co_await winrt::resume_on_signal(exitEventHandle.get());
81
82 try
83 {
84 if (auto strong_this = weak_this.get())
85 {
86 strong_this->m_exitedEvent(strong_this->ExitCode());
87 }
88 }
89 CATCH_LOG();
90 }
91
92 void Process::AttachHandle(WslcProcess handle)
93 {
94 if (m_process)
95 {
96 throw winrt::hresult_illegal_method_call(L"Process handle has already been attached");
97 }
98
99 m_process.reset(handle);
100 StartWaitingForExit();
101 }
102
103 WslcProcessCallbacks Process::GetEventCallbacks() const noexcept
104 {
105 WslcProcessCallbacks callbacks{};
106 callbacks.onStdOut = OutputCallback;
107 callbacks.onStdErr = OutputCallback;
108 callbacks.onExit = ExitCallback;
109 return callbacks;
110 }
111
112 ProcessOutputMode Process::OutputMode()
113 {
114 return m_outputMode;
115 }
116
117 void Process::Start()
118 {
119 EnsureCanStart();
120
121 wil::unique_cotaskmem_string errorMessage;
122 auto hr = WslcCreateContainerProcess(GetHandle(m_container), GetStructPointer(m_settings), m_process.put(), errorMessage.put());
123 THROW_MSG_IF_FAILED(hr, errorMessage);
124
125 m_container = nullptr;
126 m_settings = nullptr;
127
128 StartWaitingForExit();
129 }
130
131 void Process::EnsureStarted() const
132 {
133 if (!m_process)
134 {
135 throw winrt::hresult_illegal_method_call(L"Process has not been started");
136 }
137 }
138
139 void Process::EnsureNotStarted() const
140 {
141 if (m_process)
142 {
143 throw winrt::hresult_illegal_method_call(L"Process has already been started");
144 }
145 }
146
147 void Process::EnsureCanStart() const
148 {
149 EnsureNotStarted();
150
151 if (!m_container)
152 {
153 throw winrt::hresult_illegal_method_call(L"Start() cannot be called on the init process, it is started by the container");
154 }
155
156 auto cmdLine = GetImplementation(m_settings)->CommandLine();
157 if (!cmdLine || cmdLine.Size() == 0)
158 {
159 throw winrt::hresult_invalid_argument(L"Process requires a non-empty CommandLine to start");
160 }
161 }
162
163 uint32_t Process::Pid()
164 {
165 uint32_t pid;
166 winrt::check_hresult(WslcGetProcessPid(ToHandle(), &pid));
167 return pid;
168 }
169
170 winrt::Microsoft::WSL::Containers::ProcessState Process::State()
171 {
172 WslcProcessState state;
173 winrt::check_hresult(WslcGetProcessState(ToHandle(), &state));
174 return static_cast<winrt::Microsoft::WSL::Containers::ProcessState>(state);
175 }
176
177 int32_t Process::ExitCode()
178 {
179 int32_t exitCode;
180 winrt::check_hresult(WslcGetProcessExitCode(ToHandle(), &exitCode));
181 return exitCode;
182 }
183
184 void Process::Signal(winrt::Microsoft::WSL::Containers::Signal const& signal)
185 {
186 winrt::check_hresult(WslcSignalProcess(ToHandle(), static_cast<WslcSignal>(signal)));
187 }
188
189 winrt::Windows::Storage::Streams::IInputStream Process::GetOutputStream(winrt::Microsoft::WSL::Containers::ProcessOutputHandle const& outputHandle)
190 {
191 if (m_outputMode != ProcessOutputMode::Stream)
192 {
193 throw winrt::hresult_illegal_method_call(L"GetOutputStream requires OutputMode::Stream");
194 }
195
196 wil::unique_handle handle;
197 winrt::check_hresult(WslcGetProcessIOHandle(ToHandle(), static_cast<WslcProcessIOHandle>(outputHandle), handle.put()));
198 return winrt::make<IOHandleInputStream>(std::move(handle));
199 }
200
201 winrt::Windows::Storage::Streams::IOutputStream Process::GetInputStream()
202 {
203 wil::unique_handle handle;
204 winrt::check_hresult(WslcGetProcessIOHandle(ToHandle(), WSLC_PROCESS_IO_HANDLE_STDIN, handle.put()));
205 return winrt::make<IOHandleOutputStream>(std::move(handle));
206 }
207
208 winrt::event_token Process::OutputReceived(winrt::Microsoft::WSL::Containers::ProcessOutputHandler const& handler)
209 {
210 if (m_outputMode != ProcessOutputMode::Event)
211 {
212 throw winrt::hresult_illegal_method_call(L"OutputReceived requires OutputMode::Event");
213 }
214
215 return m_outputReceivedEvent.add(handler);
216 }
217
218 void Process::OutputReceived(winrt::event_token const& token) noexcept
219 {
220 m_outputReceivedEvent.remove(token);
221 }
222
223 winrt::event_token Process::ErrorReceived(winrt::Microsoft::WSL::Containers::ProcessOutputHandler const& handler)
224 {
225 if (m_outputMode != ProcessOutputMode::Event)
226 {
227 throw winrt::hresult_illegal_method_call(L"ErrorReceived requires OutputMode::Event");
228 }
229
230 return m_errorReceivedEvent.add(handler);
231 }
232
233 void Process::ErrorReceived(winrt::event_token const& token) noexcept
234 {
235 m_errorReceivedEvent.remove(token);
236 }
237
238 winrt::event_token Process::Exited(winrt::Microsoft::WSL::Containers::ProcessExitHandler const& handler)
239 {
240 return m_exitedEvent.add(handler);
241 }
242
243 void Process::Exited(winrt::event_token const& token) noexcept
244 {
245 m_exitedEvent.remove(token);
246 }
247
248 void CALLBACK Process::OutputCallback(WslcProcessIOHandle ioHandle, _In_reads_bytes_(dataBytes) const BYTE* data, _In_ uint32_t dataBytes, _In_opt_ PVOID context) noexcept
249 {
250 try
251 {
252 auto process = static_cast<Process*>(context);
253
254 auto& outputEvent = (ioHandle == WSLC_PROCESS_IO_HANDLE_STDOUT) ? process->m_outputReceivedEvent : process->m_errorReceivedEvent;
255 winrt::array_view<const uint8_t> buffer{data, dataBytes};
256 outputEvent(buffer);
257 }
258 CATCH_LOG();
259 }
260
261 void CALLBACK Process::ExitCallback(INT32 exitCode, _In_opt_ PVOID context) noexcept
262 {
263 try
264 {
265 auto process = static_cast<Process*>(context);
266 process->m_exitedEvent(exitCode);
267 }
268 CATCH_LOG();
269 }
270
271 WslcProcess Process::ToHandle()
272 {
273 EnsureStarted();
274 return m_process.get();
275 }
276
277 void Process::Close()
278 {
279 if (m_waitForExitAction)
280 {
281 m_waitForExitAction.Cancel();
282 m_waitForExitAction = nullptr;
283 }
284
285 // Methods called after Close() will fail due to EnsureStarted().
286 m_process.reset();
287 }
288
289 void Process::final_release(std::unique_ptr<Process> self)
290 {
291 // Ensure cleanup when refcount drops to zero even if Close() was not called explicitly.
292 self->Close();
293 }
294
295 } // namespace winrt::Microsoft::WSL::Containers::implementation