master
cpp 268 lines 8.26 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 main.cpp
8
9 Abstract:
10
11 This file contains the entrypoint for wslhost.
12
13 --*/
14
15 #include "precomp.h"
16 #include "CommandLine.h"
17 #include <NotificationActivationCallback.h>
18 #include <windows.ui.notifications.h>
19
20 using namespace ABI::Windows::Data::Xml::Dom;
21 using namespace ABI::Windows::UI::Notifications;
22 using namespace Microsoft::WRL;
23 using namespace wsl::windows::common;
24 using namespace wsl::shared;
25
26 namespace {
27
28 // Event used to signal that the COM server should exit.
29 wil::unique_event g_exitEvent;
30
31 void AddComRef()
32 {
33 CoAddRefServerProcess();
34 }
35
36 void ReleaseComRef()
37 {
38 if (CoReleaseServerProcess() == 0)
39 {
40 g_exitEvent.SetEvent();
41 }
42 }
43
44 void ShellExec(_In_ LPCWSTR operation, _In_ LPCWSTR file, _In_ LPCWSTR args)
45 {
46 THROW_LAST_ERROR_IF(reinterpret_cast<intptr_t>(::ShellExecuteW(nullptr, operation, file, args, nullptr, SW_SHOW)) < 32);
47 }
48
49 void LaunchWsl(_In_ LPCWSTR args)
50 {
51 const auto path = wsl::windows::common::wslutil::GetBasePath() / L"wsl.exe";
52 ShellExec(L"runas", path.c_str(), args);
53 }
54
55 } // namespace
56
57 class DECLSPEC_UUID("2B9C59C3-98F1-45C8-B87B-12AE3C7927E8") NotificationActivator
58 : public winrt::implements<NotificationActivator, INotificationActivationCallback>
59 {
60 public:
61 NotificationActivator()
62 {
63 AddComRef();
64 }
65
66 ~NotificationActivator() override
67 {
68 ReleaseComRef();
69 }
70
71 STDMETHODIMP Activate(_In_ LPCWSTR appUserModelId, _In_ LPCWSTR invokedArgs, _In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data, ULONG dataCount) noexcept override
72 try
73 {
74 // Log telemetry when a WSL notification is activated, used to determine user engagement for notifications
75 WSL_LOG_TELEMETRY("NotificationActivate", PDT_ProductAndServicePerformance, TraceLoggingValue(invokedArgs, "Arguments"));
76
77 ArgumentParser parser(invokedArgs, wslhost::binary_name, 0);
78 parser.AddArgument(
79 []() {
80 std::wstring path;
81 THROW_IF_FAILED(wil::GetSystemDirectoryW(path));
82
83 ShellExec(L"runas", (std::filesystem::path(std::move(path)) / L"eventvwr.exe").c_str(), L"/c:Application");
84 },
85 wslhost::event_viewer_arg);
86
87 parser.AddArgument([]() { ShellExec(nullptr, L"https://github.com/microsoft/WSL/releases", nullptr); }, wslhost::release_notes_arg);
88
89 parser.AddArgument([](auto) { LaunchWsl(WSL_UPDATE_ARG); }, wslhost::update_arg);
90
91 parser.AddArgument(
92 [](auto) {
93 LaunchWsl(std::format(L"{} {} {}", WSL_INSTALL_ARG, WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION, WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION)
94 .c_str());
95 },
96 wslhost::install_prerequisites_arg);
97
98 parser.AddArgument(
99 [](LPCWSTR input) {
100 if (wsl::shared::string::IsEqual(input, wslhost::docs_arg_filesystem_url, false))
101 {
102 ShellExec(nullptr, wslhost::docs_arg_filesystem_url, nullptr);
103 }
104 else
105 {
106 THROW_HR_MSG(E_INVALIDARG, "Unexpected docs arg: %ls", input);
107 }
108 },
109 wslhost::docs_arg);
110
111 parser.AddArgument(
112 [](LPCWSTR input) {
113 if (wsl::shared::string::IsEqual(input, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, false))
114 {
115 const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();
116 wsl::windows::common::registry::WriteDword(lxssKey.get(), LXSS_NOTIFICATIONS_KEY, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, 1);
117 }
118 else
119 {
120 THROW_HR_MSG(E_INVALIDARG, "Unexpected notification arg: %ls", input);
121 }
122 },
123 wslhost::disable_notification_arg);
124
125 parser.Parse();
126
127 return S_OK;
128 }
129 CATCH_RETURN()
130 };
131
132 class NotificationActivatorFactory : public winrt::implements<NotificationActivatorFactory, IClassFactory>
133 {
134 public:
135 STDMETHODIMP CreateInstance(_In_ IUnknown* outer, REFIID iid, _COM_Outptr_ void** result) noexcept override
136 try
137 {
138 *result = nullptr;
139 THROW_HR_IF(CLASS_E_NOAGGREGATION, outer != nullptr);
140
141 return winrt::make<NotificationActivator>()->QueryInterface(iid, result);
142 }
143 CATCH_RETURN()
144
145 STDMETHODIMP LockServer(BOOL lock) noexcept override
146 {
147 if (lock)
148 {
149 AddComRef();
150 }
151 else
152 {
153 ReleaseComRef();
154 }
155
156 return S_OK;
157 }
158 };
159
160 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int)
161 try
162 {
163 wsl::windows::common::wslutil::ConfigureCrt();
164 wsl::windows::common::wslutil::InitializeWil();
165
166 // Initialize logging.
167 WslTraceLoggingInitialize(LxssTelemetryProvider, !wsl::shared::OfficialBuild);
168 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] { WslTraceLoggingUninitialize(); });
169
170 // Initialize COM.
171 auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);
172 wsl::windows::common::wslutil::CoInitializeSecurity();
173
174 // Initialize winsock.
175 WSADATA data;
176 THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));
177
178 // Parse arguments.
179 wil::unique_handle event{};
180 GUID distroId{GUID_NULL};
181 wil::unique_handle handle{};
182 wil::unique_handle parent{};
183 GUID vmId{GUID_NULL};
184 wil::unique_com_class_object_cookie cookie;
185
186 wsl::shared::ArgumentParser parser(GetCommandLineW(), wslhost::binary_name);
187 parser.AddArgument(distroId, wslhost::distro_id_option);
188 parser.AddArgument(Handle(handle), wslhost::handle_option);
189 parser.AddArgument(Handle(event), wslhost::event_option);
190 parser.AddArgument(Handle(parent), wslhost::parent_option);
191 parser.AddArgument(vmId, wslhost::vm_id_option);
192 parser.AddArgument(
193 [&](auto) {
194 // Create an event to be signaled when the last COM object is released.
195 g_exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
196
197 THROW_IF_FAILED(::CoRegisterClassObject(
198 __uuidof(NotificationActivator), winrt::make<NotificationActivatorFactory>().get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &cookie));
199
200 return 0;
201 },
202 wslhost::embedding_option);
203
204 parser.Parse();
205
206 if (cookie)
207 {
208 // Wait until all objects have been released.
209 g_exitEvent.wait();
210
211 return 0;
212 }
213
214 WI_ASSERT(GetCurrentPackageId(nullptr, nullptr) != ERROR_SUCCESS);
215
216 // Launch the interop server.
217 //
218 // See GitHub #7568. There needs to be a console for interop.
219 // From GitHub #8161 we learned we can't be attached to the same
220 // console as wsl.exe. If we are we will be terminated and unable
221 // to serve daemonized processes after the console is closed.
222 wsl::windows::common::helpers::CreateConsole(nullptr);
223
224 // Register this process with the instance's lifetime management.
225 auto service = wil::CoCreateInstance<LxssUserSession, ILxssUserSession>(CLSCTX_LOCAL_SERVER);
226 if (!IsEqualGUID(distroId, GUID_NULL))
227 {
228 ClientExecutionContext context(false);
229
230 service->CreateInstance(
231 &distroId, (LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE | LXSS_CREATE_INSTANCE_FLAGS_OPEN_EXISTING), context.OutError());
232 }
233
234 // Signal the registration complete event if one was supplied.
235 if (event)
236 {
237 THROW_IF_WIN32_BOOL_FALSE(SetEvent(event.get()));
238 }
239
240 // If a parent process handle was supplied, wait for the parent
241 // process to exit before starting the worker loop.
242 if (parent)
243 {
244 WaitForSingleObject(parent.get(), INFINITE);
245 }
246
247 // Begin handling interop requests.
248 if (IsEqualGUID(vmId, GUID_NULL))
249 {
250 wsl::windows::common::interop::WorkerThread(std::move(handle));
251 }
252 else
253 {
254 wsl::shared::SocketChannel channel{wil::unique_socket{reinterpret_cast<SOCKET>(handle.release())}, "Interop-wslhost"};
255
256 // This is required because there could have been messages between the process and wsl.exe, and wslhost has no way to know what the sequence numbers were.
257 channel.IgnoreSequenceNumbers();
258
259 wsl::windows::common::interop::VmModeWorkerThread(channel, vmId, true);
260 }
261
262 return 0;
263 }
264 catch (...)
265 {
266 LOG_CAUGHT_EXCEPTION();
267 return 1;
268 }