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   Command adb_command(SubtoolPath("adb"));
151   // Avoid the adb server being started in the runtime directory and looking
152   // like a process that is still using the directory.
153   adb_command.SetWorkingDirectory("/");
154   adb_command.AddParameter("-s").AddParameter(ins.adb_ip_and_port());
155   adb_command.AddParameter("wait-for-device");
156 
157   adb_command.AddParameter("shell");
158   for (const auto& argument : command_args) {
159     adb_command.AddParameter(argument);
160   }
161   CF_EXPECT_EQ(adb_command.Start().Wait(), 0);
162   return {};
163 }
164 
HandleSuspend(ProcessMonitor & process_monitor)165 Result<void> ServerLoopImpl::HandleSuspend(ProcessMonitor& process_monitor) {
166   // right order: guest -> host
167   LOG(DEBUG) << "Suspending the guest..";
168   CF_EXPECT(
169       RunAdbShellCommand(instance_, {"/vendor/bin/snapshot_hook_pre_suspend"}));
170   CF_EXPECT(SuspendGuest());
171   LOG(DEBUG) << "The guest is suspended.";
172   CF_EXPECT(process_monitor.SuspendMonitoredProcesses(),
173             "Failed to suspend host processes.");
174   LOG(DEBUG) << "The host processes are suspended.";
175   return {};
176 }
177 
HandleResume(ProcessMonitor & process_monitor)178 Result<void> ServerLoopImpl::HandleResume(ProcessMonitor& process_monitor) {
179   // right order: host -> guest
180   CF_EXPECT(process_monitor.ResumeMonitoredProcesses(),
181             "Failed to resume host processes.");
182   LOG(DEBUG) << "The host processes are resumed.";
183   LOG(DEBUG) << "Resuming the guest..";
184   CF_EXPECT(ResumeGuest());
185   CF_EXPECT(
186       RunAdbShellCommand(instance_, {"/vendor/bin/snapshot_hook_post_resume"}));
187   LOG(DEBUG) << "The guest resumed.";
188   return {};
189 }
190 
TakeCrosvmGuestSnapshot(const Json::Value & meta_json)191 Result<void> ServerLoopImpl::TakeCrosvmGuestSnapshot(
192     const Json::Value& meta_json) {
193   const auto snapshots_parent_dir =
194       CF_EXPECT(InstanceGuestSnapshotPath(meta_json, instance_.id()));
195   const auto crosvm_bin = config_.crosvm_binary();
196   const std::string snapshot_guest_param =
197       snapshots_parent_dir + "/" + kGuestSnapshotBase;
198   // If openwrt is running in crosvm, snapshot it.
199   const auto ap_vm_name = config_.ap_vm_manager();
200   if (instance_.ap_boot_flow() != APBootFlow::None &&
201       ap_vm_name == cuttlefish::kApName) {
202     const auto& openwrt_sock =
203         GetSocketPath(ap_vm_name, vm_name_to_control_sock_);
204     if (openwrt_sock == "") {
205       return CF_ERR("The vm_manager " + ap_vm_name + " is not supported yet");
206     }
207     std::vector<std::string> openwrt_crosvm_command_args{
208         crosvm_bin, "snapshot", "take", snapshot_guest_param + "_openwrt",
209         openwrt_sock};
210     LOG(DEBUG) << "Running the following command to take snapshot..."
211                << std::endl
212                << "  ";
213     for (const auto& arg : openwrt_crosvm_command_args) {
214       LOG(DEBUG) << arg << " ";
215     }
216     CF_EXPECT(Execute(openwrt_crosvm_command_args) == 0,
217               "Executing openwrt crosvm command returned -1");
218     LOG(DEBUG) << "Guest snapshot for openwrt instance #" << instance_.id()
219                << " should have been stored in " << snapshots_parent_dir
220                << "_openwrt";
221   }
222   const auto control_socket_path =
223       CF_EXPECT(VmControlSocket(), "Failed to find crosvm control.sock path.");
224   std::vector<std::string> crosvm_command_args{crosvm_bin, "snapshot", "take",
225                                                snapshot_guest_param,
226                                                control_socket_path};
227   LOG(DEBUG) << "Running the following command to take snapshot..." << std::endl
228              << "  ";
229   for (const auto& arg : crosvm_command_args) {
230     LOG(DEBUG) << arg << " ";
231   }
232   CF_EXPECT(Execute(crosvm_command_args) == 0,
233             "Executing crosvm command failed");
234   LOG(DEBUG) << "Guest snapshot for instance #" << instance_.id()
235              << " should have been stored in " << snapshots_parent_dir;
236   return {};
237 }
238 
239 /*
240  * Parse json file at json_path, and take guest snapshot
241  */
TakeGuestSnapshot(VmmMode vm_manager,const std::string & json_path)242 Result<void> ServerLoopImpl::TakeGuestSnapshot(VmmMode vm_manager,
243                                                const std::string& json_path) {
244   // common code across vm_manager
245   CF_EXPECTF(FileExists(json_path), "{} must exist but does not.", json_path);
246   SharedFD json_fd = SharedFD::Open(json_path, O_RDONLY);
247   CF_EXPECTF(json_fd->IsOpen(), "Failed to open {}", json_path);
248   std::string json_contents;
249   CF_EXPECT_GE(ReadAll(json_fd, &json_contents), 0,
250                std::string("Failed to read from ") + json_path);
251   Json::Value meta_json = CF_EXPECTF(
252       ParseJson(json_contents), "Failed to parse json: \n{}", json_contents);
253   CF_EXPECTF(vm_manager == VmmMode::kCrosvm,
254              "{}, which is not crosvm, is not yet supported.", vm_manager);
255   CF_EXPECT(TakeCrosvmGuestSnapshot(meta_json),
256             "TakeCrosvmGuestSnapshot() failed.");
257   return {};
258 }
259 
HandleSnapshotTake(const run_cvd::SnapshotTake & snapshot_take)260 Result<void> ServerLoopImpl::HandleSnapshotTake(
261     const run_cvd::SnapshotTake& snapshot_take) {
262   CF_EXPECT(!snapshot_take.snapshot_path().empty(),
263             "snapshot_path must be non-empty");
264   CF_EXPECT(
265       TakeGuestSnapshot(config_.vm_manager(), snapshot_take.snapshot_path()),
266       "Failed to take guest snapshot");
267   return {};
268 }
269 
270 }  // namespace run_cvd_impl
271 }  // namespace cuttlefish
272