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 <iterator>
18 #include <string>
19
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include "gflags/gflags.h"
24 #include <android-base/logging.h>
25
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/archive.h"
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/subprocess.h"
30
31 #include "host/libs/config/fetcher_config.h"
32
33 #include "build_api.h"
34 #include "credential_source.h"
35 #include "install_zip.h"
36
37 namespace {
38
39 const std::string DEFAULT_BRANCH = "aosp-master";
40 const std::string DEFAULT_BUILD_TARGET = "aosp_cf_x86_phone-userdebug";
41
42 }
43
44 using cuttlefish::CurrentDirectory;
45
46 DEFINE_string(default_build, DEFAULT_BRANCH + "/" + DEFAULT_BUILD_TARGET,
47 "source for the cuttlefish build to use (vendor.img + host)");
48 DEFINE_string(system_build, "", "source for system.img and product.img");
49 DEFINE_string(kernel_build, "", "source for the kernel or gki target");
50 DEFINE_string(otatools_build, "", "source for the host ota tools");
51
52 DEFINE_bool(download_img_zip, true, "Whether to fetch the -img-*.zip file.");
53 DEFINE_bool(download_target_files_zip, false, "Whether to fetch the "
54 "-target_files-*.zip file.");
55
56 DEFINE_string(credential_source, "", "Build API credential source");
57 DEFINE_string(directory, CurrentDirectory(), "Target directory to fetch "
58 "files into");
59 DEFINE_bool(run_next_stage, false, "Continue running the device through the next stage.");
60 DEFINE_string(wait_retry_period, "20", "Retry period for pending builds given "
61 "in seconds. Set to 0 to not wait.");
62
63 namespace cuttlefish {
64 namespace {
65
66 const std::string HOST_TOOLS = "cvd-host_package.tar.gz";
67 const std::string OTA_TOOLS = "otatools.zip";
68 const std::string OTA_TOOLS_DIR = "/otatools/";
69
70 /** Returns the name of one of the artifact target zip files.
71 *
72 * For example, for a target "aosp_cf_x86_phone-userdebug" at a build "5824130",
73 * the image zip file would be "aosp_cf_x86_phone-img-5824130.zip"
74 */
TargetBuildZipFromArtifacts(const Build & build,const std::string & name,const std::vector<Artifact> & artifacts)75 std::string TargetBuildZipFromArtifacts(
76 const Build& build, const std::string& name,
77 const std::vector<Artifact>& artifacts) {
78 std::string product = std::visit([](auto&& arg) { return arg.product; }, build);
79 auto id = std::visit([](auto&& arg) { return arg.id; }, build);
80 auto match = product + "-" + name + "-" + id;
81 for (const auto& artifact : artifacts) {
82 if (artifact.Name().find(match) != std::string::npos) {
83 return artifact.Name();
84 }
85 }
86 return "";
87 }
88
download_images(BuildApi * build_api,const Build & build,const std::string & target_directory,const std::vector<std::string> & images)89 std::vector<std::string> download_images(BuildApi* build_api,
90 const Build& build,
91 const std::string& target_directory,
92 const std::vector<std::string>& images) {
93 auto artifacts = build_api->Artifacts(build);
94 std::string img_zip_name = TargetBuildZipFromArtifacts(build, "img", artifacts);
95 if (img_zip_name.size() == 0) {
96 LOG(ERROR) << "Target " << build << " did not have an img zip";
97 return {};
98 }
99 std::string local_path = target_directory + "/" + img_zip_name;
100 if (!build_api->ArtifactToFile(build, img_zip_name, local_path)) {
101 LOG(ERROR) << "Unable to download " << build << ":" << img_zip_name << " to "
102 << local_path;
103 return {};
104 }
105
106 std::vector<std::string> files = ExtractImages(local_path, target_directory, images);
107 if (files.empty()) {
108 LOG(ERROR) << "Could not extract " << local_path;
109 return {};
110 }
111 if (unlink(local_path.c_str()) != 0) {
112 LOG(ERROR) << "Could not delete " << local_path;
113 files.push_back(local_path);
114 }
115 return files;
116 }
download_images(BuildApi * build_api,const Build & build,const std::string & target_directory)117 std::vector<std::string> download_images(BuildApi* build_api,
118 const Build& build,
119 const std::string& target_directory) {
120 return download_images(build_api, build, target_directory, {});
121 }
122
download_target_files(BuildApi * build_api,const Build & build,const std::string & target_directory)123 std::vector<std::string> download_target_files(BuildApi* build_api,
124 const Build& build,
125 const std::string& target_directory) {
126 auto artifacts = build_api->Artifacts(build);
127 std::string target_zip = TargetBuildZipFromArtifacts(build, "target_files", artifacts);
128 if (target_zip.size() == 0) {
129 LOG(ERROR) << "Target " << build << " did not have a target files zip";
130 return {};
131 }
132 std::string local_path = target_directory + "/" + target_zip;
133 if (!build_api->ArtifactToFile(build, target_zip, local_path)) {
134 LOG(ERROR) << "Unable to download " << build << ":" << target_zip << " to "
135 << local_path;
136 return {};
137 }
138 return {local_path};
139 }
140
download_host_package(BuildApi * build_api,const Build & build,const std::string & target_directory)141 std::vector<std::string> download_host_package(BuildApi* build_api,
142 const Build& build,
143 const std::string& target_directory) {
144 auto artifacts = build_api->Artifacts(build);
145 bool has_host_package = false;
146 for (const auto& artifact : artifacts) {
147 has_host_package |= artifact.Name() == HOST_TOOLS;
148 }
149 if (!has_host_package) {
150 LOG(ERROR) << "Target " << build << " did not have " << HOST_TOOLS;
151 return {};
152 }
153 std::string local_path = target_directory + "/" + HOST_TOOLS;
154
155 if (!build_api->ArtifactToFile(build, HOST_TOOLS, local_path)) {
156 LOG(ERROR) << "Unable to download " << build << ":" << HOST_TOOLS << " to "
157 << local_path;
158 return {};
159 }
160
161 Archive archive(local_path);
162 if (!archive.ExtractAll(target_directory)) {
163 LOG(ERROR) << "Could not extract " << local_path;
164 return {};
165 }
166 std::vector<std::string> files = archive.Contents();
167 for (auto& file : files) {
168 file = target_directory + "/" + file;
169 }
170 if (unlink(local_path.c_str()) != 0) {
171 LOG(ERROR) << "Could not delete " << local_path;
172 files.push_back(local_path);
173 }
174 return files;
175 }
176
download_ota_tools(BuildApi * build_api,const Build & build,const std::string & target_directory)177 std::vector<std::string> download_ota_tools(BuildApi* build_api,
178 const Build& build,
179 const std::string& target_directory) {
180 auto artifacts = build_api->Artifacts(build);
181 bool has_host_package = false;
182 for (const auto& artifact : artifacts) {
183 has_host_package |= artifact.Name() == OTA_TOOLS;
184 }
185 if (!has_host_package) {
186 LOG(ERROR) << "Target " << build << " did not have " << OTA_TOOLS;
187 return {};
188 }
189 std::string local_path = target_directory + "/" + OTA_TOOLS;
190
191 if (!build_api->ArtifactToFile(build, OTA_TOOLS, local_path)) {
192 LOG(ERROR) << "Unable to download " << build << ":" << OTA_TOOLS << " to "
193 << local_path;
194 return {};
195 }
196
197 std::string otatools_dir = target_directory + OTA_TOOLS_DIR;
198 if (!DirectoryExists(otatools_dir) && mkdir(otatools_dir.c_str(), 0777) != 0) {
199 LOG(ERROR) << "Could not create " << otatools_dir;
200 return {};
201 }
202 Archive archive(local_path);
203 if (!archive.ExtractAll(otatools_dir)) {
204 LOG(ERROR) << "Could not extract " << local_path;
205 return {};
206 }
207 std::vector<std::string> files = archive.Contents();
208 for (auto& file : files) {
209 file = target_directory + OTA_TOOLS_DIR + file;
210 }
211 files.push_back(local_path);
212 return files;
213 }
214
AddFilesToConfig(FileSource purpose,const Build & build,const std::vector<std::string> & paths,FetcherConfig * config,bool override_entry=false)215 void AddFilesToConfig(FileSource purpose, const Build& build,
216 const std::vector<std::string>& paths, FetcherConfig* config,
217 bool override_entry = false) {
218 for (const std::string& path : paths) {
219 // TODO(schuffelen): Do better for local builds here.
220 auto id = std::visit([](auto&& arg) { return arg.id; }, build);
221 auto target = std::visit([](auto&& arg) { return arg.target; }, build);
222 CvdFile file(purpose, id, target, path);
223 bool added = config->add_cvd_file(file, override_entry);
224 if (!added) {
225 LOG(ERROR) << "Duplicate file " << file;
226 LOG(ERROR) << "Existing file: " << config->get_cvd_files()[path];
227 LOG(FATAL) << "Failed to add path " << path;
228 }
229 }
230 }
231
232 std::string USAGE_MESSAGE =
233 "<flags>\n"
234 "\n"
235 "\"*_build\" flags accept values in the following format:\n"
236 "\"branch/build_target\" - latest build of \"branch\" for \"build_target\"\n"
237 "\"build_id/build_target\" - build \"build_id\" for \"build_target\"\n"
238 "\"branch\" - latest build of \"branch\" for \"aosp_cf_x86_phone-userdebug\"\n"
239 "\"build_id\" - build \"build_id\" for \"aosp_cf_x86_phone-userdebug\"\n";
240
241 } // namespace
242
FetchCvdMain(int argc,char ** argv)243 int FetchCvdMain(int argc, char** argv) {
244 ::android::base::InitLogging(argv, android::base::StderrLogger);
245 gflags::SetUsageMessage(USAGE_MESSAGE);
246 gflags::ParseCommandLineFlags(&argc, &argv, true);
247
248 FetcherConfig config;
249 config.RecordFlags();
250
251 std::string target_dir = AbsolutePath(FLAGS_directory);
252 if (!DirectoryExists(target_dir) && mkdir(target_dir.c_str(), 0777) != 0) {
253 LOG(FATAL) << "Could not create " << target_dir;
254 }
255 std::chrono::seconds retry_period(std::stoi(FLAGS_wait_retry_period));
256
257 curl_global_init(CURL_GLOBAL_DEFAULT);
258 {
259 std::unique_ptr<CredentialSource> credential_source;
260 if (FLAGS_credential_source == "gce") {
261 credential_source = GceMetadataCredentialSource::make();
262 } else if (FLAGS_credential_source != "") {
263 credential_source = FixedCredentialSource::make(FLAGS_credential_source);
264 }
265 BuildApi build_api(std::move(credential_source));
266
267 auto default_build = ArgumentToBuild(&build_api, FLAGS_default_build,
268 DEFAULT_BUILD_TARGET,
269 retry_period);
270
271 std::vector<std::string> host_package_files =
272 download_host_package(&build_api, default_build, target_dir);
273 if (host_package_files.empty()) {
274 LOG(FATAL) << "Could not download host package for " << default_build;
275 }
276 AddFilesToConfig(FileSource::DEFAULT_BUILD, default_build, host_package_files, &config);
277
278 if (FLAGS_system_build != "" || FLAGS_kernel_build != "" || FLAGS_otatools_build != "") {
279 auto ota_build = default_build;
280 if (FLAGS_otatools_build != "") {
281 ota_build = ArgumentToBuild(&build_api, FLAGS_otatools_build,
282 DEFAULT_BUILD_TARGET, retry_period);
283 }
284 std::vector<std::string> ota_tools_files =
285 download_ota_tools(&build_api, ota_build, target_dir);
286 if (ota_tools_files.empty()) {
287 LOG(FATAL) << "Could not download ota tools for " << ota_build;
288 }
289 AddFilesToConfig(FileSource::DEFAULT_BUILD, default_build, ota_tools_files, &config);
290 }
291 if (FLAGS_download_img_zip) {
292 std::vector<std::string> image_files =
293 download_images(&build_api, default_build, target_dir);
294 if (image_files.empty()) {
295 LOG(FATAL) << "Could not download images for " << default_build;
296 }
297 LOG(INFO) << "Adding img-zip files for default build";
298 for (auto& file : image_files) {
299 LOG(INFO) << file;
300 }
301 AddFilesToConfig(FileSource::DEFAULT_BUILD, default_build, image_files, &config);
302 }
303 if (FLAGS_system_build != "" || FLAGS_download_target_files_zip) {
304 std::string default_target_dir = target_dir + "/default";
305 if (mkdir(default_target_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
306 LOG(FATAL) << "Could not create " << default_target_dir;
307 }
308 std::vector<std::string> target_files =
309 download_target_files(&build_api, default_build, default_target_dir);
310 if (target_files.empty()) {
311 LOG(FATAL) << "Could not download target files for " << default_build;
312 }
313 LOG(INFO) << "Adding target files for default build";
314 AddFilesToConfig(FileSource::DEFAULT_BUILD, default_build, target_files, &config);
315 }
316
317 if (FLAGS_system_build != "") {
318 auto system_build = ArgumentToBuild(&build_api, FLAGS_system_build,
319 DEFAULT_BUILD_TARGET,
320 retry_period);
321 bool system_in_img_zip = true;
322 if (FLAGS_download_img_zip) {
323 std::vector<std::string> image_files =
324 download_images(&build_api, system_build, target_dir,
325 {"system.img", "product.img"});
326 if (image_files.empty()) {
327 LOG(INFO) << "Could not find system image for " << system_build
328 << "in the img zip. Assuming a super image build, which will "
329 << "get the system image from the target zip.";
330 system_in_img_zip = false;
331 } else {
332 LOG(INFO) << "Adding img-zip files for system build";
333 AddFilesToConfig(FileSource::SYSTEM_BUILD, system_build, image_files,
334 &config, true);
335 }
336 }
337 std::string system_target_dir = target_dir + "/system";
338 if (mkdir(system_target_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
339 LOG(FATAL) << "Could not create " << system_target_dir;
340 }
341 std::vector<std::string> target_files =
342 download_target_files(&build_api, system_build, system_target_dir);
343 if (target_files.empty()) {
344 LOG(FATAL) << "Could not download target files for " << system_build;
345 return -1;
346 }
347 AddFilesToConfig(FileSource::SYSTEM_BUILD, system_build, target_files, &config);
348 if (!system_in_img_zip) {
349 if (ExtractImages(target_files[0], target_dir, {"IMAGES/system.img"})
350 != std::vector<std::string>{}) {
351 std::string extracted_system = target_dir + "/IMAGES/system.img";
352 std::string target_system = target_dir + "/system.img";
353 if (rename(extracted_system.c_str(), target_system.c_str())) {
354 int error_num = errno;
355 LOG(FATAL) << "Could not replace system.img in target directory: "
356 << strerror(error_num);
357 return -1;
358 }
359 } else {
360 LOG(FATAL) << "Could not get system.img from the target zip";
361 return -1;
362 }
363 if (ExtractImages(target_files[0], target_dir, {"IMAGES/product.img"})
364 != std::vector<std::string>{}) {
365 std::string extracted_product = target_dir + "/IMAGES/product.img";
366 std::string target_product = target_dir + "/product.img";
367 if (rename(extracted_product.c_str(), target_product.c_str())) {
368 int error_num = errno;
369 LOG(FATAL) << "Could not replace product.img in target directory"
370 << strerror(error_num);
371 return -1;
372 }
373 }
374 if (ExtractImages(target_files[0], target_dir, {"IMAGES/system_ext.img"})
375 != std::vector<std::string>{}) {
376 std::string extracted_system_ext = target_dir + "/IMAGES/system_ext.img";
377 std::string target_system_ext = target_dir + "/system_ext.img";
378 if (rename(extracted_system_ext.c_str(), target_system_ext.c_str())) {
379 int error_num = errno;
380 LOG(FATAL) << "Could not move system_ext.img in target directory: "
381 << strerror(error_num);
382 return -1;
383 }
384 }
385 if (ExtractImages(target_files[0], target_dir, {"IMAGES/vbmeta_system.img"})
386 != std::vector<std::string>{}) {
387 std::string extracted_vbmeta_system = target_dir + "/IMAGES/vbmeta_system.img";
388 std::string target_vbmeta_system = target_dir + "/vbmeta_system.img";
389 if (rename(extracted_vbmeta_system.c_str(), target_vbmeta_system.c_str())) {
390 int error_num = errno;
391 LOG(FATAL) << "Could not move vbmeta_system.img in target directory: "
392 << strerror(error_num);
393 return -1;
394 }
395 }
396 // This should technically call AddFilesToConfig with the produced files,
397 // but it will conflict with the ones produced from the default system image
398 // and pie doesn't care about the produced file list anyway.
399 }
400 }
401
402 if (FLAGS_kernel_build != "") {
403 auto kernel_build = ArgumentToBuild(&build_api, FLAGS_kernel_build,
404 "kernel", retry_period);
405
406 std::string local_path = target_dir + "/kernel";
407 if (build_api.ArtifactToFile(kernel_build, "bzImage", local_path)) {
408 AddFilesToConfig(FileSource::KERNEL_BUILD, kernel_build, {local_path}, &config);
409 } else {
410 LOG(FATAL) << "Could not download " << kernel_build << ":bzImage to "
411 << local_path;
412 }
413 std::vector<Artifact> kernel_artifacts = build_api.Artifacts(kernel_build);
414 for (const auto& artifact : kernel_artifacts) {
415 if (artifact.Name() != "initramfs.img") {
416 continue;
417 }
418 bool downloaded = build_api.ArtifactToFile(
419 kernel_build, "initramfs.img", target_dir + "/initramfs.img");
420 if (!downloaded) {
421 LOG(FATAL) << "Could not download " << kernel_build << ":initramfs.img to "
422 << target_dir + "/initramfs.img";
423 }
424 AddFilesToConfig(FileSource::KERNEL_BUILD, kernel_build,
425 {target_dir + "/initramfs.img"}, &config);
426 }
427 }
428 }
429 curl_global_cleanup();
430
431 // Due to constraints of the build system, artifacts intentionally cannot determine
432 // their own build id. So it's unclear which build number fetch_cvd itself was built at.
433 // https://android.googlesource.com/platform/build/+/979c9f3/Changes.md#build_number
434 std::string fetcher_path = target_dir + "/fetcher_config.json";
435 AddFilesToConfig(GENERATED, DeviceBuild("", ""), {fetcher_path}, &config);
436 config.SaveToFile(fetcher_path);
437
438 for (const auto& file : config.get_cvd_files()) {
439 std::cout << file.second.file_path << "\n";
440 }
441 std::cout << std::flush;
442
443 if (!FLAGS_run_next_stage) {
444 return 0;
445 }
446
447 // Ignore return code. We want to make sure there is no running instance,
448 // and stop_cvd will exit with an error code if there is already no running instance.
449 Command stop_cmd(target_dir + "/bin/stop_cvd");
450 stop_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
451 Subprocess::StdIOChannel::kStdErr);
452 stop_cmd.Start().Wait();
453
454 // gflags::ParseCommandLineFlags will remove fetch_cvd's flags from this.
455 // This depends the remove_flags argument (3rd) is "true".
456
457 auto filelist_fd = SharedFD::MemfdCreate("files_list");
458 if (!filelist_fd->IsOpen()) {
459 LOG(FATAL) << "Unable to create temp file to write file list. "
460 << filelist_fd->StrError() << " (" << filelist_fd->GetErrno() << ")";
461 }
462
463 for (const auto& file : config.get_cvd_files()) {
464 std::string file_entry = file.second.file_path + "\n";
465 auto chars_written = filelist_fd->Write(file_entry.c_str(), file_entry.size());
466 if (chars_written != file_entry.size()) {
467 LOG(FATAL) << "Unable to write entry to file list. Expected to write "
468 << file_entry.size() << " but wrote " << chars_written << ". "
469 << filelist_fd->StrError() << " (" << filelist_fd->GetErrno() << ")";
470 }
471 }
472 auto seek_result = filelist_fd->LSeek(0, SEEK_SET);
473 if (seek_result != 0) {
474 LOG(FATAL) << "Unable to seek on file list file. Expected 0, received " << seek_result
475 << filelist_fd->StrError() << " (" << filelist_fd->GetErrno() << ")";
476 }
477
478 if (filelist_fd->UNMANAGED_Dup2(0) == -1) {
479 LOG(FATAL) << "Unable to set file list to stdin. "
480 << filelist_fd->StrError() << " (" << filelist_fd->GetErrno() << ")";
481 }
482
483 // TODO(b/139199114): Go into assemble_cvd when the interface is stable and implemented.
484
485 std::string next_stage = target_dir + "/bin/launch_cvd";
486 std::vector<const char*> next_stage_argv = {"launch_cvd"};
487 LOG(INFO) << "Running " << next_stage;
488 for (int i = 1; i < argc; i++) {
489 LOG(INFO) << argv[i];
490 next_stage_argv.push_back(argv[i]);
491 }
492 next_stage_argv.push_back(nullptr);
493 execv(next_stage.c_str(), const_cast<char* const*>(next_stage_argv.data()));
494 int error = errno;
495 LOG(FATAL) << "execv returned with errno " << error << ":" << strerror(error);
496
497 return -1;
498 }
499
500 } // namespace cuttlefish
501
main(int argc,char ** argv)502 int main(int argc, char** argv) {
503 return cuttlefish::FetchCvdMain(argc, argv);
504 }
505