aboutsummaryrefslogtreecommitdiff
path: root/host/commands/run_cvd/boot_state_machine.cc
diff options
context:
space:
mode:
Diffstat (limited to 'host/commands/run_cvd/boot_state_machine.cc')
-rw-r--r--host/commands/run_cvd/boot_state_machine.cc233
1 files changed, 162 insertions, 71 deletions
diff --git a/host/commands/run_cvd/boot_state_machine.cc b/host/commands/run_cvd/boot_state_machine.cc
index 67060e07a..e78aac072 100644
--- a/host/commands/run_cvd/boot_state_machine.cc
+++ b/host/commands/run_cvd/boot_state_machine.cc
@@ -21,6 +21,7 @@
#include <memory>
#include <thread>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <gflags/gflags.h>
@@ -43,72 +44,47 @@ namespace {
// Forks run_cvd into a daemonized child process. The current process continues
// only until the child has signalled that the boot is finished.
//
-// How the child signals when the boot is finished depends on whether we are
-// restoring from a snapshot.
-//
-// * When booting normally, `DaemonizeLauncher` returns the write end of a
-// pipe. The child is expected to write a `RunnerExitCodes` into the pipe
-// when the boot finishes.
-// * When restoring from a snapshot, `DaemonizeLauncher` returns an invalid
-// `SharedFD`. The child is expected to write an arbitrary byte to the
-// instance's "restore_pipe" and then the parent assumes the restore was
-// successful.
-//
-// We should consider unifying these two types of pipes.
+// `DaemonizeLauncher` returns the write end of a pipe. The child is expected
+// to write a `RunnerExitCodes` into the pipe when the boot finishes.
Result<SharedFD> DaemonizeLauncher(const CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
- SharedFD read_end, write_end, restore_pipe_read;
- if (IsRestoring(config)) {
- restore_pipe_read =
- CF_EXPECT(SharedFD::Fifo(instance.restore_pipe_name(), 0600),
- "Unable to create restore fifo");
- } else {
- CF_EXPECT(SharedFD::Pipe(&read_end, &write_end), "Unable to create pipe");
- }
+ SharedFD read_end, write_end;
+ CF_EXPECT(SharedFD::Pipe(&read_end, &write_end), "Unable to create pipe");
auto pid = fork();
if (pid) {
// Explicitly close here, otherwise we may end up reading forever if the
// child process dies.
write_end->Close();
RunnerExitCodes exit_code;
- if (IsRestoring(config)) {
- if (!restore_pipe_read->IsOpen()) {
- LOG(ERROR) << "Error opening restore pipe: "
- << restore_pipe_read->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- // Try to read from restore pipe. IF successfully reads, that means logcat
- // has started, and the VM has resumed. Exit the thread.
- char buff[1];
- auto read = restore_pipe_read->Read(buff, 1);
- if (read <= 0) {
- LOG(ERROR) << "Could not read restore pipe: "
- << restore_pipe_read->StrError();
- std::exit(RunnerExitCodes::kDaemonizationError);
- }
- exit_code = RunnerExitCodes::kSuccess;
- LOG(INFO) << "Virtual device restored successfully";
- std::exit(exit_code);
- }
auto bytes_read = read_end->Read(&exit_code, sizeof(exit_code));
if (bytes_read != sizeof(exit_code)) {
LOG(ERROR) << "Failed to read a complete exit code, read " << bytes_read
<< " bytes only instead of the expected " << sizeof(exit_code);
exit_code = RunnerExitCodes::kPipeIOError;
} else if (exit_code == RunnerExitCodes::kSuccess) {
- LOG(INFO) << "Virtual device booted successfully";
+ if (IsRestoring(config)) {
+ LOG(INFO) << "Virtual device restored successfully";
+ } else {
+ LOG(INFO) << "Virtual device booted successfully";
+ }
} else if (exit_code == RunnerExitCodes::kVirtualDeviceBootFailed) {
- LOG(ERROR) << "Virtual device failed to boot";
+ if (IsRestoring(config)) {
+ LOG(ERROR) << "Virtual device failed to restore";
+ } else {
+ LOG(ERROR) << "Virtual device failed to boot";
+ }
if (!instance.fail_fast()) {
LOG(ERROR) << "Device has been left running for debug";
}
} else {
LOG(ERROR) << "Unexpected exit code: " << exit_code;
}
- if (exit_code == RunnerExitCodes::kSuccess) {
- LOG(INFO) << kBootCompletedMessage;
- } else {
- LOG(INFO) << kBootFailedMessage;
+ if (!IsRestoring(config)) {
+ if (exit_code == RunnerExitCodes::kSuccess) {
+ LOG(INFO) << kBootCompletedMessage;
+ } else {
+ LOG(INFO) << kBootFailedMessage;
+ }
}
std::exit(exit_code);
} else {
@@ -145,12 +121,8 @@ Result<SharedFD> DaemonizeLauncher(const CuttlefishConfig& config) {
std::exit(RunnerExitCodes::kDaemonizationError);
}
- if (IsRestoring(config)) {
- return {};
- } else {
- read_end->Close();
- return write_end;
- }
+ read_end->Close();
+ return write_end;
}
}
@@ -183,11 +155,15 @@ Result<SharedFD> ProcessLeader(
class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
public:
INJECT(
- CvdBootStateMachine(AutoSetup<ProcessLeader>::Type& process_leader,
+ CvdBootStateMachine(const CuttlefishConfig& config,
+ AutoSetup<ProcessLeader>::Type& process_leader,
KernelLogPipeProvider& kernel_log_pipe_provider,
+ const vm_manager::VmManager& vm_manager,
const CuttlefishConfig::InstanceSpecific& instance))
- : process_leader_(process_leader),
+ : config_(config),
+ process_leader_(process_leader),
kernel_log_pipe_provider_(kernel_log_pipe_provider),
+ vm_manager_(vm_manager),
instance_(instance),
state_(kBootStarted) {}
@@ -200,6 +176,14 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
if (boot_event_handler_.joinable()) {
boot_event_handler_.join();
}
+ if (restore_complete_stop_write_->IsOpen()) {
+ char c = 1;
+ CHECK_EQ(restore_complete_stop_write_->Write(&c, 1), 1)
+ << restore_complete_stop_write_->StrError();
+ }
+ if (restore_complete_handler_.joinable()) {
+ restore_complete_handler_.join();
+ }
}
// SetupFeature
@@ -228,12 +212,82 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
SharedFD boot_events_pipe = kernel_log_pipe_provider_.KernelLogPipe();
CF_EXPECTF(boot_events_pipe->IsOpen(), "Could not get boot events pipe: {}",
boot_events_pipe->StrError());
- boot_event_handler_ = std::thread(
- [this, boot_events_pipe]() { ThreadLoop(boot_events_pipe); });
+
+ // Pipe to tell `ThreadLoop` that the restore is complete.
+ SharedFD restore_complete_pipe, restore_complete_pipe_write;
+ // Pipe to tell `restore_complete_handler_` thread to give up.
+ // It isn't perfect, can only break out of the `WaitForRestoreComplete`
+ // step.
+ SharedFD restore_complete_stop_read;
+ if (IsRestoring(config_)) {
+ CF_EXPECT(
+ SharedFD::Pipe(&restore_complete_pipe, &restore_complete_pipe_write),
+ "unable to create pipe");
+ CF_EXPECT(SharedFD::Pipe(&restore_complete_stop_read,
+ &restore_complete_stop_write_),
+ "unable to create pipe");
+
+ restore_complete_handler_ = std::thread(
+ [this, restore_complete_pipe_write, restore_complete_stop_read]() {
+ const auto result =
+ vm_manager_.WaitForRestoreComplete(restore_complete_stop_read);
+ CHECK(result.ok()) << "Failed to wait for restore complete: "
+ << result.error().FormatForEnv();
+ if (!result.value()) {
+ return;
+ }
+
+ cuttlefish::SharedFD restore_adbd_pipe = cuttlefish::SharedFD::Open(
+ config_.ForDefaultInstance().restore_adbd_pipe_name().c_str(),
+ O_WRONLY);
+ CHECK(restore_adbd_pipe->IsOpen())
+ << "Error opening adbd restore pipe: "
+ << restore_adbd_pipe->StrError();
+ CHECK(cuttlefish::WriteAll(restore_adbd_pipe, "2") == 1)
+ << "Error writing to adbd restore pipe: "
+ << restore_adbd_pipe->StrError() << ". This is unrecoverable.";
+
+ auto SubtoolPath = [](const std::string& subtool_name) {
+ auto my_own_dir = android::base::GetExecutableDirectory();
+ std::stringstream subtool_path_stream;
+ subtool_path_stream << my_own_dir << "/" << subtool_name;
+ auto subtool_path = subtool_path_stream.str();
+ if (my_own_dir.empty() || !FileExists(subtool_path)) {
+ return HostBinaryPath(subtool_name);
+ }
+ return subtool_path;
+ };
+ const auto adb_bin_path = SubtoolPath("adb");
+ CHECK(Execute({adb_bin_path, "-s", instance_.adb_ip_and_port(),
+ "wait-for-device"},
+ SubprocessOptions(), WEXITED)
+ .ok())
+ << "Failed to suspend bluetooth manager.";
+ CHECK(Execute({adb_bin_path, "-s", instance_.adb_ip_and_port(),
+ "shell", "cmd", "bluetooth_manager", "enable"},
+ SubprocessOptions(), WEXITED)
+ .ok());
+ CHECK(Execute({adb_bin_path, "-s", instance_.adb_ip_and_port(),
+ "shell", "cmd", "uwb", "enable-uwb"},
+ SubprocessOptions(), WEXITED)
+ .ok());
+ // Done last so that adb is more likely to be ready.
+ CHECK(cuttlefish::WriteAll(restore_complete_pipe_write, "1") == 1)
+ << "Error writing to restore complete pipe: "
+ << restore_complete_pipe_write->StrError()
+ << ". This is unrecoverable.";
+ });
+ }
+
+ boot_event_handler_ =
+ std::thread([this, boot_events_pipe, restore_complete_pipe]() {
+ ThreadLoop(boot_events_pipe, restore_complete_pipe);
+ });
+
return {};
}
- void ThreadLoop(SharedFD boot_events_pipe) {
+ void ThreadLoop(SharedFD boot_events_pipe, SharedFD restore_complete_pipe) {
while (true) {
std::vector<PollSharedFd> poll_shared_fd = {
{
@@ -241,17 +295,26 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
.events = POLLIN | POLLHUP,
},
{
+ .fd = restore_complete_pipe,
+ .events = restore_complete_pipe->IsOpen()
+ ? (short)(POLLIN | POLLHUP)
+ : (short)0,
+ },
+ {
.fd = interrupt_fd_read_,
.events = POLLIN | POLLHUP,
- }};
+ },
+ };
int result = SharedFD::Poll(poll_shared_fd, -1);
- if (poll_shared_fd[1].revents & POLLIN) {
+ // interrupt_fd_read_
+ if (poll_shared_fd[2].revents & POLLIN) {
return;
}
if (result < 0) {
PLOG(FATAL) << "Failed to call Select";
return;
}
+ // boot_events_pipe
if (poll_shared_fd[0].revents & POLLHUP) {
LOG(ERROR) << "Failed to read a complete kernel log boot event.";
state_ |= kGuestBootFailed;
@@ -259,23 +322,46 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
break;
}
}
- if (!(poll_shared_fd[0].revents & POLLIN)) {
- continue;
+ if (poll_shared_fd[0].revents & POLLIN) {
+ auto sent_code = OnBootEvtReceived(boot_events_pipe);
+ if (sent_code) {
+ if (!BootCompleted()) {
+ if (!instance_.fail_fast()) {
+ LOG(ERROR) << "Device running, likely in a bad state";
+ break;
+ }
+ auto monitor_res = GetLauncherMonitorFromInstance(instance_, 5);
+ CHECK(monitor_res.ok()) << monitor_res.error().FormatForEnv();
+ auto fail_res = RunLauncherAction(
+ *monitor_res, LauncherAction::kFail, std::optional<int>());
+ CHECK(fail_res.ok()) << fail_res.error().FormatForEnv();
+ }
+ break;
+ }
}
- auto sent_code = OnBootEvtReceived(boot_events_pipe);
- if (sent_code) {
- if (!BootCompleted()) {
- if (!instance_.fail_fast()) {
- LOG(ERROR) << "Device running, likely in a bad state";
+ // restore_complete_pipe
+ if (poll_shared_fd[1].revents & POLLIN) {
+ char buff[1];
+ auto read = restore_complete_pipe->Read(buff, 1);
+ if (read <= 0) {
+ LOG(ERROR) << "Could not read restore pipe: "
+ << restore_complete_pipe->StrError();
+ state_ |= kGuestBootFailed;
+ if (MaybeWriteNotification()) {
break;
}
- auto monitor_res = GetLauncherMonitorFromInstance(instance_, 5);
- CHECK(monitor_res.ok()) << monitor_res.error().FormatForEnv();
- auto fail_res = RunLauncherAction(*monitor_res, LauncherAction::kFail,
- std::optional<int>());
- CHECK(fail_res.ok()) << fail_res.error().FormatForEnv();
}
- break;
+ state_ |= kGuestBootCompleted;
+ if (MaybeWriteNotification()) {
+ break;
+ }
+ }
+ if (poll_shared_fd[1].revents & POLLHUP) {
+ LOG(ERROR) << "restore_complete_pipe closed unexpectedly";
+ state_ |= kGuestBootFailed;
+ if (MaybeWriteNotification()) {
+ break;
+ }
}
}
}
@@ -325,11 +411,15 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
return BootCompleted() || (state_ & kGuestBootFailed);
}
+ const CuttlefishConfig& config_;
AutoSetup<ProcessLeader>::Type& process_leader_;
KernelLogPipeProvider& kernel_log_pipe_provider_;
+ const vm_manager::VmManager& vm_manager_;
const CuttlefishConfig::InstanceSpecific& instance_;
std::thread boot_event_handler_;
+ std::thread restore_complete_handler_;
+ SharedFD restore_complete_stop_write_;
SharedFD fg_launcher_pipe_;
SharedFD reboot_notification_;
SharedFD interrupt_fd_read_;
@@ -344,6 +434,7 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
const CuttlefishConfig::InstanceSpecific,
+ const vm_manager::VmManager,
AutoSetup<ValidateTapDevices>::Type>>
bootStateMachineComponent() {
return fruit::createComponent()