• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/commands/run_cvd/boot_state_machine.h"
18 
19 #include <memory>
20 #include <thread>
21 
22 #include "android-base/logging.h"
23 #include "common/libs/fs/shared_fd.h"
24 #include "host/commands/kernel_log_monitor/kernel_log_server.h"
25 #include "host/commands/kernel_log_monitor/utils.h"
26 #include "host/commands/run_cvd/runner_defs.h"
27 
28 namespace cuttlefish {
29 
CvdBootStateMachine(SharedFD fg_launcher_pipe,SharedFD reboot_notification,SharedFD boot_events_pipe)30 CvdBootStateMachine::CvdBootStateMachine(SharedFD fg_launcher_pipe,
31                                          SharedFD reboot_notification,
32                                          SharedFD boot_events_pipe)
33     : fg_launcher_pipe_(fg_launcher_pipe),
34       reboot_notification_(reboot_notification),
35       state_(kBootStarted) {
36   boot_event_handler_ = std::thread([this, boot_events_pipe]() {
37     while (true) {
38       SharedFDSet fd_set;
39       fd_set.Set(boot_events_pipe);
40       int result = Select(&fd_set, nullptr, nullptr, nullptr);
41       if (result < 0) {
42         PLOG(FATAL) << "Failed to call Select";
43         return;
44       }
45       if (!fd_set.IsSet(boot_events_pipe)) {
46         continue;
47       }
48       auto sent_code = OnBootEvtReceived(boot_events_pipe);
49       if (sent_code) {
50         break;
51       }
52     }
53   });
54 }
55 
~CvdBootStateMachine()56 CvdBootStateMachine::~CvdBootStateMachine() { boot_event_handler_.join(); }
57 
58 // Returns true if the machine is left in a final state
OnBootEvtReceived(SharedFD boot_events_pipe)59 bool CvdBootStateMachine::OnBootEvtReceived(SharedFD boot_events_pipe) {
60   std::optional<monitor::ReadEventResult> read_result =
61       monitor::ReadEvent(boot_events_pipe);
62   if (!read_result) {
63     LOG(ERROR) << "Failed to read a complete kernel log boot event.";
64     state_ |= kGuestBootFailed;
65     return MaybeWriteNotification();
66   }
67 
68   if (read_result->event == monitor::Event::BootCompleted) {
69     LOG(INFO) << "Virtual device booted successfully";
70     state_ |= kGuestBootCompleted;
71   } else if (read_result->event == monitor::Event::BootFailed) {
72     LOG(ERROR) << "Virtual device failed to boot";
73     state_ |= kGuestBootFailed;
74   }  // Ignore the other signals
75 
76   return MaybeWriteNotification();
77 }
78 
BootCompleted() const79 bool CvdBootStateMachine::BootCompleted() const {
80   return state_ & kGuestBootCompleted;
81 }
82 
BootFailed() const83 bool CvdBootStateMachine::BootFailed() const {
84   return state_ & kGuestBootFailed;
85 }
86 
SendExitCode(RunnerExitCodes exit_code,SharedFD fd)87 void CvdBootStateMachine::SendExitCode(RunnerExitCodes exit_code, SharedFD fd) {
88   fd->Write(&exit_code, sizeof(exit_code));
89   // The foreground process will exit after receiving the exit code, if we try
90   // to write again we'll get a SIGPIPE
91   fd->Close();
92 }
93 
MaybeWriteNotification()94 bool CvdBootStateMachine::MaybeWriteNotification() {
95   std::vector<SharedFD> fds = {reboot_notification_, fg_launcher_pipe_};
96   for (auto& fd : fds) {
97     if (fd->IsOpen()) {
98       if (BootCompleted()) {
99         SendExitCode(RunnerExitCodes::kSuccess, fd);
100       } else if (state_ & kGuestBootFailed) {
101         SendExitCode(RunnerExitCodes::kVirtualDeviceBootFailed, fd);
102       }
103     }
104   }
105   // Either we sent the code before or just sent it, in any case the state is
106   // final
107   return BootCompleted() || (state_ & kGuestBootFailed);
108 }
109 
110 }  // namespace cuttlefish
111