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 <ctype.h>
18 #include <getopt.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21
22 #include <string>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/macros.h>
27 #include <android-base/strings.h>
28 #include <android-base/stringprintf.h>
29 #include <modprobe/modprobe.h>
30
31 #include <sys/utsname.h>
32
33 namespace {
34
35 enum modprobe_mode {
36 AddModulesMode,
37 RemoveModulesMode,
38 ListModulesMode,
39 ShowDependenciesMode,
40 };
41
print_usage(void)42 void print_usage(void) {
43 LOG(INFO) << "Usage:";
44 LOG(INFO);
45 LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
46 LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
47 LOG(INFO);
48 LOG(INFO) << "Options:";
49 LOG(INFO) << " --all=FILE: FILE to acquire module names from";
50 LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
51 LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
52 LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
53 LOG(INFO) << " -h, --help: Print this help";
54 LOG(INFO) << " -l, --list: List modules matching pattern";
55 LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
56 LOG(INFO) << " -s, --syslog: print to syslog also";
57 LOG(INFO) << " -q, --quiet: disable messages";
58 LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
59 LOG(INFO);
60 }
61
62 #define check_mode() \
63 if (mode != AddModulesMode) { \
64 LOG(ERROR) << "multiple mode flags specified"; \
65 print_usage(); \
66 return EXIT_FAILURE; \
67 }
68
stripComments(const std::string & str)69 std::string stripComments(const std::string& str) {
70 for (std::string rv = str;;) {
71 auto comment = rv.find('#');
72 if (comment == std::string::npos) return rv;
73 auto end = rv.find('\n', comment);
74 if (end != std::string::npos) end = end - comment;
75 rv.erase(comment, end);
76 }
77 /* NOTREACHED */
78 }
79
80 auto syslog = false;
81
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)82 void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
83 const char* file, unsigned int line, const char* message) {
84 android::base::StdioLogger(id, severity, tag, file, line, message);
85 if (syslog && message[0]) {
86 android::base::KernelLogger(id, severity, tag, file, line, message);
87 }
88 }
89
ModDirMatchesKernelPageSize(const char * mod_dir)90 static bool ModDirMatchesKernelPageSize(const char* mod_dir) {
91 static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;
92 unsigned int mod_pgsize_kb = 16; // 16k default since android15-6.6
93
94 if (mod_dir && strstr(mod_dir, "-4k") != NULL) {
95 mod_pgsize_kb = 4;
96 }
97
98 return kernel_pgsize_kb == mod_pgsize_kb;
99 }
100
ModDirMatchesKernelPageSizeLegacy(const char * mod_dir)101 static bool ModDirMatchesKernelPageSizeLegacy(const char* mod_dir) {
102 static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;
103 const char* mod_sfx = strrchr(mod_dir, '_');
104 unsigned int mod_pgsize_kb;
105 int mod_sfx_len;
106
107 if (mod_sfx == NULL || sscanf(mod_sfx, "_%uk%n", &mod_pgsize_kb, &mod_sfx_len) != 1 ||
108 strlen(mod_sfx) != mod_sfx_len) {
109 mod_pgsize_kb = 4;
110 }
111
112 return kernel_pgsize_kb == mod_pgsize_kb;
113 }
114
115 // Find directories in format of "/lib/modules/x.y.z-*".
KernelVersionNameFilter(const dirent * de)116 static int KernelVersionNameFilter(const dirent* de) {
117 static unsigned int major, minor;
118 static std::string kernel_version;
119 utsname uts;
120
121 if (kernel_version.empty()) {
122 if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
123 LOG(ERROR) << "Could not parse the kernel version from uname";
124 return 0;
125 }
126 kernel_version = android::base::StringPrintf("%u.%u", major, minor);
127 }
128
129 if (android::base::StartsWith(de->d_name, kernel_version)) {
130 // Check for GKI to avoid breaking non-GKI Android devices.
131 if (UNLIKELY(strstr(de->d_name, "-android") == NULL)) {
132 // For non-GKI, just match when the major and minor versions match.
133 return 1;
134 }
135
136 // For android15-6.6 and later, GKI adds `-4k` to the UTS release
137 // string to identify 4kb page size kernels. If there is no page size
138 // suffix, then the kernel page size is 16kb.
139 if (major > 6 || (major == 6 && minor >= 6)) {
140 return ModDirMatchesKernelPageSize(de->d_name);
141 } else {
142 return ModDirMatchesKernelPageSizeLegacy(de->d_name);
143 }
144 }
145 return 0;
146 }
147
148 } // anonymous namespace
149
modprobe_main(int argc,char ** argv)150 extern "C" int modprobe_main(int argc, char** argv) {
151 android::base::InitLogging(argv, MyLogger);
152 android::base::SetMinimumLogSeverity(android::base::INFO);
153
154 std::vector<std::string> modules;
155 std::string modules_load_file;
156 std::string module_parameters;
157 std::string mods;
158 std::vector<std::string> mod_dirs;
159 modprobe_mode mode = AddModulesMode;
160 bool blocklist = false;
161 int rv = EXIT_SUCCESS;
162
163 int opt, fd;
164 int option_index = 0;
165 // NB: We have non-standard short options -l and -D to make it easier for
166 // OEMs to transition from toybox.
167 // clang-format off
168 static struct option long_options[] = {
169 { "all", optional_argument, 0, 'a' },
170 { "use-blocklist", no_argument, 0, 'b' },
171 { "dirname", required_argument, 0, 'd' },
172 { "show-depends", no_argument, 0, 'D' },
173 { "help", no_argument, 0, 'h' },
174 { "list", no_argument, 0, 'l' },
175 { "quiet", no_argument, 0, 'q' },
176 { "remove", no_argument, 0, 'r' },
177 { "syslog", no_argument, 0, 's' },
178 { "verbose", no_argument, 0, 'v' },
179 };
180 // clang-format on
181 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
182 switch (opt) {
183 case 'a':
184 // toybox modprobe supported -a to load multiple modules, this
185 // is supported here by default, ignore flag if no argument.
186 check_mode();
187 if (optarg == NULL) break;
188
189 // Since libmodprobe doesn't fail when the modules load file
190 // doesn't exist, let's check that here so that we don't
191 // silently fail.
192 fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
193 if (fd == -1) {
194 PLOG(ERROR) << "Failed to open " << optarg;
195 return EXIT_FAILURE;
196 }
197 close(fd);
198
199 mod_dirs.emplace_back(android::base::Dirname(optarg));
200 modules_load_file = android::base::Basename(optarg);
201 break;
202 case 'b':
203 blocklist = true;
204 break;
205 case 'd':
206 mod_dirs.emplace_back(optarg);
207 break;
208 case 'D':
209 check_mode();
210 mode = ShowDependenciesMode;
211 break;
212 case 'h':
213 android::base::SetMinimumLogSeverity(android::base::INFO);
214 print_usage();
215 return rv;
216 case 'l':
217 check_mode();
218 mode = ListModulesMode;
219 break;
220 case 'q':
221 android::base::SetMinimumLogSeverity(android::base::WARNING);
222 break;
223 case 'r':
224 check_mode();
225 mode = RemoveModulesMode;
226 break;
227 case 's':
228 syslog = true;
229 break;
230 case 'v':
231 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
232 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
233 } else {
234 android::base::SetMinimumLogSeverity(android::base::DEBUG);
235 }
236 break;
237 default:
238 LOG(ERROR) << "Unrecognized option: " << opt;
239 print_usage();
240 return EXIT_FAILURE;
241 }
242 }
243
244 int parameter_count = 0;
245 for (opt = optind; opt < argc; opt++) {
246 if (!strchr(argv[opt], '=')) {
247 modules.emplace_back(argv[opt]);
248 } else {
249 parameter_count++;
250 if (module_parameters.empty()) {
251 module_parameters = argv[opt];
252 } else {
253 module_parameters = module_parameters + " " + argv[opt];
254 }
255 }
256 }
257
258 if (mod_dirs.empty()) {
259 static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
260 dirent** kernel_dirs = NULL;
261
262 int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
263 if (n == -1) {
264 PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
265 return EXIT_FAILURE;
266 } else if (n > 0) {
267 while (n--) {
268 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
269 }
270 }
271 free(kernel_dirs);
272
273 if (mod_dirs.empty() || getpagesize() == 4096) {
274 // Allow modules to be directly inside /lib/modules
275 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
276 }
277 }
278
279 LOG(DEBUG) << "mode is " << mode;
280 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
281 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
282 LOG(DEBUG) << "modules load file is: " << modules_load_file;
283 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
284
285 if (modules.empty()) {
286 if (mode == ListModulesMode) {
287 // emulate toybox modprobe list with no pattern (list all)
288 modules.emplace_back("*");
289 } else if (modules_load_file.empty()) {
290 LOG(ERROR) << "No modules given.";
291 print_usage();
292 return EXIT_FAILURE;
293 }
294 }
295 if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
296 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
297 print_usage();
298 return EXIT_FAILURE;
299 }
300
301 Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
302 if (mode == AddModulesMode && !modules_load_file.empty()) {
303 if (!m.LoadListedModules(false)) {
304 PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
305 return EXIT_FAILURE;
306 }
307 /* Fall-through to load modules provided on the command line (if any)*/
308 }
309
310 for (const auto& module : modules) {
311 switch (mode) {
312 case AddModulesMode:
313 if (!m.LoadWithAliases(module, true, module_parameters)) {
314 if (m.IsBlocklisted(module)) continue;
315 PLOG(ERROR) << "Failed to load module " << module;
316 rv = EXIT_FAILURE;
317 }
318 break;
319 case RemoveModulesMode:
320 if (!m.Remove(module)) {
321 PLOG(ERROR) << "Failed to remove module " << module;
322 rv = EXIT_FAILURE;
323 }
324 break;
325 case ListModulesMode: {
326 std::vector<std::string> list = m.ListModules(module);
327 LOG(INFO) << android::base::Join(list, "\n");
328 break;
329 }
330 case ShowDependenciesMode: {
331 std::vector<std::string> pre_deps;
332 std::vector<std::string> deps;
333 std::vector<std::string> post_deps;
334 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
335 rv = EXIT_FAILURE;
336 break;
337 }
338 LOG(INFO) << "Dependencies for " << module << ":";
339 LOG(INFO) << "Soft pre-dependencies:";
340 LOG(INFO) << android::base::Join(pre_deps, "\n");
341 LOG(INFO) << "Hard dependencies:";
342 LOG(INFO) << android::base::Join(deps, "\n");
343 LOG(INFO) << "Soft post-dependencies:";
344 LOG(INFO) << android::base::Join(post_deps, "\n");
345 break;
346 }
347 default:
348 LOG(ERROR) << "Bad mode";
349 rv = EXIT_FAILURE;
350 }
351 }
352
353 return rv;
354 }
355