blob: b0e76ea81bdc300241c73f1f09e9da723780e742 [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>
Kelvin Zhangffdb0172024-04-19 17:57:01 -070018#include <fcntl.h>
Steve Muckle64a55342019-07-30 11:53:15 -070019#include <getopt.h>
20#include <stdlib.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>
Kelvin Zhangffdb0172024-04-19 17:57:01 -070031#include <unistd.h>
Vincent Donnefort83207782023-01-24 17:39:16 +000032
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070033namespace {
34
Steve Muckle64a55342019-07-30 11:53:15 -070035enum modprobe_mode {
36 AddModulesMode,
37 RemoveModulesMode,
38 ListModulesMode,
39 ShowDependenciesMode,
40};
41
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070042void print_usage(void) {
Mark Salyzyn63368be2020-06-24 03:02:39 -070043 LOG(INFO) << "Usage:";
44 LOG(INFO);
Vincent Donnefort83207782023-01-24 17:39:16 +000045 LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
46 LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
Mark Salyzyn63368be2020-06-24 03:02:39 -070047 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);
Steve Muckle64a55342019-07-30 11:53:15 -070060}
61
Mark Salyzyn63368be2020-06-24 03:02:39 -070062#define check_mode() \
63 if (mode != AddModulesMode) { \
64 LOG(ERROR) << "multiple mode flags specified"; \
65 print_usage(); \
66 return EXIT_FAILURE; \
Steve Muckle64a55342019-07-30 11:53:15 -070067 }
68
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -070069std::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
Mark Salyzyn63368be2020-06-24 03:02:39 -070080auto syslog = false;
81
82void 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
Will McVickerd278f1d2023-05-25 16:43:31 -070090// Find directories in format of "/lib/modules/x.y.z-*".
91static int KernelVersionNameFilter(const dirent* de) {
92 unsigned int major, minor;
93 static std::string kernel_version;
94 utsname uts;
95
96 if (kernel_version.empty()) {
97 if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
98 LOG(ERROR) << "Could not parse the kernel version from uname";
99 return 0;
100 }
101 kernel_version = android::base::StringPrintf("%u.%u", major, minor);
102 }
103
104 if (android::base::StartsWith(de->d_name, kernel_version)) {
105 return 1;
106 }
107 return 0;
108}
109
Kelvin Zhangffdb0172024-04-19 17:57:01 -0700110std::string GetPageSizeSuffix() {
111 static const size_t page_size = sysconf(_SC_PAGE_SIZE);
112 return android::base::StringPrintf("_%zuk", page_size / 1024);
113}
114
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700115} // anonymous namespace
116
Steve Muckle64a55342019-07-30 11:53:15 -0700117extern "C" int modprobe_main(int argc, char** argv) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700118 android::base::InitLogging(argv, MyLogger);
119 android::base::SetMinimumLogSeverity(android::base::INFO);
120
Steve Muckle64a55342019-07-30 11:53:15 -0700121 std::vector<std::string> modules;
Will McVicker7d400f92024-03-07 11:18:41 -0800122 std::string modules_load_file;
Steve Muckle64a55342019-07-30 11:53:15 -0700123 std::string module_parameters;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700124 std::string mods;
Steve Muckle64a55342019-07-30 11:53:15 -0700125 std::vector<std::string> mod_dirs;
126 modprobe_mode mode = AddModulesMode;
Mark Salyzyn703fb742020-06-15 11:51:59 -0700127 bool blocklist = false;
Steve Muckle64a55342019-07-30 11:53:15 -0700128 int rv = EXIT_SUCCESS;
129
Will McVicker7d400f92024-03-07 11:18:41 -0800130 int opt, fd;
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700131 int option_index = 0;
132 // NB: We have non-standard short options -l and -D to make it easier for
133 // OEMs to transition from toybox.
134 // clang-format off
135 static struct option long_options[] = {
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700136 { "all", optional_argument, 0, 'a' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700137 { "use-blocklist", no_argument, 0, 'b' },
138 { "dirname", required_argument, 0, 'd' },
139 { "show-depends", no_argument, 0, 'D' },
140 { "help", no_argument, 0, 'h' },
141 { "list", no_argument, 0, 'l' },
142 { "quiet", no_argument, 0, 'q' },
143 { "remove", no_argument, 0, 'r' },
Mark Salyzyn63368be2020-06-24 03:02:39 -0700144 { "syslog", no_argument, 0, 's' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700145 { "verbose", no_argument, 0, 'v' },
146 };
147 // clang-format on
Mark Salyzyn63368be2020-06-24 03:02:39 -0700148 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
Steve Muckle64a55342019-07-30 11:53:15 -0700149 switch (opt) {
150 case 'a':
151 // toybox modprobe supported -a to load multiple modules, this
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700152 // is supported here by default, ignore flag if no argument.
Steve Muckle64a55342019-07-30 11:53:15 -0700153 check_mode();
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700154 if (optarg == NULL) break;
Will McVicker7d400f92024-03-07 11:18:41 -0800155
156 // Since libmodprobe doesn't fail when the modules load file
157 // doesn't exist, let's check that here so that we don't
158 // silently fail.
159 fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
160 if (fd == -1) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700161 PLOG(ERROR) << "Failed to open " << optarg;
Will McVicker7d400f92024-03-07 11:18:41 -0800162 return EXIT_FAILURE;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700163 }
Will McVicker7d400f92024-03-07 11:18:41 -0800164 close(fd);
165
166 mod_dirs.emplace_back(android::base::Dirname(optarg));
167 modules_load_file = android::base::Basename(optarg);
Steve Muckle64a55342019-07-30 11:53:15 -0700168 break;
169 case 'b':
Mark Salyzyn703fb742020-06-15 11:51:59 -0700170 blocklist = true;
Steve Muckle64a55342019-07-30 11:53:15 -0700171 break;
172 case 'd':
173 mod_dirs.emplace_back(optarg);
174 break;
175 case 'D':
176 check_mode();
177 mode = ShowDependenciesMode;
178 break;
179 case 'h':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700180 android::base::SetMinimumLogSeverity(android::base::INFO);
Steve Muckle64a55342019-07-30 11:53:15 -0700181 print_usage();
Mark Salyzyn63368be2020-06-24 03:02:39 -0700182 return rv;
Steve Muckle64a55342019-07-30 11:53:15 -0700183 case 'l':
184 check_mode();
185 mode = ListModulesMode;
186 break;
187 case 'q':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700188 android::base::SetMinimumLogSeverity(android::base::WARNING);
Steve Muckle64a55342019-07-30 11:53:15 -0700189 break;
190 case 'r':
191 check_mode();
192 mode = RemoveModulesMode;
193 break;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700194 case 's':
195 syslog = true;
196 break;
Steve Muckle64a55342019-07-30 11:53:15 -0700197 case 'v':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700198 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
199 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
200 } else {
201 android::base::SetMinimumLogSeverity(android::base::DEBUG);
202 }
Steve Muckle64a55342019-07-30 11:53:15 -0700203 break;
204 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700205 LOG(ERROR) << "Unrecognized option: " << opt;
206 print_usage();
Steve Muckle64a55342019-07-30 11:53:15 -0700207 return EXIT_FAILURE;
208 }
209 }
210
211 int parameter_count = 0;
212 for (opt = optind; opt < argc; opt++) {
213 if (!strchr(argv[opt], '=')) {
214 modules.emplace_back(argv[opt]);
215 } else {
216 parameter_count++;
217 if (module_parameters.empty()) {
218 module_parameters = argv[opt];
219 } else {
220 module_parameters = module_parameters + " " + argv[opt];
221 }
222 }
223 }
224
Vincent Donnefort83207782023-01-24 17:39:16 +0000225 if (mod_dirs.empty()) {
Will McVickerd278f1d2023-05-25 16:43:31 -0700226 static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
227 dirent** kernel_dirs = NULL;
228
229 int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
230 if (n == -1) {
231 PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
232 return EXIT_FAILURE;
233 } else if (n > 0) {
234 while (n--) {
235 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
236 }
237 }
238 free(kernel_dirs);
239
240 // Allow modules to be directly inside /lib/modules
241 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
Vincent Donnefort83207782023-01-24 17:39:16 +0000242 }
Kelvin Zhangffdb0172024-04-19 17:57:01 -0700243 if (getpagesize() != 4096) {
244 struct utsname uts {};
245 if (uname(&uts)) {
246 PLOG(FATAL) << "Failed to get kernel version";
247 }
248 const auto module_dir = android::base::StringPrintf("/lib/modules/%s%s", uts.release,
249 GetPageSizeSuffix().c_str());
250 struct stat st {};
251 if (stat(module_dir.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
252 mod_dirs.clear();
253 mod_dirs.emplace_back(module_dir);
254 }
255 }
Vincent Donnefort83207782023-01-24 17:39:16 +0000256
Mark Salyzyn63368be2020-06-24 03:02:39 -0700257 LOG(DEBUG) << "mode is " << mode;
258 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
259 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
Will McVicker7d400f92024-03-07 11:18:41 -0800260 LOG(DEBUG) << "modules load file is: " << modules_load_file;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700261 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
Steve Muckle64a55342019-07-30 11:53:15 -0700262
263 if (modules.empty()) {
264 if (mode == ListModulesMode) {
265 // emulate toybox modprobe list with no pattern (list all)
266 modules.emplace_back("*");
Will McVicker7d400f92024-03-07 11:18:41 -0800267 } else if (modules_load_file.empty()) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700268 LOG(ERROR) << "No modules given.";
Steve Muckle64a55342019-07-30 11:53:15 -0700269 print_usage();
270 return EXIT_FAILURE;
271 }
272 }
Will McVicker7d400f92024-03-07 11:18:41 -0800273 if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700274 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
Steve Muckle64a55342019-07-30 11:53:15 -0700275 print_usage();
276 return EXIT_FAILURE;
277 }
278
Will McVicker7d400f92024-03-07 11:18:41 -0800279 Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
280 if (mode == AddModulesMode && !modules_load_file.empty()) {
281 if (!m.LoadListedModules(false)) {
282 PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
283 return EXIT_FAILURE;
284 }
285 /* Fall-through to load modules provided on the command line (if any)*/
286 }
Steve Muckle64a55342019-07-30 11:53:15 -0700287
288 for (const auto& module : modules) {
289 switch (mode) {
290 case AddModulesMode:
291 if (!m.LoadWithAliases(module, true, module_parameters)) {
Will McVickerb9dead12024-03-07 11:15:37 -0800292 if (m.IsBlocklisted(module)) continue;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700293 PLOG(ERROR) << "Failed to load module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700294 rv = EXIT_FAILURE;
295 }
296 break;
297 case RemoveModulesMode:
298 if (!m.Remove(module)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700299 PLOG(ERROR) << "Failed to remove module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700300 rv = EXIT_FAILURE;
301 }
302 break;
303 case ListModulesMode: {
304 std::vector<std::string> list = m.ListModules(module);
Mark Salyzyn63368be2020-06-24 03:02:39 -0700305 LOG(INFO) << android::base::Join(list, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700306 break;
307 }
308 case ShowDependenciesMode: {
309 std::vector<std::string> pre_deps;
310 std::vector<std::string> deps;
311 std::vector<std::string> post_deps;
312 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
313 rv = EXIT_FAILURE;
314 break;
315 }
Mark Salyzyn63368be2020-06-24 03:02:39 -0700316 LOG(INFO) << "Dependencies for " << module << ":";
317 LOG(INFO) << "Soft pre-dependencies:";
318 LOG(INFO) << android::base::Join(pre_deps, "\n");
319 LOG(INFO) << "Hard dependencies:";
320 LOG(INFO) << android::base::Join(deps, "\n");
321 LOG(INFO) << "Soft post-dependencies:";
322 LOG(INFO) << android::base::Join(post_deps, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700323 break;
324 }
325 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700326 LOG(ERROR) << "Bad mode";
Steve Muckle64a55342019-07-30 11:53:15 -0700327 rv = EXIT_FAILURE;
328 }
329 }
330
331 return rv;
332}