/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "host/commands/run_cvd/boot_state_machine.h" #include #include #include "android-base/logging.h" #include "common/libs/fs/shared_fd.h" #include "host/commands/kernel_log_monitor/kernel_log_server.h" #include "host/commands/kernel_log_monitor/utils.h" #include "host/commands/run_cvd/runner_defs.h" namespace cuttlefish { CvdBootStateMachine::CvdBootStateMachine(SharedFD fg_launcher_pipe, SharedFD reboot_notification, SharedFD boot_events_pipe) : fg_launcher_pipe_(fg_launcher_pipe), reboot_notification_(reboot_notification), state_(kBootStarted) { boot_event_handler_ = std::thread([this, boot_events_pipe]() { while (true) { SharedFDSet fd_set; fd_set.Set(boot_events_pipe); int result = Select(&fd_set, nullptr, nullptr, nullptr); if (result < 0) { PLOG(FATAL) << "Failed to call Select"; return; } if (!fd_set.IsSet(boot_events_pipe)) { continue; } auto sent_code = OnBootEvtReceived(boot_events_pipe); if (sent_code) { break; } } }); } CvdBootStateMachine::~CvdBootStateMachine() { boot_event_handler_.join(); } // Returns true if the machine is left in a final state bool CvdBootStateMachine::OnBootEvtReceived(SharedFD boot_events_pipe) { std::optional read_result = monitor::ReadEvent(boot_events_pipe); if (!read_result) { LOG(ERROR) << "Failed to read a complete kernel log boot event."; state_ |= kGuestBootFailed; return MaybeWriteNotification(); } if (read_result->event == monitor::Event::BootCompleted) { LOG(INFO) << "Virtual device booted successfully"; state_ |= kGuestBootCompleted; } else if (read_result->event == monitor::Event::BootFailed) { LOG(ERROR) << "Virtual device failed to boot"; state_ |= kGuestBootFailed; } // Ignore the other signals return MaybeWriteNotification(); } bool CvdBootStateMachine::BootCompleted() const { return state_ & kGuestBootCompleted; } bool CvdBootStateMachine::BootFailed() const { return state_ & kGuestBootFailed; } void CvdBootStateMachine::SendExitCode(RunnerExitCodes exit_code, SharedFD fd) { fd->Write(&exit_code, sizeof(exit_code)); // The foreground process will exit after receiving the exit code, if we try // to write again we'll get a SIGPIPE fd->Close(); } bool CvdBootStateMachine::MaybeWriteNotification() { std::vector fds = {reboot_notification_, fg_launcher_pipe_}; for (auto& fd : fds) { if (fd->IsOpen()) { if (BootCompleted()) { SendExitCode(RunnerExitCodes::kSuccess, fd); } else if (state_ & kGuestBootFailed) { SendExitCode(RunnerExitCodes::kVirtualDeviceBootFailed, fd); } } } // Either we sent the code before or just sent it, in any case the state is // final return BootCompleted() || (state_ & kGuestBootFailed); } } // namespace cuttlefish