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