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