• 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 
18 #include <android-base/strings.h>
19 #include <android-base/logging.h>
20 #include <gflags/gflags.h>
21 
22 #include "common/libs/fs/shared_buf.h"
23 #include "common/libs/fs/shared_fd.h"
24 #include "common/libs/utils/environment.h"
25 #include "common/libs/utils/files.h"
26 #include "common/libs/utils/tee_logging.h"
27 #include "host/commands/assemble_cvd/clean.h"
28 #include "host/commands/assemble_cvd/disk_flags.h"
29 #include "host/commands/assemble_cvd/flags.h"
30 #include "host/libs/config/fetcher_config.h"
31 
32 using cuttlefish::StringFromEnv;
33 
34 DEFINE_string(assembly_dir, StringFromEnv("HOME", ".") + "/cuttlefish_assembly",
35               "A directory to put generated files common between instances");
36 DEFINE_string(instance_dir, StringFromEnv("HOME", ".") + "/cuttlefish_runtime",
37               "A directory to put all instance specific files");
38 DEFINE_bool(resume, true, "Resume using the disk from the last session, if "
39                           "possible. i.e., if --noresume is passed, the disk "
40                           "will be reset to the state it was initially launched "
41                           "in. This flag is ignored if the underlying partition "
42                           "images have been updated since the first launch.");
43 DEFINE_int32(modem_simulator_count, 1,
44              "Modem simulator count corresponding to maximum sim number");
45 
46 namespace cuttlefish {
47 namespace {
48 
49 std::string kFetcherConfigFile = "fetcher_config.json";
50 
FindFetcherConfig(const std::vector<std::string> & files)51 FetcherConfig FindFetcherConfig(const std::vector<std::string>& files) {
52   FetcherConfig fetcher_config;
53   for (const auto& file : files) {
54     auto expected_pos = file.size() - kFetcherConfigFile.size();
55     if (file.rfind(kFetcherConfigFile) == expected_pos) {
56       if (fetcher_config.LoadFromFile(file)) {
57         return fetcher_config;
58       }
59       LOG(ERROR) << "Could not load fetcher config file.";
60     }
61   }
62   return fetcher_config;
63 }
64 
GetLegacyConfigFilePath(const CuttlefishConfig & config)65 std::string GetLegacyConfigFilePath(const CuttlefishConfig& config) {
66   return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json");
67 }
68 
SaveConfig(const CuttlefishConfig & tmp_config_obj)69 bool SaveConfig(const CuttlefishConfig& tmp_config_obj) {
70   auto config_file = GetConfigFilePath(tmp_config_obj);
71   auto config_link = GetGlobalConfigFileLink();
72   // Save the config object before starting any host process
73   if (!tmp_config_obj.SaveToFile(config_file)) {
74     LOG(ERROR) << "Unable to save config object";
75     return false;
76   }
77   auto legacy_config_file = GetLegacyConfigFilePath(tmp_config_obj);
78   if (!tmp_config_obj.SaveToFile(legacy_config_file)) {
79     LOG(ERROR) << "Unable to save legacy config object";
80     return false;
81   }
82   setenv(kCuttlefishConfigEnvVarName, config_file.c_str(), true);
83   if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
84     LOG(ERROR) << "Failed to create symlink to config file at " << config_link
85                << ": " << strerror(errno);
86     return false;
87   }
88 
89   return true;
90 }
91 
ValidateAdbModeFlag(const CuttlefishConfig & config)92 void ValidateAdbModeFlag(const CuttlefishConfig& config) {
93   auto adb_modes = config.adb_mode();
94   adb_modes.erase(AdbMode::Unknown);
95   if (adb_modes.size() < 1) {
96     LOG(INFO) << "ADB not enabled";
97   }
98 }
99 
100 #ifndef O_TMPFILE
101 # define O_TMPFILE (020000000 | O_DIRECTORY)
102 #endif
103 
InitFilesystemAndCreateConfig(FetcherConfig fetcher_config,KernelConfig kernel_config)104 const CuttlefishConfig* InitFilesystemAndCreateConfig(
105     FetcherConfig fetcher_config, KernelConfig kernel_config) {
106   std::string assembly_dir_parent = AbsolutePath(FLAGS_assembly_dir);
107   while (assembly_dir_parent[assembly_dir_parent.size() - 1] == '/') {
108     assembly_dir_parent =
109         assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/'));
110   }
111   assembly_dir_parent =
112       assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/'));
113   auto log =
114       SharedFD::Open(
115           assembly_dir_parent,
116           O_WRONLY | O_TMPFILE,
117           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
118   if (!log->IsOpen()) {
119     LOG(ERROR) << "Could not open O_TMPFILE precursor to assemble_cvd.log: "
120                << log->StrError();
121   } else {
122     android::base::SetLogger(TeeLogger({
123         {ConsoleSeverity(), SharedFD::Dup(2), MetadataLevel::ONLY_MESSAGE},
124         {LogFileSeverity(), log, MetadataLevel::FULL},
125     }));
126   }
127 
128   {
129     // The config object is created here, but only exists in memory until the
130     // SaveConfig line below. Don't launch cuttlefish subprocesses between these
131     // two operations, as those will assume they can read the config object from
132     // disk.
133     auto config = InitializeCuttlefishConfiguration(
134         FLAGS_instance_dir, FLAGS_modem_simulator_count, kernel_config);
135     std::set<std::string> preserving;
136     if (FLAGS_resume && ShouldCreateAllCompositeDisks(config)) {
137       LOG(INFO) << "Requested resuming a previous session (the default behavior) "
138                 << "but the base images have changed under the overlay, making the "
139                 << "overlay incompatible. Wiping the overlay files.";
140     } else if (FLAGS_resume && !ShouldCreateAllCompositeDisks(config)) {
141       preserving.insert("overlay.img");
142       preserving.insert("os_composite_disk_config.txt");
143       preserving.insert("os_composite_gpt_header.img");
144       preserving.insert("os_composite_gpt_footer.img");
145       preserving.insert("os_composite.img");
146       preserving.insert("sdcard.img");
147       preserving.insert("boot_repacked.img");
148       preserving.insert("vendor_boot_repacked.img");
149       preserving.insert("access-kregistry");
150       preserving.insert("NVChip");
151       preserving.insert("gatekeeper_secure");
152       preserving.insert("gatekeeper_insecure");
153       preserving.insert("modem_nvram.json");
154       preserving.insert("recording");
155       preserving.insert("persistent_composite_disk_config.txt");
156       preserving.insert("persistent_composite_gpt_header.img");
157       preserving.insert("persistent_composite_gpt_footer.img");
158       preserving.insert("persistent_composite.img");
159       preserving.insert("uboot_env.img");
160       preserving.insert("factory_reset_protected.img");
161       std::stringstream ss;
162       for (int i = 0; i < FLAGS_modem_simulator_count; i++) {
163         ss.clear();
164         ss << "iccprofile_for_sim" << i << ".xml";
165         preserving.insert(ss.str());
166         ss.str("");
167       }
168     }
169     CHECK(CleanPriorFiles(preserving, FLAGS_assembly_dir, FLAGS_instance_dir))
170         << "Failed to clean prior files";
171 
172     // Create assembly directory if it doesn't exist.
173     CHECK(EnsureDirectoryExists(FLAGS_assembly_dir));
174     if (log->LinkAtCwd(config.AssemblyPath("assemble_cvd.log"))) {
175       LOG(ERROR) << "Unable to persist assemble_cvd log at "
176                   << config.AssemblyPath("assemble_cvd.log")
177                   << ": " << log->StrError();
178     }
179     for (const auto& instance : config.Instances()) {
180       // Create instance directory if it doesn't exist.
181       CHECK(EnsureDirectoryExists(instance.instance_dir()));
182       auto internal_dir = instance.instance_dir() + "/" + kInternalDirName;
183       CHECK(EnsureDirectoryExists(internal_dir));
184       auto shared_dir = instance.instance_dir() + "/" + kSharedDirName;
185       CHECK(EnsureDirectoryExists(shared_dir));
186       auto recording_dir = instance.instance_dir() + "/recording";
187       CHECK(EnsureDirectoryExists(recording_dir));
188     }
189     CHECK(SaveConfig(config)) << "Failed to initialize configuration";
190   }
191 
192   std::string first_instance = FLAGS_instance_dir + "." + std::to_string(GetInstance());
193   CHECK_EQ(symlink(first_instance.c_str(), FLAGS_instance_dir.c_str()), 0)
194       << "Could not symlink \"" << first_instance << "\" to \"" << FLAGS_instance_dir << "\"";
195 
196   // Do this early so that the config object is ready for anything that needs it
197   auto config = CuttlefishConfig::Get();
198   CHECK(config) << "Failed to obtain config singleton";
199 
200   ValidateAdbModeFlag(*config);
201 
202   CreateDynamicDiskFiles(fetcher_config, config);
203 
204   return config;
205 }
206 
207 const std::string kKernelDefaultPath = "kernel";
208 const std::string kInitramfsImg = "initramfs.img";
ExtractKernelParamsFromFetcherConfig(const FetcherConfig & fetcher_config)209 static void ExtractKernelParamsFromFetcherConfig(
210     const FetcherConfig& fetcher_config) {
211   std::string discovered_kernel =
212       fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
213   std::string discovered_ramdisk =
214       fetcher_config.FindCvdFileWithSuffix(kInitramfsImg);
215 
216   SetCommandLineOptionWithMode("kernel_path", discovered_kernel.c_str(),
217                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
218 
219   SetCommandLineOptionWithMode("initramfs_path", discovered_ramdisk.c_str(),
220                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
221 }
222 } // namespace
223 
AssembleCvdMain(int argc,char ** argv)224 int AssembleCvdMain(int argc, char** argv) {
225   setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0);
226   ::android::base::InitLogging(argv, android::base::StderrLogger);
227 
228   int tty = isatty(0);
229   int error_num = errno;
230   CHECK_EQ(tty, 0)
231       << "stdin was a tty, expected to be passed the output of a previous stage. "
232       << "Did you mean to run launch_cvd?";
233   CHECK(error_num != EBADF)
234       << "stdin was not a valid file descriptor, expected to be passed the output "
235       << "of launch_cvd. Did you mean to run launch_cvd?";
236 
237   std::string input_files_str;
238   {
239     auto input_fd = SharedFD::Dup(0);
240     auto bytes_read = ReadAll(input_fd, &input_files_str);
241     CHECK(bytes_read >= 0)
242         << "Failed to read input files. Error was \"" << input_fd->StrError() << "\"";
243   }
244   std::vector<std::string> input_files = android::base::Split(input_files_str, "\n");
245 
246   FetcherConfig fetcher_config = FindFetcherConfig(input_files);
247   // set gflags defaults to point to kernel/RD from fetcher config
248   ExtractKernelParamsFromFetcherConfig(fetcher_config);
249 
250   KernelConfig kernel_config;
251   CHECK(ParseCommandLineFlags(&argc, &argv, &kernel_config)) << "Failed to parse arguments";
252 
253   auto config =
254       InitFilesystemAndCreateConfig(std::move(fetcher_config), kernel_config);
255 
256   std::cout << GetConfigFilePath(*config) << "\n";
257   std::cout << std::flush;
258 
259   return 0;
260 }
261 
262 } // namespace cuttlefish
263 
main(int argc,char ** argv)264 int main(int argc, char** argv) {
265   return cuttlefish::AssembleCvdMain(argc, argv);
266 }
267