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

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: rebase Created 4 years, 3 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.
Luis Héctor Chávez 2016/09/07 23:38:32 nit (pedantic): this actually creates a single pip
hidehiko 2016/09/08 16:53:41 Done.
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) << "pipe2()";
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()";
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()";
Luis Héctor Chávez 2016/09/07 23:38:32 nit: "Stop() was called during ConnectMojo()"
hidehiko 2016/09/08 16:53:41 Done.
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 state, Stop() can be called. It does not immediately stop the
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.
Luis Héctor Chávez 2016/09/07 23:38:32 nit: "the STOPPED state".
hidehiko 2016/09/08 16:53:41 Done.
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
141 // the main tasks are not blocking.
Luis Héctor Chávez 2016/09/07 23:38:32 After reading this (and the rest of the code) more
hidehiko 2016/09/08 16:53:41 Done.
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 its callback, it checks if ARC instance is successfully
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 unexpectedly terminates. ArcInstanceStopped() turns the state into
162 // STOPPED 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.
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);
Luis Héctor Chávez 2016/09/07 23:38:32 nit: OnFreeDiskSpaceObtained?
hidehiko 2016/09/08 16:53:41 Done.
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.
Luis Héctor Chávez 2016/09/07 23:38:32 nit: s/thsi/this/
hidehiko 2016/09/08 16:53:41 Done.
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 VLOG(2) << "Starting ARC session.";
193 return; 285 VLOG(2) << "Checking disk space...";
194 } 286 state_ = State::CHECKING_DISK_SPACE;
195 stop_reason_ = ArcBridgeService::StopReason::SHUTDOWN; 287
196 // TODO(crbug.com/628124): Move disk space checking logic to session_manager. 288 // TODO(crbug.com/628124): Move disk space checking logic to session_manager.
197 SetState(State::DISK_SPACE_CHECKING);
198 base::PostTaskAndReplyWithResult( 289 base::PostTaskAndReplyWithResult(
199 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 290 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
200 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace, 291 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace,
201 base::FilePath(kDiskCheckPath)), 292 base::FilePath(kDiskCheckPath)),
202 base::Bind(&ArcBridgeBootstrapImpl::OnDiskSpaceChecked, 293 base::Bind(&ArcBridgeBootstrapImpl::OnDiskSpaceObtained,
203 weak_factory_.GetWeakPtr())); 294 weak_factory_.GetWeakPtr()));
204 } 295 }
205 296
206 void ArcBridgeBootstrapImpl::OnDiskSpaceChecked(int64_t disk_free_bytes) { 297 void ArcBridgeBootstrapImpl::Stop() {
207 DCHECK(thread_checker_.CalledOnValidThread()); 298 DCHECK(thread_checker_.CalledOnValidThread());
208 if (state_ != State::DISK_SPACE_CHECKING) { 299 DCHECK(delegate_);
300 VLOG(2) << "Stopping ARC session is requested.";
301
302 // For second time or later, just do nothing.
303 // It is already in the stopping phase.
304 if (stop_requested_)
305 return;
306
307 stop_requested_ = true;
308 arc_bridge_host_.reset();
309 switch (state_) {
310 case State::NOT_STARTED:
311 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
312 return;
313
314 case State::CHECKING_DISK_SPACE:
315 case State::CREATING_SOCKET:
316 case State::STARTING_INSTANCE:
317 // Before starting the ARC instance, we do nothing here.
318 // At some point, a callback will be invoked on UI thread,
319 // and stopping procedure will be run there.
320 return;
321
322 case State::CONNECTING_MOJO:
323 // Mojo connection is being waited on a WorkerPool thread.
324 // Request to cancel it. Following stopping procedure will run
325 // in its callback.
326 DCHECK(accept_cancel_pipe_.get());
327 accept_cancel_pipe_.reset();
328 return;
329
330 case State::RUNNING:
331 // Now ARC instance is running. Request to stop it.
332 StopArcInstance();
333 return;
334
335 case State::STOPPED:
336 // The instance is already stopped. Do nothing.
337 return;
338 }
339 }
340
341 void ArcBridgeBootstrapImpl::OnStopped(ArcBridgeService::StopReason reason) {
342 DCHECK(thread_checker_.CalledOnValidThread());
343 // OnStopped() should be called once per instance.
344 DCHECK_NE(state_, State::STOPPED);
345 VLOG(2) << "ARC session is stopped.";
346 arc_bridge_host_.reset();
347 state_ = State::STOPPED;
348 delegate_->OnStopped(reason);
349 }
350
351 void ArcBridgeBootstrapImpl::OnDiskSpaceObtained(int64_t disk_free_bytes) {
352 DCHECK(thread_checker_.CalledOnValidThread());
353 DCHECK_EQ(state_, State::CHECKING_DISK_SPACE);
354
355 if (stop_requested_) {
209 VLOG(1) << "Stop() called while checking disk space"; 356 VLOG(1) << "Stop() called while checking disk space";
357 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
210 return; 358 return;
211 } 359 }
360
212 if (disk_free_bytes < 0) { 361 if (disk_free_bytes < 0) {
213 LOG(ERROR) << "ARC: Failed to get free disk space"; 362 LOG(ERROR) << "ARC: Failed to get free disk space";
214 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 363 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
215 return; 364 return;
216 } 365 }
217 if (disk_free_bytes < kCriticalDiskFreeBytes) { 366 if (disk_free_bytes < kCriticalDiskFreeBytes) {
218 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC"; 367 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC";
219 AbortBoot(ArcBridgeService::StopReason::LOW_DISK_SPACE); 368 OnStopped(ArcBridgeService::StopReason::LOW_DISK_SPACE);
220 return; 369 return;
221 } 370 }
222 SetState(State::SOCKET_CREATING); 371
372 VLOG(2) << "Disk space check is done. Creating socket...";
373 state_ = State::CREATING_SOCKET;
223 base::PostTaskAndReplyWithResult( 374 base::PostTaskAndReplyWithResult(
224 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 375 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
225 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), 376 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket),
226 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, 377 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated,
227 weak_factory_.GetWeakPtr())); 378 weak_factory_.GetWeakPtr()));
228 } 379 }
229 380
230 // static 381 // static
231 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { 382 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() {
232 base::FilePath socket_path(kArcBridgeSocketPath); 383 base::FilePath socket_path(kArcBridgeSocketPath);
233 384
234 int raw_fd = -1; 385 int raw_fd = -1;
235 if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd)) { 386 if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd))
236 return base::ScopedFD(); 387 return base::ScopedFD();
237 }
238 base::ScopedFD socket_fd(raw_fd); 388 base::ScopedFD socket_fd(raw_fd);
239 389
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. 390 // Change permissions on the socket.
252 struct group arc_bridge_group; 391 struct group arc_bridge_group;
253 struct group* arc_bridge_group_res = nullptr; 392 struct group* arc_bridge_group_res = nullptr;
254 char buf[10000]; 393 char buf[10000];
255 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf, 394 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf,
256 sizeof(buf), &arc_bridge_group_res)) < 0) { 395 sizeof(buf), &arc_bridge_group_res)) < 0) {
257 PLOG(ERROR) << "getgrnam_r"; 396 PLOG(ERROR) << "getgrnam_r";
258 return base::ScopedFD(); 397 return base::ScopedFD();
259 } 398 }
260 399
(...skipping 11 matching lines...) Expand all
272 if (!base::SetPosixFilePermissions(socket_path, 0660)) { 411 if (!base::SetPosixFilePermissions(socket_path, 0660)) {
273 PLOG(ERROR) << "Could not set permissions: " << socket_path.value(); 412 PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
274 return base::ScopedFD(); 413 return base::ScopedFD();
275 } 414 }
276 415
277 return socket_fd; 416 return socket_fd;
278 } 417 }
279 418
280 void ArcBridgeBootstrapImpl::OnSocketCreated(base::ScopedFD socket_fd) { 419 void ArcBridgeBootstrapImpl::OnSocketCreated(base::ScopedFD socket_fd) {
281 DCHECK(thread_checker_.CalledOnValidThread()); 420 DCHECK(thread_checker_.CalledOnValidThread());
282 if (state_ != State::SOCKET_CREATING) { 421 DCHECK_EQ(state_, State::CREATING_SOCKET);
422
423 if (stop_requested_) {
283 VLOG(1) << "Stop() called while connecting"; 424 VLOG(1) << "Stop() called while connecting";
425 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
284 return; 426 return;
285 } 427 }
286 SetState(State::STARTING);
287 428
288 if (!socket_fd.is_valid()) { 429 if (!socket_fd.is_valid()) {
289 LOG(ERROR) << "ARC: Error creating socket"; 430 LOG(ERROR) << "ARC: Error creating socket";
290 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 431 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
291 return; 432 return;
292 } 433 }
293 434
435 VLOG(2) << "Socket is created. Starting ARC instance...";
436 state_ = State::STARTING_INSTANCE;
294 user_manager::UserManager* user_manager = user_manager::UserManager::Get(); 437 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
295 DCHECK(user_manager->GetPrimaryUser()); 438 DCHECK(user_manager->GetPrimaryUser());
296 const cryptohome::Identification cryptohome_id( 439 const cryptohome::Identification cryptohome_id(
297 user_manager->GetPrimaryUser()->GetAccountId()); 440 user_manager->GetPrimaryUser()->GetAccountId());
298 441
299 chromeos::SessionManagerClient* session_manager_client = 442 chromeos::SessionManagerClient* session_manager_client =
300 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 443 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
301 session_manager_client->StartArcInstance( 444 session_manager_client->StartArcInstance(
302 cryptohome_id, 445 cryptohome_id,
303 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, 446 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted,
304 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); 447 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd)));
305 } 448 }
306 449
307 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, 450 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd,
308 bool success) { 451 bool success) {
309 DCHECK(thread_checker_.CalledOnValidThread()); 452 DCHECK(thread_checker_.CalledOnValidThread());
453 if (state_ == State::STOPPED) {
454 // This is the case that error is notified via DBus before the
455 // OnInstanceStarted() callback is invoked. The stopping procedure has
456 // been run, so do nothing.
457 return;
458 }
459
460 DCHECK_EQ(state_, State::STARTING_INSTANCE);
461
462 if (stop_requested_) {
463 if (success) {
464 // The ARC instance has started to run. Request to stop.
465 StopArcInstance();
466 return;
467 }
468 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
469 return;
470 }
471
310 if (!success) { 472 if (!success) {
311 LOG(ERROR) << "Failed to start ARC instance"; 473 LOG(ERROR) << "Failed to start ARC instance";
312 // Roll back the state to SOCKET_CREATING to avoid sending the D-Bus signal 474 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; 475 return;
317 } 476 }
318 if (state_ != State::STARTING) { 477
319 VLOG(1) << "Stop() called when ARC is not running"; 478 VLOG(2) << "ARC instance is successfully started. Connecting Mojo...";
479 state_ = State::CONNECTING_MOJO;
480
481 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on
482 // Stop().
483 base::ScopedFD cancel_fd;
484 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
485 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
320 return; 486 return;
321 } 487 }
322 SetState(State::STARTED);
323 488
324 base::PostTaskAndReplyWithResult( 489 base::PostTaskAndReplyWithResult(
325 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, 490 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
326 base::Bind(&ArcBridgeBootstrapImpl::AcceptInstanceConnection, 491 base::Bind(&ArcBridgeBootstrapImpl::ConnectMojo, base::Passed(&socket_fd),
327 base::Passed(&socket_fd)), 492 base::Passed(&cancel_fd)),
328 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceConnected, 493 base::Bind(&ArcBridgeBootstrapImpl::OnMojoConnected,
329 weak_factory_.GetWeakPtr())); 494 weak_factory_.GetWeakPtr()));
330 } 495 }
331 496
332 // static 497 // static
333 base::ScopedFD ArcBridgeBootstrapImpl::AcceptInstanceConnection( 498 base::ScopedFD ArcBridgeBootstrapImpl::ConnectMojo(base::ScopedFD socket_fd,
334 base::ScopedFD socket_fd) { 499 base::ScopedFD cancel_fd) {
500 if (!WaitForSocketReadable(socket_fd.get(), cancel_fd.get())) {
501 VLOG(0) << "Mojo connecting is cancelled.";
502 return base::ScopedFD();
503 }
504
335 int raw_fd = -1; 505 int raw_fd = -1;
336 if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) { 506 if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) {
337 return base::ScopedFD(); 507 return base::ScopedFD();
338 } 508 }
339 base::ScopedFD scoped_fd(raw_fd); 509 base::ScopedFD scoped_fd(raw_fd);
340 510
341 // Hardcode pid 0 since it is unused in mojo. 511 // Hardcode pid 0 since it is unused in mojo.
342 const base::ProcessHandle kUnusedChildProcessHandle = 0; 512 const base::ProcessHandle kUnusedChildProcessHandle = 0;
343 mojo::edk::PlatformChannelPair channel_pair; 513 mojo::edk::PlatformChannelPair channel_pair;
344 mojo::edk::ChildProcessLaunched(kUnusedChildProcessHandle, 514 mojo::edk::ChildProcessLaunched(kUnusedChildProcessHandle,
345 channel_pair.PassServerHandle(), 515 channel_pair.PassServerHandle(),
346 mojo::edk::GenerateRandomToken()); 516 mojo::edk::GenerateRandomToken());
347 517
348 mojo::edk::ScopedPlatformHandleVectorPtr handles( 518 mojo::edk::ScopedPlatformHandleVectorPtr handles(
349 new mojo::edk::PlatformHandleVector{ 519 new mojo::edk::PlatformHandleVector{
350 channel_pair.PassClientHandle().release()}); 520 channel_pair.PassClientHandle().release()});
351 521
352 struct iovec iov = {const_cast<char*>(""), 1}; 522 struct iovec iov = {const_cast<char*>(""), 1};
353 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles( 523 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
354 mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(), 524 mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(),
355 handles->size()); 525 handles->size());
356 if (result == -1) { 526 if (result == -1) {
357 PLOG(ERROR) << "sendmsg"; 527 PLOG(ERROR) << "sendmsg";
358 return base::ScopedFD(); 528 return base::ScopedFD();
359 } 529 }
360 530
361 return scoped_fd; 531 return scoped_fd;
362 } 532 }
363 533
364 void ArcBridgeBootstrapImpl::OnInstanceConnected(base::ScopedFD fd) { 534 void ArcBridgeBootstrapImpl::OnMojoConnected(base::ScopedFD fd) {
365 DCHECK(thread_checker_.CalledOnValidThread()); 535 DCHECK(thread_checker_.CalledOnValidThread());
366 if (state_ != State::STARTED) { 536
367 VLOG(1) << "Stop() called when ARC is not running"; 537 if (state_ == State::STOPPED) {
538 // This is the case that error is notified via DBus before the
539 // OnMojoConnected() callback is invoked. The stopping procedure has
540 // been run, so do nothing.
368 return; 541 return;
369 } 542 }
543
544 DCHECK_EQ(state_, State::CONNECTING_MOJO);
545 accept_cancel_pipe_.reset();
546
547 if (stop_requested_) {
548 StopArcInstance();
549 return;
550 }
551
370 if (!fd.is_valid()) { 552 if (!fd.is_valid()) {
371 LOG(ERROR) << "Invalid handle"; 553 LOG(ERROR) << "Invalid handle";
372 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 554 StopArcInstance();
373 return; 555 return;
374 } 556 }
557
375 mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe( 558 mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe(
376 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release()))); 559 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release())));
377 if (!server_pipe.is_valid()) { 560 if (!server_pipe.is_valid()) {
378 LOG(ERROR) << "Invalid pipe"; 561 LOG(ERROR) << "Invalid pipe";
379 AbortBoot(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); 562 StopArcInstance();
380 return; 563 return;
381 } 564 }
382 SetState(State::READY); 565
383 mojom::ArcBridgeInstancePtr instance; 566 mojom::ArcBridgeInstancePtr instance;
384 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>( 567 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
385 std::move(server_pipe), 0u)); 568 std::move(server_pipe), 0u));
569 // TODO(hidehiko): Move to creating ArcBridgeHost here to fix the twisted
570 // state change.
571
572 VLOG(2) << "Mojo is connected. ARC is running.";
573 state_ = State::RUNNING;
386 delegate_->OnConnectionEstablished(std::move(instance)); 574 delegate_->OnConnectionEstablished(std::move(instance));
387 } 575 }
388 576
389 void ArcBridgeBootstrapImpl::Stop() { 577 void ArcBridgeBootstrapImpl::StopArcInstance() {
390 DCHECK(thread_checker_.CalledOnValidThread()); 578 DCHECK(thread_checker_.CalledOnValidThread());
391 if (state_ == State::STOPPED || state_ == State::STOPPING) { 579 DCHECK(state_ == State::STARTING_INSTANCE ||
392 VLOG(1) << "Stop() called when ARC is not running"; 580 state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
393 return; 581
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(). 582 // Notification will arrive through ArcInstanceStopped().
583 VLOG(2) << "Requesting to stop ARC instance";
403 chromeos::SessionManagerClient* session_manager_client = 584 chromeos::SessionManagerClient* session_manager_client =
404 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 585 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
405 session_manager_client->StopArcInstance( 586 session_manager_client->StopArcInstance(
406 base::Bind(&DoNothingInstanceStopped)); 587 base::Bind(&DoNothingInstanceStopped));
407 } 588 }
408 589
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) { 590 void ArcBridgeBootstrapImpl::ArcInstanceStopped(bool clean) {
420 DCHECK(thread_checker_.CalledOnValidThread()); 591 DCHECK(thread_checker_.CalledOnValidThread());
421 if (!clean) { 592 VLOG(2) << "Notified that ARC instance is stopped "
422 LOG(ERROR) << "ARC instance crashed"; 593 << (clean ? "cleanly" : "uncleanly");
423 // In case of multiple errors, report the first one. 594
424 if (stop_reason_ == ArcBridgeService::StopReason::SHUTDOWN) { 595 // In case that crash happens during connecting Mojo, we unlock the
425 stop_reason_ = ArcBridgeService::StopReason::CRASH; 596 // WorkerPool thread.
426 } 597 accept_cancel_pipe_.reset();
598
599 ArcBridgeService::StopReason reason;
600 if (stop_requested_) {
601 // If the ARC instance is stopped after its explicit request,
602 // return SHUTDOWN.
603 reason = ArcBridgeService::StopReason::SHUTDOWN;
604 } else if (clean) {
605 // If the ARC instance is stopped, but it is not explicitly requested,
606 // then this is triggered by some failure during the starting procedure.
607 // Return GENERIC_BOOT_FAILURE for the case.
608 reason = ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE;
609 } else {
610 // Otherwise, this is caused by CRASH occured inside of the ARC instance.
611 reason = ArcBridgeService::StopReason::CRASH;
427 } 612 }
428 SetState(State::STOPPED); 613 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_);
437 if (state_ == State::STOPPED) {
438 DCHECK(delegate_);
439 delegate_->OnStopped(stop_reason_);
440 }
441 } 614 }
442 615
443 } // namespace 616 } // namespace
444 617
445 // static 618 // static
446 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { 619 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() {
447 return base::WrapUnique(new ArcBridgeBootstrapImpl()); 620 return base::WrapUnique(new ArcBridgeBootstrapImpl());
448 } 621 }
449 622
450 } // namespace arc 623 } // namespace arc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698