OLD | NEW |
---|---|
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 <poll.h> |
10 #include <unistd.h> | 10 #include <unistd.h> |
(...skipping 28 matching lines...) Expand all Loading... | |
39 | 39 |
40 namespace arc { | 40 namespace arc { |
41 | 41 |
42 namespace { | 42 namespace { |
43 | 43 |
44 const base::FilePath::CharType kArcBridgeSocketPath[] = | 44 const base::FilePath::CharType kArcBridgeSocketPath[] = |
45 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock"); | 45 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock"); |
46 | 46 |
47 const char kArcBridgeSocketGroup[] = "arc-bridge"; | 47 const char kArcBridgeSocketGroup[] = "arc-bridge"; |
48 | 48 |
49 const base::FilePath::CharType kDiskCheckPath[] = "/home"; | 49 // TODO(hidehiko): Share the constant between Chrome and ChromeOS. |
Daniel Erat
2016/10/06 14:53:54
i agree that this constant should be moved to syst
hidehiko
2016/10/07 05:28:38
Done.
| |
50 | 50 constexpr char kArcLowDiskError[] = |
51 const int64_t kCriticalDiskFreeBytes = 64 << 20; // 64MB | 51 "org.chromium.SessionManagerInterface.LowFreeDisk"; |
52 | 52 |
53 // This is called when StopArcInstance D-Bus method completes. Since we have the | 53 // This is called when StopArcInstance D-Bus method completes. Since we have the |
54 // ArcInstanceStopped() callback and are notified if StartArcInstance fails, we | 54 // ArcInstanceStopped() callback and are notified if StartArcInstance fails, we |
55 // don't need to do anything when StopArcInstance completes. | 55 // don't need to do anything when StopArcInstance completes. |
56 void DoNothingInstanceStopped(bool) {} | 56 void DoNothingInstanceStopped(bool) {} |
57 | 57 |
58 chromeos::SessionManagerClient* GetSessionManagerClient() { | 58 chromeos::SessionManagerClient* GetSessionManagerClient() { |
59 // If the DBusThreadManager or the SessionManagerClient aren't available, | 59 // If the DBusThreadManager or the SessionManagerClient aren't available, |
60 // there isn't much we can do. This should only happen when running tests. | 60 // there isn't much we can do. This should only happen when running tests. |
61 if (!chromeos::DBusThreadManager::IsInitialized() || | 61 if (!chromeos::DBusThreadManager::IsInitialized() || |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least | 108 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least |
109 // state-machine part. | 109 // state-machine part. |
110 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap, | 110 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap, |
111 public chromeos::SessionManagerClient::Observer { | 111 public chromeos::SessionManagerClient::Observer { |
112 public: | 112 public: |
113 // The possible states of the bootstrap connection. In the normal flow, | 113 // The possible states of the bootstrap connection. In the normal flow, |
114 // the state changes in the following sequence: | 114 // the state changes in the following sequence: |
115 // | 115 // |
116 // NOT_STARTED | 116 // NOT_STARTED |
117 // Start() -> | 117 // Start() -> |
118 // CHECKING_DISK_SPACE | |
119 // OnDiskSpaceChecked() -> | |
120 // CREATING_SOCKET | 118 // CREATING_SOCKET |
121 // CreateSocket() -> OnSocketCreated() -> | 119 // CreateSocket() -> OnSocketCreated() -> |
122 // STARTING_INSTANCE | 120 // STARTING_INSTANCE |
123 // -> OnInstanceStarted() -> | 121 // -> OnInstanceStarted() -> |
124 // CONNECTING_MOJO | 122 // CONNECTING_MOJO |
125 // ConnectMojo() -> OnMojoConnected() -> | 123 // ConnectMojo() -> OnMojoConnected() -> |
126 // RUNNING | 124 // RUNNING |
127 // | 125 // |
128 // At any state, Stop() can be called. It does not immediately stop the | 126 // At any state, Stop() can be called. It does not immediately stop the |
129 // instance, but will eventually stop it. | 127 // instance, but will eventually stop it. |
130 // The actual stop will be notified via Observer::OnStopped(). | 128 // The actual stop will be notified via Observer::OnStopped(). |
131 // | 129 // |
132 // When Stop() is called, it makes various behavior based on the current | 130 // When Stop() is called, it makes various behavior based on the current |
133 // phase. | 131 // phase. |
134 // | 132 // |
135 // NOT_STARTED: | 133 // NOT_STARTED: |
136 // Do nothing. Immediately transition to the STOPPED state. | 134 // Do nothing. Immediately transition to the STOPPED state. |
137 // CHECKING_DISK_SPACE, CREATING_SOCKET: | 135 // CREATING_SOCKET: |
138 // The main task of those phases runs on WorkerPool thread. So, Stop() | 136 // The main task of the phase runs on WorkerPool thread. So, Stop() just |
139 // just sets the flag and return. On the main task completion, a callback | 137 // sets the flag and return. On the main task completion, a callback |
140 // will run on the main (practically UI) thread, and the flag is checked | 138 // will run on the main (practically UI) thread, and the flag is checked |
141 // at the beginning of them. This should work under the assumption that | 139 // at the beginning of them. This should work under the assumption that |
142 // the main tasks do not block indefinitely. | 140 // the main tasks do not block indefinitely. |
143 // STARTING_INSTANCE: | 141 // STARTING_INSTANCE: |
144 // The ARC instance is starting via SessionManager. So, similar to | 142 // The ARC instance is starting via SessionManager. So, similar to |
145 // CHECKING_DISK_SPACE/CREATING_SOCKET cases, Stop() just sets the flag | 143 // CREATING_SOCKET case, Stop() just sets the flag and return. In its |
146 // and return. In its callback, it checks if ARC instance is successfully | 144 // callback, it checks if ARC instance is successfully started or not. |
147 // started or not. In case of success, a request to stop the ARC instance | 145 // In case of success, a request to stop the ARC instance is sent to |
148 // is sent to SessionManager. Its completion will be notified via | 146 // SessionManager. Its completion will be notified via ArcInstanceStopped. |
149 // ArcInstanceStopped. Otherwise, it just turns into STOPPED state. | 147 // Otherwise, it just turns into STOPPED state. |
150 // CONNECTING_MOJO: | 148 // CONNECTING_MOJO: |
151 // The main task runs on WorkerPool thread, but it is blocking call. | 149 // The main task runs on WorkerPool thread, but it is blocking call. |
152 // So, Stop() sends a request to cancel the blocking by closing the pipe | 150 // So, Stop() sends a request to cancel the blocking by closing the pipe |
153 // whose read side is also polled. Then, in its callback, similar to | 151 // whose read side is also polled. Then, in its callback, similar to |
154 // STARTING_INSTANCE, a request to stop the ARC instance is sent to | 152 // STARTING_INSTANCE, a request to stop the ARC instance is sent to |
155 // SessionManager, and ArcInstanceStopped handles remaining procedure. | 153 // SessionManager, and ArcInstanceStopped handles remaining procedure. |
156 // RUNNING: | 154 // RUNNING: |
157 // There is no more callback which runs on normal flow, so Stop() requests | 155 // There is no more callback which runs on normal flow, so Stop() requests |
158 // to stop the ARC instance via SessionManager. | 156 // to stop the ARC instance via SessionManager. |
159 // | 157 // |
(...skipping 19 matching lines...) Expand all Loading... | |
179 // Specifically, in STOPPED state, there may be inflight operations or | 177 // Specifically, in STOPPED state, there may be inflight operations or |
180 // pending callbacks. Though, what they do is just do-nothing conceptually | 178 // pending callbacks. Though, what they do is just do-nothing conceptually |
181 // and they can be safely ignored. | 179 // and they can be safely ignored. |
182 // | 180 // |
183 // 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 |
184 // in chronological order. | 182 // in chronological order. |
185 enum class State { | 183 enum class State { |
186 // ARC is not yet started. | 184 // ARC is not yet started. |
187 NOT_STARTED, | 185 NOT_STARTED, |
188 | 186 |
189 // Checking the disk space. | |
190 CHECKING_DISK_SPACE, | |
191 | |
192 // An UNIX socket is being created. | 187 // An UNIX socket is being created. |
193 CREATING_SOCKET, | 188 CREATING_SOCKET, |
194 | 189 |
195 // The request to start the instance has been sent. | 190 // The request to start the instance has been sent. |
196 STARTING_INSTANCE, | 191 STARTING_INSTANCE, |
197 | 192 |
198 // The instance has started. Waiting for it to connect to the IPC bridge. | 193 // The instance has started. Waiting for it to connect to the IPC bridge. |
199 CONNECTING_MOJO, | 194 CONNECTING_MOJO, |
200 | 195 |
201 // The instance is fully set up. | 196 // The instance is fully set up. |
202 RUNNING, | 197 RUNNING, |
203 | 198 |
204 // ARC is terminated. | 199 // ARC is terminated. |
205 STOPPED, | 200 STOPPED, |
206 }; | 201 }; |
207 | 202 |
208 ArcBridgeBootstrapImpl(); | 203 ArcBridgeBootstrapImpl(); |
209 ~ArcBridgeBootstrapImpl() override; | 204 ~ArcBridgeBootstrapImpl() override; |
210 | 205 |
211 // ArcBridgeBootstrap: | 206 // ArcBridgeBootstrap: |
212 void Start() override; | 207 void Start() override; |
213 void Stop() override; | 208 void Stop() override; |
214 | 209 |
215 private: | 210 private: |
216 // Called after getting the device free disk space. | |
217 void OnFreeDiskSpaceObtained(int64_t disk_free_bytes); | |
218 | |
219 // Creates the UNIX socket on the bootstrap thread and then processes its | 211 // Creates the UNIX socket on the bootstrap thread and then processes its |
220 // file descriptor. | 212 // file descriptor. |
221 static base::ScopedFD CreateSocket(); | 213 static base::ScopedFD CreateSocket(); |
222 void OnSocketCreated(base::ScopedFD fd); | 214 void OnSocketCreated(base::ScopedFD fd); |
223 | 215 |
224 // DBus callback for StartArcInstance(). | 216 // DBus callback for StartArcInstance(). |
225 void OnInstanceStarted(base::ScopedFD socket_fd, bool success); | 217 void OnInstanceStarted(base::ScopedFD socket_fd, |
218 bool success, | |
219 const std::string& error); | |
226 | 220 |
227 // Synchronously accepts a connection on |socket_fd| and then processes the | 221 // Synchronously accepts a connection on |socket_fd| and then processes the |
228 // connected socket's file descriptor. | 222 // connected socket's file descriptor. |
229 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd, | 223 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd, |
230 base::ScopedFD cancel_fd); | 224 base::ScopedFD cancel_fd); |
231 void OnMojoConnected(base::ScopedFD fd); | 225 void OnMojoConnected(base::ScopedFD fd); |
232 | 226 |
233 // Request to stop ARC instance via DBus. | 227 // Request to stop ARC instance via DBus. |
234 void StopArcInstance(); | 228 void StopArcInstance(); |
235 | 229 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
276 chromeos::SessionManagerClient* client = GetSessionManagerClient(); | 270 chromeos::SessionManagerClient* client = GetSessionManagerClient(); |
277 if (client == nullptr) | 271 if (client == nullptr) |
278 return; | 272 return; |
279 client->RemoveObserver(this); | 273 client->RemoveObserver(this); |
280 } | 274 } |
281 | 275 |
282 void ArcBridgeBootstrapImpl::Start() { | 276 void ArcBridgeBootstrapImpl::Start() { |
283 DCHECK(thread_checker_.CalledOnValidThread()); | 277 DCHECK(thread_checker_.CalledOnValidThread()); |
284 DCHECK_EQ(state_, State::NOT_STARTED); | 278 DCHECK_EQ(state_, State::NOT_STARTED); |
285 VLOG(2) << "Starting ARC session."; | 279 VLOG(2) << "Starting ARC session."; |
286 VLOG(2) << "Checking disk space..."; | 280 VLOG(2) << "Creating socket..."; |
287 state_ = State::CHECKING_DISK_SPACE; | |
288 | 281 |
289 // TODO(crbug.com/628124): Move disk space checking logic to session_manager. | |
290 base::PostTaskAndReplyWithResult( | |
291 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, | |
292 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace, | |
293 base::FilePath(kDiskCheckPath)), | |
294 base::Bind(&ArcBridgeBootstrapImpl::OnFreeDiskSpaceObtained, | |
295 weak_factory_.GetWeakPtr())); | |
296 } | |
297 | |
298 void ArcBridgeBootstrapImpl::OnFreeDiskSpaceObtained(int64_t disk_free_bytes) { | |
299 DCHECK(thread_checker_.CalledOnValidThread()); | |
300 DCHECK_EQ(state_, State::CHECKING_DISK_SPACE); | |
301 | |
302 if (stop_requested_) { | |
303 VLOG(1) << "Stop() called while checking disk space"; | |
304 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | |
305 return; | |
306 } | |
307 | |
308 if (disk_free_bytes < 0) { | |
309 LOG(ERROR) << "ARC: Failed to get free disk space"; | |
310 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); | |
311 return; | |
312 } | |
313 if (disk_free_bytes < kCriticalDiskFreeBytes) { | |
314 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC"; | |
315 OnStopped(ArcBridgeService::StopReason::LOW_DISK_SPACE); | |
316 return; | |
317 } | |
318 | |
319 VLOG(2) << "Disk space check is done. Creating socket..."; | |
320 state_ = State::CREATING_SOCKET; | 282 state_ = State::CREATING_SOCKET; |
321 base::PostTaskAndReplyWithResult( | 283 base::PostTaskAndReplyWithResult( |
322 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, | 284 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, |
323 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), | 285 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), |
324 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, | 286 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, |
325 weak_factory_.GetWeakPtr())); | 287 weak_factory_.GetWeakPtr())); |
326 } | 288 } |
327 | 289 |
328 // static | 290 // static |
329 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { | 291 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 chromeos::SessionManagerClient* session_manager_client = | 354 chromeos::SessionManagerClient* session_manager_client = |
393 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); | 355 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); |
394 session_manager_client->StartArcInstance( | 356 session_manager_client->StartArcInstance( |
395 cryptohome_id, | 357 cryptohome_id, |
396 disable_boot_completed_broadcast, | 358 disable_boot_completed_broadcast, |
397 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, | 359 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, |
398 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); | 360 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); |
399 } | 361 } |
400 | 362 |
401 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, | 363 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, |
402 bool success) { | 364 bool success, |
365 const std::string& error) { | |
403 DCHECK(thread_checker_.CalledOnValidThread()); | 366 DCHECK(thread_checker_.CalledOnValidThread()); |
404 if (state_ == State::STOPPED) { | 367 if (state_ == State::STOPPED) { |
405 // This is the case that error is notified via DBus before the | 368 // This is the case that error is notified via DBus before the |
406 // OnInstanceStarted() callback is invoked. The stopping procedure has | 369 // OnInstanceStarted() callback is invoked. The stopping procedure has |
407 // been run, so do nothing. | 370 // been run, so do nothing. |
408 return; | 371 return; |
409 } | 372 } |
410 | 373 |
411 DCHECK_EQ(state_, State::STARTING_INSTANCE); | 374 DCHECK_EQ(state_, State::STARTING_INSTANCE); |
412 | 375 |
413 if (stop_requested_) { | 376 if (stop_requested_) { |
414 if (success) { | 377 if (success) { |
415 // The ARC instance has started to run. Request to stop. | 378 // The ARC instance has started to run. Request to stop. |
416 StopArcInstance(); | 379 StopArcInstance(); |
417 return; | 380 return; |
418 } | 381 } |
419 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | 382 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); |
420 return; | 383 return; |
421 } | 384 } |
422 | 385 |
423 if (!success) { | 386 if (!success) { |
424 LOG(ERROR) << "Failed to start ARC instance"; | 387 LOG(ERROR) << "Failed to start ARC instance"; |
425 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); | 388 OnStopped(error == kArcLowDiskError |
389 ? ArcBridgeService::StopReason::LOW_DISK_SPACE | |
390 : ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); | |
426 return; | 391 return; |
427 } | 392 } |
428 | 393 |
429 VLOG(2) << "ARC instance is successfully started. Connecting Mojo..."; | 394 VLOG(2) << "ARC instance is successfully started. Connecting Mojo..."; |
430 state_ = State::CONNECTING_MOJO; | 395 state_ = State::CONNECTING_MOJO; |
431 | 396 |
432 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on | 397 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on |
433 // Stop(). | 398 // Stop(). |
434 base::ScopedFD cancel_fd; | 399 base::ScopedFD cancel_fd; |
435 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) { | 400 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) { |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
533 if (stop_requested_) | 498 if (stop_requested_) |
534 return; | 499 return; |
535 | 500 |
536 stop_requested_ = true; | 501 stop_requested_ = true; |
537 arc_bridge_host_.reset(); | 502 arc_bridge_host_.reset(); |
538 switch (state_) { | 503 switch (state_) { |
539 case State::NOT_STARTED: | 504 case State::NOT_STARTED: |
540 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | 505 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); |
541 return; | 506 return; |
542 | 507 |
543 case State::CHECKING_DISK_SPACE: | |
544 case State::CREATING_SOCKET: | 508 case State::CREATING_SOCKET: |
545 case State::STARTING_INSTANCE: | 509 case State::STARTING_INSTANCE: |
546 // Before starting the ARC instance, we do nothing here. | 510 // Before starting the ARC instance, we do nothing here. |
547 // At some point, a callback will be invoked on UI thread, | 511 // At some point, a callback will be invoked on UI thread, |
548 // and stopping procedure will be run there. | 512 // and stopping procedure will be run there. |
549 // On Chrome shutdown, it is not the case because the message loop is | 513 // On Chrome shutdown, it is not the case because the message loop is |
550 // already stopped here. Practically, it is not a problem because; | 514 // already stopped here. Practically, it is not a problem because; |
551 // - On disk space checking or on socket creating, it is ok to simply | 515 // - On socket creating, it is ok to simply ignore such cases, |
552 // ignore such cases, because we no-longer continue the bootstrap | 516 // because we no-longer continue the bootstrap procedure. |
553 // procedure. | |
554 // - On starting instance, the container instance can be leaked. | 517 // - On starting instance, the container instance can be leaked. |
555 // Practically it is not problematic because the session manager will | 518 // Practically it is not problematic because the session manager will |
556 // clean it up. | 519 // clean it up. |
557 return; | 520 return; |
558 | 521 |
559 case State::CONNECTING_MOJO: | 522 case State::CONNECTING_MOJO: |
560 // Mojo connection is being waited on a WorkerPool thread. | 523 // Mojo connection is being waited on a WorkerPool thread. |
561 // Request to cancel it. Following stopping procedure will run | 524 // Request to cancel it. Following stopping procedure will run |
562 // in its callback. | 525 // in its callback. |
563 DCHECK(accept_cancel_pipe_.get()); | 526 DCHECK(accept_cancel_pipe_.get()); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
637 void ArcBridgeBootstrap::RemoveObserver(Observer* observer) { | 600 void ArcBridgeBootstrap::RemoveObserver(Observer* observer) { |
638 observer_list_.RemoveObserver(observer); | 601 observer_list_.RemoveObserver(observer); |
639 } | 602 } |
640 | 603 |
641 // static | 604 // static |
642 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { | 605 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { |
643 return base::MakeUnique<ArcBridgeBootstrapImpl>(); | 606 return base::MakeUnique<ArcBridgeBootstrapImpl>(); |
644 } | 607 } |
645 | 608 |
646 } // namespace arc | 609 } // namespace arc |
OLD | NEW |