• 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 <iostream>
17 #include <string_view>
18 
19 #include <android-base/logging.h>
20 #include <android-base/parsebool.h>
21 #include <android-base/parseint.h>
22 #include <android-base/strings.h>
23 #include <gflags/gflags.h>
24 
25 #include "common/libs/fs/shared_buf.h"
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/contains.h"
28 #include "common/libs/utils/environment.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/flag_parser.h"
31 #include "common/libs/utils/in_sandbox.h"
32 #include "common/libs/utils/known_paths.h"
33 #include "common/libs/utils/tee_logging.h"
34 #include "host/commands/assemble_cvd/clean.h"
35 #include "host/commands/assemble_cvd/disk_flags.h"
36 #include "host/commands/assemble_cvd/display.h"
37 #include "host/commands/assemble_cvd/flag_feature.h"
38 #include "host/commands/assemble_cvd/flags.h"
39 #include "host/commands/assemble_cvd/flags_defaults.h"
40 #include "host/commands/assemble_cvd/touchpad.h"
41 #include "host/libs/command_util/snapshot_utils.h"
42 #include "host/libs/config/adb/adb.h"
43 #include "host/libs/config/config_flag.h"
44 #include "host/libs/config/custom_actions.h"
45 #include "host/libs/config/fastboot/fastboot.h"
46 #include "host/libs/config/fetcher_config.h"
47 #include "host/libs/config/inject.h"
48 
49 using cuttlefish::StringFromEnv;
50 
51 DEFINE_string(assembly_dir, CF_DEFAULTS_ASSEMBLY_DIR,
52               "A directory to put generated files common between instances");
53 DEFINE_string(instance_dir, CF_DEFAULTS_INSTANCE_DIR,
54               "This is a directory that will hold the cuttlefish generated"
55               "files, including both instance-specific and common files");
56 DEFINE_string(snapshot_path, "",
57               "Path to snapshot. Must not be empty if the device is to be "
58               "restored from a snapshot");
59 DEFINE_bool(resume, CF_DEFAULTS_RESUME,
60             "Resume using the disk from the last session, if "
61             "possible. i.e., if --noresume is passed, the disk "
62             "will be reset to the state it was initially launched "
63             "in. This flag is ignored if the underlying partition "
64             "images have been updated since the first launch."
65             "If the device starts from a snapshot, this will be always true.");
66 
67 DECLARE_bool(use_overlay);
68 
69 namespace cuttlefish {
70 namespace {
71 
72 static constexpr std::string_view kFetcherConfigFile = "fetcher_config.json";
73 
FindFetcherConfig(const std::vector<std::string> & files)74 FetcherConfig FindFetcherConfig(const std::vector<std::string>& files) {
75   FetcherConfig fetcher_config;
76   for (const auto& file : files) {
77     if (android::base::EndsWith(file, kFetcherConfigFile)) {
78       std::string home_directory = StringFromEnv("HOME", CurrentDirectory());
79       std::string fetcher_file = file;
80       if (!FileExists(file) &&
81           FileExists(home_directory + "/" + fetcher_file)) {
82         LOG(INFO) << "Found " << fetcher_file << " in HOME directory ('"
83                   << home_directory << "') and not current working directory";
84         fetcher_file = home_directory + "/" + fetcher_file;
85       }
86 
87       if (fetcher_config.LoadFromFile(fetcher_file)) {
88         return fetcher_config;
89       }
90       LOG(ERROR) << "Could not load fetcher config file.";
91     }
92   }
93   LOG(DEBUG) << "Could not locate fetcher config file.";
94   return fetcher_config;
95 }
96 
GetLegacyConfigFilePath(const CuttlefishConfig & config)97 std::string GetLegacyConfigFilePath(const CuttlefishConfig& config) {
98   return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json");
99 }
100 
SaveConfig(const CuttlefishConfig & tmp_config_obj)101 Result<void> SaveConfig(const CuttlefishConfig& tmp_config_obj) {
102   auto config_file = GetConfigFilePath(tmp_config_obj);
103   auto config_link = GetGlobalConfigFileLink();
104   // Save the config object before starting any host process
105   CF_EXPECT(tmp_config_obj.SaveToFile(config_file),
106             "Failed to save to \"" << config_file << "\"");
107   auto legacy_config_file = GetLegacyConfigFilePath(tmp_config_obj);
108   CF_EXPECT(tmp_config_obj.SaveToFile(legacy_config_file),
109             "Failed to save to \"" << legacy_config_file << "\"");
110 
111   setenv(kCuttlefishConfigEnvVarName, config_file.c_str(), true);
112   // TODO(schuffelen): Find alternative for host-sandboxing mode
113   if (!InSandbox()) {
114     if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
115       return CF_ERRNO("symlink(\"" << config_file << "\", \"" << config_link
116                                    << ") failed");
117     }
118   }
119 
120   return {};
121 }
122 
123 #ifndef O_TMPFILE
124 # define O_TMPFILE (020000000 | O_DIRECTORY)
125 #endif
126 
CreateLegacySymlinks(const CuttlefishConfig::InstanceSpecific & instance,const CuttlefishConfig::EnvironmentSpecific & environment)127 Result<void> CreateLegacySymlinks(
128     const CuttlefishConfig::InstanceSpecific& instance,
129     const CuttlefishConfig::EnvironmentSpecific& environment) {
130   std::string log_files[] = {"kernel.log",
131                              "launcher.log",
132                              "logcat",
133                              "metrics.log",
134                              "modem_simulator.log",
135                              "crosvm_openwrt.log",
136                              "crosvm_openwrt_boot.log"};
137   for (const auto& log_file : log_files) {
138     auto symlink_location = instance.PerInstancePath(log_file.c_str());
139     auto log_target = "logs/" + log_file;  // Relative path
140     if (FileExists(symlink_location, /* follow_symlinks */ false)) {
141       CF_EXPECT(RemoveFile(symlink_location),
142                 "Failed to remove symlink " << symlink_location);
143     }
144     if (symlink(log_target.c_str(), symlink_location.c_str()) != 0) {
145       return CF_ERRNO("symlink(\"" << log_target << ", " << symlink_location
146                                    << ") failed");
147     }
148   }
149 
150   std::stringstream legacy_instance_path_stream;
151   legacy_instance_path_stream << FLAGS_instance_dir;
152   if (gflags::GetCommandLineFlagInfoOrDie("instance_dir").is_default) {
153     legacy_instance_path_stream << "_runtime";
154   }
155   legacy_instance_path_stream << "." << instance.id();
156   auto legacy_instance_path = legacy_instance_path_stream.str();
157 
158   if (DirectoryExists(legacy_instance_path, /* follow_symlinks */ false)) {
159     CF_EXPECT(RecursivelyRemoveDirectory(legacy_instance_path));
160   } else if (FileExists(legacy_instance_path, /* follow_symlinks */ false)) {
161     CF_EXPECT(RemoveFile(legacy_instance_path),
162               "Failed to remove instance_dir symlink " << legacy_instance_path);
163   }
164   // TODO(schuffelen): Find alternative for host-sandboxing mode
165   if (!InSandbox()) {
166     if (symlink(instance.instance_dir().c_str(),
167                 legacy_instance_path.c_str())) {
168       return CF_ERRNO("symlink(\"" << instance.instance_dir() << "\", \""
169                                    << legacy_instance_path << "\") failed");
170     }
171   }
172 
173   const auto mac80211_uds_name = "vhost_user_mac80211";
174 
175   const auto mac80211_uds_path =
176       environment.PerEnvironmentUdsPath(mac80211_uds_name);
177   const auto legacy_mac80211_uds_path =
178       instance.PerInstanceInternalPath(mac80211_uds_name);
179 
180   if (symlink(mac80211_uds_path.c_str(), legacy_mac80211_uds_path.c_str())) {
181     return CF_ERRNO("symlink(\"" << mac80211_uds_path << "\", \""
182                                  << legacy_mac80211_uds_path << "\") failed");
183   }
184 
185   return {};
186 }
187 
RestoreHostFiles(const std::string & cuttlefish_root_dir,const std::string & snapshot_dir_path)188 Result<void> RestoreHostFiles(const std::string& cuttlefish_root_dir,
189                               const std::string& snapshot_dir_path) {
190   const auto meta_json_path = SnapshotMetaJsonPath(snapshot_dir_path);
191 
192   auto guest_snapshot_dirs =
193       CF_EXPECT(GuestSnapshotDirectories(snapshot_dir_path));
194   auto filter_guest_dir =
195       [&guest_snapshot_dirs](const std::string& src_dir) -> bool {
196     if (src_dir.ends_with("logs") && Contains(guest_snapshot_dirs, src_dir)) {
197       return false;
198     }
199     return !Contains(guest_snapshot_dirs, src_dir);
200   };
201   // cp -r snapshot_dir_path HOME
202   CF_EXPECT(CopyDirectoryRecursively(snapshot_dir_path, cuttlefish_root_dir,
203                                      /* delete destination first */ false,
204                                      filter_guest_dir));
205 
206   return {};
207 }
208 
PreservingOnResume(const bool creating_os_disk,const int modem_simulator_count)209 Result<std::set<std::string>> PreservingOnResume(
210     const bool creating_os_disk, const int modem_simulator_count) {
211   const auto snapshot_path = FLAGS_snapshot_path;
212   const bool resume_requested = FLAGS_resume || !snapshot_path.empty();
213   if (!resume_requested) {
214     if (InSandbox()) {
215       return {{"launcher.log"}};
216     } else {
217       return {};
218     }
219   }
220   CF_EXPECT(snapshot_path.empty() || !creating_os_disk,
221             "Restoring from snapshot requires not creating OS disks");
222   if (creating_os_disk) {
223     // not snapshot restore, must be --resume
224     LOG(INFO) << "Requested resuming a previous session (the default behavior) "
225               << "but the base images have changed under the overlay, making "
226               << "the overlay incompatible. Wiping the overlay files.";
227     if (InSandbox()) {
228       return {{"launcher.log"}};
229     } else {
230       return {};
231     }
232   }
233 
234   // either --resume && !creating_os_disk, or restoring from a snapshot
235   std::set<std::string> preserving;
236   preserving.insert("overlay.img");
237   preserving.insert("ap_composite.img");
238   preserving.insert("ap_composite_disk_config.txt");
239   preserving.insert("ap_composite_gpt_footer.img");
240   preserving.insert("ap_composite_gpt_header.img");
241   preserving.insert("ap_overlay.img");
242   preserving.insert("os_composite_disk_config.txt");
243   preserving.insert("os_composite_gpt_header.img");
244   preserving.insert("os_composite_gpt_footer.img");
245   preserving.insert("os_composite.img");
246   preserving.insert("os_vbmeta.img");
247   preserving.insert("sdcard.img");
248   preserving.insert("sdcard_overlay.img");
249   preserving.insert("boot_repacked.img");
250   preserving.insert("vendor_dlkm_repacked.img");
251   preserving.insert("vendor_boot_repacked.img");
252   preserving.insert("access-kregistry");
253   preserving.insert("hwcomposer-pmem");
254   preserving.insert("NVChip");
255   preserving.insert("gatekeeper_secure");
256   preserving.insert("gatekeeper_insecure");
257   preserving.insert("keymint_secure_deletion_data");
258   preserving.insert("modem_nvram.json");
259   preserving.insert("recording");
260   preserving.insert("persistent_composite_disk_config.txt");
261   preserving.insert("persistent_composite_gpt_header.img");
262   preserving.insert("persistent_composite_gpt_footer.img");
263   preserving.insert("persistent_composite.img");
264   preserving.insert("persistent_composite_overlay.img");
265   preserving.insert("pflash.img");
266   preserving.insert("uboot_env.img");
267   preserving.insert("factory_reset_protected.img");
268   preserving.insert("misc.img");
269   preserving.insert("vmmtruststore.img");
270   preserving.insert("metadata.img");
271   preserving.insert("persistent_vbmeta.img");
272   preserving.insert("oemlock_secure");
273   preserving.insert("oemlock_insecure");
274   // Preserve logs if restoring from a snapshot.
275   if (!snapshot_path.empty()) {
276     preserving.insert("kernel.log");
277     preserving.insert("launcher.log");
278     preserving.insert("logcat");
279     preserving.insert("modem_simulator.log");
280     preserving.insert("crosvm_openwrt.log");
281     preserving.insert("crosvm_openwrt_boot.log");
282     preserving.insert("metrics.log");
283   }
284   if (InSandbox()) {
285     preserving.insert("launcher.log");  // Created before `assemble_cvd` runs
286   }
287   for (int i = 0; i < modem_simulator_count; i++) {
288     std::stringstream ss;
289     ss << "iccprofile_for_sim" << i << ".xml";
290     preserving.insert(ss.str());
291   }
292   return preserving;
293 }
294 
SetLogger(std::string runtime_dir_parent)295 Result<SharedFD> SetLogger(std::string runtime_dir_parent) {
296   SharedFD log_file;
297   if (InSandbox()) {
298     log_file = SharedFD::Open(
299         runtime_dir_parent + "/instances/cvd-1/logs/launcher.log",
300         O_WRONLY | O_APPEND);
301   } else {
302     while (runtime_dir_parent[runtime_dir_parent.size() - 1] == '/') {
303       runtime_dir_parent =
304           runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
305     }
306     runtime_dir_parent =
307         runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
308     log_file = SharedFD::Open(runtime_dir_parent, O_WRONLY | O_TMPFILE,
309                               S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
310   }
311   if (!log_file->IsOpen()) {
312     LOG(ERROR) << "Could not open initial log file: " << log_file->StrError();
313   } else {
314     android::base::SetLogger(TeeLogger({
315         {ConsoleSeverity(), SharedFD::Dup(2), MetadataLevel::ONLY_MESSAGE},
316         {LogFileSeverity(), log_file, MetadataLevel::FULL},
317     }));
318   }
319   return log_file;
320 }
321 
InitFilesystemAndCreateConfig(FetcherConfig fetcher_config,const std::vector<GuestConfig> & guest_configs,fruit::Injector<> & injector,SharedFD log)322 Result<const CuttlefishConfig*> InitFilesystemAndCreateConfig(
323     FetcherConfig fetcher_config, const std::vector<GuestConfig>& guest_configs,
324     fruit::Injector<>& injector, SharedFD log) {
325   {
326     // The config object is created here, but only exists in memory until the
327     // SaveConfig line below. Don't launch cuttlefish subprocesses between these
328     // two operations, as those will assume they can read the config object from
329     // disk.
330     auto config = CF_EXPECT(
331         InitializeCuttlefishConfiguration(FLAGS_instance_dir, guest_configs,
332                                           injector, fetcher_config),
333         "cuttlefish configuration initialization failed");
334 
335     const std::string snapshot_path = FLAGS_snapshot_path;
336     if (!snapshot_path.empty()) {
337       CF_EXPECT(RestoreHostFiles(config.root_dir(), snapshot_path));
338 
339       // Add a delimiter to each log file so that we can clearly tell what
340       // happened before vs after the restore.
341       const std::string snapshot_delimiter =
342           "\n\n\n"
343           "============ SNAPSHOT RESTORE POINT ============\n"
344           "Lines above are pre-snapshot.\n"
345           "Lines below are post-restore.\n"
346           "================================================\n"
347           "\n\n\n";
348       for (const auto& instance : config.Instances()) {
349         const auto log_files =
350             CF_EXPECT(DirectoryContents(instance.PerInstanceLogPath("")));
351         for (const auto& filename : log_files) {
352           const std::string path = instance.PerInstanceLogPath(filename);
353           auto fd = SharedFD::Open(path, O_WRONLY | O_APPEND);
354           CF_EXPECT(fd->IsOpen(),
355                     "failed to open " << path << ": " << fd->StrError());
356           const ssize_t n = WriteAll(fd, snapshot_delimiter);
357           CF_EXPECT(n == snapshot_delimiter.size(),
358                     "failed to write to " << path << ": " << fd->StrError());
359         }
360       }
361     }
362 
363     // take the max value of modem_simulator_instance_number in each instance
364     // which is used for preserving/deleting iccprofile_for_simX.xml files
365     int modem_simulator_count = 0;
366 
367     bool creating_os_disk = false;
368     // if any device needs to rebuild its composite disk,
369     // then don't preserve any files and delete everything.
370     for (const auto& instance : config.Instances()) {
371       auto os_builder = OsCompositeDiskBuilder(config, instance);
372       creating_os_disk |= CF_EXPECT(os_builder.WillRebuildCompositeDisk());
373       if (instance.ap_boot_flow() != CuttlefishConfig::InstanceSpecific::APBootFlow::None) {
374         auto ap_builder = ApCompositeDiskBuilder(config, instance);
375         creating_os_disk |= CF_EXPECT(ap_builder.WillRebuildCompositeDisk());
376       }
377       if (instance.modem_simulator_instance_number() > modem_simulator_count) {
378         modem_simulator_count = instance.modem_simulator_instance_number();
379       }
380     }
381     // TODO(schuffelen): Add smarter decision for when to delete runtime files.
382     // Files like NVChip are tightly bound to Android keymint and should be
383     // deleted when userdata is reset. However if the user has ever run without
384     // the overlay, then we want to keep this until userdata.img was externally
385     // replaced.
386     creating_os_disk &= FLAGS_use_overlay;
387 
388     std::set<std::string> preserving =
389         CF_EXPECT(PreservingOnResume(creating_os_disk, modem_simulator_count),
390                   "Error in Preserving set calculation.");
391     auto instance_dirs = config.instance_dirs();
392     auto environment_dirs = config.environment_dirs();
393     std::vector<std::string> clean_dirs;
394     clean_dirs.push_back(config.assembly_dir());
395     clean_dirs.insert(clean_dirs.end(), instance_dirs.begin(),
396                       instance_dirs.end());
397     clean_dirs.insert(clean_dirs.end(), environment_dirs.begin(),
398                       environment_dirs.end());
399     CF_EXPECT(CleanPriorFiles(preserving, clean_dirs),
400               "Failed to clean prior files");
401 
402     auto default_group = "cvdnetwork";
403     const mode_t default_mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
404 
405     CF_EXPECT(EnsureDirectoryExists(config.root_dir()));
406     CF_EXPECT(EnsureDirectoryExists(config.assembly_dir()));
407     CF_EXPECT(EnsureDirectoryExists(config.instances_dir()));
408     CF_EXPECT(EnsureDirectoryExists(config.instances_uds_dir(), default_mode,
409                                     default_group));
410     CF_EXPECT(EnsureDirectoryExists(config.environments_dir(), default_mode,
411                                     default_group));
412     CF_EXPECT(EnsureDirectoryExists(config.environments_uds_dir(), default_mode,
413                                     default_group));
414     if (!snapshot_path.empty()) {
415       SharedFD temp = SharedFD::Creat(config.AssemblyPath("restore"), 0660);
416       if (!temp->IsOpen()) {
417         return CF_ERR("Failed to create restore file: " << temp->StrError());
418       }
419     }
420 
421     auto environment =
422         const_cast<const CuttlefishConfig&>(config).ForDefaultEnvironment();
423 
424     CF_EXPECT(EnsureDirectoryExists(environment.environment_dir(), default_mode,
425                                     default_group));
426     CF_EXPECT(EnsureDirectoryExists(environment.environment_uds_dir(),
427                                     default_mode, default_group));
428     CF_EXPECT(EnsureDirectoryExists(environment.PerEnvironmentLogPath(""),
429                                     default_mode, default_group));
430     CF_EXPECT(
431         EnsureDirectoryExists(environment.PerEnvironmentGrpcSocketPath(""),
432                               default_mode, default_group));
433 
434     LOG(INFO) << "Path for instance UDS: " << config.instances_uds_dir();
435 
436     if (log->LinkAtCwd(config.AssemblyPath("assemble_cvd.log"))) {
437       LOG(ERROR) << "Unable to persist assemble_cvd log at "
438                   << config.AssemblyPath("assemble_cvd.log")
439                   << ": " << log->StrError();
440     }
441     for (const auto& instance : config.Instances()) {
442       // Create instance directory if it doesn't exist.
443       CF_EXPECT(EnsureDirectoryExists(instance.instance_dir()));
444       auto internal_dir = instance.instance_dir() + "/" + kInternalDirName;
445       CF_EXPECT(EnsureDirectoryExists(internal_dir));
446       auto shared_dir = instance.instance_dir() + "/" + kSharedDirName;
447       CF_EXPECT(EnsureDirectoryExists(shared_dir));
448       auto recording_dir = instance.instance_dir() + "/recording";
449       CF_EXPECT(EnsureDirectoryExists(recording_dir));
450       CF_EXPECT(EnsureDirectoryExists(instance.PerInstanceLogPath("")));
451 
452       CF_EXPECT(EnsureDirectoryExists(instance.instance_uds_dir(), default_mode,
453                                       default_group));
454       CF_EXPECT(EnsureDirectoryExists(instance.instance_internal_uds_dir(),
455                                       default_mode, default_group));
456       CF_EXPECT(EnsureDirectoryExists(instance.PerInstanceGrpcSocketPath(""),
457                                       default_mode, default_group));
458       std::string vsock_dir = fmt::format("{}/vsock_{}_{}", TempDir(),
459                                           instance.vsock_guest_cid(), getuid());
460       if (DirectoryExists(vsock_dir, /* follow_symlinks */ false) &&
461           !IsDirectoryEmpty(vsock_dir)) {
462         CF_EXPECT(RecursivelyRemoveDirectory(vsock_dir));
463       }
464       CF_EXPECT(EnsureDirectoryExists(vsock_dir, default_mode, default_group));
465 
466       // TODO(schuffelen): Move this code somewhere better
467       CF_EXPECT(CreateLegacySymlinks(instance, environment));
468     }
469     CF_EXPECT(SaveConfig(config), "Failed to initialize configuration");
470   }
471 
472   // Do this early so that the config object is ready for anything that needs
473   // it
474   auto config = CuttlefishConfig::Get();
475   CF_EXPECT(config != nullptr, "Failed to obtain config singleton");
476 
477   if (DirectoryExists(FLAGS_assembly_dir, /* follow_symlinks */ false)) {
478     CF_EXPECT(RecursivelyRemoveDirectory(FLAGS_assembly_dir));
479   } else if (FileExists(FLAGS_assembly_dir, /* follow_symlinks */ false)) {
480     CF_EXPECT(RemoveFile(FLAGS_assembly_dir),
481               "Failed to remove file" << FLAGS_assembly_dir);
482   }
483   // TODO(schuffelen): Find alternative for host-sandboxing mode
484   if (!InSandbox()) {
485     if (symlink(config->assembly_dir().c_str(), FLAGS_assembly_dir.c_str())) {
486       return CF_ERRNO("symlink(\"" << config->assembly_dir() << "\", \""
487                                    << FLAGS_assembly_dir << "\") failed");
488     }
489   }
490 
491   std::string first_instance = config->Instances()[0].instance_dir();
492   std::string double_legacy_instance_dir = FLAGS_instance_dir + "_runtime";
493   if (FileExists(double_legacy_instance_dir, /* follow_symlinks */ false)) {
494     CF_EXPECT(RemoveFile(double_legacy_instance_dir),
495               "Failed to remove symlink " << double_legacy_instance_dir);
496   }
497   // TODO(schuffelen): Find alternative for host-sandboxing mode
498   if (!InSandbox()) {
499     if (symlink(first_instance.c_str(), double_legacy_instance_dir.c_str())) {
500       return CF_ERRNO("symlink(\"" << first_instance << "\", \""
501                                    << double_legacy_instance_dir
502                                    << "\") failed");
503     }
504   }
505 
506   CF_EXPECT(CreateDynamicDiskFiles(fetcher_config, *config));
507 
508   return config;
509 }
510 
511 const std::string kKernelDefaultPath = "kernel";
512 const std::string kInitramfsImg = "initramfs.img";
ExtractKernelParamsFromFetcherConfig(const FetcherConfig & fetcher_config)513 static void ExtractKernelParamsFromFetcherConfig(
514     const FetcherConfig& fetcher_config) {
515   std::string discovered_kernel =
516       fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
517   std::string discovered_ramdisk =
518       fetcher_config.FindCvdFileWithSuffix(kInitramfsImg);
519 
520   SetCommandLineOptionWithMode("kernel_path", discovered_kernel.c_str(),
521                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
522 
523   SetCommandLineOptionWithMode("initramfs_path", discovered_ramdisk.c_str(),
524                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
525 }
526 
VerifyConditionsOnSnapshotRestore(const std::string & snapshot_path)527 Result<void> VerifyConditionsOnSnapshotRestore(
528     const std::string& snapshot_path) {
529   if (snapshot_path.empty()) {
530     return {};
531   }
532   const std::string instance_dir(FLAGS_instance_dir);
533   const std::string assembly_dir(FLAGS_assembly_dir);
534   CF_EXPECT(snapshot_path.empty() || FLAGS_resume,
535             "--resume must be true when restoring from snapshot.");
536   CF_EXPECT_EQ(instance_dir, CF_DEFAULTS_INSTANCE_DIR,
537                "--snapshot_path does not allow customizing --instance_dir");
538   CF_EXPECT_EQ(assembly_dir, CF_DEFAULTS_ASSEMBLY_DIR,
539                "--snapshot_path does not allow customizing --assembly_dir");
540   return {};
541 }
542 
FlagsComponent()543 fruit::Component<> FlagsComponent() {
544   return fruit::createComponent()
545       .install(AdbConfigComponent)
546       .install(AdbConfigFlagComponent)
547       .install(AdbConfigFragmentComponent)
548       .install(DisplaysConfigsComponent)
549       .install(DisplaysConfigsFlagComponent)
550       .install(DisplaysConfigsFragmentComponent)
551       .install(TouchpadsConfigsComponent)
552       .install(TouchpadsConfigsFlagComponent)
553       .install(FastbootConfigComponent)
554       .install(FastbootConfigFlagComponent)
555       .install(FastbootConfigFragmentComponent)
556       .install(GflagsComponent)
557       .install(ConfigFlagComponent)
558       .install(CustomActionsComponent);
559 }
560 
561 } // namespace
562 
AssembleCvdMain(int argc,char ** argv)563 Result<int> AssembleCvdMain(int argc, char** argv) {
564   setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0);
565   ::android::base::InitLogging(argv, android::base::StderrLogger);
566 
567   auto log = CF_EXPECT(SetLogger(AbsolutePath(FLAGS_instance_dir)));
568 
569   int tty = isatty(0);
570   int error_num = errno;
571   CF_EXPECT(tty == 0,
572             "stdin was a tty, expected to be passed the output of a "
573             "previous stage. Did you mean to run launch_cvd?");
574   CF_EXPECT(error_num != EBADF,
575             "stdin was not a valid file descriptor, expected to be "
576             "passed the output of launch_cvd. Did you mean to run launch_cvd?");
577 
578   std::string input_files_str;
579   {
580     auto input_fd = SharedFD::Dup(0);
581     auto bytes_read = ReadAll(input_fd, &input_files_str);
582     CF_EXPECT(bytes_read >= 0, "Failed to read input files. Error was \""
583                                    << input_fd->StrError() << "\"");
584   }
585   std::vector<std::string> input_files = android::base::Split(input_files_str, "\n");
586 
587   FetcherConfig fetcher_config = FindFetcherConfig(input_files);
588 
589   // set gflags defaults to point to kernel/RD from fetcher config
590   ExtractKernelParamsFromFetcherConfig(fetcher_config);
591 
592   auto args = ArgsToVec(argc - 1, argv + 1);
593 
594   bool help = false;
595   std::string help_str;
596   bool helpxml = false;
597 
598   std::vector<Flag> help_flags = {
599       GflagsCompatFlag("help", help),
600       GflagsCompatFlag("helpfull", help),
601       GflagsCompatFlag("helpshort", help),
602       GflagsCompatFlag("helpmatch", help_str),
603       GflagsCompatFlag("helpon", help_str),
604       GflagsCompatFlag("helppackage", help_str),
605       GflagsCompatFlag("helpxml", helpxml),
606   };
607   for (const auto& help_flag : help_flags) {
608     CF_EXPECT(help_flag.Parse(args), "Failed to process help flag");
609   }
610 
611   fruit::Injector<> injector(FlagsComponent);
612 
613   for (auto& late_injected : injector.getMultibindings<LateInjected>()) {
614     CF_EXPECT(late_injected->LateInject(injector));
615   }
616 
617   auto flag_features = injector.getMultibindings<FlagFeature>();
618   CF_EXPECT(FlagFeature::ProcessFlags(flag_features, args),
619             "Failed to parse flags.");
620 
621   if (help || !help_str.empty()) {
622     LOG(WARNING) << "TODO(schuffelen): Implement `--help` for assemble_cvd.";
623     LOG(WARNING) << "In the meantime, call `launch_cvd --help`";
624     return 1;
625   } else if (helpxml) {
626     if (!FlagFeature::WriteGflagsHelpXml(flag_features, std::cout)) {
627       LOG(ERROR) << "Failure in writing gflags helpxml output";
628     }
629     return 1;  // For parity with gflags
630   }
631 
632   CF_EXPECT(VerifyConditionsOnSnapshotRestore(FLAGS_snapshot_path),
633             "The conditions for --snapshot_path=<dir> do not meet.");
634 
635   // TODO(schuffelen): Put in "unknown flag" guards after gflags is removed.
636   // gflags either consumes all arguments that start with - or leaves all of
637   // them in place, and either errors out on unknown flags or accepts any flags.
638 
639   auto guest_configs =
640       CF_EXPECT(GetGuestConfigAndSetDefaults(), "Failed to parse arguments");
641 
642   auto config =
643       CF_EXPECT(InitFilesystemAndCreateConfig(std::move(fetcher_config),
644                                               guest_configs, injector, log),
645                 "Failed to create config");
646 
647   std::cout << GetConfigFilePath(*config) << "\n";
648   std::cout << std::flush;
649 
650   return 0;
651 }
652 
653 } // namespace cuttlefish
654 
main(int argc,char ** argv)655 int main(int argc, char** argv) {
656   auto res = cuttlefish::AssembleCvdMain(argc, argv);
657   if (res.ok()) {
658     return *res;
659   }
660   LOG(ERROR) << "assemble_cvd failed: \n" << res.error().FormatForEnv();
661   abort();
662 }
663