1 //
2 // Copyright (C) 2019 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 #include <fstream>
17 #include <iostream>
18 #include <sstream>
19 #include <unordered_set>
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/no_destructor.h>
24 #include <android-base/parseint.h>
25 #include <gflags/gflags.h>
26
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/flag_parser.h"
31 #include "common/libs/utils/subprocess.h"
32 #include "host/commands/assemble_cvd/flags_defaults.h"
33 #include "host/commands/start/filesystem_explorer.h"
34 #include "host/commands/start/flag_forwarder.h"
35 #include "host/commands/start/override_bool_arg.h"
36 #include "host/commands/start/validate_metrics_confirmation.h"
37 #include "host/libs/config/config_utils.h"
38 #include "host/libs/config/cuttlefish_config.h"
39 #include "host/libs/config/fetcher_config.h"
40 #include "host/libs/config/host_tools_version.h"
41 #include "host/libs/config/instance_nums.h"
42
43 DEFINE_int32(num_instances, CF_DEFAULTS_NUM_INSTANCES,
44 "Number of Android guests to launch");
45 DEFINE_string(report_anonymous_usage_stats,
46 CF_DEFAULTS_REPORT_ANONYMOUS_USAGE_STATS,
47 "Report anonymous usage "
48 "statistics for metrics collection and analysis.");
49 DEFINE_int32(
50 base_instance_num, CF_DEFAULTS_BASE_INSTANCE_NUM,
51 "The instance number of the device created. When `-num_instances N`"
52 " is used, N instance numbers are claimed starting at this number.");
53 DEFINE_string(instance_nums, CF_DEFAULTS_INSTANCE_NUMS,
54 "A comma-separated list of instance numbers "
55 "to use. Mutually exclusive with base_instance_num.");
56 DEFINE_string(verbosity, CF_DEFAULTS_VERBOSITY,
57 "Console logging verbosity. Options are VERBOSE,"
58 "DEBUG,INFO,WARNING,ERROR");
59 DEFINE_string(file_verbosity, CF_DEFAULTS_FILE_VERBOSITY,
60 "Log file logging verbosity. Options are VERBOSE,DEBUG,INFO,"
61 "WARNING,ERROR");
62 DEFINE_bool(use_overlay, CF_DEFAULTS_USE_OVERLAY,
63 "Capture disk writes an overlay. This is a "
64 "prerequisite for powerwash_cvd or multiple instances.");
65 DEFINE_bool(track_host_tools_crc, CF_DEFAULTS_TRACK_HOST_TOOLS_CRC,
66 "Track changes to host executables");
67
68 namespace cuttlefish {
69 namespace {
70
71 using android::base::NoDestructor;
72
SubtoolPath(const std::string & subtool_base)73 std::string SubtoolPath(const std::string& subtool_base) {
74 auto my_own_dir = android::base::GetExecutableDirectory();
75 std::stringstream subtool_path_stream;
76 subtool_path_stream << my_own_dir << "/" << subtool_base;
77 auto subtool_path = subtool_path_stream.str();
78 if (my_own_dir.empty() || !FileExists(subtool_path)) {
79 return HostBinaryPath(subtool_base);
80 }
81 return subtool_path;
82 }
83
AssemblerPath()84 std::string AssemblerPath() { return SubtoolPath("assemble_cvd"); }
RunnerPath()85 std::string RunnerPath() { return SubtoolPath("run_cvd"); }
86
InvokeAssembler(const std::string & assembler_stdin,std::string & assembler_stdout,const std::vector<std::string> & argv)87 int InvokeAssembler(const std::string& assembler_stdin,
88 std::string& assembler_stdout,
89 const std::vector<std::string>& argv) {
90 Command assemble_cmd(AssemblerPath());
91 for (const auto& arg : argv) {
92 assemble_cmd.AddParameter(arg);
93 }
94 return RunWithManagedStdio(std::move(assemble_cmd), &assembler_stdin,
95 &assembler_stdout, nullptr);
96 }
97
StartRunner(SharedFD runner_stdin,const CuttlefishConfig::InstanceSpecific & instance,const std::vector<std::string> & argv)98 Subprocess StartRunner(SharedFD runner_stdin,
99 const CuttlefishConfig::InstanceSpecific& instance,
100 const std::vector<std::string>& argv) {
101 Command run_cmd(RunnerPath());
102 for (const auto& arg : argv) {
103 run_cmd.AddParameter(arg);
104 }
105 run_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, runner_stdin);
106 run_cmd.SetWorkingDirectory(instance.instance_dir());
107 return run_cmd.Start();
108 }
109
WriteFiles(FetcherConfig fetcher_config)110 std::string WriteFiles(FetcherConfig fetcher_config) {
111 std::stringstream output_streambuf;
112 for (const auto& file : fetcher_config.get_cvd_files()) {
113 output_streambuf << file.first << "\n";
114 }
115 return output_streambuf.str();
116 }
117
HostToolsUpdated()118 bool HostToolsUpdated() {
119 if (CuttlefishConfig::ConfigExists()) {
120 auto config = CuttlefishConfig::Get();
121 if (config) {
122 auto current_tools = HostToolsCrc();
123 auto last_tools = config->host_tools_version();
124 return current_tools != last_tools;
125 }
126 }
127 return true;
128 }
129
130 // Hash table for all bool flag names
131 // Used to find bool flag and convert "flag"/"noflag" to "--flag=value"
132 // This is the solution for vectorize bool flags in gFlags
133
BoolFlags()134 const std::unordered_set<std::string>& BoolFlags() {
135 static const NoDestructor<std::unordered_set<std::string>> bool_flags({
136 "chromeos_boot",
137 "console",
138 "daemon",
139 "enable_audio",
140 "enable_bootanimation",
141 "enable_gpu_udmabuf",
142 "enable_gpu_vhost_user",
143 "enable_kernel_log",
144 "enable_minimal_mode",
145 "enable_modem_simulator",
146 "enable_sandbox",
147 "enable_usb",
148 "enable_virtiofs",
149 "fail_fast",
150 "guest_enforce_security",
151 "kgdb",
152 "pause_in_bootloader",
153 "protected_vm",
154 "record_screen",
155 "restart_subprocesses",
156 "smt",
157 "start_gnss_proxy",
158 "start_webrtc",
159 "use_allocd",
160 "use_random_serial",
161 "use_sdcard",
162 "vhost_net",
163 "vhost_user_block",
164 "vhost_user_vsock",
165 });
166 return *bool_flags;
167 }
168
CvdInternalStartMain(int argc,char ** argv)169 int CvdInternalStartMain(int argc, char** argv) {
170 ::android::base::InitLogging(argv, android::base::StderrLogger);
171
172 std::vector<std::string> args(argv + 1, argv + argc);
173
174 std::vector<std::string> assemble_args;
175 std::string image_dir;
176 std::vector<std::string> args_copy = args;
177 auto parse_res = ConsumeFlags(
178 {GflagsCompatFlag("system_image_dir", image_dir)}, args_copy);
179 LOG(INFO) << "Using system_image_dir of: " << image_dir;
180
181 if (!parse_res.ok()) {
182 LOG(ERROR) << "Error extracting system_image_dir from args: "
183 << parse_res.error().FormatForEnv();
184 return -1;
185 } else if (!image_dir.empty()) {
186 assemble_args = {"--system_image_dir=" + image_dir};
187 }
188
189 std::vector<std::vector<std::string>> spargs = {assemble_args, {}};
190 FlagForwarder forwarder({AssemblerPath(), RunnerPath()}, spargs);
191
192 // Used to find bool flag and convert "flag"/"noflag" to "--flag=value"
193 // This is the solution for vectorize bool flags in gFlags
194 args = OverrideBoolArg(std::move(args), BoolFlags());
195 for (int i = 1; i < argc; i++) {
196 argv[i] = args[i - 1].data(); // args[] start from 0
197 }
198
199 gflags::ParseCommandLineNonHelpFlags(&argc, &argv, false);
200
201 forwarder.UpdateFlagDefaults();
202
203 gflags::HandleCommandLineHelpFlags();
204
205 setenv("CF_CONSOLE_SEVERITY", FLAGS_verbosity.c_str(), /* replace */ false);
206 setenv("CF_FILE_SEVERITY", FLAGS_file_verbosity.c_str(), /* replace */ false);
207
208 auto use_metrics = FLAGS_report_anonymous_usage_stats;
209 FLAGS_report_anonymous_usage_stats = ValidateMetricsConfirmation(use_metrics);
210
211 if (FLAGS_track_host_tools_crc) {
212 // TODO(b/159068082) Make decisions based on this value in assemble_cvd
213 LOG(INFO) << "Host changed from last run: " << HostToolsUpdated();
214 }
215
216 auto instance_nums = InstanceNumsCalculator().FromGlobalGflags().Calculate();
217 if (!instance_nums.ok()) {
218 LOG(ERROR) << instance_nums.error().FormatForEnv();
219 abort();
220 }
221
222 // TODO(schuffelen): Lift instance id assumptions in sandboxing
223
224 if (CuttlefishConfig::ConfigExists()) {
225 auto previous_config = CuttlefishConfig::Get();
226 CHECK(previous_config);
227 CHECK(!previous_config->Instances().empty());
228 auto previous_instance = previous_config->Instances()[0];
229 const auto& disks = previous_instance.virtual_disk_paths();
230 auto overlay = previous_instance.PerInstancePath("overlay.img");
231 auto used_overlay =
232 std::find(disks.begin(), disks.end(), overlay) != disks.end();
233 CHECK(used_overlay == FLAGS_use_overlay)
234 << "Cannot transition between different values of --use_overlay "
235 << "(Previous = " << used_overlay << ", current = " << FLAGS_use_overlay
236 << "). To fix this, delete \"" << previous_config->root_dir()
237 << "\" and any image files.";
238 }
239
240 CHECK(!instance_nums->empty()) << "Expected at least one instance";
241 auto instance_num_str = std::to_string(*instance_nums->begin());
242 setenv(kCuttlefishInstanceEnvVarName, instance_num_str.c_str(),
243 /* overwrite */ 1);
244
245 #if defined(__BIONIC__)
246 // These environment variables are needed in case when Bionic is used.
247 // b/171754977
248 setenv("ANDROID_DATA", DefaultHostArtifactsPath("").c_str(),
249 /* overwrite */ 0);
250 setenv("ANDROID_TZDATA_ROOT", DefaultHostArtifactsPath("").c_str(),
251 /* overwrite */ 0);
252 setenv("ANDROID_ROOT", DefaultHostArtifactsPath("").c_str(),
253 /* overwrite */ 0);
254 #endif
255
256 auto assembler_input = WriteFiles(AvailableFilesReport());
257 std::string assembler_output;
258 auto assemble_ret =
259 InvokeAssembler(assembler_input, assembler_output,
260 forwarder.ArgvForSubprocess(AssemblerPath(), args));
261
262 if (assemble_ret != 0) {
263 LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
264 return assemble_ret;
265 } else {
266 LOG(DEBUG) << "assemble_cvd exited successfully.";
267 }
268
269 std::string conf_path;
270 for (const auto& line : android::base::Tokenize(assembler_output, "\n")) {
271 if (android::base::EndsWith(line, "cuttlefish_config.json")) {
272 conf_path = line;
273 }
274 }
275 CHECK(!conf_path.empty()) << "could not find config";
276 auto config = CuttlefishConfig::GetFromFile(conf_path);
277 CHECK(config) << "Could not load config object";
278 setenv(kCuttlefishConfigEnvVarName, conf_path.c_str(), /* overwrite */ true);
279
280 std::vector<Subprocess> runners;
281 for (const auto& instance : config->Instances()) {
282 SharedFD runner_stdin = SharedFD::Open("/dev/null", O_RDONLY);
283 CHECK(runner_stdin->IsOpen()) << runner_stdin->StrError();
284 setenv(kCuttlefishInstanceEnvVarName, instance.id().c_str(),
285 /* overwrite */ 1);
286
287 auto run_proc = StartRunner(std::move(runner_stdin), instance,
288 forwarder.ArgvForSubprocess(RunnerPath()));
289 runners.push_back(std::move(run_proc));
290 }
291
292 bool run_cvd_failure = false;
293 for (auto& run_proc : runners) {
294 auto run_ret = run_proc.Wait();
295 if (run_ret != 0) {
296 run_cvd_failure = true;
297 LOG(ERROR) << "run_cvd returned " << run_ret;
298 } else {
299 LOG(DEBUG) << "run_cvd exited successfully.";
300 }
301 }
302 return run_cvd_failure ? -1 : 0;
303 }
304
305 } // namespace
306 } // namespace cuttlefish
307
main(int argc,char ** argv)308 int main(int argc, char** argv) {
309 return cuttlefish::CvdInternalStartMain(argc, argv);
310 }
311