• 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 <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