blob: 45dd9b8c18d9ae2211a677e16a19fd948a46b555 [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>
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070020
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -070021#include <string>
Steve Muckle64a55342019-07-30 11:53:15 -070022
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070023#include <android-base/file.h>
Mark Salyzyn63368be2020-06-24 03:02:39 -070024#include <android-base/logging.h>
Steve Muckle64a55342019-07-30 11:53:15 -070025#include <android-base/strings.h>
Vincent Donnefort83207782023-01-24 17:39:16 +000026#include <android-base/stringprintf.h>
Steve Muckle64a55342019-07-30 11:53:15 -070027#include <modprobe/modprobe.h>
28
Vincent Donnefort83207782023-01-24 17:39:16 +000029#include <sys/utsname.h>
30
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070031namespace {
32
Steve Muckle64a55342019-07-30 11:53:15 -070033enum modprobe_mode {
34 AddModulesMode,
35 RemoveModulesMode,
36 ListModulesMode,
37 ShowDependenciesMode,
38};
39
Mark Salyzyn3ad274b2020-06-22 08:49:14 -070040void print_usage(void) {
Mark Salyzyn63368be2020-06-24 03:02:39 -070041 LOG(INFO) << "Usage:";
42 LOG(INFO);
Vincent Donnefort83207782023-01-24 17:39:16 +000043 LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
44 LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
Mark Salyzyn63368be2020-06-24 03:02:39 -070045 LOG(INFO);
46 LOG(INFO) << "Options:";
47 LOG(INFO) << " --all=FILE: FILE to acquire module names from";
48 LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
49 LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
50 LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
51 LOG(INFO) << " -h, --help: Print this help";
52 LOG(INFO) << " -l, --list: List modules matching pattern";
53 LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
54 LOG(INFO) << " -s, --syslog: print to syslog also";
55 LOG(INFO) << " -q, --quiet: disable messages";
56 LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
57 LOG(INFO);
Steve Muckle64a55342019-07-30 11:53:15 -070058}
59
Mark Salyzyn63368be2020-06-24 03:02:39 -070060#define check_mode() \
61 if (mode != AddModulesMode) { \
62 LOG(ERROR) << "multiple mode flags specified"; \
63 print_usage(); \
64 return EXIT_FAILURE; \
Steve Muckle64a55342019-07-30 11:53:15 -070065 }
66
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -070067std::string stripComments(const std::string& str) {
68 for (std::string rv = str;;) {
69 auto comment = rv.find('#');
70 if (comment == std::string::npos) return rv;
71 auto end = rv.find('\n', comment);
72 if (end != std::string::npos) end = end - comment;
73 rv.erase(comment, end);
74 }
75 /* NOTREACHED */
76}
77
Mark Salyzyn63368be2020-06-24 03:02:39 -070078auto syslog = false;
79
80void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
81 const char* file, unsigned int line, const char* message) {
82 android::base::StdioLogger(id, severity, tag, file, line, message);
83 if (syslog && message[0]) {
84 android::base::KernelLogger(id, severity, tag, file, line, message);
85 }
86}
87
Will McVickerd278f1d2023-05-25 16:43:31 -070088// Find directories in format of "/lib/modules/x.y.z-*".
89static int KernelVersionNameFilter(const dirent* de) {
90 unsigned int major, minor;
91 static std::string kernel_version;
92 utsname uts;
93
94 if (kernel_version.empty()) {
95 if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
96 LOG(ERROR) << "Could not parse the kernel version from uname";
97 return 0;
98 }
99 kernel_version = android::base::StringPrintf("%u.%u", major, minor);
100 }
101
102 if (android::base::StartsWith(de->d_name, kernel_version)) {
103 return 1;
104 }
105 return 0;
106}
107
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700108} // anonymous namespace
109
Steve Muckle64a55342019-07-30 11:53:15 -0700110extern "C" int modprobe_main(int argc, char** argv) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700111 android::base::InitLogging(argv, MyLogger);
112 android::base::SetMinimumLogSeverity(android::base::INFO);
113
Steve Muckle64a55342019-07-30 11:53:15 -0700114 std::vector<std::string> modules;
Will McVicker7d400f92024-03-07 11:18:41 -0800115 std::string modules_load_file;
Steve Muckle64a55342019-07-30 11:53:15 -0700116 std::string module_parameters;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700117 std::string mods;
Steve Muckle64a55342019-07-30 11:53:15 -0700118 std::vector<std::string> mod_dirs;
119 modprobe_mode mode = AddModulesMode;
Mark Salyzyn703fb742020-06-15 11:51:59 -0700120 bool blocklist = false;
Steve Muckle64a55342019-07-30 11:53:15 -0700121 int rv = EXIT_SUCCESS;
122
Will McVicker7d400f92024-03-07 11:18:41 -0800123 int opt, fd;
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700124 int option_index = 0;
125 // NB: We have non-standard short options -l and -D to make it easier for
126 // OEMs to transition from toybox.
127 // clang-format off
128 static struct option long_options[] = {
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700129 { "all", optional_argument, 0, 'a' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700130 { "use-blocklist", no_argument, 0, 'b' },
131 { "dirname", required_argument, 0, 'd' },
132 { "show-depends", no_argument, 0, 'D' },
133 { "help", no_argument, 0, 'h' },
134 { "list", no_argument, 0, 'l' },
135 { "quiet", no_argument, 0, 'q' },
136 { "remove", no_argument, 0, 'r' },
Mark Salyzyn63368be2020-06-24 03:02:39 -0700137 { "syslog", no_argument, 0, 's' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700138 { "verbose", no_argument, 0, 'v' },
139 };
140 // clang-format on
Mark Salyzyn63368be2020-06-24 03:02:39 -0700141 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
Steve Muckle64a55342019-07-30 11:53:15 -0700142 switch (opt) {
143 case 'a':
144 // toybox modprobe supported -a to load multiple modules, this
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700145 // is supported here by default, ignore flag if no argument.
Steve Muckle64a55342019-07-30 11:53:15 -0700146 check_mode();
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700147 if (optarg == NULL) break;
Will McVicker7d400f92024-03-07 11:18:41 -0800148
149 // Since libmodprobe doesn't fail when the modules load file
150 // doesn't exist, let's check that here so that we don't
151 // silently fail.
152 fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
153 if (fd == -1) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700154 PLOG(ERROR) << "Failed to open " << optarg;
Will McVicker7d400f92024-03-07 11:18:41 -0800155 return EXIT_FAILURE;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700156 }
Will McVicker7d400f92024-03-07 11:18:41 -0800157 close(fd);
158
159 mod_dirs.emplace_back(android::base::Dirname(optarg));
160 modules_load_file = android::base::Basename(optarg);
Steve Muckle64a55342019-07-30 11:53:15 -0700161 break;
162 case 'b':
Mark Salyzyn703fb742020-06-15 11:51:59 -0700163 blocklist = true;
Steve Muckle64a55342019-07-30 11:53:15 -0700164 break;
165 case 'd':
166 mod_dirs.emplace_back(optarg);
167 break;
168 case 'D':
169 check_mode();
170 mode = ShowDependenciesMode;
171 break;
172 case 'h':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700173 android::base::SetMinimumLogSeverity(android::base::INFO);
Steve Muckle64a55342019-07-30 11:53:15 -0700174 print_usage();
Mark Salyzyn63368be2020-06-24 03:02:39 -0700175 return rv;
Steve Muckle64a55342019-07-30 11:53:15 -0700176 case 'l':
177 check_mode();
178 mode = ListModulesMode;
179 break;
180 case 'q':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700181 android::base::SetMinimumLogSeverity(android::base::WARNING);
Steve Muckle64a55342019-07-30 11:53:15 -0700182 break;
183 case 'r':
184 check_mode();
185 mode = RemoveModulesMode;
186 break;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700187 case 's':
188 syslog = true;
189 break;
Steve Muckle64a55342019-07-30 11:53:15 -0700190 case 'v':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700191 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
192 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
193 } else {
194 android::base::SetMinimumLogSeverity(android::base::DEBUG);
195 }
Steve Muckle64a55342019-07-30 11:53:15 -0700196 break;
197 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700198 LOG(ERROR) << "Unrecognized option: " << opt;
199 print_usage();
Steve Muckle64a55342019-07-30 11:53:15 -0700200 return EXIT_FAILURE;
201 }
202 }
203
204 int parameter_count = 0;
205 for (opt = optind; opt < argc; opt++) {
206 if (!strchr(argv[opt], '=')) {
207 modules.emplace_back(argv[opt]);
208 } else {
209 parameter_count++;
210 if (module_parameters.empty()) {
211 module_parameters = argv[opt];
212 } else {
213 module_parameters = module_parameters + " " + argv[opt];
214 }
215 }
216 }
217
Vincent Donnefort83207782023-01-24 17:39:16 +0000218 if (mod_dirs.empty()) {
Will McVickerd278f1d2023-05-25 16:43:31 -0700219 static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
220 dirent** kernel_dirs = NULL;
221
222 int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
223 if (n == -1) {
224 PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
225 return EXIT_FAILURE;
226 } else if (n > 0) {
227 while (n--) {
228 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
229 }
230 }
231 free(kernel_dirs);
232
233 // Allow modules to be directly inside /lib/modules
234 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
Vincent Donnefort83207782023-01-24 17:39:16 +0000235 }
236
Mark Salyzyn63368be2020-06-24 03:02:39 -0700237 LOG(DEBUG) << "mode is " << mode;
238 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
239 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
Will McVicker7d400f92024-03-07 11:18:41 -0800240 LOG(DEBUG) << "modules load file is: " << modules_load_file;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700241 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
Steve Muckle64a55342019-07-30 11:53:15 -0700242
243 if (modules.empty()) {
244 if (mode == ListModulesMode) {
245 // emulate toybox modprobe list with no pattern (list all)
246 modules.emplace_back("*");
Will McVicker7d400f92024-03-07 11:18:41 -0800247 } else if (modules_load_file.empty()) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700248 LOG(ERROR) << "No modules given.";
Steve Muckle64a55342019-07-30 11:53:15 -0700249 print_usage();
250 return EXIT_FAILURE;
251 }
252 }
Will McVicker7d400f92024-03-07 11:18:41 -0800253 if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700254 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
Steve Muckle64a55342019-07-30 11:53:15 -0700255 print_usage();
256 return EXIT_FAILURE;
257 }
258
Will McVicker7d400f92024-03-07 11:18:41 -0800259 Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
260 if (mode == AddModulesMode && !modules_load_file.empty()) {
261 if (!m.LoadListedModules(false)) {
262 PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
263 return EXIT_FAILURE;
264 }
265 /* Fall-through to load modules provided on the command line (if any)*/
266 }
Steve Muckle64a55342019-07-30 11:53:15 -0700267
268 for (const auto& module : modules) {
269 switch (mode) {
270 case AddModulesMode:
271 if (!m.LoadWithAliases(module, true, module_parameters)) {
Will McVickerb9dead12024-03-07 11:15:37 -0800272 if (m.IsBlocklisted(module)) continue;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700273 PLOG(ERROR) << "Failed to load module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700274 rv = EXIT_FAILURE;
275 }
276 break;
277 case RemoveModulesMode:
278 if (!m.Remove(module)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700279 PLOG(ERROR) << "Failed to remove module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700280 rv = EXIT_FAILURE;
281 }
282 break;
283 case ListModulesMode: {
284 std::vector<std::string> list = m.ListModules(module);
Mark Salyzyn63368be2020-06-24 03:02:39 -0700285 LOG(INFO) << android::base::Join(list, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700286 break;
287 }
288 case ShowDependenciesMode: {
289 std::vector<std::string> pre_deps;
290 std::vector<std::string> deps;
291 std::vector<std::string> post_deps;
292 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
293 rv = EXIT_FAILURE;
294 break;
295 }
Mark Salyzyn63368be2020-06-24 03:02:39 -0700296 LOG(INFO) << "Dependencies for " << module << ":";
297 LOG(INFO) << "Soft pre-dependencies:";
298 LOG(INFO) << android::base::Join(pre_deps, "\n");
299 LOG(INFO) << "Hard dependencies:";
300 LOG(INFO) << android::base::Join(deps, "\n");
301 LOG(INFO) << "Soft post-dependencies:";
302 LOG(INFO) << android::base::Join(post_deps, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700303 break;
304 }
305 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700306 LOG(ERROR) << "Bad mode";
Steve Muckle64a55342019-07-30 11:53:15 -0700307 rv = EXIT_FAILURE;
308 }
309 }
310
311 return rv;
312}