master
cpp 222 lines 6.89 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 ConsoleManager.cpp
8
9 Abstract:
10
11 This file contains function definitions around console management.
12
13 --*/
14
15 #include "precomp.h"
16 #include "LxssConsoleManager.h"
17
18 using namespace std::placeholders;
19
20 ConsoleManager::ConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port) : m_initPort(Port)
21 {
22 }
23
24 ConsoleManager::~ConsoleManager()
25 {
26 //
27 // N.B. Leave the callback cleanup to the lifetime manager destructor.
28 // Because a shared pointer to the ConsoleManager is passed as a
29 // parameter when adding process callback to lifetime manager, the only
30 // way this destructor could be reached is if the callbacks are no
31 // longer valid.
32 //
33 }
34
35 std::shared_ptr<ConsoleManager> ConsoleManager::CreateConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port)
36 {
37 std::shared_ptr<ConsoleManager> newConsoleManager(new ConsoleManager(Port));
38 return newConsoleManager;
39 }
40
41 std::shared_ptr<LxssPort> ConsoleManager::GetSessionLeader(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_opt_ bool* Created)
42 {
43 auto lock = m_initPort->Lock();
44 ULONG ConsoleId = ULONG_MAX;
45 auto LocalPort = _RegisterProcess(ConsoleData, Elevated, &ConsoleId);
46 auto ConsoleManagerEraser =
47 wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _UnregisterProcess(ConsoleData.ConsoleHandle, Elevated); });
48
49 //
50 // If the session leader doesn't exist yet for a given console, create one.
51 //
52
53 bool createdLocal = false;
54 if (!LocalPort)
55 {
56 LocalPort = m_initPort->CreateSessionLeader(ConsoleData.ClientProcess.get());
57 _SetPort(ConsoleId, Elevated, LocalPort);
58 createdLocal = true;
59 }
60
61 ConsoleManagerEraser.release();
62 if (ARGUMENT_PRESENT(Created))
63 {
64 *Created = createdLocal;
65 }
66
67 return LocalPort;
68 }
69
70 std::shared_ptr<LxssPort> ConsoleManager::_RegisterProcess(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_ ULONG* ConsoleId)
71 {
72 ULONG ConsoleIdLocal;
73 wil::unique_handle ConhostHandle;
74 std::shared_ptr<LxssPort> Port;
75 ULONG64 ClientCallbackId;
76
77 _GetConsoleInfo(ConsoleData.ConsoleHandle, ConsoleIdLocal, ConhostHandle);
78 {
79 std::lock_guard<std::mutex> lock(m_mappingListLock);
80 SessionLeaderKey key{ConsoleIdLocal, Elevated};
81 const auto mapping = m_mappings.find({ConsoleIdLocal, Elevated});
82 if (mapping == m_mappings.end())
83 {
84 SessionLeaderMapping newMapping;
85 newMapping.console = std::move(ConhostHandle);
86 newMapping.clientCallbackId = m_lifetimeManager.GetRegistrationId();
87 THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(
88 GetCurrentProcess(), ConsoleData.ClientProcess.get(), GetCurrentProcess(), &newMapping.firstClient, 0, FALSE, DUPLICATE_SAME_ACCESS));
89
90 newMapping.port = nullptr;
91 ClientCallbackId = newMapping.clientCallbackId;
92 m_mappings.emplace(std::make_pair(key, std::move(newMapping)));
93 }
94 else
95 {
96 Port = mapping->second.port;
97 ClientCallbackId = mapping->second.clientCallbackId;
98 }
99
100 m_lifetimeManager.RegisterCallback(
101 ClientCallbackId, std::bind(s_OnProcessTerminated, this, ConsoleIdLocal, Elevated), ConsoleData.ClientProcess.get());
102 }
103
104 *ConsoleId = ConsoleIdLocal;
105 return Port;
106 }
107
108 void ConsoleManager::_SetPort(_In_ ULONG ConsoleId, _In_ bool Elevated, _In_ std::shared_ptr<LxssPort>& Port)
109 {
110 std::lock_guard<std::mutex> lock(m_mappingListLock);
111 const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});
112 if (mapping != m_mappings.end())
113 {
114 mapping->second.port = Port;
115 }
116 }
117
118 void ConsoleManager::_UnregisterProcess(_In_ const wil::unique_handle& ConsoleHandle, _In_ bool Elevated)
119 {
120 ULONG ConsoleId;
121 wil::unique_handle ConhostHandle;
122 _GetConsoleInfo(ConsoleHandle, ConsoleId, ConhostHandle);
123 std::lock_guard<std::mutex> lock(m_mappingListLock);
124 const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});
125 if (mapping != m_mappings.end())
126 {
127 WI_VERIFY(m_lifetimeManager.RemoveCallback(mapping->second.clientCallbackId));
128 m_mappings.erase(mapping);
129 }
130 }
131
132 ULONG ConsoleManager::s_GetConhostServerId(_In_ HANDLE ConsoleHandle)
133 {
134 IO_STATUS_BLOCK IoStatus;
135 HANDLE ServerPid;
136
137 //
138 // N.B.: The ioctl for getting server pid requires a handle as its buffer,
139 // but it isn't really a handle but a process id.
140 //
141
142 THROW_IF_NTSTATUS_FAILED(NtDeviceIoControlFile(
143 ConsoleHandle, NULL, NULL, NULL, &IoStatus, IOCTL_CONDRV_GET_SERVER_PID, NULL, 0, &ServerPid, sizeof(ServerPid)));
144
145 return HandleToUlong(ServerPid);
146 }
147
148 void ConsoleManager::_OnProcessDisconnect(_In_ ULONG ConsoleId, _In_ bool Elevated)
149 {
150 wil::unique_handle firstClient;
151 std::shared_ptr<LxssPort> port;
152 {
153 std::lock_guard<std::mutex> lock(m_mappingListLock);
154 const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});
155 if (mapping != m_mappings.end())
156 {
157 if (!m_lifetimeManager.IsAnyProcessRegistered(mapping->second.clientCallbackId))
158 {
159 firstClient = std::move(mapping->second.firstClient);
160 port = mapping->second.port;
161 m_mappings.erase(mapping);
162 }
163 }
164 }
165
166 if (firstClient && port)
167 {
168 port->DisconnectConsole(firstClient.get());
169 }
170 }
171
172 void ConsoleManager::_GetConsoleInfo(_In_ const wil::unique_handle& ConsoleHandle, _Out_ ULONG& ConsoleId, _Out_ wil::unique_handle& ConhostHandle)
173 {
174 FILE_FS_DEVICE_INFORMATION FsDeviceInformation;
175 IO_STATUS_BLOCK IoStatus;
176
177 //
178 // If no console handle was provided, use zero as the identifier.
179 //
180
181 if (!ConsoleHandle)
182 {
183 ConsoleId = 0;
184 return;
185 }
186
187 THROW_IF_NTSTATUS_FAILED(NtQueryVolumeInformationFile(
188 ConsoleHandle.get(), &IoStatus, &FsDeviceInformation, sizeof(FsDeviceInformation), FileFsDeviceInformation));
189
190 if (FsDeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)
191 {
192 THROW_HR(E_UNEXPECTED);
193 }
194
195 ConsoleId = s_GetConhostServerId(ConsoleHandle.get());
196
197 //
198 // Open the conhost console process so it doesn't get closed and recycled while the process is
199 // running.
200 //
201
202 ConhostHandle.reset(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, ConsoleId));
203 THROW_LAST_ERROR_IF(!ConhostHandle);
204
205 //
206 // The conhost id needs to be queried again since it could get recycled between
207 // the query and the open.
208 //
209
210 if (ConsoleId != s_GetConhostServerId(ConsoleHandle.get()))
211 {
212 THROW_HR(E_UNEXPECTED);
213 }
214
215 WI_ASSERT(ConsoleId != 0);
216 }
217
218 bool ConsoleManager::s_OnProcessTerminated(_In_ ConsoleManager* Self, _In_ ULONG ConsoleId, _In_ bool Elevated)
219 {
220 Self->_OnProcessDisconnect(ConsoleId, Elevated);
221 return true;
222 }