Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(382)

Side by Side Diff: tools/android/forwarder2/host_controllers_manager.cc

Issue 2736053003: [Android] Fix port leak in the forwarder. (Closed)
Patch Set: tedchoc comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/android/forwarder2/host_controllers_manager.h"
6
7 #include "base/process/launch.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "tools/android/forwarder2/util.h"
12
13 namespace forwarder2 {
14
15 HostControllersManager::HostControllersManager(
16 base::Callback<int()> exit_notifier_fd_callback)
17 : controllers_(new HostControllerMap()),
18 exit_notifier_fd_callback_(exit_notifier_fd_callback),
19 has_failed_(false),
20 weak_ptr_factory_(this) {}
21
22 HostControllersManager::~HostControllersManager() {
23 if (!thread_.get())
24 return;
25 // Delete the controllers on the thread they were created on.
26 thread_->task_runner()->DeleteSoon(FROM_HERE, controllers_.release());
27 }
28
29 void HostControllersManager::HandleRequest(
30 const std::string& adb_path,
31 const std::string& device_serial,
32 int command,
33 int device_port,
34 int host_port,
35 std::unique_ptr<Socket> client_socket) {
36 // Lazy initialize so that the CLI process doesn't get this thread created.
37 InitOnce();
38 thread_->task_runner()->PostTask(
39 FROM_HERE,
40 base::Bind(&HostControllersManager::HandleRequestOnInternalThread,
41 base::Unretained(this), adb_path, device_serial, command,
42 device_port, host_port, base::Passed(&client_socket)));
43 }
44
45 // static
46 std::string HostControllersManager::MakeHostControllerMapKey(int adb_port,
47 int device_port) {
48 return base::StringPrintf("%d:%d", adb_port, device_port);
49 }
50
51 void HostControllersManager::InitOnce() {
52 if (thread_.get())
53 return;
54 at_exit_manager_.reset(new base::AtExitManager());
55 thread_.reset(new base::Thread("HostControllersManagerThread"));
56 thread_->Start();
57 }
58
59 // static
60 void HostControllersManager::DeleteHostController(
61 const base::WeakPtr<HostControllersManager>& manager_ptr,
62 std::unique_ptr<HostController> host_controller) {
63 HostController* const controller = host_controller.release();
64 HostControllersManager* const manager = manager_ptr.get();
65 if (!manager) {
66 // Note that |controller| is not leaked in this case since the host
67 // controllers manager owns the controllers. If the manager was deleted
68 // then all the controllers (including |controller|) were also deleted.
69 return;
70 }
71 DCHECK(manager->thread_->task_runner()->RunsTasksOnCurrentThread());
72 // Note that this will delete |controller| which is owned by the map.
73 DeleteRefCountedValueInMap(
74 MakeHostControllerMapKey(controller->adb_port(),
75 controller->device_port()),
76 manager->controllers_.get());
77 }
78
79 void HostControllersManager::Map(const std::string& adb_path,
80 const std::string& device_serial,
81 int adb_port,
82 int device_port,
83 int host_port,
84 Socket* client_socket) {
85 if (host_port < 0) {
86 SendMessage("ERROR: missing host port\n", client_socket);
87 return;
88 }
89 const bool use_dynamic_port_allocation = device_port == 0;
90 if (!use_dynamic_port_allocation) {
91 const std::string controller_key =
92 MakeHostControllerMapKey(adb_port, device_port);
93 if (controllers_->find(controller_key) != controllers_->end()) {
94 LOG(INFO) << "Already forwarding device port " << device_port
95 << " to host port " << host_port;
96 SendMessage(base::StringPrintf("%d:%d", device_port, host_port),
97 client_socket);
98 return;
99 }
100 }
101 // Create a new host controller.
102 std::unique_ptr<HostController> host_controller(HostController::Create(
103 device_serial, device_port, host_port, adb_port,
104 exit_notifier_fd_callback_.Run(),
105 base::Bind(&HostControllersManager::DeleteHostController,
106 weak_ptr_factory_.GetWeakPtr())));
107 if (!host_controller.get()) {
108 has_failed_ = true;
109 SendMessage("ERROR: Connection to device failed.\n", client_socket);
110 LogExistingControllers(client_socket);
111 return;
112 }
113 // Get the current allocated port.
114 device_port = host_controller->device_port();
115 LOG(INFO) << "Forwarding device port " << device_port << " to host port "
116 << host_port;
117 const std::string msg = base::StringPrintf("%d:%d", device_port, host_port);
118 if (!SendMessage(msg, client_socket))
119 return;
120 host_controller->Start();
121 controllers_->insert(
122 std::make_pair(MakeHostControllerMapKey(adb_port, device_port),
123 linked_ptr<HostController>(host_controller.release())));
124 }
125
126 void HostControllersManager::Unmap(const std::string& adb_path,
127 const std::string& device_serial,
128 int adb_port,
129 int device_port,
130 Socket* client_socket) {
131 // Remove the previously created host controller.
132 const std::string controller_key =
133 MakeHostControllerMapKey(adb_port, device_port);
134 const bool controller_did_exist =
135 DeleteRefCountedValueInMap(controller_key, controllers_.get());
136 if (!controller_did_exist) {
137 SendMessage("ERROR: could not unmap port.\n", client_socket);
138 LogExistingControllers(client_socket);
139 } else {
140 SendMessage("OK", client_socket);
141 }
142
143 RemoveAdbPortForDeviceIfNeeded(adb_path, device_serial);
144 }
145
146 void HostControllersManager::UnmapAll(const std::string& adb_path,
147 const std::string& device_serial,
148 int adb_port,
149 Socket* client_socket) {
150 const std::string adb_port_str = base::StringPrintf("%d", adb_port);
151 for (HostControllerMap::const_iterator controller_key =
152 controllers_->cbegin();
153 controller_key != controllers_->cend(); ++controller_key) {
154 std::vector<std::string> pieces =
155 base::SplitString(controller_key->first, ":", base::KEEP_WHITESPACE,
156 base::SPLIT_WANT_ALL);
157 if (pieces.size() == 2) {
158 if (pieces[0] == adb_port_str) {
159 DeleteRefCountedValueInMapFromIterator(controller_key,
160 controllers_.get());
161 }
162 } else {
163 LOG(ERROR) << "Unexpected controller key: " << controller_key->first;
164 }
165 }
166
167 RemoveAdbPortForDeviceIfNeeded(adb_path, device_serial);
168 SendMessage("OK", client_socket);
169 }
170
171 void HostControllersManager::HandleRequestOnInternalThread(
172 const std::string& adb_path,
173 const std::string& device_serial,
174 int command,
175 int device_port,
176 int host_port,
177 std::unique_ptr<Socket> client_socket) {
178 const int adb_port = GetAdbPortForDevice(adb_path, device_serial);
179 if (adb_port < 0) {
180 SendMessage(
181 "ERROR: could not get adb port for device. You might need to add "
182 "'adb' to your PATH or provide the device serial id.\n",
183 client_socket.get());
184 return;
185 }
186 switch (command) {
187 case MAP:
188 Map(adb_path, device_serial, adb_port, device_port, host_port,
189 client_socket.get());
190 break;
191 case UNMAP:
192 Unmap(adb_path, device_serial, adb_port, device_port,
193 client_socket.get());
194 break;
195 case UNMAP_ALL:
196 UnmapAll(adb_path, device_serial, adb_port, client_socket.get());
197 break;
198 default:
199 SendMessage(
200 base::StringPrintf("ERROR: unrecognized command %d\n", command),
201 client_socket.get());
202 break;
203 }
204 }
205
206 void HostControllersManager::LogExistingControllers(Socket* client_socket) {
207 SendMessage("ERROR: Existing controllers:\n", client_socket);
208 for (const auto& controller : *controllers_) {
209 SendMessage(base::StringPrintf("ERROR: %s\n", controller.first.c_str()),
210 client_socket);
211 }
212 }
213
214 bool HostControllersManager::Adb(const std::string& adb_path,
215 const std::string& device_serial,
216 const std::string& command,
217 std::string* output_and_error) {
218 // We use the vector version of GetAppOutputAndError rather than the
219 // more standard base::CommandLine version because base::CommandLine
220 // reorders the command s.t. switches precede arguments and doing so
221 // here creates an invalid adb command.
222 std::vector<std::string> adb_command{adb_path};
223 if (!device_serial.empty()) {
224 adb_command.push_back("-s");
225 adb_command.push_back(device_serial);
226 }
227 const std::vector<std::string> split_command = base::SplitString(
228 command, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
229 adb_command.insert(adb_command.end(), split_command.begin(),
230 split_command.end());
231 return GetAppOutputAndError(adb_command, output_and_error);
232 }
233
234 void HostControllersManager::RemoveAdbPortForDeviceIfNeeded(
235 const std::string& adb_path,
236 const std::string& device_serial) {
237 base::hash_map<std::string, int>::const_iterator it =
238 device_serial_to_adb_port_map_.find(device_serial);
239 if (it == device_serial_to_adb_port_map_.end())
240 return;
241
242 int port = it->second;
243 const std::string prefix = base::StringPrintf("%d:", port);
244 for (HostControllerMap::const_iterator others = controllers_->begin();
245 others != controllers_->end(); ++others) {
246 if (base::StartsWith(others->first, prefix, base::CompareCase::SENSITIVE))
247 return;
248 }
249 // No other port is being forwarded to this device:
250 // - Remove it from our internal serial -> adb port map.
251 // - Remove from "adb forward" command.
252 LOG(INFO) << "Device " << device_serial << " has no more ports.";
253 device_serial_to_adb_port_map_.erase(device_serial);
254 const std::string command =
255 base::StringPrintf("forward --remove tcp:%d", port);
256 std::string output;
257 if (!Adb(adb_path, device_serial, command, &output)) {
258 LOG(ERROR) << command << " failed. output: \"" << output << "\"";
259 } else {
260 LOG(INFO) << command << " (output: \"" << output << "\")";
261 }
262 // Wait for the socket to be fully unmapped.
263 const std::string port_mapped_cmd = base::StringPrintf("lsof -nPi:%d", port);
264 const std::vector<std::string> port_mapped_split_cmd = base::SplitString(
265 port_mapped_cmd, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
266 const int poll_interval_us = 500 * 1000;
267 int retries = 3;
268 while (retries) {
269 // lsof failure means the port was successfully unmapped.
270 bool port_unmapped = !GetAppOutputAndError(port_mapped_split_cmd, &output);
271 LOG(INFO) << "Device " << device_serial << " port " << port
272 << (port_unmapped ? "" : " not") << " unmapped";
273 if (port_unmapped)
274 break;
275 --retries;
276 usleep(poll_interval_us);
277 }
278 }
279
280 int HostControllersManager::GetAdbPortForDevice(
281 const std::string adb_path,
282 const std::string& device_serial) {
283 base::hash_map<std::string, int>::const_iterator it =
284 device_serial_to_adb_port_map_.find(device_serial);
285 if (it != device_serial_to_adb_port_map_.end())
286 return it->second;
287 Socket bind_socket;
288 CHECK(bind_socket.BindTcp("127.0.0.1", 0));
289 const int port = bind_socket.GetPort();
290 bind_socket.Close();
291 const std::string command = base::StringPrintf(
292 "forward tcp:%d localabstract:chrome_device_forwarder", port);
293 std::string output;
294 if (!Adb(adb_path, device_serial, command, &output)) {
295 LOG(ERROR) << command << " failed. output: " << output;
296 return -1;
297 }
298 LOG(INFO) << command;
299 device_serial_to_adb_port_map_[device_serial] = port;
300 return port;
301 }
302
303 bool HostControllersManager::SendMessage(const std::string& msg,
304 Socket* client_socket) {
305 bool result = client_socket->WriteString(msg);
306 DCHECK(result);
307 if (!result)
308 has_failed_ = true;
309 return result;
310 }
311
312 bool HostControllersManager::GetAppOutputAndError(
313 const std::vector<std::string>& argv,
314 std::string* output) {
315 return base::GetAppOutputAndError(argv, output);
316 }
317
318 } // namespace forwarder2
OLDNEW
« no previous file with comments | « tools/android/forwarder2/host_controllers_manager.h ('k') | tools/android/forwarder2/host_controllers_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698