1 /*
2  * Copyright (C) 2023 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/server_loop_impl.h"
18 
19 #include <sstream>
20 #include <string>
21 
22 #include <android-base/file.h>
23 
24 #include "common/libs/fs/shared_buf.h"
25 #include "common/libs/fs/shared_fd.h"
26 #include "common/libs/utils/contains.h"
27 #include "common/libs/utils/files.h"
28 #include "common/libs/utils/json.h"
29 #include "common/libs/utils/result.h"
30 #include "host/libs/command_util/runner/defs.h"
31 #include "host/libs/command_util/snapshot_utils.h"
32 #include "host/libs/command_util/util.h"
33 #include "host/libs/vm_manager/crosvm_manager.h"
34 #include "host/libs/vm_manager/qemu_manager.h"
35 #include "run_cvd.pb.h"
36 
37 namespace cuttlefish {
38 namespace run_cvd_impl {
39 using APBootFlow = CuttlefishConfig::InstanceSpecific::APBootFlow;
40 
41 std::unordered_map<std::string, std::string>
InitializeVmToControlSockPath(const CuttlefishConfig::InstanceSpecific & instance)42 ServerLoopImpl::InitializeVmToControlSockPath(
43     const CuttlefishConfig::InstanceSpecific& instance) {
44   return std::unordered_map<std::string, std::string>{
45       // TODO(kwstephenkim): add the following two lines to support QEMU
46       // {ToString(VmmMode::kQemu),
47       // instance.PerInstanceInternalUdsPath("qemu_monitor.sock")},
48       {ToString(VmmMode::kCrosvm), instance.CrosvmSocketPath()},
49       {cuttlefish::kApName, instance.OpenwrtCrosvmSocketPath()},
50   };
51 }
52 
SubtoolPath(const std::string & subtool_name)53 static std::string SubtoolPath(const std::string& subtool_name) {
54   auto my_own_dir = android::base::GetExecutableDirectory();
55   std::stringstream subtool_path_stream;
56   subtool_path_stream << my_own_dir << "/" << subtool_name;
57   auto subtool_path = subtool_path_stream.str();
58   if (my_own_dir.empty() || !FileExists(subtool_path)) {
59     return HostBinaryPath(subtool_name);
60   }
61   return subtool_path;
62 }
63 
GetSocketPath(const std::string name,std::unordered_map<std::string,std::string> & vm_name_to_control_sock_)64 static std::string GetSocketPath(
65     const std::string name,
66     std::unordered_map<std::string, std::string>& vm_name_to_control_sock_) {
67   if (!Contains(vm_name_to_control_sock_, name)) {
68     return "";
69   }
70   return vm_name_to_control_sock_.at(name);
71 }
72 
SuspendCrosvm(const std::string & vm_sock_path)73 static Result<void> SuspendCrosvm(const std::string& vm_sock_path) {
74   const auto crosvm_bin_path = SubtoolPath("crosvm");
75   std::vector<std::string> command_args{crosvm_bin_path, "suspend",
76                                         vm_sock_path, "--full"};
77   auto infop = CF_EXPECT(Execute(command_args, SubprocessOptions(), WEXITED));
78   CF_EXPECT_EQ(infop.si_code, CLD_EXITED);
79   CF_EXPECTF(infop.si_status == 0, "crosvm suspend returns non zero code {}",
80              infop.si_status);
81   return {};
82 }
83 
ResumeCrosvm(const std::string & vm_sock_path)84 static Result<void> ResumeCrosvm(const std::string& vm_sock_path) {
85   const auto crosvm_bin_path = SubtoolPath("crosvm");
86   std::vector<std::string> command_args{crosvm_bin_path, "resume", vm_sock_path,
87                                         "--full"};
88   auto infop = CF_EXPECT(Execute(command_args, SubprocessOptions(), WEXITED));
89   CF_EXPECT_EQ(infop.si_code, CLD_EXITED);
90   CF_EXPECTF(infop.si_status == 0, "crosvm resume returns non zero code {}",
91              infop.si_status);
92   return {};
93 }
94 
SuspendGuest()95 Result<void> ServerLoopImpl::SuspendGuest() {
96   // If openwrt is running in crosvm, suspend it.
97   const auto ap_vm_name = config_.ap_vm_manager();
98   if (instance_.ap_boot_flow() != APBootFlow::None &&
99       ap_vm_name == cuttlefish::kApName) {
100     const auto openwrt_sock =
101         GetSocketPath(ap_vm_name, vm_name_to_control_sock_);
102     if (openwrt_sock == "") {
103       return CF_ERR("The vm_manager " + ap_vm_name + " is not supported yet");
104     }
105     CF_EXPECT(SuspendCrosvm(openwrt_sock),
106               "failed to suspend openwrt crosvm instance.");
107   }
108   const auto main_vmm = config_.vm_manager();
109   if (main_vmm == VmmMode::kCrosvm) {
110     const auto& vm_sock =
111         GetSocketPath(ToString(main_vmm), vm_name_to_control_sock_);
112     if (vm_sock == "") {
113       return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
114     }
115     return SuspendCrosvm(vm_sock);
116   } else {
117     return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
118   }
119 }
120 
ResumeGuest()121 Result<void> ServerLoopImpl::ResumeGuest() {
122   // If openwrt is running in crosvm, resume it.
123   const auto ap_vm_name = config_.ap_vm_manager();
124   if (instance_.ap_boot_flow() != APBootFlow::None &&
125       ap_vm_name == cuttlefish::kApName) {
126     const auto& openwrt_sock =
127         GetSocketPath(ap_vm_name, vm_name_to_control_sock_);
128     if (openwrt_sock == "") {
129       return CF_ERR("The vm_manager " + ap_vm_name + " is not supported yet");
130     }
131     CF_EXPECT(ResumeCrosvm(openwrt_sock),
132               "failed to resume openwrt crosvm instance.");
133   }
134   const auto main_vmm = config_.vm_manager();
135   if (main_vmm == VmmMode::kCrosvm) {
136     const auto& vm_sock =
137         GetSocketPath(ToString(main_vmm), vm_name_to_control_sock_);
138     if (vm_sock == "") {
139       return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
140     }
141     return ResumeCrosvm(vm_sock);
142   } else {
143     return CF_ERR("The vm_manager " << main_vmm << " is not supported yet");
144   }
145 }
146 
RunAdbShellCommand(const CuttlefishConfig::InstanceSpecific & ins,const std::vector<std::string> & command_args)147 static Result<void> RunAdbShellCommand(
148     const CuttlefishConfig::InstanceSpecific& ins,
149     const std::vector<std::string>& command_args) {
150   // Make sure device is connected, otherwise the following `adb -s SERIAL
151   // wait-for-device shell ...` would get stuck.
152   Command connect_cmd(SubtoolPath("adb"));
153   // Avoid the adb server being started in the runtime directory and looking
154   // like a process that is still using the directory.
155   connect_cmd.SetWorkingDirectory("/");
156   connect_cmd.AddParameter("connect");
157   connect_cmd.AddParameter(ins.adb_ip_and_port());
158   CF_EXPECT_EQ(connect_cmd.Start().Wait(), 0);
159 
160   // Run the shell commands.
161   Command shell_cmd(SubtoolPath("adb"));
162   shell_cmd.AddParameter("-s").AddParameter(ins.adb_ip_and_port());
163   shell_cmd.AddParameter("shell");
164   for (const auto& argument : command_args) {
165     shell_cmd.AddParameter(argument);
166   }
167   CF_EXPECT_EQ(shell_cmd.Start().Wait(), 0);
168   return {};
169 }
170 
HandleSuspend(ProcessMonitor & process_monitor)171 Result<void> ServerLoopImpl::HandleSuspend(ProcessMonitor& process_monitor) {
172   // right order: guest -> host
173   LOG(DEBUG) << "Suspending the guest..";
174   CF_EXPECT(
175       RunAdbShellCommand(instance_, {"/vendor/bin/snapshot_hook_pre_suspend"}));
176   CF_EXPECT(SuspendGuest());
177   LOG(DEBUG) << "The guest is suspended.";
178   CF_EXPECT(process_monitor.SuspendMonitoredProcesses(),
179             "Failed to suspend host processes.");
180   LOG(DEBUG) << "The host processes are suspended.";
181   return {};
182 }
183 
HandleResume(ProcessMonitor & process_monitor)184 Result<void> ServerLoopImpl::HandleResume(ProcessMonitor& process_monitor) {
185   // right order: host -> guest
186   CF_EXPECT(process_monitor.ResumeMonitoredProcesses(),
187             "Failed to resume host processes.");
188   LOG(DEBUG) << "The host processes are resumed.";
189   LOG(DEBUG) << "Resuming the guest..";
190   CF_EXPECT(ResumeGuest());
191   CF_EXPECT(
192       RunAdbShellCommand(instance_, {"/vendor/bin/snapshot_hook_post_resume"}));
193   LOG(DEBUG) << "The guest resumed.";
194   return {};
195 }
196 
TakeCrosvmGuestSnapshot(const Json::Value & meta_json)197 Result<void> ServerLoopImpl::TakeCrosvmGuestSnapshot(
198     const Json::Value& meta_json) {
199   const auto snapshots_parent_dir =
200       CF_EXPECT(InstanceGuestSnapshotPath(meta_json, instance_.id()));
201   const auto crosvm_bin = config_.crosvm_binary();
202   const std::string snapshot_guest_param =
203       snapshots_parent_dir + "/" + kGuestSnapshotBase;
204   // If openwrt is running in crosvm, snapshot it.
205   const auto ap_vm_name = config_.ap_vm_manager();
206   if (instance_.ap_boot_flow() != APBootFlow::None &&
207       ap_vm_name == cuttlefish::kApName) {
208     const auto& openwrt_sock =
209         GetSocketPath(ap_vm_name, vm_name_to_control_sock_);
210     if (openwrt_sock == "") {
211       return CF_ERR("The vm_manager " + ap_vm_name + " is not supported yet");
212     }
213     std::vector<std::string> openwrt_crosvm_command_args{
214         crosvm_bin, "snapshot", "take", snapshot_guest_param + "_openwrt",
215         openwrt_sock};
216     LOG(DEBUG) << "Running the following command to take snapshot..."
217                << std::endl
218                << "  ";
219     for (const auto& arg : openwrt_crosvm_command_args) {
220       LOG(DEBUG) << arg << " ";
221     }
222     CF_EXPECT(Execute(openwrt_crosvm_command_args) == 0,
223               "Executing openwrt crosvm command returned -1");
224     LOG(DEBUG) << "Guest snapshot for openwrt instance #" << instance_.id()
225                << " should have been stored in " << snapshots_parent_dir
226                << "_openwrt";
227   }
228   const auto control_socket_path =
229       CF_EXPECT(VmControlSocket(), "Failed to find crosvm control.sock path.");
230   std::vector<std::string> crosvm_command_args{crosvm_bin, "snapshot", "take",
231                                                snapshot_guest_param,
232                                                control_socket_path};
233   LOG(DEBUG) << "Running the following command to take snapshot..." << std::endl
234              << "  ";
235   for (const auto& arg : crosvm_command_args) {
236     LOG(DEBUG) << arg << " ";
237   }
238   CF_EXPECT(Execute(crosvm_command_args) == 0,
239             "Executing crosvm command failed");
240   LOG(DEBUG) << "Guest snapshot for instance #" << instance_.id()
241              << " should have been stored in " << snapshots_parent_dir;
242   return {};
243 }
244 
245 /*
246  * Parse json file at json_path, and take guest snapshot
247  */
TakeGuestSnapshot(VmmMode vm_manager,const std::string & json_path)248 Result<void> ServerLoopImpl::TakeGuestSnapshot(VmmMode vm_manager,
249                                                const std::string& json_path) {
250   // common code across vm_manager
251   CF_EXPECTF(FileExists(json_path), "{} must exist but does not.", json_path);
252   SharedFD json_fd = SharedFD::Open(json_path, O_RDONLY);
253   CF_EXPECTF(json_fd->IsOpen(), "Failed to open {}", json_path);
254   std::string json_contents;
255   CF_EXPECT_GE(ReadAll(json_fd, &json_contents), 0,
256                std::string("Failed to read from ") + json_path);
257   Json::Value meta_json = CF_EXPECTF(
258       ParseJson(json_contents), "Failed to parse json: \n{}", json_contents);
259   CF_EXPECTF(vm_manager == VmmMode::kCrosvm,
260              "{}, which is not crosvm, is not yet supported.", vm_manager);
261   CF_EXPECT(TakeCrosvmGuestSnapshot(meta_json),
262             "TakeCrosvmGuestSnapshot() failed.");
263   return {};
264 }
265 
HandleSnapshotTake(const run_cvd::SnapshotTake & snapshot_take)266 Result<void> ServerLoopImpl::HandleSnapshotTake(
267     const run_cvd::SnapshotTake& snapshot_take) {
268   CF_EXPECT(!snapshot_take.snapshot_path().empty(),
269             "snapshot_path must be non-empty");
270   CF_EXPECT(
271       TakeGuestSnapshot(config_.vm_manager(), snapshot_take.snapshot_path()),
272       "Failed to take guest snapshot");
273   return {};
274 }
275 
276 }  // namespace run_cvd_impl
277 }  // namespace cuttlefish
278