• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/parseint.h>
24 #include <gflags/gflags.h>
25 
26 #include "common/libs/fs/shared_buf.h"
27 #include "common/libs/fs/shared_fd.h"
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/subprocess.h"
30 #include "host/commands/assemble_cvd/flags_defaults.h"
31 #include "host/commands/start/filesystem_explorer.h"
32 #include "host/commands/start/flag_forwarder.h"
33 #include "host/libs/config/cuttlefish_config.h"
34 #include "host/libs/config/fetcher_config.h"
35 #include "host/libs/config/host_tools_version.h"
36 #include "host/libs/config/instance_nums.h"
37 /**
38  * If stdin is a tty, that means a user is invoking launch_cvd on the command
39  * line and wants automatic file detection for assemble_cvd.
40  *
41  * If stdin is not a tty, that means launch_cvd is being passed a list of files
42  * and that list should be forwarded to assemble_cvd.
43  *
44  * Controllable with a flag for extraordinary scenarios such as running from a
45  * daemon which closes its own stdin.
46  */
47 DEFINE_bool(run_file_discovery, CF_DEFAULTS_RUN_FILE_DISCOVERY,
48             "Whether to run file discovery or get input files from stdin.");
49 DEFINE_int32(num_instances, CF_DEFAULTS_NUM_INSTANCES,
50              "Number of Android guests to launch");
51 DEFINE_string(report_anonymous_usage_stats,
52               CF_DEFAULTS_REPORT_ANONYMOUS_USAGE_STATS,
53               "Report anonymous usage "
54               "statistics for metrics collection and analysis.");
55 DEFINE_int32(
56     base_instance_num, CF_DEFAULTS_BASE_INSTANCE_NUM,
57     "The instance number of the device created. When `-num_instances N`"
58     " is used, N instance numbers are claimed starting at this number.");
59 DEFINE_string(instance_nums, CF_DEFAULTS_INSTANCE_NUMS,
60               "A comma-separated list of instance numbers "
61               "to use. Mutually exclusive with base_instance_num.");
62 DEFINE_string(verbosity, CF_DEFAULTS_VERBOSITY,
63               "Console logging verbosity. Options are VERBOSE,"
64               "DEBUG,INFO,WARNING,ERROR");
65 DEFINE_string(file_verbosity, CF_DEFAULTS_FILE_VERBOSITY,
66               "Log file logging verbosity. Options are VERBOSE,DEBUG,INFO,"
67               "WARNING,ERROR");
68 DEFINE_bool(use_overlay, CF_DEFAULTS_USE_OVERLAY,
69             "Capture disk writes an overlay. This is a "
70             "prerequisite for powerwash_cvd or multiple instances.");
71 
72 namespace {
73 
SubtoolPath(const std::string & subtool_base)74 std::string SubtoolPath(const std::string& subtool_base) {
75   auto my_own_dir = android::base::GetExecutableDirectory();
76   std::stringstream subtool_path_stream;
77   subtool_path_stream << my_own_dir << "/" << subtool_base;
78   auto subtool_path = subtool_path_stream.str();
79   if (my_own_dir.empty() || !cuttlefish::FileExists(subtool_path)) {
80     return cuttlefish::HostBinaryPath(subtool_base);
81   }
82   return subtool_path;
83 }
84 
85 std::string kAssemblerBin = SubtoolPath("assemble_cvd");
86 std::string kRunnerBin = SubtoolPath("run_cvd");
87 
StartAssembler(cuttlefish::SharedFD assembler_stdin,cuttlefish::SharedFD assembler_stdout,const std::vector<std::string> & argv)88 cuttlefish::Subprocess StartAssembler(cuttlefish::SharedFD assembler_stdin,
89                                cuttlefish::SharedFD assembler_stdout,
90                                const std::vector<std::string>& argv) {
91   cuttlefish::Command assemble_cmd(kAssemblerBin);
92   for (const auto& arg : argv) {
93     assemble_cmd.AddParameter(arg);
94   }
95   if (assembler_stdin->IsOpen()) {
96     assemble_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdIn, assembler_stdin);
97   }
98   assemble_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdOut, assembler_stdout);
99   return assemble_cmd.Start();
100 }
101 
StartRunner(cuttlefish::SharedFD runner_stdin,const std::vector<std::string> & argv)102 cuttlefish::Subprocess StartRunner(cuttlefish::SharedFD runner_stdin,
103                             const std::vector<std::string>& argv) {
104   cuttlefish::Command run_cmd(kRunnerBin);
105   for (const auto& arg : argv) {
106     run_cmd.AddParameter(arg);
107   }
108   run_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdIn, runner_stdin);
109   return run_cmd.Start();
110 }
111 
WriteFiles(cuttlefish::FetcherConfig fetcher_config,cuttlefish::SharedFD out)112 void WriteFiles(cuttlefish::FetcherConfig fetcher_config, cuttlefish::SharedFD out) {
113   std::stringstream output_streambuf;
114   for (const auto& file : fetcher_config.get_cvd_files()) {
115     output_streambuf << file.first << "\n";
116   }
117   std::string output_string = output_streambuf.str();
118   int written = cuttlefish::WriteAll(out, output_string);
119   if (written < 0) {
120     LOG(FATAL) << "Could not write file report (" << strerror(out->GetErrno())
121                << ")";
122   }
123 }
124 
ValidateMetricsConfirmation(std::string use_metrics)125 std::string ValidateMetricsConfirmation(std::string use_metrics) {
126   if (use_metrics == "") {
127     if (cuttlefish::CuttlefishConfig::ConfigExists()) {
128       auto config = cuttlefish::CuttlefishConfig::Get();
129       if (config) {
130         if (config->enable_metrics() == cuttlefish::CuttlefishConfig::kYes) {
131           use_metrics = "y";
132         } else if (config->enable_metrics() == cuttlefish::CuttlefishConfig::kNo) {
133           use_metrics = "n";
134         }
135       }
136     }
137   }
138 
139   std::cout << "===================================================================\n";
140   std::cout << "NOTICE:\n\n";
141   std::cout << "By using this Android Virtual Device, you agree to\n";
142   std::cout << "Google Terms of Service (https://policies.google.com/terms).\n";
143   std::cout << "The Google Privacy Policy (https://policies.google.com/privacy)\n";
144   std::cout << "describes how Google handles information generated as you use\n";
145   std::cout << "Google Services.";
146   char ch = !use_metrics.empty() ? tolower(use_metrics.at(0)) : -1;
147   if (ch != 'n') {
148     if (use_metrics.empty()) {
149       std::cout << "\n===================================================================\n";
150       std::cout << "Automatically send diagnostic information to Google, such as crash\n";
151       std::cout << "reports and usage data from this Android Virtual Device. You can\n";
152       std::cout << "adjust this permission at any time by running\n";
153       std::cout << "\"launch_cvd -report_anonymous_usage_stats=n\". (Y/n)?:";
154     } else {
155       std::cout << " You can adjust the permission for sending\n";
156       std::cout << "diagnostic information to Google, such as crash reports and usage\n";
157       std::cout << "data from this Android Virtual Device, at any time by running\n";
158       std::cout << "\"launch_cvd -report_anonymous_usage_stats=n\"\n";
159       std::cout << "===================================================================\n\n";
160     }
161   } else {
162     std::cout << "\n===================================================================\n\n";
163   }
164   for (;;) {
165     switch (ch) {
166       case 0:
167       case '\r':
168       case '\n':
169       case 'y':
170         return "y";
171       case 'n':
172         return "n";
173       default:
174         std::cout << "Must accept/reject anonymous usage statistics reporting (Y/n): ";
175         FALLTHROUGH_INTENDED;
176       case -1:
177         std::cin.get(ch);
178         // if there's no tty the EOF flag is set, in which case default to 'n'
179         if (std::cin.eof()) {
180           ch = 'n';
181           std::cout << "n\n";  // for consistency with user input
182         }
183         ch = tolower(ch);
184     }
185   }
186   return "";
187 }
188 
HostToolsUpdated()189 bool HostToolsUpdated() {
190   if (cuttlefish::CuttlefishConfig::ConfigExists()) {
191     auto config = cuttlefish::CuttlefishConfig::Get();
192     if (config) {
193       auto current_tools = cuttlefish::HostToolsCrc();
194       auto last_tools = config->host_tools_version();
195       return current_tools != last_tools;
196     }
197   }
198   return true;
199 }
200 
201 // Hash table for all bool flag names
202 // Used to find bool flag and convert "flag"/"noflag" to "--flag=value"
203 // This is the solution for vectorize bool flags in gFlags
204 
205 std::unordered_set<std::string> kBoolFlags = {"guest_enforce_security",
206                                               "use_random_serial",
207                                               "use_allocd",
208                                               "use_sdcard",
209                                               "pause_in_bootloader",
210                                               "daemon",
211                                               "enable_minimal_mode",
212                                               "enable_modem_simulator",
213                                               "console",
214                                               "enable_sandbox",
215                                               "restart_subprocesses",
216                                               "enable_gpu_udmabuf",
217                                               "enable_gpu_angle",
218                                               "enable_audio",
219                                               "start_gnss_proxy",
220                                               "enable_bootanimation",
221                                               "record_screen",
222                                               "protected_vm",
223                                               "enable_kernel_log",
224                                               "kgdb",
225                                               "start_webrtc",
226                                               "smt",
227                                               "vhost_net"};
228 
229 struct BooleanFlag {
230   bool is_bool_flag;
231   bool bool_flag_value;
232   std::string name;
233 };
IsBoolArg(const std::string & argument)234 BooleanFlag IsBoolArg(const std::string& argument) {
235   // Validate format
236   // we only deal with special bool case: -flag, --flag, -noflag, --noflag
237   // and convert to -flag=true, --flag=true, -flag=false, --flag=false
238   // others not in this format just return false
239   std::string_view name = argument;
240   if (!android::base::ConsumePrefix(&name, "-")) {
241     return {false, false, ""};
242   }
243   android::base::ConsumePrefix(&name, "-");
244   std::size_t found = name.find('=');
245   if (found != std::string::npos) {
246     // found "=", --flag=value case, it doesn't need convert
247     return {false, false, ""};
248   }
249 
250   // Validate it is part of the set
251   std::string result_name(name);
252   std::string_view new_name = result_name;
253   if (result_name.length() == 0) {
254     return {false, false, ""};
255   }
256   if (kBoolFlags.find(result_name) != kBoolFlags.end()) {
257     // matched -flag, --flag
258     return {true, true, result_name};
259   } else if (android::base::ConsumePrefix(&new_name, "no")) {
260     // 2nd chance to check -noflag, --noflag
261     result_name = new_name;
262     if (kBoolFlags.find(result_name) != kBoolFlags.end()) {
263       // matched -noflag, --noflag
264       return {true, false, result_name};
265     }
266   }
267   // return status
268   return {false, false, ""};
269 }
270 
FormatBoolString(const std::string & name_str,bool value)271 std::string FormatBoolString(const std::string& name_str, bool value) {
272   std::string new_flag = "--" + name_str;
273   if (value) {
274     new_flag += "=true";
275   } else {
276     new_flag += "=false";
277   }
278   return new_flag;
279 }
280 
OverrideBoolArg(std::vector<std::string> & args)281 bool OverrideBoolArg(std::vector<std::string>& args) {
282   bool overrided = false;
283   for (int index = 0; index < args.size(); index++) {
284     const std::string curr_arg = args[index];
285     BooleanFlag value = IsBoolArg(curr_arg);
286     if (value.is_bool_flag) {
287       // Override the value
288       args[index] = FormatBoolString(value.name, value.bool_flag_value);
289       overrided = true;
290     }
291   }
292   return overrided;
293 }
294 
295 } // namespace
296 
main(int argc,char ** argv)297 int main(int argc, char** argv) {
298   ::android::base::InitLogging(argv, android::base::StderrLogger);
299 
300   FlagForwarder forwarder({kAssemblerBin, kRunnerBin});
301 
302   // Used to find bool flag and convert "flag"/"noflag" to "--flag=value"
303   // This is the solution for vectorize bool flags in gFlags
304   std::vector<std::string> args(argv + 1, argv + argc);
305   if (OverrideBoolArg(args)) {
306     for (int i = 1; i < argc; i++) {
307       argv[i] = &args[i-1][0]; // args[] start from 0
308     }
309   }
310 
311   gflags::ParseCommandLineNonHelpFlags(&argc, &argv, false);
312 
313   forwarder.UpdateFlagDefaults();
314 
315   gflags::HandleCommandLineHelpFlags();
316 
317   setenv("CF_CONSOLE_SEVERITY", FLAGS_verbosity.c_str(), /* replace */ false);
318   setenv("CF_FILE_SEVERITY", FLAGS_file_verbosity.c_str(), /* replace */ false);
319 
320   auto use_metrics = FLAGS_report_anonymous_usage_stats;
321   FLAGS_report_anonymous_usage_stats = ValidateMetricsConfirmation(use_metrics);
322 
323   // TODO(b/159068082) Make decisions based on this value in assemble_cvd
324   LOG(INFO) << "Host changed from last run: " << HostToolsUpdated();
325 
326   cuttlefish::SharedFD assembler_stdout, assembler_stdout_capture;
327   cuttlefish::SharedFD::Pipe(&assembler_stdout_capture, &assembler_stdout);
328 
329   cuttlefish::SharedFD launcher_report, assembler_stdin;
330   bool should_generate_report = FLAGS_run_file_discovery;
331   if (should_generate_report) {
332     cuttlefish::SharedFD::Pipe(&assembler_stdin, &launcher_report);
333   }
334 
335   auto instance_nums =
336       cuttlefish::InstanceNumsCalculator().FromGlobalGflags().Calculate();
337   if (!instance_nums.ok()) {
338     LOG(ERROR) << instance_nums.error().Message();
339     LOG(DEBUG) << instance_nums.error().Trace();
340     abort();
341   }
342 
343   if (cuttlefish::CuttlefishConfig::ConfigExists()) {
344     auto previous_config = cuttlefish::CuttlefishConfig::Get();
345     CHECK(previous_config);
346     CHECK(!previous_config->Instances().empty());
347     auto previous_instance = previous_config->Instances()[0];
348     const auto& disks = previous_instance.virtual_disk_paths();
349     auto overlay = previous_instance.PerInstancePath("overlay.img");
350     auto used_overlay =
351         std::find(disks.begin(), disks.end(), overlay) != disks.end();
352     CHECK(used_overlay == FLAGS_use_overlay)
353         << "Cannot transition between different values of --use_overlay "
354         << "(Previous = " << used_overlay << ", current = " << FLAGS_use_overlay
355         << "). To fix this, delete \"" << previous_config->root_dir()
356         << "\" and any image files.";
357   }
358 
359   CHECK(!instance_nums->empty()) << "Expected at least one instance";
360   auto instance_num_str = std::to_string(*instance_nums->begin());
361   setenv(cuttlefish::kCuttlefishInstanceEnvVarName, instance_num_str.c_str(),
362          /* overwrite */ 1);
363 
364 #if defined(__BIONIC__)
365   // These environment variables are needed in case when Bionic is used.
366   // b/171754977
367   setenv("ANDROID_DATA", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
368   setenv("ANDROID_TZDATA_ROOT", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
369   setenv("ANDROID_ROOT", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
370 #endif
371 
372   // SharedFDs are std::move-d in to avoid dangling references.
373   // Removing the std::move will probably make run_cvd hang as its stdin never closes.
374   auto assemble_proc =
375       StartAssembler(std::move(assembler_stdin), std::move(assembler_stdout),
376                      forwarder.ArgvForSubprocess(kAssemblerBin, args));
377 
378   if (should_generate_report) {
379     WriteFiles(AvailableFilesReport(), std::move(launcher_report));
380   }
381 
382   std::string assembler_output;
383   if (cuttlefish::ReadAll(assembler_stdout_capture, &assembler_output) < 0) {
384     int error_num = errno;
385     LOG(ERROR) << "Read error getting output from assemble_cvd: " << strerror(error_num);
386     return -1;
387   }
388 
389   auto assemble_ret = assemble_proc.Wait();
390   if (assemble_ret != 0) {
391     LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
392     return assemble_ret;
393   } else {
394     LOG(DEBUG) << "assemble_cvd exited successfully.";
395   }
396 
397   std::vector<cuttlefish::Subprocess> runners;
398   for (const auto& instance_num : *instance_nums) {
399     cuttlefish::SharedFD runner_stdin_in, runner_stdin_out;
400     cuttlefish::SharedFD::Pipe(&runner_stdin_out, &runner_stdin_in);
401     std::string instance_num_str = std::to_string(instance_num);
402     setenv(cuttlefish::kCuttlefishInstanceEnvVarName, instance_num_str.c_str(),
403            /* overwrite */ 1);
404 
405     auto run_proc = StartRunner(std::move(runner_stdin_out),
406                                 forwarder.ArgvForSubprocess(kRunnerBin));
407     runners.push_back(std::move(run_proc));
408     if (cuttlefish::WriteAll(runner_stdin_in, assembler_output) < 0) {
409       int error_num = errno;
410       LOG(ERROR) << "Could not write to run_cvd: " << strerror(error_num);
411       return -1;
412     }
413   }
414 
415   bool run_cvd_failure = false;
416   for (auto& run_proc : runners) {
417     auto run_ret = run_proc.Wait();
418     if (run_ret != 0) {
419       run_cvd_failure = true;
420       LOG(ERROR) << "run_cvd returned " << run_ret;
421     } else {
422       LOG(DEBUG) << "run_cvd exited successfully.";
423     }
424   }
425   return run_cvd_failure ? -1 : 0;
426 }
427