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