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

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

Powered by Google App Engine
This is Rietveld 408576698