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 
17 #include <climits>
18 #include <cstdlib>
19 #include <cstring>
20 #include <fstream>
21 #include <iostream>
22 #include <string>
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 
30 #include <android-base/result.h>
31 
32 #include "linkerconfig/apex.h"
33 #include "linkerconfig/apexconfig.h"
34 #include "linkerconfig/baseconfig.h"
35 #include "linkerconfig/context.h"
36 #include "linkerconfig/environment.h"
37 #include "linkerconfig/legacy.h"
38 #include "linkerconfig/log.h"
39 #include "linkerconfig/namespacebuilder.h"
40 #include "linkerconfig/recovery.h"
41 #include "linkerconfig/variableloader.h"
42 #include "linkerconfig/variables.h"
43 
44 using android::base::ErrnoError;
45 using android::base::Error;
46 using android::base::Result;
47 using android::linkerconfig::contents::Context;
48 using android::linkerconfig::modules::ApexInfo;
49 using android::linkerconfig::modules::Configuration;
50 
51 namespace {
52 const static struct option program_options[] = {
53     {"target", required_argument, 0, 't'},
54     {"strict", no_argument, 0, 's'},
55 #ifndef __ANDROID__
56     {"root", required_argument, 0, 'r'},
57     {"vndk", required_argument, 0, 'v'},
58     {"recovery", no_argument, 0, 'y'},
59     {"legacy", no_argument, 0, 'l'},
60 #endif
61     {"help", no_argument, 0, 'h'},
62     {0, 0, 0, 0}};
63 
64 struct ProgramArgs {
65   std::string target_directory;
66   bool strict;
67   std::string root;
68   std::string vndk_version;
69   bool is_recovery;
70   bool is_legacy;
71 };
72 
73 [[noreturn]] void PrintUsage(int status = EXIT_SUCCESS) {
74   std::cerr << "Usage : linkerconfig [--target <target_directory>]"
75                " [--strict]"
76 #ifndef __ANDROID__
77                " --root <root dir>"
78                " --vndk <vndk version>"
79                " --recovery"
80                " --legacy"
81 #endif
82                " [--help]"
83             << std::endl;
84   exit(status);
85 }
86 
87 std::string RealPath(std::string_view path) {
88   char resolved_path[PATH_MAX];
89   if (realpath(path.data(), resolved_path) != nullptr) {
90     return resolved_path;
91   }
92   PrintUsage(-1);
93 }
94 
95 bool ParseArgs(int argc, char* argv[], ProgramArgs* args) {
96   int parse_result;
97   while ((parse_result = getopt_long(
98               argc, argv, "t:sr:v:hyl", program_options, NULL)) != -1) {
99     switch (parse_result) {
100       case 't':
101         args->target_directory = optarg;
102         break;
103       case 's':
104         args->strict = true;
105         break;
106       case 'r':
107         args->root = RealPath(optarg);
108         break;
109       case 'v':
110         args->vndk_version = optarg;
111         break;
112       case 'y':
113         args->is_recovery = true;
114         break;
115       case 'l':
116         args->is_legacy = true;
117         break;
118       case 'h':
119         PrintUsage();
120       default:
121         return false;
122     }
123   }
124 
125   if (optind < argc) {
126     return false;
127   }
128 
129   return true;
130 }
131 
132 void LoadVariables(ProgramArgs args) {
133 #ifndef __ANDROID__
134   if (!args.is_recovery && (args.root == "" || args.vndk_version == "")) {
135     PrintUsage();
136   }
137   android::linkerconfig::modules::Variables::AddValue("ro.vndk.version",
138                                                       args.vndk_version);
139 #endif
140 
141   if (!args.is_recovery) {
142     android::linkerconfig::generator::LoadVariables(args.root);
143   }
144 }
145 
146 Result<void> WriteConfigurationToFile(Configuration& conf,
147                                       std::string file_path) {
148   std::ostream* out = &std::cout;
149   std::ofstream file_out;
150 
151   if (file_path != "") {
152     file_out.open(file_path);
153     if (file_out.fail()) {
154       return ErrnoError() << "Failed to open file " << file_path;
155     }
156     out = &file_out;
157   }
158 
159   android::linkerconfig::modules::ConfigWriter config_writer;
160 
161   conf.WriteConfig(config_writer);
162   *out << config_writer.ToString();
163   if (!out->good()) {
164     return ErrnoError() << "Failed to write content to " << file_path;
165   }
166 
167   return {};
168 }
169 
170 Result<void> UpdatePermission([[maybe_unused]] const std::string& file_path) {
171 #ifdef __ANDROID__
172   if (fchmodat(AT_FDCWD,
173                file_path.c_str(),
174                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
175                AT_SYMLINK_NOFOLLOW) < 0) {
176     return ErrnoError() << "Failed to update permission of " << file_path;
177   }
178 #endif
179 
180   return {};
181 }
182 
183 Context GetContext(ProgramArgs args) {
184   auto apex_list = android::linkerconfig::modules::ScanActiveApexes(args.root);
185   Context ctx;
186   for (auto const& apex_item : apex_list) {
187     auto apex_info = apex_item.second;
188     if (apex_info.has_bin || apex_info.has_lib) {
189       ctx.AddApexModule(std::move(apex_info));
190     }
191   }
192   if (args.strict) {
193     ctx.SetStrictMode(true);
194   }
195   android::linkerconfig::contents::RegisterApexNamespaceBuilders(ctx);
196   return ctx;
197 }
198 
199 Configuration GetConfiguration(Context& ctx) {
200   if (android::linkerconfig::modules::IsRecoveryMode()) {
201     return android::linkerconfig::contents::CreateRecoveryConfiguration(ctx);
202   }
203 
204   if (android::linkerconfig::modules::IsLegacyDevice()) {
205     return android::linkerconfig::contents::CreateLegacyConfiguration(ctx);
206   }
207 
208   // Use base configuration in default
209   return android::linkerconfig::contents::CreateBaseConfiguration(ctx);
210 }
211 
212 Result<void> GenerateConfiguration(Configuration config, std::string dir_path,
213                                    bool update_permission) {
214   std::string file_path = "";
215   if (dir_path != "") {
216     file_path = dir_path + "/ld.config.txt";
217   }
218 
219   auto write_config = WriteConfigurationToFile(config, file_path);
220   if (!write_config.ok()) {
221     return write_config;
222   } else if (update_permission && file_path != "") {
223     return UpdatePermission(file_path);
224   }
225 
226   return {};
227 }
228 
229 Result<void> GenerateBaseLinkerConfiguration(Context& ctx,
230                                              const std::string& dir_path) {
231   return GenerateConfiguration(GetConfiguration(ctx), dir_path, true);
232 }
233 
234 Result<void> GenerateRecoveryLinkerConfiguration(Context& ctx,
235                                                  const std::string& dir_path) {
236   return GenerateConfiguration(
237       android::linkerconfig::contents::CreateRecoveryConfiguration(ctx),
238       dir_path,
239       false);
240 }
241 
242 Result<void> GenerateLegacyLinkerConfiguration(Context& ctx,
243                                                const std::string& dir_path) {
244   return GenerateConfiguration(
245       android::linkerconfig::contents::CreateLegacyConfiguration(ctx),
246       dir_path,
247       false);
248 }
249 
250 Result<void> GenerateApexConfiguration(
251     const std::string& base_dir, android::linkerconfig::contents::Context& ctx,
252     const android::linkerconfig::modules::ApexInfo& target_apex) {
253   std::string dir_path = base_dir + "/" + target_apex.name;
254   if (auto ret = mkdir(dir_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
255     return ErrnoError() << "Failed to create directory " << dir_path;
256   }
257 
258   return GenerateConfiguration(
259       android::linkerconfig::contents::CreateApexConfiguration(ctx, target_apex),
260       dir_path,
261       true);
262 }
263 
264 void GenerateApexConfigurations(Context& ctx, const std::string& dir_path) {
265   for (auto const& apex_item : ctx.GetApexModules()) {
266     if (apex_item.has_bin) {
267       auto result = GenerateApexConfiguration(dir_path, ctx, apex_item);
268       if (!result.ok()) {
269         LOG(WARNING) << result.error();
270       }
271     }
272   }
273 }
274 
275 void ExitOnFailure(Result<void> task) {
276   if (!task.ok()) {
277     LOG(FATAL) << task.error();
278     exit(EXIT_FAILURE);
279   }
280 }
281 
282 #ifdef __ANDROID__
283 struct CombinedLogger {
284   android::base::LogdLogger logd;
285 
286   void operator()(android::base::LogId id, android::base::LogSeverity severity,
287                   const char* tag, const char* file, unsigned int line,
288                   const char* message) {
289     logd(id, severity, tag, file, line, message);
290     KernelLogger(id, severity, tag, file, line, message);
291   }
292 };
293 #endif
294 }  // namespace
295 
296 int main(int argc, char* argv[]) {
297   android::base::InitLogging(argv
298 #ifdef __ANDROID__
299                              ,
300                              CombinedLogger()
301 #endif
302   );
303 
304   ProgramArgs args = {};
305 
306   if (!ParseArgs(argc, argv, &args)) {
307     PrintUsage(EXIT_FAILURE);
308   }
309 
310   LoadVariables(args);
311   Context ctx = GetContext(args);
312 
313   // when exec'ed from init, this is 0x0077, which makes the subdirectories
314   // inaccessible for others. set umask to 0x0022 so that they can be
315   // accessible.
316   umask(0x0022);
317 
318   if (args.is_recovery) {
319     ExitOnFailure(
320         GenerateRecoveryLinkerConfiguration(ctx, args.target_directory));
321   } else if (args.is_legacy) {
322     ExitOnFailure(GenerateLegacyLinkerConfiguration(ctx, args.target_directory));
323   } else {
324     ExitOnFailure(GenerateBaseLinkerConfiguration(ctx, args.target_directory));
325     GenerateApexConfigurations(ctx, args.target_directory);
326   }
327 
328   return EXIT_SUCCESS;
329 }
330