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