blob: 13026ac30aa896b79cc66ab34d1ba5b653d738ba [file] [log] [blame]
Steve Muckle64a55342019-07-30 11:53:15 -07001/*
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>
Will Deaconc991c3d2024-05-31 17:25:49 +000020#include <unistd.h>
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070021
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -070022#include <string>
Steve Muckle64a55342019-07-30 11:53:15 -070023
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070024#include <android-base/file.h>
Mark Salyzyn63368be2020-06-24 03:02:39 -070025#include <android-base/logging.h>
Steve Muckle64a55342019-07-30 11:53:15 -070026#include <android-base/strings.h>
Vincent Donnefort83207782023-01-24 17:39:16 +000027#include <android-base/stringprintf.h>
Steve Muckle64a55342019-07-30 11:53:15 -070028#include <modprobe/modprobe.h>
29
Vincent Donnefort83207782023-01-24 17:39:16 +000030#include <sys/utsname.h>
31
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070032namespace {
33
Steve Muckle64a55342019-07-30 11:53:15 -070034enum modprobe_mode {
35 AddModulesMode,
36 RemoveModulesMode,
37 ListModulesMode,
38 ShowDependenciesMode,
39};
40
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070041void print_usage(void) {
Mark Salyzyn63368be2020-06-24 03:02:39 -070042 LOG(INFO) << "Usage:";
43 LOG(INFO);
Vincent Donnefort83207782023-01-24 17:39:16 +000044 LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
45 LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
Mark Salyzyn63368be2020-06-24 03:02:39 -070046 LOG(INFO);
47 LOG(INFO) << "Options:";
48 LOG(INFO) << " --all=FILE: FILE to acquire module names from";
49 LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
50 LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
51 LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
52 LOG(INFO) << " -h, --help: Print this help";
53 LOG(INFO) << " -l, --list: List modules matching pattern";
54 LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
55 LOG(INFO) << " -s, --syslog: print to syslog also";
56 LOG(INFO) << " -q, --quiet: disable messages";
57 LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
58 LOG(INFO);
Steve Muckle64a55342019-07-30 11:53:15 -070059}
60
Mark Salyzyn63368be2020-06-24 03:02:39 -070061#define check_mode() \
62 if (mode != AddModulesMode) { \
63 LOG(ERROR) << "multiple mode flags specified"; \
64 print_usage(); \
65 return EXIT_FAILURE; \
Steve Muckle64a55342019-07-30 11:53:15 -070066 }
67
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -070068std::string stripComments(const std::string& str) {
69 for (std::string rv = str;;) {
70 auto comment = rv.find('#');
71 if (comment == std::string::npos) return rv;
72 auto end = rv.find('\n', comment);
73 if (end != std::string::npos) end = end - comment;
74 rv.erase(comment, end);
75 }
76 /* NOTREACHED */
77}
78
Mark Salyzyn63368be2020-06-24 03:02:39 -070079auto syslog = false;
80
81void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
82 const char* file, unsigned int line, const char* message) {
83 android::base::StdioLogger(id, severity, tag, file, line, message);
84 if (syslog && message[0]) {
85 android::base::KernelLogger(id, severity, tag, file, line, message);
86 }
87}
88
Will Deaconc991c3d2024-05-31 17:25:49 +000089static bool ModDirMatchesKernelPageSize(const char* mod_dir) {
90 static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;
91 const char* mod_sfx = strrchr(mod_dir, '_');
92 unsigned int mod_pgsize_kb;
93 int mod_sfx_len;
94
95 if (mod_sfx == NULL || sscanf(mod_sfx, "_%uk%n", &mod_pgsize_kb, &mod_sfx_len) != 1 ||
96 strlen(mod_sfx) != mod_sfx_len) {
97 mod_pgsize_kb = 4;
98 }
99
100 return kernel_pgsize_kb == mod_pgsize_kb;
101}
102
Will McVickerd278f1d2023-05-25 16:43:31 -0700103// Find directories in format of "/lib/modules/x.y.z-*".
104static int KernelVersionNameFilter(const dirent* de) {
105 unsigned int major, minor;
106 static std::string kernel_version;
107 utsname uts;
108
109 if (kernel_version.empty()) {
110 if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
111 LOG(ERROR) << "Could not parse the kernel version from uname";
112 return 0;
113 }
114 kernel_version = android::base::StringPrintf("%u.%u", major, minor);
115 }
116
117 if (android::base::StartsWith(de->d_name, kernel_version)) {
Will Deaconc991c3d2024-05-31 17:25:49 +0000118 return ModDirMatchesKernelPageSize(de->d_name);
Will McVickerd278f1d2023-05-25 16:43:31 -0700119 }
120 return 0;
121}
122
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700123} // anonymous namespace
124
Steve Muckle64a55342019-07-30 11:53:15 -0700125extern "C" int modprobe_main(int argc, char** argv) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700126 android::base::InitLogging(argv, MyLogger);
127 android::base::SetMinimumLogSeverity(android::base::INFO);
128
Steve Muckle64a55342019-07-30 11:53:15 -0700129 std::vector<std::string> modules;
Will McVicker7d400f92024-03-07 11:18:41 -0800130 std::string modules_load_file;
Steve Muckle64a55342019-07-30 11:53:15 -0700131 std::string module_parameters;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700132 std::string mods;
Steve Muckle64a55342019-07-30 11:53:15 -0700133 std::vector<std::string> mod_dirs;
134 modprobe_mode mode = AddModulesMode;
Mark Salyzyn703fb742020-06-15 11:51:59 -0700135 bool blocklist = false;
Steve Muckle64a55342019-07-30 11:53:15 -0700136 int rv = EXIT_SUCCESS;
137
Will McVicker7d400f92024-03-07 11:18:41 -0800138 int opt, fd;
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700139 int option_index = 0;
140 // NB: We have non-standard short options -l and -D to make it easier for
141 // OEMs to transition from toybox.
142 // clang-format off
143 static struct option long_options[] = {
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700144 { "all", optional_argument, 0, 'a' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700145 { "use-blocklist", no_argument, 0, 'b' },
146 { "dirname", required_argument, 0, 'd' },
147 { "show-depends", no_argument, 0, 'D' },
148 { "help", no_argument, 0, 'h' },
149 { "list", no_argument, 0, 'l' },
150 { "quiet", no_argument, 0, 'q' },
151 { "remove", no_argument, 0, 'r' },
Mark Salyzyn63368be2020-06-24 03:02:39 -0700152 { "syslog", no_argument, 0, 's' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700153 { "verbose", no_argument, 0, 'v' },
154 };
155 // clang-format on
Mark Salyzyn63368be2020-06-24 03:02:39 -0700156 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
Steve Muckle64a55342019-07-30 11:53:15 -0700157 switch (opt) {
158 case 'a':
159 // toybox modprobe supported -a to load multiple modules, this
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700160 // is supported here by default, ignore flag if no argument.
Steve Muckle64a55342019-07-30 11:53:15 -0700161 check_mode();
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700162 if (optarg == NULL) break;
Will McVicker7d400f92024-03-07 11:18:41 -0800163
164 // Since libmodprobe doesn't fail when the modules load file
165 // doesn't exist, let's check that here so that we don't
166 // silently fail.
167 fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
168 if (fd == -1) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700169 PLOG(ERROR) << "Failed to open " << optarg;
Will McVicker7d400f92024-03-07 11:18:41 -0800170 return EXIT_FAILURE;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700171 }
Will McVicker7d400f92024-03-07 11:18:41 -0800172 close(fd);
173
174 mod_dirs.emplace_back(android::base::Dirname(optarg));
175 modules_load_file = android::base::Basename(optarg);
Steve Muckle64a55342019-07-30 11:53:15 -0700176 break;
177 case 'b':
Mark Salyzyn703fb742020-06-15 11:51:59 -0700178 blocklist = true;
Steve Muckle64a55342019-07-30 11:53:15 -0700179 break;
180 case 'd':
181 mod_dirs.emplace_back(optarg);
182 break;
183 case 'D':
184 check_mode();
185 mode = ShowDependenciesMode;
186 break;
187 case 'h':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700188 android::base::SetMinimumLogSeverity(android::base::INFO);
Steve Muckle64a55342019-07-30 11:53:15 -0700189 print_usage();
Mark Salyzyn63368be2020-06-24 03:02:39 -0700190 return rv;
Steve Muckle64a55342019-07-30 11:53:15 -0700191 case 'l':
192 check_mode();
193 mode = ListModulesMode;
194 break;
195 case 'q':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700196 android::base::SetMinimumLogSeverity(android::base::WARNING);
Steve Muckle64a55342019-07-30 11:53:15 -0700197 break;
198 case 'r':
199 check_mode();
200 mode = RemoveModulesMode;
201 break;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700202 case 's':
203 syslog = true;
204 break;
Steve Muckle64a55342019-07-30 11:53:15 -0700205 case 'v':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700206 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
207 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
208 } else {
209 android::base::SetMinimumLogSeverity(android::base::DEBUG);
210 }
Steve Muckle64a55342019-07-30 11:53:15 -0700211 break;
212 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700213 LOG(ERROR) << "Unrecognized option: " << opt;
214 print_usage();
Steve Muckle64a55342019-07-30 11:53:15 -0700215 return EXIT_FAILURE;
216 }
217 }
218
219 int parameter_count = 0;
220 for (opt = optind; opt < argc; opt++) {
221 if (!strchr(argv[opt], '=')) {
222 modules.emplace_back(argv[opt]);
223 } else {
224 parameter_count++;
225 if (module_parameters.empty()) {
226 module_parameters = argv[opt];
227 } else {
228 module_parameters = module_parameters + " " + argv[opt];
229 }
230 }
231 }
232
Vincent Donnefort83207782023-01-24 17:39:16 +0000233 if (mod_dirs.empty()) {
Will McVickerd278f1d2023-05-25 16:43:31 -0700234 static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
235 dirent** kernel_dirs = NULL;
236
237 int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
238 if (n == -1) {
239 PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
240 return EXIT_FAILURE;
241 } else if (n > 0) {
242 while (n--) {
243 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
244 }
245 }
246 free(kernel_dirs);
247
Juan Yescas64776332024-06-10 20:50:31 -0700248 if (mod_dirs.empty() || getpagesize() == 4096) {
249 // Allow modules to be directly inside /lib/modules
250 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
251 }
Vincent Donnefort83207782023-01-24 17:39:16 +0000252 }
253
Mark Salyzyn63368be2020-06-24 03:02:39 -0700254 LOG(DEBUG) << "mode is " << mode;
255 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
256 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
Will McVicker7d400f92024-03-07 11:18:41 -0800257 LOG(DEBUG) << "modules load file is: " << modules_load_file;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700258 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
Steve Muckle64a55342019-07-30 11:53:15 -0700259
260 if (modules.empty()) {
261 if (mode == ListModulesMode) {
262 // emulate toybox modprobe list with no pattern (list all)
263 modules.emplace_back("*");
Will McVicker7d400f92024-03-07 11:18:41 -0800264 } else if (modules_load_file.empty()) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700265 LOG(ERROR) << "No modules given.";
Steve Muckle64a55342019-07-30 11:53:15 -0700266 print_usage();
267 return EXIT_FAILURE;
268 }
269 }
Will McVicker7d400f92024-03-07 11:18:41 -0800270 if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700271 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
Steve Muckle64a55342019-07-30 11:53:15 -0700272 print_usage();
273 return EXIT_FAILURE;
274 }
275
Will McVicker7d400f92024-03-07 11:18:41 -0800276 Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
277 if (mode == AddModulesMode && !modules_load_file.empty()) {
278 if (!m.LoadListedModules(false)) {
279 PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
280 return EXIT_FAILURE;
281 }
282 /* Fall-through to load modules provided on the command line (if any)*/
283 }
Steve Muckle64a55342019-07-30 11:53:15 -0700284
285 for (const auto& module : modules) {
286 switch (mode) {
287 case AddModulesMode:
288 if (!m.LoadWithAliases(module, true, module_parameters)) {
Will McVickerb9dead12024-03-07 11:15:37 -0800289 if (m.IsBlocklisted(module)) continue;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700290 PLOG(ERROR) << "Failed to load module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700291 rv = EXIT_FAILURE;
292 }
293 break;
294 case RemoveModulesMode:
295 if (!m.Remove(module)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700296 PLOG(ERROR) << "Failed to remove module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700297 rv = EXIT_FAILURE;
298 }
299 break;
300 case ListModulesMode: {
301 std::vector<std::string> list = m.ListModules(module);
Mark Salyzyn63368be2020-06-24 03:02:39 -0700302 LOG(INFO) << android::base::Join(list, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700303 break;
304 }
305 case ShowDependenciesMode: {
306 std::vector<std::string> pre_deps;
307 std::vector<std::string> deps;
308 std::vector<std::string> post_deps;
309 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
310 rv = EXIT_FAILURE;
311 break;
312 }
Mark Salyzyn63368be2020-06-24 03:02:39 -0700313 LOG(INFO) << "Dependencies for " << module << ":";
314 LOG(INFO) << "Soft pre-dependencies:";
315 LOG(INFO) << android::base::Join(pre_deps, "\n");
316 LOG(INFO) << "Hard dependencies:";
317 LOG(INFO) << android::base::Join(deps, "\n");
318 LOG(INFO) << "Soft post-dependencies:";
319 LOG(INFO) << android::base::Join(post_deps, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700320 break;
321 }
322 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700323 LOG(ERROR) << "Bad mode";
Steve Muckle64a55342019-07-30 11:53:15 -0700324 rv = EXIT_FAILURE;
325 }
326 }
327
328 return rv;
329}