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