blob: 64eb893f5f46dcd13c4be9e8f08c0d339da98c3c [file] [log] [blame]
/*
* 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 <memory>
#include <thread>
#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<monitor::ReadEventResult> 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<SharedFD> 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