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 <poll.h>
20
21 #include <memory>
22 #include <thread>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <gflags/gflags.h>
27 #include <grpc/grpc.h>
28 #include <grpcpp/channel.h>
29 #include <grpcpp/client_context.h>
30 #include <grpcpp/create_channel.h>
31 #include "common/libs/utils/result.h"
32
33 #include "common/libs/fs/shared_buf.h"
34 #include "common/libs/fs/shared_fd.h"
35 #include "common/libs/utils/files.h"
36 #include "common/libs/utils/tee_logging.h"
37 #include "host/commands/assemble_cvd/flags_defaults.h"
38 #include "host/commands/kernel_log_monitor/kernel_log_server.h"
39 #include "host/commands/kernel_log_monitor/utils.h"
40 #include "host/commands/run_cvd/validate.h"
41 #include "host/libs/command_util/runner/defs.h"
42 #include "host/libs/command_util/util.h"
43 #include "host/libs/config/feature.h"
44 #include "openwrt_control.grpc.pb.h"
45
46 using grpc::ClientContext;
47 using openwrtcontrolserver::LuciRpcReply;
48 using openwrtcontrolserver::LuciRpcRequest;
49 using openwrtcontrolserver::OpenwrtControlService;
50 using openwrtcontrolserver::OpenwrtIpaddrReply;
51
52 DEFINE_int32(reboot_notification_fd, CF_DEFAULTS_REBOOT_NOTIFICATION_FD,
53 "A file descriptor to notify when boot completes.");
54
55 namespace cuttlefish {
56 namespace {
57
MoveThreadsToCgroup(const std::string & from_path,const std::string & to_path)58 Result<void> MoveThreadsToCgroup(const std::string& from_path,
59 const std::string& to_path) {
60 std::string file_path = from_path + "/cgroup.threads";
61
62 if (FileExists(file_path)) {
63 Result<std::string> content_result = ReadFileContents(file_path);
64 if (!content_result.ok()) {
65 LOG(INFO) << "Failed to open threads file and assume it is empty: "
66 << file_path;
67 return {};
68 }
69
70 std::istringstream is(content_result.value());
71 std::string each_id;
72 while (std::getline(is, each_id)) {
73 std::string proc_status_path = "/proc/" + each_id;
74 proc_status_path.append("/status");
75 Result<std::string> proc_status = ReadFileContents(proc_status_path);
76 if (!proc_status.ok()) {
77 LOG(INFO) << "Failed to open proc status file and skip: "
78 << proc_status_path;
79 continue;
80 }
81
82 std::string proc_status_str = proc_status.value();
83 if (proc_status_str.find("crosvm_vcpu") == std::string::npos &&
84 proc_status_str.find("vcpu_throttle") == std::string::npos) {
85 // other proc moved to workers cgroup
86 std::string to_path_file = to_path + "/cgroup.threads";
87 SharedFD fd = SharedFD::Open(to_path_file, O_WRONLY | O_APPEND);
88 CF_EXPECT(fd->IsOpen(),
89 "failed to open " << to_path_file << ": " << fd->StrError());
90 if (WriteAll(fd, each_id) != each_id.size()) {
91 return CF_ERR("failed to write to" << to_path_file);
92 }
93 }
94 }
95 }
96
97 return {};
98 }
99
100 // See go/vcpuinheritance for more context on why this Rebalance is
101 // required and what the stop gap/longterm solutions are.
WattsonRebalanceThreads(const std::string & id)102 Result<void> WattsonRebalanceThreads(const std::string& id) {
103 auto root_path = "/sys/fs/cgroup/vsoc-" + id + "-cf";
104 const auto files = CF_EXPECT(DirectoryContents(root_path));
105
106 CF_EXPECT(MoveThreadsToCgroup(root_path, root_path + "/workers"));
107
108 for (const auto& filename : files) {
109 if (filename.find("vcpu-domain") != std::string::npos) {
110 CF_EXPECT(MoveThreadsToCgroup(root_path + "/" + filename,
111 root_path + "/workers"));
112 }
113 }
114 return {};
115 }
116
117 // Forks run_cvd into a daemonized child process. The current process continues
118 // only until the child has signalled that the boot is finished.
119 //
120 // `DaemonizeLauncher` returns the write end of a pipe. The child is expected
121 // to write a `RunnerExitCodes` into the pipe when the boot finishes.
DaemonizeLauncher(const CuttlefishConfig & config)122 Result<SharedFD> DaemonizeLauncher(const CuttlefishConfig& config) {
123 auto instance = config.ForDefaultInstance();
124 SharedFD read_end, write_end;
125 CF_EXPECT(SharedFD::Pipe(&read_end, &write_end), "Unable to create pipe");
126 auto pid = fork();
127 if (pid) {
128 // Explicitly close here, otherwise we may end up reading forever if the
129 // child process dies.
130 write_end->Close();
131 RunnerExitCodes exit_code;
132 auto bytes_read = read_end->Read(&exit_code, sizeof(exit_code));
133 if (bytes_read != sizeof(exit_code)) {
134 LOG(ERROR) << "Failed to read a complete exit code, read " << bytes_read
135 << " bytes only instead of the expected " << sizeof(exit_code);
136 exit_code = RunnerExitCodes::kPipeIOError;
137 } else if (exit_code == RunnerExitCodes::kSuccess) {
138 if (IsRestoring(config)) {
139 LOG(INFO) << "Virtual device restored successfully";
140 } else {
141 LOG(INFO) << "Virtual device booted successfully";
142 if (!instance.vcpu_config_path().empty()) {
143 CF_EXPECT(WattsonRebalanceThreads(instance.id()));
144 }
145 }
146 } else if (exit_code == RunnerExitCodes::kVirtualDeviceBootFailed) {
147 if (IsRestoring(config)) {
148 LOG(ERROR) << "Virtual device failed to restore";
149 } else {
150 LOG(ERROR) << "Virtual device failed to boot";
151 }
152 if (!instance.fail_fast()) {
153 LOG(ERROR) << "Device has been left running for debug";
154 }
155 } else {
156 LOG(ERROR) << "Unexpected exit code: " << exit_code;
157 }
158 if (!IsRestoring(config)) {
159 if (exit_code == RunnerExitCodes::kSuccess) {
160 LOG(INFO) << kBootCompletedMessage;
161 } else {
162 LOG(INFO) << kBootFailedMessage;
163 }
164 }
165 std::exit(exit_code);
166 } else {
167 // The child returns the write end of the pipe
168 if (daemon(/*nochdir*/ 1, /*noclose*/ 1) != 0) {
169 LOG(ERROR) << "Failed to daemonize child process: " << strerror(errno);
170 std::exit(RunnerExitCodes::kDaemonizationError);
171 }
172 // Redirect standard I/O
173 auto log_path = instance.launcher_log_path();
174 auto log = SharedFD::Open(log_path.c_str(), O_CREAT | O_WRONLY | O_APPEND,
175 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
176 if (!log->IsOpen()) {
177 LOG(ERROR) << "Failed to create launcher log file: " << log->StrError();
178 std::exit(RunnerExitCodes::kDaemonizationError);
179 }
180 ::android::base::SetLogger(
181 TeeLogger({{LogFileSeverity(), log, MetadataLevel::FULL}}));
182 auto dev_null = SharedFD::Open("/dev/null", O_RDONLY);
183 if (!dev_null->IsOpen()) {
184 LOG(ERROR) << "Failed to open /dev/null: " << dev_null->StrError();
185 std::exit(RunnerExitCodes::kDaemonizationError);
186 }
187 if (dev_null->UNMANAGED_Dup2(0) < 0) {
188 LOG(ERROR) << "Failed dup2 stdin: " << dev_null->StrError();
189 std::exit(RunnerExitCodes::kDaemonizationError);
190 }
191 if (log->UNMANAGED_Dup2(1) < 0) {
192 LOG(ERROR) << "Failed dup2 stdout: " << log->StrError();
193 std::exit(RunnerExitCodes::kDaemonizationError);
194 }
195 if (log->UNMANAGED_Dup2(2) < 0) {
196 LOG(ERROR) << "Failed dup2 seterr: " << log->StrError();
197 std::exit(RunnerExitCodes::kDaemonizationError);
198 }
199
200 read_end->Close();
201 return write_end;
202 }
203 }
204
ProcessLeader(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,AutoSetup<ValidateTapDevices>::Type &)205 Result<SharedFD> ProcessLeader(
206 const CuttlefishConfig& config,
207 const CuttlefishConfig::InstanceSpecific& instance,
208 AutoSetup<ValidateTapDevices>::Type& /* dependency */) {
209 if (IsRestoring(config)) {
210 CF_EXPECT(SharedFD::Fifo(instance.restore_adbd_pipe_name(), 0600),
211 "Unable to create adbd restore fifo");
212 }
213 /* These two paths result in pretty different process state, but both
214 * achieve the same goal of making the current process the leader of a
215 * process group, and are therefore grouped together. */
216 if (instance.run_as_daemon()) {
217 return CF_EXPECT(DaemonizeLauncher(config), "DaemonizeLauncher failed");
218 }
219 // Make sure the launcher runs in its own process group even when running
220 // in the foreground
221 if (getsid(0) != getpid()) {
222 CF_EXPECTF(setpgid(0, 0) == 0, "Failed to create new process group: {}",
223 strerror(errno));
224 }
225 return {};
226 }
227
228 // Maintains the state of the boot process, once a final state is reached
229 // (success or failure) it sends the appropriate exit code to the foreground
230 // launcher process
231 class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
232 public:
INJECT(CvdBootStateMachine (const CuttlefishConfig & config,AutoSetup<ProcessLeader>::Type & process_leader,KernelLogPipeProvider & kernel_log_pipe_provider,const vm_manager::VmManager & vm_manager,const CuttlefishConfig::InstanceSpecific & instance))233 INJECT(
234 CvdBootStateMachine(const CuttlefishConfig& config,
235 AutoSetup<ProcessLeader>::Type& process_leader,
236 KernelLogPipeProvider& kernel_log_pipe_provider,
237 const vm_manager::VmManager& vm_manager,
238 const CuttlefishConfig::InstanceSpecific& instance))
239 : config_(config),
240 process_leader_(process_leader),
241 kernel_log_pipe_provider_(kernel_log_pipe_provider),
242 vm_manager_(vm_manager),
243 instance_(instance),
244 state_(kBootStarted) {}
245
~CvdBootStateMachine()246 ~CvdBootStateMachine() {
247 if (interrupt_fd_write_->IsOpen()) {
248 char c = 1;
249 CHECK_EQ(interrupt_fd_write_->Write(&c, 1), 1)
250 << interrupt_fd_write_->StrError();
251 }
252 if (boot_event_handler_.joinable()) {
253 boot_event_handler_.join();
254 }
255 if (restore_complete_stop_write_->IsOpen()) {
256 char c = 1;
257 CHECK_EQ(restore_complete_stop_write_->Write(&c, 1), 1)
258 << restore_complete_stop_write_->StrError();
259 }
260 if (restore_complete_handler_.joinable()) {
261 restore_complete_handler_.join();
262 }
263 }
264
265 // SetupFeature
Name() const266 std::string Name() const override { return "CvdBootStateMachine"; }
267
268 private:
Dependencies() const269 std::unordered_set<SetupFeature*> Dependencies() const {
270 return {
271 static_cast<SetupFeature*>(&process_leader_),
272 static_cast<SetupFeature*>(&kernel_log_pipe_provider_),
273 };
274 }
ResultSetup()275 Result<void> ResultSetup() override {
276 CF_EXPECT(SharedFD::Pipe(&interrupt_fd_read_, &interrupt_fd_write_));
277 CF_EXPECT(interrupt_fd_read_->IsOpen(), interrupt_fd_read_->StrError());
278 CF_EXPECT(interrupt_fd_write_->IsOpen(), interrupt_fd_write_->StrError());
279 fg_launcher_pipe_ = *process_leader_;
280 if (FLAGS_reboot_notification_fd >= 0) {
281 reboot_notification_ = SharedFD::Dup(FLAGS_reboot_notification_fd);
282 CF_EXPECTF(reboot_notification_->IsOpen(),
283 "Could not dup fd given for reboot_notification_fd: {}",
284 reboot_notification_->StrError());
285 close(FLAGS_reboot_notification_fd);
286 }
287 SharedFD boot_events_pipe = kernel_log_pipe_provider_.KernelLogPipe();
288 CF_EXPECTF(boot_events_pipe->IsOpen(), "Could not get boot events pipe: {}",
289 boot_events_pipe->StrError());
290
291 // Pipe to tell `ThreadLoop` that the restore is complete.
292 SharedFD restore_complete_pipe, restore_complete_pipe_write;
293 // Pipe to tell `restore_complete_handler_` thread to give up.
294 // It isn't perfect, can only break out of the `WaitForRestoreComplete`
295 // step.
296 SharedFD restore_complete_stop_read;
297 if (IsRestoring(config_)) {
298 CF_EXPECT(
299 SharedFD::Pipe(&restore_complete_pipe, &restore_complete_pipe_write),
300 "unable to create pipe");
301 CF_EXPECT(SharedFD::Pipe(&restore_complete_stop_read,
302 &restore_complete_stop_write_),
303 "unable to create pipe");
304
305 restore_complete_handler_ = std::thread(
306 [this, restore_complete_pipe_write, restore_complete_stop_read]() {
307 const auto result =
308 vm_manager_.WaitForRestoreComplete(restore_complete_stop_read);
309 CHECK(result.ok()) << "Failed to wait for restore complete: "
310 << result.error().FormatForEnv();
311 if (!result.value()) {
312 return;
313 }
314
315 cuttlefish::SharedFD restore_adbd_pipe = cuttlefish::SharedFD::Open(
316 config_.ForDefaultInstance().restore_adbd_pipe_name().c_str(),
317 O_WRONLY);
318 CHECK(restore_adbd_pipe->IsOpen())
319 << "Error opening adbd restore pipe: "
320 << restore_adbd_pipe->StrError();
321 CHECK(cuttlefish::WriteAll(restore_adbd_pipe, "2") == 1)
322 << "Error writing to adbd restore pipe: "
323 << restore_adbd_pipe->StrError() << ". This is unrecoverable.";
324
325 // Restart network service in OpenWRT, broken on restore.
326 CHECK(FileExists(instance_.grpc_socket_path() +
327 "/OpenwrtControlServer.sock"))
328 << "unable to find grpc socket for OpenwrtControlServer";
329 auto openwrt_channel =
330 grpc::CreateChannel("unix:" + instance_.grpc_socket_path() +
331 "/OpenwrtControlServer.sock",
332 grpc::InsecureChannelCredentials());
333 auto stub_ = OpenwrtControlService::NewStub(openwrt_channel);
334 LuciRpcRequest request;
335 request.set_subpath("sys");
336 request.set_method("exec");
337 request.add_params("service network restart");
338 LuciRpcReply response;
339 ClientContext context;
340 grpc::Status status = stub_->LuciRpc(&context, request, &response);
341 CHECK(status.ok())
342 << "Failed to send network service reset" << status.error_code()
343 << ": " << status.error_message();
344 LOG(DEBUG) << "OpenWRT `service network restart` response: "
345 << response.result();
346
347 auto SubtoolPath = [](const std::string& subtool_name) {
348 auto my_own_dir = android::base::GetExecutableDirectory();
349 std::stringstream subtool_path_stream;
350 subtool_path_stream << my_own_dir << "/" << subtool_name;
351 auto subtool_path = subtool_path_stream.str();
352 if (my_own_dir.empty() || !FileExists(subtool_path)) {
353 return HostBinaryPath(subtool_name);
354 }
355 return subtool_path;
356 };
357 // Run the in-guest post-restore script.
358 Command adb_command(SubtoolPath("adb"));
359 // Avoid the adb server being started in the runtime directory and
360 // looking like a process that is still using the directory.
361 adb_command.SetWorkingDirectory("/");
362 adb_command.AddParameter("-s").AddParameter(
363 instance_.adb_ip_and_port());
364 adb_command.AddParameter("wait-for-device");
365 adb_command.AddParameter("shell");
366 adb_command.AddParameter("/vendor/bin/snapshot_hook_post_resume");
367 CHECK_EQ(adb_command.Start().Wait(), 0)
368 << "Failed to run /vendor/bin/snapshot_hook_post_resume";
369 // Done last so that adb is more likely to be ready.
370 CHECK(cuttlefish::WriteAll(restore_complete_pipe_write, "1") == 1)
371 << "Error writing to restore complete pipe: "
372 << restore_complete_pipe_write->StrError()
373 << ". This is unrecoverable.";
374 });
375 }
376
377 boot_event_handler_ =
378 std::thread([this, boot_events_pipe, restore_complete_pipe]() {
379 ThreadLoop(boot_events_pipe, restore_complete_pipe);
380 });
381
382 return {};
383 }
384
ThreadLoop(SharedFD boot_events_pipe,SharedFD restore_complete_pipe)385 void ThreadLoop(SharedFD boot_events_pipe, SharedFD restore_complete_pipe) {
386 while (true) {
387 std::vector<PollSharedFd> poll_shared_fd = {
388 {
389 .fd = boot_events_pipe,
390 .events = POLLIN | POLLHUP,
391 },
392 {
393 .fd = restore_complete_pipe,
394 .events = restore_complete_pipe->IsOpen()
395 ? (short)(POLLIN | POLLHUP)
396 : (short)0,
397 },
398 {
399 .fd = interrupt_fd_read_,
400 .events = POLLIN | POLLHUP,
401 },
402 };
403 int result = SharedFD::Poll(poll_shared_fd, -1);
404 // interrupt_fd_read_
405 if (poll_shared_fd[2].revents & POLLIN) {
406 return;
407 }
408 if (result < 0) {
409 PLOG(FATAL) << "Failed to call Select";
410 return;
411 }
412 // boot_events_pipe
413 if (poll_shared_fd[0].revents & POLLHUP) {
414 LOG(ERROR) << "Failed to read a complete kernel log boot event.";
415 state_ |= kGuestBootFailed;
416 if (MaybeWriteNotification()) {
417 break;
418 }
419 }
420 if (poll_shared_fd[0].revents & POLLIN) {
421 auto sent_code = OnBootEvtReceived(boot_events_pipe);
422 if (sent_code) {
423 if (!BootCompleted()) {
424 if (!instance_.fail_fast()) {
425 LOG(ERROR) << "Device running, likely in a bad state";
426 break;
427 }
428 auto monitor_res = GetLauncherMonitorFromInstance(instance_, 5);
429 CHECK(monitor_res.ok()) << monitor_res.error().FormatForEnv();
430 auto fail_res = RunLauncherAction(
431 *monitor_res, LauncherAction::kFail, std::optional<int>());
432 CHECK(fail_res.ok()) << fail_res.error().FormatForEnv();
433 }
434 break;
435 }
436 }
437 // restore_complete_pipe
438 if (poll_shared_fd[1].revents & POLLIN) {
439 char buff[1];
440 auto read = restore_complete_pipe->Read(buff, 1);
441 if (read <= 0) {
442 LOG(ERROR) << "Could not read restore pipe: "
443 << restore_complete_pipe->StrError();
444 state_ |= kGuestBootFailed;
445 if (MaybeWriteNotification()) {
446 break;
447 }
448 }
449 state_ |= kGuestBootCompleted;
450 if (MaybeWriteNotification()) {
451 break;
452 }
453 }
454 if (poll_shared_fd[1].revents & POLLHUP) {
455 LOG(ERROR) << "restore_complete_pipe closed unexpectedly";
456 state_ |= kGuestBootFailed;
457 if (MaybeWriteNotification()) {
458 break;
459 }
460 }
461 }
462 }
463
464 // Returns true if the machine is left in a final state
OnBootEvtReceived(SharedFD boot_events_pipe)465 bool OnBootEvtReceived(SharedFD boot_events_pipe) {
466 Result<std::optional<monitor::ReadEventResult>> read_result =
467 monitor::ReadEvent(boot_events_pipe);
468 if (!read_result) {
469 LOG(ERROR) << "Failed to read a complete kernel log boot event: "
470 << read_result.error().FormatForEnv();
471 state_ |= kGuestBootFailed;
472 return MaybeWriteNotification();
473 } else if (!*read_result) {
474 LOG(ERROR) << "EOF from kernel log monitor";
475 state_ |= kGuestBootFailed;
476 return MaybeWriteNotification();
477 }
478
479 if ((*read_result)->event == monitor::Event::BootCompleted) {
480 LOG(INFO) << "Virtual device booted successfully";
481 state_ |= kGuestBootCompleted;
482 if (!instance_.vcpu_config_path().empty()) {
483 auto res = WattsonRebalanceThreads(instance_.id());
484 if (!res.ok()) {
485 LOG(ERROR) << res.error().FormatForEnv();
486 }
487 }
488 } else if ((*read_result)->event == monitor::Event::BootFailed) {
489 LOG(ERROR) << "Virtual device failed to boot";
490 state_ |= kGuestBootFailed;
491 } // Ignore the other signals
492
493 return MaybeWriteNotification();
494 }
BootCompleted() const495 bool BootCompleted() const { return state_ & kGuestBootCompleted; }
BootFailed() const496 bool BootFailed() const { return state_ & kGuestBootFailed; }
497
SendExitCode(RunnerExitCodes exit_code,SharedFD fd)498 void SendExitCode(RunnerExitCodes exit_code, SharedFD fd) {
499 fd->Write(&exit_code, sizeof(exit_code));
500 // The foreground process will exit after receiving the exit code, if we try
501 // to write again we'll get a SIGPIPE
502 fd->Close();
503 }
MaybeWriteNotification()504 bool MaybeWriteNotification() {
505 std::vector<SharedFD> fds = {reboot_notification_, fg_launcher_pipe_};
506 for (auto& fd : fds) {
507 if (fd->IsOpen()) {
508 if (BootCompleted()) {
509 SendExitCode(RunnerExitCodes::kSuccess, fd);
510 } else if (state_ & kGuestBootFailed) {
511 SendExitCode(RunnerExitCodes::kVirtualDeviceBootFailed, fd);
512 }
513 }
514 }
515 // Either we sent the code before or just sent it, in any case the state is
516 // final
517 return BootCompleted() || (state_ & kGuestBootFailed);
518 }
519
520 const CuttlefishConfig& config_;
521 AutoSetup<ProcessLeader>::Type& process_leader_;
522 KernelLogPipeProvider& kernel_log_pipe_provider_;
523 const vm_manager::VmManager& vm_manager_;
524 const CuttlefishConfig::InstanceSpecific& instance_;
525
526 std::thread boot_event_handler_;
527 std::thread restore_complete_handler_;
528 SharedFD restore_complete_stop_write_;
529 SharedFD fg_launcher_pipe_;
530 SharedFD reboot_notification_;
531 SharedFD interrupt_fd_read_;
532 SharedFD interrupt_fd_write_;
533 int state_;
534 static const int kBootStarted = 0;
535 static const int kGuestBootCompleted = 1 << 0;
536 static const int kGuestBootFailed = 1 << 1;
537 };
538
539 } // namespace
540
541 fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
542 const CuttlefishConfig::InstanceSpecific,
543 const vm_manager::VmManager,
544 AutoSetup<ValidateTapDevices>::Type>>
bootStateMachineComponent()545 bootStateMachineComponent() {
546 return fruit::createComponent()
547 .addMultibinding<KernelLogPipeConsumer, CvdBootStateMachine>()
548 .addMultibinding<SetupFeature, CvdBootStateMachine>()
549 .install(AutoSetup<ProcessLeader>::Component);
550 }
551
552 } // namespace cuttlefish
553