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