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