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/tee_logging.h"
33 #include "host/commands/assemble_cvd/clean.h"
34 #include "host/commands/assemble_cvd/disk_flags.h"
35 #include "host/commands/assemble_cvd/display.h"
36 #include "host/commands/assemble_cvd/flag_feature.h"
37 #include "host/commands/assemble_cvd/flags.h"
38 #include "host/commands/assemble_cvd/flags_defaults.h"
39 #include "host/commands/assemble_cvd/touchpad.h"
40 #include "host/libs/command_util/snapshot_utils.h"
41 #include "host/libs/config/adb/adb.h"
42 #include "host/libs/config/config_flag.h"
43 #include "host/libs/config/custom_actions.h"
44 #include "host/libs/config/fastboot/fastboot.h"
45 #include "host/libs/config/fetcher_config.h"
46 #include "host/libs/config/inject.h"
47
48 using cuttlefish::StringFromEnv;
49
50 DEFINE_string(assembly_dir, CF_DEFAULTS_ASSEMBLY_DIR,
51 "A directory to put generated files common between instances");
52 DEFINE_string(instance_dir, CF_DEFAULTS_INSTANCE_DIR,
53 "This is a directory that will hold the cuttlefish generated"
54 "files, including both instance-specific and common files");
55 DEFINE_string(snapshot_path, "",
56 "Path to snapshot. Must not be empty if the device is to be "
57 "restored from a snapshot");
58 DEFINE_bool(resume, CF_DEFAULTS_RESUME,
59 "Resume using the disk from the last session, if "
60 "possible. i.e., if --noresume is passed, the disk "
61 "will be reset to the state it was initially launched "
62 "in. This flag is ignored if the underlying partition "
63 "images have been updated since the first launch."
64 "If the device starts from a snapshot, this will be always true.");
65
66 DECLARE_bool(use_overlay);
67
68 namespace cuttlefish {
69 namespace {
70
71 static constexpr std::string_view kFetcherConfigFile = "fetcher_config.json";
72
FindFetcherConfig(const std::vector<std::string> & files)73 FetcherConfig FindFetcherConfig(const std::vector<std::string>& files) {
74 FetcherConfig fetcher_config;
75 for (const auto& file : files) {
76 if (android::base::EndsWith(file, kFetcherConfigFile)) {
77 std::string home_directory = StringFromEnv("HOME", CurrentDirectory());
78 std::string fetcher_file = file;
79 if (!FileExists(file) &&
80 FileExists(home_directory + "/" + fetcher_file)) {
81 LOG(INFO) << "Found " << fetcher_file << " in HOME directory ('"
82 << home_directory << "') and not current working directory";
83 fetcher_file = home_directory + "/" + fetcher_file;
84 }
85
86 if (fetcher_config.LoadFromFile(fetcher_file)) {
87 return fetcher_config;
88 }
89 LOG(ERROR) << "Could not load fetcher config file.";
90 }
91 }
92 LOG(DEBUG) << "Could not locate fetcher config file.";
93 return fetcher_config;
94 }
95
GetLegacyConfigFilePath(const CuttlefishConfig & config)96 std::string GetLegacyConfigFilePath(const CuttlefishConfig& config) {
97 return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json");
98 }
99
SaveConfig(const CuttlefishConfig & tmp_config_obj)100 Result<void> SaveConfig(const CuttlefishConfig& tmp_config_obj) {
101 auto config_file = GetConfigFilePath(tmp_config_obj);
102 auto config_link = GetGlobalConfigFileLink();
103 // Save the config object before starting any host process
104 CF_EXPECT(tmp_config_obj.SaveToFile(config_file),
105 "Failed to save to \"" << config_file << "\"");
106 auto legacy_config_file = GetLegacyConfigFilePath(tmp_config_obj);
107 CF_EXPECT(tmp_config_obj.SaveToFile(legacy_config_file),
108 "Failed to save to \"" << legacy_config_file << "\"");
109
110 setenv(kCuttlefishConfigEnvVarName, config_file.c_str(), true);
111 // TODO(schuffelen): Find alternative for host-sandboxing mode
112 if (!InSandbox()) {
113 if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
114 return CF_ERRNO("symlink(\"" << config_file << "\", \"" << config_link
115 << ") failed");
116 }
117 }
118
119 return {};
120 }
121
122 #ifndef O_TMPFILE
123 # define O_TMPFILE (020000000 | O_DIRECTORY)
124 #endif
125
CreateLegacySymlinks(const CuttlefishConfig::InstanceSpecific & instance,const CuttlefishConfig::EnvironmentSpecific & environment)126 Result<void> CreateLegacySymlinks(
127 const CuttlefishConfig::InstanceSpecific& instance,
128 const CuttlefishConfig::EnvironmentSpecific& environment) {
129 std::string log_files[] = {"kernel.log",
130 "launcher.log",
131 "logcat",
132 "metrics.log",
133 "modem_simulator.log",
134 "crosvm_openwrt.log",
135 "crosvm_openwrt_boot.log"};
136 for (const auto& log_file : log_files) {
137 auto symlink_location = instance.PerInstancePath(log_file.c_str());
138 auto log_target = "logs/" + log_file; // Relative path
139 if (FileExists(symlink_location, /* follow_symlinks */ false)) {
140 CF_EXPECT(RemoveFile(symlink_location),
141 "Failed to remove symlink " << symlink_location);
142 }
143 if (symlink(log_target.c_str(), symlink_location.c_str()) != 0) {
144 return CF_ERRNO("symlink(\"" << log_target << ", " << symlink_location
145 << ") failed");
146 }
147 }
148
149 std::stringstream legacy_instance_path_stream;
150 legacy_instance_path_stream << FLAGS_instance_dir;
151 if (gflags::GetCommandLineFlagInfoOrDie("instance_dir").is_default) {
152 legacy_instance_path_stream << "_runtime";
153 }
154 legacy_instance_path_stream << "." << instance.id();
155 auto legacy_instance_path = legacy_instance_path_stream.str();
156
157 if (DirectoryExists(legacy_instance_path, /* follow_symlinks */ false)) {
158 CF_EXPECT(RecursivelyRemoveDirectory(legacy_instance_path));
159 } else if (FileExists(legacy_instance_path, /* follow_symlinks */ false)) {
160 CF_EXPECT(RemoveFile(legacy_instance_path),
161 "Failed to remove instance_dir symlink " << legacy_instance_path);
162 }
163 // TODO(schuffelen): Find alternative for host-sandboxing mode
164 if (!InSandbox()) {
165 if (symlink(instance.instance_dir().c_str(),
166 legacy_instance_path.c_str())) {
167 return CF_ERRNO("symlink(\"" << instance.instance_dir() << "\", \""
168 << legacy_instance_path << "\") failed");
169 }
170 }
171
172 const auto mac80211_uds_name = "vhost_user_mac80211";
173
174 const auto mac80211_uds_path =
175 environment.PerEnvironmentUdsPath(mac80211_uds_name);
176 const auto legacy_mac80211_uds_path =
177 instance.PerInstanceInternalPath(mac80211_uds_name);
178
179 if (symlink(mac80211_uds_path.c_str(), legacy_mac80211_uds_path.c_str())) {
180 return CF_ERRNO("symlink(\"" << mac80211_uds_path << "\", \""
181 << legacy_mac80211_uds_path << "\") failed");
182 }
183
184 return {};
185 }
186
RestoreHostFiles(const std::string & cuttlefish_root_dir,const std::string & snapshot_dir_path)187 Result<void> RestoreHostFiles(const std::string& cuttlefish_root_dir,
188 const std::string& snapshot_dir_path) {
189 const auto meta_json_path = SnapshotMetaJsonPath(snapshot_dir_path);
190
191 auto guest_snapshot_dirs =
192 CF_EXPECT(GuestSnapshotDirectories(snapshot_dir_path));
193 auto filter_guest_dir =
194 [&guest_snapshot_dirs](const std::string& src_dir) -> bool {
195 if (src_dir.ends_with("logs") && Contains(guest_snapshot_dirs, src_dir)) {
196 return false;
197 }
198 return !Contains(guest_snapshot_dirs, src_dir);
199 };
200 // cp -r snapshot_dir_path HOME
201 CF_EXPECT(CopyDirectoryRecursively(snapshot_dir_path, cuttlefish_root_dir,
202 /* delete destination first */ false,
203 filter_guest_dir));
204
205 return {};
206 }
207
PreservingOnResume(const bool creating_os_disk,const int modem_simulator_count)208 Result<std::set<std::string>> PreservingOnResume(
209 const bool creating_os_disk, const int modem_simulator_count) {
210 const auto snapshot_path = FLAGS_snapshot_path;
211 const bool resume_requested = FLAGS_resume || !snapshot_path.empty();
212 if (!resume_requested) {
213 if (InSandbox()) {
214 return {{"launcher.log"}};
215 } else {
216 return {};
217 }
218 }
219 CF_EXPECT(snapshot_path.empty() || !creating_os_disk,
220 "Restoring from snapshot requires not creating OS disks");
221 if (creating_os_disk) {
222 // not snapshot restore, must be --resume
223 LOG(INFO) << "Requested resuming a previous session (the default behavior) "
224 << "but the base images have changed under the overlay, making "
225 << "the overlay incompatible. Wiping the overlay files.";
226 if (InSandbox()) {
227 return {{"launcher.log"}};
228 } else {
229 return {};
230 }
231 }
232
233 // either --resume && !creating_os_disk, or restoring from a snapshot
234 std::set<std::string> preserving;
235 preserving.insert("overlay.img");
236 preserving.insert("ap_composite.img");
237 preserving.insert("ap_composite_disk_config.txt");
238 preserving.insert("ap_composite_gpt_footer.img");
239 preserving.insert("ap_composite_gpt_header.img");
240 preserving.insert("ap_overlay.img");
241 preserving.insert("os_composite_disk_config.txt");
242 preserving.insert("os_composite_gpt_header.img");
243 preserving.insert("os_composite_gpt_footer.img");
244 preserving.insert("os_composite.img");
245 preserving.insert("os_vbmeta.img");
246 preserving.insert("sdcard.img");
247 preserving.insert("sdcard_overlay.img");
248 preserving.insert("boot_repacked.img");
249 preserving.insert("vendor_dlkm_repacked.img");
250 preserving.insert("vendor_boot_repacked.img");
251 preserving.insert("access-kregistry");
252 preserving.insert("hwcomposer-pmem");
253 preserving.insert("NVChip");
254 preserving.insert("gatekeeper_secure");
255 preserving.insert("gatekeeper_insecure");
256 preserving.insert("keymint_secure_deletion_data");
257 preserving.insert("modem_nvram.json");
258 preserving.insert("recording");
259 preserving.insert("persistent_composite_disk_config.txt");
260 preserving.insert("persistent_composite_gpt_header.img");
261 preserving.insert("persistent_composite_gpt_footer.img");
262 preserving.insert("persistent_composite.img");
263 preserving.insert("persistent_composite_overlay.img");
264 preserving.insert("pflash.img");
265 preserving.insert("uboot_env.img");
266 preserving.insert("factory_reset_protected.img");
267 preserving.insert("misc.img");
268 preserving.insert("vmmtruststore.img");
269 preserving.insert("metadata.img");
270 preserving.insert("persistent_vbmeta.img");
271 preserving.insert("oemlock_secure");
272 preserving.insert("oemlock_insecure");
273 // Preserve logs if restoring from a snapshot.
274 if (!snapshot_path.empty()) {
275 preserving.insert("kernel.log");
276 preserving.insert("launcher.log");
277 preserving.insert("logcat");
278 preserving.insert("modem_simulator.log");
279 preserving.insert("crosvm_openwrt.log");
280 preserving.insert("crosvm_openwrt_boot.log");
281 preserving.insert("metrics.log");
282 }
283 if (InSandbox()) {
284 preserving.insert("launcher.log"); // Created before `assemble_cvd` runs
285 }
286 for (int i = 0; i < modem_simulator_count; i++) {
287 std::stringstream ss;
288 ss << "iccprofile_for_sim" << i << ".xml";
289 preserving.insert(ss.str());
290 }
291 return preserving;
292 }
293
SetLogger(std::string runtime_dir_parent)294 Result<SharedFD> SetLogger(std::string runtime_dir_parent) {
295 SharedFD log_file;
296 if (InSandbox()) {
297 log_file = SharedFD::Open(
298 runtime_dir_parent + "/instances/cvd-1/logs/launcher.log",
299 O_WRONLY | O_APPEND);
300 } else {
301 while (runtime_dir_parent[runtime_dir_parent.size() - 1] == '/') {
302 runtime_dir_parent =
303 runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
304 }
305 runtime_dir_parent =
306 runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
307 log_file = SharedFD::Open(runtime_dir_parent, O_WRONLY | O_TMPFILE,
308 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
309 }
310 if (!log_file->IsOpen()) {
311 LOG(ERROR) << "Could not open initial log file: " << log_file->StrError();
312 } else {
313 android::base::SetLogger(TeeLogger({
314 {ConsoleSeverity(), SharedFD::Dup(2), MetadataLevel::ONLY_MESSAGE},
315 {LogFileSeverity(), log_file, MetadataLevel::FULL},
316 }));
317 }
318 return log_file;
319 }
320
InitFilesystemAndCreateConfig(FetcherConfig fetcher_config,const std::vector<GuestConfig> & guest_configs,fruit::Injector<> & injector,SharedFD log)321 Result<const CuttlefishConfig*> InitFilesystemAndCreateConfig(
322 FetcherConfig fetcher_config, const std::vector<GuestConfig>& guest_configs,
323 fruit::Injector<>& injector, SharedFD log) {
324 {
325 // The config object is created here, but only exists in memory until the
326 // SaveConfig line below. Don't launch cuttlefish subprocesses between these
327 // two operations, as those will assume they can read the config object from
328 // disk.
329 auto config = CF_EXPECT(
330 InitializeCuttlefishConfiguration(FLAGS_instance_dir, guest_configs,
331 injector, fetcher_config),
332 "cuttlefish configuration initialization failed");
333
334 const std::string snapshot_path = FLAGS_snapshot_path;
335 if (!snapshot_path.empty()) {
336 CF_EXPECT(RestoreHostFiles(config.root_dir(), snapshot_path));
337
338 // Add a delimiter to each log file so that we can clearly tell what
339 // happened before vs after the restore.
340 const std::string snapshot_delimiter =
341 "\n\n\n"
342 "============ SNAPSHOT RESTORE POINT ============\n"
343 "Lines above are pre-snapshot.\n"
344 "Lines below are post-restore.\n"
345 "================================================\n"
346 "\n\n\n";
347 for (const auto& instance : config.Instances()) {
348 const auto log_files =
349 CF_EXPECT(DirectoryContents(instance.PerInstanceLogPath("")));
350 for (const auto& filename : log_files) {
351 const std::string path = instance.PerInstanceLogPath(filename);
352 auto fd = SharedFD::Open(path, O_WRONLY | O_APPEND);
353 CF_EXPECT(fd->IsOpen(),
354 "failed to open " << path << ": " << fd->StrError());
355 const ssize_t n = WriteAll(fd, snapshot_delimiter);
356 CF_EXPECT(n == snapshot_delimiter.size(),
357 "failed to write to " << path << ": " << fd->StrError());
358 }
359 }
360 }
361
362 // take the max value of modem_simulator_instance_number in each instance
363 // which is used for preserving/deleting iccprofile_for_simX.xml files
364 int modem_simulator_count = 0;
365
366 bool creating_os_disk = false;
367 // if any device needs to rebuild its composite disk,
368 // then don't preserve any files and delete everything.
369 for (const auto& instance : config.Instances()) {
370 auto os_builder = OsCompositeDiskBuilder(config, instance);
371 creating_os_disk |= CF_EXPECT(os_builder.WillRebuildCompositeDisk());
372 if (instance.ap_boot_flow() != CuttlefishConfig::InstanceSpecific::APBootFlow::None) {
373 auto ap_builder = ApCompositeDiskBuilder(config, instance);
374 creating_os_disk |= CF_EXPECT(ap_builder.WillRebuildCompositeDisk());
375 }
376 if (instance.modem_simulator_instance_number() > modem_simulator_count) {
377 modem_simulator_count = instance.modem_simulator_instance_number();
378 }
379 }
380 // TODO(schuffelen): Add smarter decision for when to delete runtime files.
381 // Files like NVChip are tightly bound to Android keymint and should be
382 // deleted when userdata is reset. However if the user has ever run without
383 // the overlay, then we want to keep this until userdata.img was externally
384 // replaced.
385 creating_os_disk &= FLAGS_use_overlay;
386
387 std::set<std::string> preserving =
388 CF_EXPECT(PreservingOnResume(creating_os_disk, modem_simulator_count),
389 "Error in Preserving set calculation.");
390 auto instance_dirs = config.instance_dirs();
391 auto environment_dirs = config.environment_dirs();
392 std::vector<std::string> clean_dirs;
393 clean_dirs.push_back(config.assembly_dir());
394 clean_dirs.insert(clean_dirs.end(), instance_dirs.begin(),
395 instance_dirs.end());
396 clean_dirs.insert(clean_dirs.end(), environment_dirs.begin(),
397 environment_dirs.end());
398 CF_EXPECT(CleanPriorFiles(preserving, clean_dirs),
399 "Failed to clean prior files");
400
401 auto default_group = "cvdnetwork";
402 const mode_t default_mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
403
404 CF_EXPECT(EnsureDirectoryExists(config.root_dir()));
405 CF_EXPECT(EnsureDirectoryExists(config.assembly_dir()));
406 CF_EXPECT(EnsureDirectoryExists(config.instances_dir()));
407 CF_EXPECT(EnsureDirectoryExists(config.instances_uds_dir(), default_mode,
408 default_group));
409 CF_EXPECT(EnsureDirectoryExists(config.environments_dir(), default_mode,
410 default_group));
411 CF_EXPECT(EnsureDirectoryExists(config.environments_uds_dir(), default_mode,
412 default_group));
413 if (!snapshot_path.empty()) {
414 SharedFD temp = SharedFD::Creat(config.AssemblyPath("restore"), 0660);
415 if (!temp->IsOpen()) {
416 return CF_ERR("Failed to create restore file: " << temp->StrError());
417 }
418 }
419
420 auto environment =
421 const_cast<const CuttlefishConfig&>(config).ForDefaultEnvironment();
422
423 CF_EXPECT(EnsureDirectoryExists(environment.environment_dir(), default_mode,
424 default_group));
425 CF_EXPECT(EnsureDirectoryExists(environment.environment_uds_dir(),
426 default_mode, default_group));
427 CF_EXPECT(EnsureDirectoryExists(environment.PerEnvironmentLogPath(""),
428 default_mode, default_group));
429 CF_EXPECT(
430 EnsureDirectoryExists(environment.PerEnvironmentGrpcSocketPath(""),
431 default_mode, default_group));
432
433 LOG(INFO) << "Path for instance UDS: " << config.instances_uds_dir();
434
435 if (log->LinkAtCwd(config.AssemblyPath("assemble_cvd.log"))) {
436 LOG(ERROR) << "Unable to persist assemble_cvd log at "
437 << config.AssemblyPath("assemble_cvd.log")
438 << ": " << log->StrError();
439 }
440 for (const auto& instance : config.Instances()) {
441 // Create instance directory if it doesn't exist.
442 CF_EXPECT(EnsureDirectoryExists(instance.instance_dir()));
443 auto internal_dir = instance.instance_dir() + "/" + kInternalDirName;
444 CF_EXPECT(EnsureDirectoryExists(internal_dir));
445 auto shared_dir = instance.instance_dir() + "/" + kSharedDirName;
446 CF_EXPECT(EnsureDirectoryExists(shared_dir));
447 auto recording_dir = instance.instance_dir() + "/recording";
448 CF_EXPECT(EnsureDirectoryExists(recording_dir));
449 CF_EXPECT(EnsureDirectoryExists(instance.PerInstanceLogPath("")));
450
451 CF_EXPECT(EnsureDirectoryExists(instance.instance_uds_dir(), default_mode,
452 default_group));
453 CF_EXPECT(EnsureDirectoryExists(instance.instance_internal_uds_dir(),
454 default_mode, default_group));
455 CF_EXPECT(EnsureDirectoryExists(instance.PerInstanceGrpcSocketPath(""),
456 default_mode, default_group));
457 auto vsock_dir =
458 fmt::format("/tmp/vsock_{0}_{1}", instance.vsock_guest_cid(),
459 std::to_string(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