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

Side by Side Diff: components/arc/arc_bridge_bootstrap.cc

Issue 2194193002: Fix ArcBridgeBootstrap race issues. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix ArcBridgeBootstrap race issues. Created 4 years, 4 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/arc/arc_bridge_bootstrap.h" 5 #include "components/arc/arc_bridge_bootstrap.h"
6 6
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <grp.h> 8 #include <grp.h>
9 #include <poll.h>
9 #include <unistd.h> 10 #include <unistd.h>
10 11
11 #include <utility> 12 #include <utility>
12 13
13 #include "base/files/file_path.h" 14 #include "base/files/file_path.h"
14 #include "base/files/file_util.h" 15 #include "base/files/file_util.h"
15 #include "base/location.h" 16 #include "base/location.h"
16 #include "base/macros.h" 17 #include "base/macros.h"
17 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
18 #include "base/posix/eintr_wrapper.h" 19 #include "base/posix/eintr_wrapper.h"
19 #include "base/sys_info.h" 20 #include "base/sys_info.h"
20 #include "base/task_runner_util.h" 21 #include "base/task_runner_util.h"
21 #include "base/threading/thread_checker.h" 22 #include "base/threading/thread_checker.h"
22 #include "base/threading/thread_task_runner_handle.h" 23 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/threading/worker_pool.h" 24 #include "base/threading/worker_pool.h"
24 #include "chromeos/cryptohome/cryptohome_parameters.h" 25 #include "chromeos/cryptohome/cryptohome_parameters.h"
25 #include "chromeos/dbus/dbus_method_call_status.h" 26 #include "chromeos/dbus/dbus_method_call_status.h"
26 #include "chromeos/dbus/dbus_thread_manager.h" 27 #include "chromeos/dbus/dbus_thread_manager.h"
27 #include "chromeos/dbus/session_manager_client.h" 28 #include "chromeos/dbus/session_manager_client.h"
29 #include "components/arc/arc_bridge_host_impl.h"
28 #include "components/user_manager/user_manager.h" 30 #include "components/user_manager/user_manager.h"
29 #include "ipc/unix_domain_socket_util.h" 31 #include "ipc/unix_domain_socket_util.h"
30 #include "mojo/edk/embedder/embedder.h" 32 #include "mojo/edk/embedder/embedder.h"
31 #include "mojo/edk/embedder/platform_channel_pair.h" 33 #include "mojo/edk/embedder/platform_channel_pair.h"
32 #include "mojo/edk/embedder/platform_channel_utils_posix.h" 34 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
33 #include "mojo/edk/embedder/platform_handle_vector.h" 35 #include "mojo/edk/embedder/platform_handle_vector.h"
34 #include "mojo/edk/embedder/scoped_platform_handle.h" 36 #include "mojo/edk/embedder/scoped_platform_handle.h"
35 #include "mojo/public/cpp/bindings/binding.h" 37 #include "mojo/public/cpp/bindings/binding.h"
36 38
37 namespace arc { 39 namespace arc {
(...skipping 17 matching lines...) Expand all
55 chromeos::SessionManagerClient* GetSessionManagerClient() { 57 chromeos::SessionManagerClient* GetSessionManagerClient() {
56 // If the DBusThreadManager or the SessionManagerClient aren't available, 58 // If the DBusThreadManager or the SessionManagerClient aren't available,
57 // there isn't much we can do. This should only happen when running tests. 59 // there isn't much we can do. This should only happen when running tests.
58 if (!chromeos::DBusThreadManager::IsInitialized() || 60 if (!chromeos::DBusThreadManager::IsInitialized() ||
59 !chromeos::DBusThreadManager::Get() || 61 !chromeos::DBusThreadManager::Get() ||
60 !chromeos::DBusThreadManager::Get()->GetSessionManagerClient()) 62 !chromeos::DBusThreadManager::Get()->GetSessionManagerClient())
61 return nullptr; 63 return nullptr;
62 return chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 64 return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
63 } 65 }
64 66
67 // Creates a pair of pipes. Returns true on success, otherwise false.
68 // On success, |read_fd| will be set to the fd of the read side, and
69 // |write_fd| will be set to the one of write side.
70 bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
71 int fds[2];
72 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
73 PLOG(ERROR) << "Failed to create a pipe.";
74 return false;
75 }
76
77 read_fd->reset(fds[0]);
78 write_fd->reset(fds[1]);
79 return true;
80 }
81
82 // Waits until |raw_socket_fd| is readable.
83 // The operation may be cancelled originally triggered by user interaction to
84 // disable ARC, or ARC instance is unexpectedly stopped (e.g. crash).
85 // To notify such a situation, |raw_cancel_fd| is also passed to here, and the
86 // write side will be closed in such a case.
87 bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
88 struct pollfd fds[2] = {
89 {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
90 };
91
92 if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
93 PLOG(ERROR) << "Poll() is failed.";
Luis Héctor Chávez 2016/08/01 17:47:38 nit: Just "poll()" is fine.
hidehiko 2016/08/02 11:36:21 Done.
94 return false;
95 }
96
97 if (fds[1].revents) {
98 // Notified that Stop() is invoked. Cancel the Mojo connecting.
99 VLOG(0) << "Stop() is called during the ConnectMojo()";
100 return false;
101 }
102
103 DCHECK(fds[0].revents);
104 return true;
105 }
106
107 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least
108 // state-machine part.
65 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap, 109 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap,
66 public chromeos::SessionManagerClient::Observer { 110 public chromeos::SessionManagerClient::Observer {
67 public: 111 public:
68 // The possible states of the bootstrap connection. In the normal flow, 112 // The possible states of the bootstrap connection. In the normal flow,
69 // the state changes in the following sequence: 113 // the state changes in the following sequence:
70 // 114 //
71 // STOPPED 115 // NOT_STARTED
72 // Start() -> 116 // Start() ->
73 // DISK_SPACE_CHECKING 117 // CHECKING_DISK_SPACE
74 // CheckDiskSpace() -> OnDiskSpaceChecked() -> 118 // OnDiskSpaceChecked() ->
75 // SOCKET_CREATING 119 // CREATING_SOCKET
76 // CreateSocket() -> OnSocketCreated() -> 120 // CreateSocket() -> OnSocketCreated() ->
77 // STARTING 121 // STARTING_INSTANCE
78 // StartArcInstance() -> OnInstanceStarted() -> 122 // -> OnInstanceStarted() ->
79 // STARTED 123 // CONNECTING_MOJO
80 // AcceptInstanceConnection() -> OnInstanceConnected() -> 124 // ConnectMojo() -> OnMojoConnected() ->
81 // READY 125 // RUNNING
82 // 126 //
83 // When Stop() or AbortBoot() is called from any state, either because an 127 // At any phase, Stop() can be called. It does not immediately stop the
Luis Héctor Chávez 2016/08/01 17:47:38 nt: s/phase/state/ throughout this comment.
hidehiko 2016/08/02 11:36:21 Done.
84 // operation resulted in an error or because the user is logging out: 128 // instance, but will eventually stop it.
129 // The actual stop will be notified via OnStopped() of the |delegate_|.
85 // 130 //
86 // (any) 131 // When Stop() is called, it makes various behavior based on the current
87 // Stop()/AbortBoot() -> 132 // phase.
88 // STOPPING
89 // StopInstance() ->
90 // STOPPED
91 // 133 //
92 // When the instance crashes while it was ready, it will be stopped: 134 // NOT_STARTED:
93 // READY -> STOPPING -> STOPPED 135 // Do nothing. Immediately transition to the STOPPED.
94 // and then restarted: 136 // CHECKING_DISK_SPACE, CREATING_SOCKET:
95 // STOPPED -> DISK_SPACE_CHECKING -> ... -> READY). 137 // The main task of those phases runs on WorkerPool thread. So, Stop()
138 // just sets the flag and return. On the main task completion, a callback
139 // will run on the main (practically UI) thread, and the flag is checked
140 // at the beginning of them. This should work under the assumption that
Luis Héctor Chávez 2016/08/01 17:47:38 Why is this an assumption? (Or rather, why does th
hidehiko 2016/08/02 11:36:21 If the background task is very heavy or blocking f
141 // the main tasks do not take much time.
142 // STARTING_INSTANCE:
143 // The ARC instance is starting via SessionManager. So, similar to
144 // CHECKING_DISK_SPACE/CREATING_SOCKET cases, Stop() just sets the flag
145 // and return. In a callback, it checks if ARC instance is successfully
Luis Héctor Chávez 2016/08/01 17:47:37 nit: s/In a callback/In its callback/.
hidehiko 2016/08/02 11:36:21 Done.
146 // started or not. In case of success, a request to stop the ARC instance
147 // is sent to SessionManager. Its completion will be notified via
148 // ArcInstanceStopped. Otherwise, it just turns into STOPPED state.
149 // CONNECTING_MOJO:
150 // The main task runs on WorkerPool thread, but it is blocking call.
151 // So, Stop() sends a request to cancel the blocking by closing the pipe
152 // whose read side is also polled. Then, in its callback, similar to
153 // STARTING_INSTANCE, a request to stop the ARC instance is sent to
154 // SessionManager, and ArcInstanceStopped handles remaining procedure.
155 // RUNNING:
156 // There is no more callback which runs on normal flow, so Stop() requests
157 // to stop the ARC instance via SessionManager.
158 //
159 // Another trigger to change the state coming from outside of this class
160 // is an event ArcInstanceStopped() sent from SessionManager, when ARC
161 // instace is crashed. ArcInstanceStopped() turns the state into STOPPED
Luis Héctor Chávez 2016/08/01 17:47:37 nit: s/instace is crashed/instance unexpectedly te
hidehiko 2016/08/02 11:36:21 Done.
162 // immediately.
163 // This happens only when STARTING_INSTANCE, CONNECTING_MOJO or RUNNING
164 // state.
165 //
166 // STARTING_INSTANCE:
167 // In OnInstanceStarted(), |state_| is checked at the beginning. If it is
168 // STOPPED, then ArcInstanceStopped() is called. Do nothing in that case.
169 // CONNECTING_MOJO:
170 // Similar to Stop() case above, ArcInstanceStopped() also notifies to
171 // WorkerPool() thread to cancel it to unblock the thread. In
172 // OnMojoConnected(), similar to OnInstanceStarted(), check if |state_| is
173 // STOPPED, then do nothing.
174 // RUNNING:
175 // It is not necessary to do anything special here.
176 //
177 // In NOT_STARTED or STOPPED state, the instance can be safely destructed.
Luis Héctor Chávez 2016/08/01 17:47:38 What happens if the instance is destroyed in any o
hidehiko 2016/08/02 11:36:21 It would be enter to mysterious ChromeOS state, bu
178 // Specifically, in STOPPED state, there may be inflight operations or
179 // pending callbacks. Though, what they do is just do-nothing conceptually
180 // and they can be safely ignored.
96 // 181 //
97 // Note: Order of constants below matters. Please make sure to sort them 182 // Note: Order of constants below matters. Please make sure to sort them
98 // in chronological order. 183 // in chronological order.
99 enum class State { 184 enum class State {
100 // ARC is not currently running. 185 // ARC is not yet started.
101 STOPPED, 186 NOT_STARTED,
102 187
103 // Checking the disk space. 188 // Checking the disk space.
104 DISK_SPACE_CHECKING, 189 CHECKING_DISK_SPACE,
105 190
106 // An UNIX socket is being created. 191 // An UNIX socket is being created.
107 SOCKET_CREATING, 192 CREATING_SOCKET,
108 193
109 // The request to start the instance has been sent. 194 // The request to start the instance has been sent.
110 STARTING, 195 STARTING_INSTANCE,
111 196
112 // The instance has started. Waiting for it to connect to the IPC bridge. 197 // The instance has started. Waiting for it to connect to the IPC bridge.
113 STARTED, 198 CONNECTING_MOJO,
114 199
115 // The instance is fully connected. 200 // The instance is fully set up.
116 READY, 201 RUNNING,
117 202
118 // The request to shut down the instance has been sent. 203 // ARC is terminated.
119 STOPPING, 204 STOPPED,
120 }; 205 };
121 206
122 ArcBridgeBootstrapImpl(); 207 ArcBridgeBootstrapImpl();
123 ~ArcBridgeBootstrapImpl() override; 208 ~ArcBridgeBootstrapImpl() override;
124 209
125 // ArcBridgeBootstrap: 210 // ArcBridgeBootstrap:
126 void Start() override; 211 void Start() override;
127 void Stop() override; 212 void Stop() override;
128 213
129 private: 214 private:
130 // Aborts ARC instance boot. This is called from various state-machine 215 // Completes the termination procedure.
131 // functions when they encounter an error during boot. 216 void OnStopped(ArcBridgeService::StopReason reason);
132 void AbortBoot(ArcBridgeService::StopReason reason);
133 217
134 // Called after getting the device free disk space. 218 // Called after getting the device free disk space.
135 void OnDiskSpaceChecked(int64_t disk_free_bytes); 219 void OnDiskSpaceObtained(int64_t disk_free_bytes);
136 220
137 // Creates the UNIX socket on the bootstrap thread and then processes its 221 // Creates the UNIX socket on the bootstrap thread and then processes its
138 // file descriptor. 222 // file descriptor.
139 static base::ScopedFD CreateSocket(); 223 static base::ScopedFD CreateSocket();
140 void OnSocketCreated(base::ScopedFD fd); 224 void OnSocketCreated(base::ScopedFD fd);
141 225
226 // DBus callbacks.
227 void OnInstanceStarted(base::ScopedFD socket_fd, bool success);
228
142 // Synchronously accepts a connection on |socket_fd| and then processes the 229 // Synchronously accepts a connection on |socket_fd| and then processes the
143 // connected socket's file descriptor. 230 // connected socket's file descriptor.
144 static base::ScopedFD AcceptInstanceConnection(base::ScopedFD socket_fd); 231 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd,
145 void OnInstanceConnected(base::ScopedFD fd); 232 base::ScopedFD cancel_fd);
233 void OnMojoConnected(base::ScopedFD fd);
146 234
147 void SetState(State state); 235 // Request to stop ARC instance via DBus.
148 236 void StopArcInstance();
149 // DBus callbacks.
150 void OnInstanceStarted(base::ScopedFD socket_fd, bool success);
151 237
152 // chromeos::SessionManagerClient::Observer: 238 // chromeos::SessionManagerClient::Observer:
153 void ArcInstanceStopped(bool clean) override; 239 void ArcInstanceStopped(bool clean) override;
154 240
155 // The state of the bootstrap connection. 241 // The state of the bootstrap connection.
156 State state_ = State::STOPPED; 242 State state_ = State::NOT_STARTED;
157 243
158 // The reason the ARC instance is stopped. 244 // When Stop() is called, thsi flag is set.
159 ArcBridgeService::StopReason stop_reason_ = 245 bool stop_requested_ = false;
160 ArcBridgeService::StopReason::SHUTDOWN; 246
247 // In CONNECTING_MOJO state, this is set to the write side of the pipe
248 // to notify cancelling of the procedure.
249 base::ScopedFD accept_cancel_pipe_;
250
251 // Mojo endpoint.
252 std::unique_ptr<mojom::ArcBridgeHost> arc_bridge_host_;
161 253
162 base::ThreadChecker thread_checker_; 254 base::ThreadChecker thread_checker_;
163 255
164 // WeakPtrFactory to use callbacks. 256 // WeakPtrFactory to use callbacks.
165 base::WeakPtrFactory<ArcBridgeBootstrapImpl> weak_factory_; 257 base::WeakPtrFactory<ArcBridgeBootstrapImpl> weak_factory_;
166 258
167 private: 259 private:
168 DISALLOW_COPY_AND_ASSIGN(ArcBridgeBootstrapImpl); 260 DISALLOW_COPY_AND_ASSIGN(ArcBridgeBootstrapImpl);
169 }; 261 };
170 262
171 ArcBridgeBootstrapImpl::ArcBridgeBootstrapImpl() 263 ArcBridgeBootstrapImpl::ArcBridgeBootstrapImpl()
172 : weak_factory_(this) { 264 : weak_factory_(this) {
173 chromeos::SessionManagerClient* client = GetSessionManagerClient(); 265 chromeos::SessionManagerClient* client = GetSessionManagerClient();
174 if (client == nullptr) 266 if (client == nullptr)
175 return; 267 return;
176 client->AddObserver(this); 268 client->AddObserver(this);
177 } 269 }
178 270
179 ArcBridgeBootstrapImpl::~ArcBridgeBootstrapImpl() { 271 ArcBridgeBootstrapImpl::~ArcBridgeBootstrapImpl() {
180 DCHECK(thread_checker_.CalledOnValidThread()); 272 DCHECK(thread_checker_.CalledOnValidThread());
181 DCHECK(state_ == State::STOPPED || state_ == State::STOPPING); 273 DCHECK(state_ == State::NOT_STARTED || state_ == State::STOPPED);
182 chromeos::SessionManagerClient* client = GetSessionManagerClient(); 274 chromeos::SessionManagerClient* client = GetSessionManagerClient();
183 if (client == nullptr) 275 if (client == nullptr)
184 return; 276 return;
185 client->RemoveObserver(this); 277 client->RemoveObserver(this);
186 } 278 }
187 279
188 void ArcBridgeBootstrapImpl::Start() { 280 void ArcBridgeBootstrapImpl::Start() {
189 DCHECK(thread_checker_.CalledOnValidThread()); 281 DCHECK(thread_checker_.CalledOnValidThread());
190 DCHECK(delegate_); 282 DCHECK(delegate_);
191 if (state_ != State::STOPPED) { 283 DCHECK_EQ(state_, State::NOT_STARTED);
192 VLOG(1) << "Start() called when instance is not stopped"; 284 state_ = State::CHECKING_DISK_SPACE;
193 return; 285
194 }
195 stop_reason_ = ArcBridgeService::StopReason::SHUTDOWN;
196 // TODO(crbug.com/628124): Move disk space checking logic to session_manager. 286 // TODO(crbug.com/628124): Move disk space checking logic to session_manager.
197 SetState(State::DISK_SPACE_CHECKING);
198 base::PostTaskAndReplyWithResult( 287 base::PostTaskAndReplyWithResult(
199 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 288 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
200 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace, 289 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace,
201 base::FilePath(kDiskCheckPath)), 290 base::FilePath(kDiskCheckPath)),
202 base::Bind(&ArcBridgeBootstrapImpl::OnDiskSpaceChecked, 291 base::Bind(&ArcBridgeBootstrapImpl::OnDiskSpaceObtained,
203 weak_factory_.GetWeakPtr())); 292 weak_factory_.GetWeakPtr()));
204 } 293 }
205 294
206 void ArcBridgeBootstrapImpl::OnDiskSpaceChecked(int64_t disk_free_bytes) { 295 void ArcBridgeBootstrapImpl::Stop() {
207 DCHECK(thread_checker_.CalledOnValidThread()); 296 DCHECK(thread_checker_.CalledOnValidThread());
208 if (state_ != State::DISK_SPACE_CHECKING) { 297 DCHECK(delegate_);
298
299 // For second time or later, just do nothing.
300 // It is already in the stopping phase.
301 if (stop_requested_)
302 return;
303
304 stop_requested_ = true;
305 arc_bridge_host_.reset();
306 switch (state_) {
307 case State::NOT_STARTED:
308 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
309 return;
310
311 case State::CHECKING_DISK_SPACE:
312 case State::CREATING_SOCKET:
313 case State::STARTING_INSTANCE:
314 // Before starting the ARC instance, we do nothing here.
315 // At some point, a callback will be invoked on UI thread,
316 // and stopping procedure will be run there.
317 return;
318
319 case State::CONNECTING_MOJO:
320 // Mojo connection is being waited on a WorkerPool thread.
321 // Request to cancel it. Following stopping procedure will run
322 // in its callback.
323 DCHECK(accept_cancel_pipe_.get());
324 accept_cancel_pipe_.reset();
325 return;
326
327 case State::RUNNING:
328 // Now ARC instance is running. Request to stop it.
329 StopArcInstance();
330 return;
331
332 case State::STOPPED:
333 // The instance is already stopped. Do nothing.
334 return;
335 }
336 }
337
338 void ArcBridgeBootstrapImpl::OnStopped(ArcBridgeService::StopReason reason) {
339 DCHECK(thread_checker_.CalledOnValidThread());
340 // OnStopped() should be called once per instance.
341 DCHECK_NE(state_, State::STOPPED);
342 arc_bridge_host_.reset();
343 state_ = State::STOPPED;
344 delegate_->OnStopped(reason);
345 }
346
347 void ArcBridgeBootstrapImpl::OnDiskSpaceObtained(int64_t disk_free_bytes) {
348 DCHECK(thread_checker_.CalledOnValidThread());
349 DCHECK_EQ(state_, State::CHECKING_DISK_SPACE);
350
351 if (stop_requested_) {
209 VLOG(1) << "Stop() called while checking disk space"; 352 VLOG(1) << "Stop() called while checking disk space";
353 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
210 return; 354 return;
211 } 355 }
356
212 if (disk_free_bytes < 0) { 357 if (disk_free_bytes < 0) {
213 LOG(ERROR) << "ARC: Failed to get free disk space"; 358 LOG(ERROR) << "ARC: Failed to get free disk space";
214 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 359 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
215 return; 360 return;
216 } 361 }
217 if (disk_free_bytes < kCriticalDiskFreeBytes) { 362 if (disk_free_bytes < kCriticalDiskFreeBytes) {
218 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC"; 363 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC";
219 AbortBoot(ArcBridgeService::StopReason::LOW_DISK_SPACE); 364 OnStopped(ArcBridgeService::StopReason::LOW_DISK_SPACE);
220 return; 365 return;
221 } 366 }
222 SetState(State::SOCKET_CREATING); 367
368 state_ = State::CREATING_SOCKET;
223 base::PostTaskAndReplyWithResult( 369 base::PostTaskAndReplyWithResult(
224 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 370 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
225 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), 371 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket),
226 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, 372 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated,
227 weak_factory_.GetWeakPtr())); 373 weak_factory_.GetWeakPtr()));
228 } 374 }
229 375
230 // static 376 // static
231 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { 377 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() {
232 base::FilePath socket_path(kArcBridgeSocketPath); 378 base::FilePath socket_path(kArcBridgeSocketPath);
233 379
234 int raw_fd = -1; 380 int raw_fd = -1;
235 if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd)) { 381 if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd))
236 return base::ScopedFD(); 382 return base::ScopedFD();
237 }
238 base::ScopedFD socket_fd(raw_fd); 383 base::ScopedFD socket_fd(raw_fd);
239 384
240 // Make socket blocking.
241 int flags = HANDLE_EINTR(fcntl(socket_fd.get(), F_GETFL));
242 if (flags == -1) {
243 PLOG(ERROR) << "fcntl(F_GETFL)";
244 return base::ScopedFD();
245 }
246 if (HANDLE_EINTR(fcntl(socket_fd.get(), F_SETFL, flags & ~O_NONBLOCK)) < 0) {
247 PLOG(ERROR) << "fcntl(O_NONBLOCK)";
248 return base::ScopedFD();
249 }
250
251 // Change permissions on the socket. 385 // Change permissions on the socket.
252 struct group arc_bridge_group; 386 struct group arc_bridge_group;
253 struct group* arc_bridge_group_res = nullptr; 387 struct group* arc_bridge_group_res = nullptr;
254 char buf[10000]; 388 char buf[10000];
255 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf, 389 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf,
256 sizeof(buf), &arc_bridge_group_res)) < 0) { 390 sizeof(buf), &arc_bridge_group_res)) < 0) {
257 PLOG(ERROR) << "getgrnam_r"; 391 PLOG(ERROR) << "getgrnam_r";
258 return base::ScopedFD(); 392 return base::ScopedFD();
259 } 393 }
260 394
(...skipping 11 matching lines...) Expand all
272 if (!base::SetPosixFilePermissions(socket_path, 0660)) { 406 if (!base::SetPosixFilePermissions(socket_path, 0660)) {
273 PLOG(ERROR) << "Could not set permissions: " << socket_path.value(); 407 PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
274 return base::ScopedFD(); 408 return base::ScopedFD();
275 } 409 }
276 410
277 return socket_fd; 411 return socket_fd;
278 } 412 }
279 413
280 void ArcBridgeBootstrapImpl::OnSocketCreated(base::ScopedFD socket_fd) { 414 void ArcBridgeBootstrapImpl::OnSocketCreated(base::ScopedFD socket_fd) {
281 DCHECK(thread_checker_.CalledOnValidThread()); 415 DCHECK(thread_checker_.CalledOnValidThread());
282 if (state_ != State::SOCKET_CREATING) { 416 DCHECK_EQ(state_, State::CREATING_SOCKET);
417
418 if (stop_requested_) {
283 VLOG(1) << "Stop() called while connecting"; 419 VLOG(1) << "Stop() called while connecting";
420 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
284 return; 421 return;
285 } 422 }
286 SetState(State::STARTING);
287 423
288 if (!socket_fd.is_valid()) { 424 if (!socket_fd.is_valid()) {
289 LOG(ERROR) << "ARC: Error creating socket"; 425 LOG(ERROR) << "ARC: Error creating socket";
290 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 426 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
291 return; 427 return;
292 } 428 }
293 429
430 state_ = State::STARTING_INSTANCE;
294 user_manager::UserManager* user_manager = user_manager::UserManager::Get(); 431 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
295 DCHECK(user_manager->GetPrimaryUser()); 432 DCHECK(user_manager->GetPrimaryUser());
296 const cryptohome::Identification cryptohome_id( 433 const cryptohome::Identification cryptohome_id(
297 user_manager->GetPrimaryUser()->GetAccountId()); 434 user_manager->GetPrimaryUser()->GetAccountId());
298 435
299 chromeos::SessionManagerClient* session_manager_client = 436 chromeos::SessionManagerClient* session_manager_client =
300 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 437 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
301 session_manager_client->StartArcInstance( 438 session_manager_client->StartArcInstance(
302 cryptohome_id, 439 cryptohome_id,
303 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, 440 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted,
304 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); 441 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd)));
305 } 442 }
306 443
307 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, 444 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd,
308 bool success) { 445 bool success) {
309 DCHECK(thread_checker_.CalledOnValidThread()); 446 DCHECK(thread_checker_.CalledOnValidThread());
447 if (state_ == State::STOPPED) {
448 // This is the case that error is notified via DBus before the
449 // OnInstanceStarted() callback is invoked. The stopping procedure has
450 // been run, so do nothing.
451 return;
452 }
453
454 DCHECK_EQ(state_, State::STARTING_INSTANCE);
455
456 if (stop_requested_) {
457 if (success) {
458 // The ARC instance has started to run. Request to stop.
459 StopArcInstance();
460 return;
461 }
462 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
463 return;
464 }
465
310 if (!success) { 466 if (!success) {
311 LOG(ERROR) << "Failed to start ARC instance"; 467 LOG(ERROR) << "Failed to start ARC instance";
312 // Roll back the state to SOCKET_CREATING to avoid sending the D-Bus signal 468 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
313 // to stop the failed instance.
314 SetState(State::SOCKET_CREATING);
315 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
316 return; 469 return;
317 } 470 }
318 if (state_ != State::STARTING) { 471
319 VLOG(1) << "Stop() called when ARC is not running"; 472 state_ = State::CONNECTING_MOJO;
473
474 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on
475 // Stop().
476 base::ScopedFD cancel_fd;
477 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
478 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
320 return; 479 return;
321 } 480 }
322 SetState(State::STARTED);
323 481
324 base::PostTaskAndReplyWithResult( 482 base::PostTaskAndReplyWithResult(
325 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 483 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
326 base::Bind(&ArcBridgeBootstrapImpl::AcceptInstanceConnection, 484 base::Bind(&ArcBridgeBootstrapImpl::ConnectMojo, base::Passed(&socket_fd),
327 base::Passed(&socket_fd)), 485 base::Passed(&cancel_fd)),
328 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceConnected, 486 base::Bind(&ArcBridgeBootstrapImpl::OnMojoConnected,
329 weak_factory_.GetWeakPtr())); 487 weak_factory_.GetWeakPtr()));
330 } 488 }
331 489
332 // static 490 // static
333 base::ScopedFD ArcBridgeBootstrapImpl::AcceptInstanceConnection( 491 base::ScopedFD ArcBridgeBootstrapImpl::ConnectMojo(base::ScopedFD socket_fd,
334 base::ScopedFD socket_fd) { 492 base::ScopedFD cancel_fd) {
493 if (!WaitForSocketReadable(socket_fd.get(), cancel_fd.get())) {
494 VLOG(0) << "Mojo connecting is cancelled.";
495 return base::ScopedFD();
496 }
497
335 int raw_fd = -1; 498 int raw_fd = -1;
336 if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) { 499 if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) {
337 return base::ScopedFD(); 500 return base::ScopedFD();
338 } 501 }
339 base::ScopedFD scoped_fd(raw_fd); 502 base::ScopedFD scoped_fd(raw_fd);
340 503
341 // Hardcode pid 0 since it is unused in mojo. 504 // Hardcode pid 0 since it is unused in mojo.
342 const base::ProcessHandle kUnusedChildProcessHandle = 0; 505 const base::ProcessHandle kUnusedChildProcessHandle = 0;
343 mojo::edk::PlatformChannelPair channel_pair; 506 mojo::edk::PlatformChannelPair channel_pair;
344 mojo::edk::ChildProcessLaunched(kUnusedChildProcessHandle, 507 mojo::edk::ChildProcessLaunched(kUnusedChildProcessHandle,
345 channel_pair.PassServerHandle(), 508 channel_pair.PassServerHandle(),
346 mojo::edk::GenerateRandomToken()); 509 mojo::edk::GenerateRandomToken());
347 510
348 mojo::edk::ScopedPlatformHandleVectorPtr handles( 511 mojo::edk::ScopedPlatformHandleVectorPtr handles(
349 new mojo::edk::PlatformHandleVector{ 512 new mojo::edk::PlatformHandleVector{
350 channel_pair.PassClientHandle().release()}); 513 channel_pair.PassClientHandle().release()});
351 514
352 struct iovec iov = {const_cast<char*>(""), 1}; 515 struct iovec iov = {const_cast<char*>(""), 1};
353 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles( 516 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
354 mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(), 517 mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(),
355 handles->size()); 518 handles->size());
356 if (result == -1) { 519 if (result == -1) {
357 PLOG(ERROR) << "sendmsg"; 520 PLOG(ERROR) << "sendmsg";
358 return base::ScopedFD(); 521 return base::ScopedFD();
359 } 522 }
360 523
361 return scoped_fd; 524 return scoped_fd;
362 } 525 }
363 526
364 void ArcBridgeBootstrapImpl::OnInstanceConnected(base::ScopedFD fd) { 527 void ArcBridgeBootstrapImpl::OnMojoConnected(base::ScopedFD fd) {
365 DCHECK(thread_checker_.CalledOnValidThread()); 528 DCHECK(thread_checker_.CalledOnValidThread());
366 if (state_ != State::STARTED) { 529
367 VLOG(1) << "Stop() called when ARC is not running"; 530 if (state_ == State::STOPPED) {
531 // This is the case that error is notified via DBus before the
532 // OnMojoConnected() callback is invoked. The stopping procedure has
533 // been run, so do nothing.
368 return; 534 return;
369 } 535 }
536
537 DCHECK_EQ(state_, State::CONNECTING_MOJO);
538 accept_cancel_pipe_.reset();
539
540 if (stop_requested_) {
541 StopArcInstance();
542 return;
543 }
544
370 if (!fd.is_valid()) { 545 if (!fd.is_valid()) {
371 LOG(ERROR) << "Invalid handle"; 546 LOG(ERROR) << "Invalid handle";
372 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 547 StopArcInstance();
373 return; 548 return;
374 } 549 }
550
375 mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe( 551 mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe(
376 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release()))); 552 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release())));
377 if (!server_pipe.is_valid()) { 553 if (!server_pipe.is_valid()) {
378 LOG(ERROR) << "Invalid pipe"; 554 LOG(ERROR) << "Invalid pipe";
379 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 555 StopArcInstance();
380 return; 556 return;
381 } 557 }
382 SetState(State::READY); 558
383 mojom::ArcBridgeInstancePtr instance; 559 mojom::ArcBridgeInstancePtr instance;
384 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>( 560 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
385 std::move(server_pipe), 0u)); 561 std::move(server_pipe), 0u));
386 delegate_->OnConnectionEstablished(std::move(instance)); 562 arc_bridge_host_.reset(new ArcBridgeHostImpl(std::move(instance)));
563
564 state_ = State::RUNNING;
565 delegate_->OnReady();
387 } 566 }
388 567
389 void ArcBridgeBootstrapImpl::Stop() { 568 void ArcBridgeBootstrapImpl::StopArcInstance() {
390 DCHECK(thread_checker_.CalledOnValidThread()); 569 DCHECK(thread_checker_.CalledOnValidThread());
391 if (state_ == State::STOPPED || state_ == State::STOPPING) { 570 DCHECK(state_ == State::STARTING_INSTANCE ||
392 VLOG(1) << "Stop() called when ARC is not running"; 571 state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
393 return; 572
394 }
395 if (state_ < State::STARTING) {
396 // This was stopped before the D-Bus command to start the instance. Skip
397 // the D-Bus command to stop it.
398 SetState(State::STOPPED);
399 return;
400 }
401 SetState(State::STOPPING);
402 // Notification will arrive through ArcInstanceStopped(). 573 // Notification will arrive through ArcInstanceStopped().
403 chromeos::SessionManagerClient* session_manager_client = 574 chromeos::SessionManagerClient* session_manager_client =
404 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 575 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
405 session_manager_client->StopArcInstance( 576 session_manager_client->StopArcInstance(
406 base::Bind(&DoNothingInstanceStopped)); 577 base::Bind(&DoNothingInstanceStopped));
407 } 578 }
408 579
409 void ArcBridgeBootstrapImpl::AbortBoot(ArcBridgeService::StopReason reason) {
410 DCHECK(thread_checker_.CalledOnValidThread());
411 DCHECK(reason != ArcBridgeService::StopReason::SHUTDOWN);
412 // In case of multiple errors, report the first one.
413 if (stop_reason_ == ArcBridgeService::StopReason::SHUTDOWN) {
414 stop_reason_ = reason;
415 }
416 Stop();
417 }
418
419 void ArcBridgeBootstrapImpl::ArcInstanceStopped(bool clean) { 580 void ArcBridgeBootstrapImpl::ArcInstanceStopped(bool clean) {
420 DCHECK(thread_checker_.CalledOnValidThread()); 581 DCHECK(thread_checker_.CalledOnValidThread());
421 if (!clean) { 582 // In case that crash happens during connecting Mojo, we unlock the
Luis Héctor Chávez 2016/08/01 17:47:38 nit: s/during connecting Mojo/before the Mojo chan
hidehiko 2016/08/02 11:36:21 This could call before starting Mojo channel conne
Luis Héctor Chávez 2016/09/07 23:38:32 Right, but for the purposes of resetting |accept_c
hidehiko 2016/09/08 16:53:40 Makes sense. Done. Thank you.
422 LOG(ERROR) << "ARC instance crashed"; 583 // WorkerPool thread.
423 // In case of multiple errors, report the first one. 584 accept_cancel_pipe_.reset();
424 if (stop_reason_ == ArcBridgeService::StopReason::SHUTDOWN) { 585
425 stop_reason_ = ArcBridgeService::StopReason::CRASH; 586 ArcBridgeService::StopReason reason;
426 } 587 if (stop_requested_) {
588 // If the ARC instance is stopped after its explicit request,
589 // returns SHUTDOWN.
Luis Héctor Chávez 2016/08/01 17:47:38 nit: s/returns/return/
hidehiko 2016/08/02 11:36:21 Done.
590 reason = ArcBridgeService::StopReason::SHUTDOWN;
591 } else if (clean) {
592 // If the ARC instance is stopped, but it is not explicitly requested,
593 // then this is triggered by some failure during the starting procedure.
594 // Return GENERIC_BOOT_FAILURE for the case.
595 reason = ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE;
Luis Héctor Chávez 2016/08/01 17:47:37 Have you actually seen this case happen? We should
hidehiko 2016/08/02 11:36:21 No, practically.
596 } else {
597 // Otherwise, this is caused by CRASH occured inside of the ARC instance.
598 reason = ArcBridgeService::StopReason::CRASH;
427 } 599 }
428 SetState(State::STOPPED); 600 OnStopped(reason);
429 }
430
431 void ArcBridgeBootstrapImpl::SetState(State state) {
432 DCHECK(thread_checker_.CalledOnValidThread());
433 // DCHECK on enum classes not supported.
434 DCHECK(state_ != state);
435 state_ = state;
436 VLOG(2) << "State: " << static_cast<uint32_t>(state_);
Luis Héctor Chávez 2016/08/01 17:47:38 We're losing some amount of useful logging :( Is i
hidehiko 2016/08/02 11:36:21 Instead of introducing SetState, added VLOGs for e
437 if (state_ == State::STOPPED) {
438 DCHECK(delegate_);
439 delegate_->OnStopped(stop_reason_);
440 }
441 } 601 }
442 602
443 } // namespace 603 } // namespace
444 604
445 // static 605 // static
446 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { 606 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() {
447 return base::WrapUnique(new ArcBridgeBootstrapImpl()); 607 return base::WrapUnique(new ArcBridgeBootstrapImpl());
448 } 608 }
449 609
450 } // namespace arc 610 } // namespace arc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698