• 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 <iostream>
17 #include <sstream>
18 #include <fstream>
19 
20 #include <gflags/gflags.h>
21 #include <android-base/logging.h>
22 
23 #include "common/libs/fs/shared_buf.h"
24 #include "common/libs/fs/shared_fd.h"
25 #include "common/libs/utils/subprocess.h"
26 #include "host/commands/start/filesystem_explorer.h"
27 #include "host/commands/start/flag_forwarder.h"
28 #include "host/libs/config/cuttlefish_config.h"
29 #include "host/libs/config/host_tools_version.h"
30 #include "host/libs/config/fetcher_config.h"
31 
32 /**
33  * If stdin is a tty, that means a user is invoking launch_cvd on the command
34  * line and wants automatic file detection for assemble_cvd.
35  *
36  * If stdin is not a tty, that means launch_cvd is being passed a list of files
37  * and that list should be forwarded to assemble_cvd.
38  *
39  * Controllable with a flag for extraordinary scenarios such as running from a
40  * daemon which closes its own stdin.
41  */
42 DEFINE_bool(run_file_discovery, true,
43             "Whether to run file discovery or get input files from stdin.");
44 DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
45 DEFINE_string(report_anonymous_usage_stats, "", "Report anonymous usage "
46             "statistics for metrics collection and analysis.");
47 DEFINE_int32(base_instance_num,
48              cuttlefish::GetInstance(),
49              "The instance number of the device created. When `-num_instances N`"
50              " is used, N instance numbers are claimed starting at this number.");
51 DEFINE_string(verbosity, "INFO", "Console logging verbosity. Options are VERBOSE,"
52                                  "DEBUG,INFO,WARNING,ERROR");
53 DEFINE_string(file_verbosity, "DEBUG",
54               "Log file logging verbosity. Options are VERBOSE,DEBUG,INFO,"
55               "WARNING,ERROR");
56 
57 namespace {
58 
59 std::string kAssemblerBin = cuttlefish::HostBinaryPath("assemble_cvd");
60 std::string kRunnerBin = cuttlefish::HostBinaryPath("run_cvd");
61 
StartAssembler(cuttlefish::SharedFD assembler_stdin,cuttlefish::SharedFD assembler_stdout,const std::vector<std::string> & argv)62 cuttlefish::Subprocess StartAssembler(cuttlefish::SharedFD assembler_stdin,
63                                cuttlefish::SharedFD assembler_stdout,
64                                const std::vector<std::string>& argv) {
65   cuttlefish::Command assemble_cmd(kAssemblerBin);
66   for (const auto& arg : argv) {
67     assemble_cmd.AddParameter(arg);
68   }
69   if (assembler_stdin->IsOpen()) {
70     assemble_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdIn, assembler_stdin);
71   }
72   assemble_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdOut, assembler_stdout);
73   return assemble_cmd.Start();
74 }
75 
StartRunner(cuttlefish::SharedFD runner_stdin,const std::vector<std::string> & argv)76 cuttlefish::Subprocess StartRunner(cuttlefish::SharedFD runner_stdin,
77                             const std::vector<std::string>& argv) {
78   cuttlefish::Command run_cmd(kRunnerBin);
79   for (const auto& arg : argv) {
80     run_cmd.AddParameter(arg);
81   }
82   run_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdIn, runner_stdin);
83   return run_cmd.Start();
84 }
85 
WriteFiles(cuttlefish::FetcherConfig fetcher_config,cuttlefish::SharedFD out)86 void WriteFiles(cuttlefish::FetcherConfig fetcher_config, cuttlefish::SharedFD out) {
87   std::stringstream output_streambuf;
88   for (const auto& file : fetcher_config.get_cvd_files()) {
89     output_streambuf << file.first << "\n";
90   }
91   std::string output_string = output_streambuf.str();
92   int written = cuttlefish::WriteAll(out, output_string);
93   if (written < 0) {
94     LOG(FATAL) << "Could not write file report (" << strerror(out->GetErrno())
95                << ")";
96   }
97 }
98 
ValidateMetricsConfirmation(std::string use_metrics)99 std::string ValidateMetricsConfirmation(std::string use_metrics) {
100   if (use_metrics == "") {
101     if (cuttlefish::CuttlefishConfig::ConfigExists()) {
102       auto config = cuttlefish::CuttlefishConfig::Get();
103       if (config) {
104         if (config->enable_metrics() == cuttlefish::CuttlefishConfig::kYes) {
105           use_metrics = "y";
106         } else if (config->enable_metrics() == cuttlefish::CuttlefishConfig::kNo) {
107           use_metrics = "n";
108         }
109       }
110     }
111   }
112   char ch = !use_metrics.empty() ? tolower(use_metrics.at(0)) : -1;
113   if (ch != 'n') {
114     std::cout << "===================================================================\n";
115     std::cout << "NOTICE:\n\n";
116     std::cout << "We collect usage statistics in accordance with our\n"
117                  "Content Licenses (https://source.android.com/setup/start/licenses),\n"
118                  "Contributor License Agreement (https://cla.developers.google.com/),\n"
119                  "Privacy Policy (https://policies.google.com/privacy) and\n"
120                  "Terms of Service (https://policies.google.com/terms).\n";
121     std::cout << "===================================================================\n\n";
122     if (use_metrics.empty()) {
123       std::cout << "Do you accept anonymous usage statistics reporting (Y/n)?: ";
124     }
125   }
126   for (;;) {
127     switch (ch) {
128       case 0:
129       case '\r':
130       case '\n':
131       case 'y':
132         return "y";
133       case 'n':
134         return "n";
135       default:
136         std::cout << "Must accept/reject anonymous usage statistics reporting (Y/n): ";
137         FALLTHROUGH_INTENDED;
138       case -1:
139         std::cin.get(ch);
140         // if there's no tty the EOF flag is set, in which case default to 'n'
141         if (std::cin.eof()) {
142           ch = 'n';
143           std::cout << "n\n";  // for consistency with user input
144         }
145         ch = tolower(ch);
146     }
147   }
148   return "";
149 }
150 
HostToolsUpdated()151 bool HostToolsUpdated() {
152   if (cuttlefish::CuttlefishConfig::ConfigExists()) {
153     auto config = cuttlefish::CuttlefishConfig::Get();
154     if (config) {
155       auto current_tools = cuttlefish::HostToolsCrc();
156       auto last_tools = config->host_tools_version();
157       return current_tools != last_tools;
158     }
159   }
160   return true;
161 }
162 
163 } // namespace
164 
main(int argc,char ** argv)165 int main(int argc, char** argv) {
166   ::android::base::InitLogging(argv, android::base::StderrLogger);
167 
168   FlagForwarder forwarder({kAssemblerBin, kRunnerBin});
169 
170   gflags::ParseCommandLineNonHelpFlags(&argc, &argv, false);
171 
172   forwarder.UpdateFlagDefaults();
173 
174   gflags::HandleCommandLineHelpFlags();
175 
176   setenv("CF_CONSOLE_SEVERITY", FLAGS_verbosity.c_str(), /* replace */ false);
177   setenv("CF_FILE_SEVERITY", FLAGS_file_verbosity.c_str(), /* replace */ false);
178 
179   auto use_metrics = FLAGS_report_anonymous_usage_stats;
180   FLAGS_report_anonymous_usage_stats = ValidateMetricsConfirmation(use_metrics);
181 
182   // TODO(b/159068082) Make decisions based on this value in assemble_cvd
183   LOG(INFO) << "Host changed from last run: " << HostToolsUpdated();
184 
185   cuttlefish::SharedFD assembler_stdout, assembler_stdout_capture;
186   cuttlefish::SharedFD::Pipe(&assembler_stdout_capture, &assembler_stdout);
187 
188   cuttlefish::SharedFD launcher_report, assembler_stdin;
189   bool should_generate_report = FLAGS_run_file_discovery;
190   if (should_generate_report) {
191     cuttlefish::SharedFD::Pipe(&assembler_stdin, &launcher_report);
192   }
193 
194   auto instance_num_str = std::to_string(FLAGS_base_instance_num);
195   setenv("CUTTLEFISH_INSTANCE", instance_num_str.c_str(), /* overwrite */ 1);
196 
197 #if defined(__BIONIC__)
198   // These environment variables are needed in case when Bionic is used.
199   // b/171754977
200   setenv("ANDROID_DATA", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
201   setenv("ANDROID_TZDATA_ROOT", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
202   setenv("ANDROID_ROOT", cuttlefish::DefaultHostArtifactsPath("").c_str(), /* overwrite */ 0);
203 #endif
204 
205   // SharedFDs are std::move-d in to avoid dangling references.
206   // Removing the std::move will probably make run_cvd hang as its stdin never closes.
207   auto assemble_proc = StartAssembler(std::move(assembler_stdin),
208                                       std::move(assembler_stdout),
209                                       forwarder.ArgvForSubprocess(kAssemblerBin));
210 
211   if (should_generate_report) {
212     WriteFiles(AvailableFilesReport(), std::move(launcher_report));
213   }
214 
215   std::string assembler_output;
216   if (cuttlefish::ReadAll(assembler_stdout_capture, &assembler_output) < 0) {
217     int error_num = errno;
218     LOG(ERROR) << "Read error getting output from assemble_cvd: " << strerror(error_num);
219     return -1;
220   }
221 
222   auto assemble_ret = assemble_proc.Wait();
223   if (assemble_ret != 0) {
224     LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
225     return assemble_ret;
226   } else {
227     LOG(DEBUG) << "assemble_cvd exited successfully.";
228   }
229 
230   std::vector<cuttlefish::Subprocess> runners;
231   for (int i = 0; i < FLAGS_num_instances; i++) {
232     cuttlefish::SharedFD runner_stdin_in, runner_stdin_out;
233     cuttlefish::SharedFD::Pipe(&runner_stdin_out, &runner_stdin_in);
234     std::string instance_name = std::to_string(i + FLAGS_base_instance_num);
235     setenv("CUTTLEFISH_INSTANCE", instance_name.c_str(), /* overwrite */ 1);
236 
237     auto run_proc = StartRunner(std::move(runner_stdin_out),
238                                 forwarder.ArgvForSubprocess(kRunnerBin));
239     runners.push_back(std::move(run_proc));
240     if (cuttlefish::WriteAll(runner_stdin_in, assembler_output) < 0) {
241       int error_num = errno;
242       LOG(ERROR) << "Could not write to run_cvd: " << strerror(error_num);
243       return -1;
244     }
245   }
246 
247   bool run_cvd_failure = false;
248   for (auto& run_proc : runners) {
249     auto run_ret = run_proc.Wait();
250     if (run_ret != 0) {
251       run_cvd_failure = true;
252       LOG(ERROR) << "run_cvd returned " << run_ret;
253     } else {
254       LOG(DEBUG) << "run_cvd exited successfully.";
255     }
256   }
257   return run_cvd_failure ? -1 : 0;
258 }
259