blob: 17d4e319b6b553f3439d48f241cca6ccc0040640 [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 McVickere067e962023-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;
115 std::string module_parameters;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700116 std::string mods;
Steve Muckle64a55342019-07-30 11:53:15 -0700117 std::vector<std::string> mod_dirs;
118 modprobe_mode mode = AddModulesMode;
Mark Salyzyn703fb742020-06-15 11:51:59 -0700119 bool blocklist = false;
Steve Muckle64a55342019-07-30 11:53:15 -0700120 int rv = EXIT_SUCCESS;
121
122 int opt;
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700123 int option_index = 0;
124 // NB: We have non-standard short options -l and -D to make it easier for
125 // OEMs to transition from toybox.
126 // clang-format off
127 static struct option long_options[] = {
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700128 { "all", optional_argument, 0, 'a' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700129 { "use-blocklist", no_argument, 0, 'b' },
130 { "dirname", required_argument, 0, 'd' },
131 { "show-depends", no_argument, 0, 'D' },
132 { "help", no_argument, 0, 'h' },
133 { "list", no_argument, 0, 'l' },
134 { "quiet", no_argument, 0, 'q' },
135 { "remove", no_argument, 0, 'r' },
Mark Salyzyn63368be2020-06-24 03:02:39 -0700136 { "syslog", no_argument, 0, 's' },
Mark Salyzyn3ad274b2020-06-22 08:49:14 -0700137 { "verbose", no_argument, 0, 'v' },
138 };
139 // clang-format on
Mark Salyzyn63368be2020-06-24 03:02:39 -0700140 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
Steve Muckle64a55342019-07-30 11:53:15 -0700141 switch (opt) {
142 case 'a':
143 // toybox modprobe supported -a to load multiple modules, this
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700144 // is supported here by default, ignore flag if no argument.
Steve Muckle64a55342019-07-30 11:53:15 -0700145 check_mode();
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700146 if (optarg == NULL) break;
147 if (!android::base::ReadFileToString(optarg, &mods)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700148 PLOG(ERROR) << "Failed to open " << optarg;
Mark Salyzyn6c9a1e42020-06-22 08:49:14 -0700149 rv = EXIT_FAILURE;
150 }
151 for (auto mod : android::base::Split(stripComments(mods), "\n")) {
152 mod = android::base::Trim(mod);
153 if (mod == "") continue;
154 if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
155 modules.emplace_back(mod);
156 }
Steve Muckle64a55342019-07-30 11:53:15 -0700157 break;
158 case 'b':
Mark Salyzyn703fb742020-06-15 11:51:59 -0700159 blocklist = true;
Steve Muckle64a55342019-07-30 11:53:15 -0700160 break;
161 case 'd':
162 mod_dirs.emplace_back(optarg);
163 break;
164 case 'D':
165 check_mode();
166 mode = ShowDependenciesMode;
167 break;
168 case 'h':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700169 android::base::SetMinimumLogSeverity(android::base::INFO);
Steve Muckle64a55342019-07-30 11:53:15 -0700170 print_usage();
Mark Salyzyn63368be2020-06-24 03:02:39 -0700171 return rv;
Steve Muckle64a55342019-07-30 11:53:15 -0700172 case 'l':
173 check_mode();
174 mode = ListModulesMode;
175 break;
176 case 'q':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700177 android::base::SetMinimumLogSeverity(android::base::WARNING);
Steve Muckle64a55342019-07-30 11:53:15 -0700178 break;
179 case 'r':
180 check_mode();
181 mode = RemoveModulesMode;
182 break;
Mark Salyzyn63368be2020-06-24 03:02:39 -0700183 case 's':
184 syslog = true;
185 break;
Steve Muckle64a55342019-07-30 11:53:15 -0700186 case 'v':
Mark Salyzyn63368be2020-06-24 03:02:39 -0700187 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
188 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
189 } else {
190 android::base::SetMinimumLogSeverity(android::base::DEBUG);
191 }
Steve Muckle64a55342019-07-30 11:53:15 -0700192 break;
193 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700194 LOG(ERROR) << "Unrecognized option: " << opt;
195 print_usage();
Steve Muckle64a55342019-07-30 11:53:15 -0700196 return EXIT_FAILURE;
197 }
198 }
199
200 int parameter_count = 0;
201 for (opt = optind; opt < argc; opt++) {
202 if (!strchr(argv[opt], '=')) {
203 modules.emplace_back(argv[opt]);
204 } else {
205 parameter_count++;
206 if (module_parameters.empty()) {
207 module_parameters = argv[opt];
208 } else {
209 module_parameters = module_parameters + " " + argv[opt];
210 }
211 }
212 }
213
Vincent Donnefort83207782023-01-24 17:39:16 +0000214 if (mod_dirs.empty()) {
Will McVickere067e962023-05-25 16:43:31 -0700215 static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
216 dirent** kernel_dirs = NULL;
217
218 int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
219 if (n == -1) {
220 PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
221 return EXIT_FAILURE;
222 } else if (n > 0) {
223 while (n--) {
224 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
225 }
226 }
227 free(kernel_dirs);
228
229 // Allow modules to be directly inside /lib/modules
230 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
Vincent Donnefort83207782023-01-24 17:39:16 +0000231 }
232
Mark Salyzyn63368be2020-06-24 03:02:39 -0700233 LOG(DEBUG) << "mode is " << mode;
234 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
235 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
236 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
Steve Muckle64a55342019-07-30 11:53:15 -0700237
238 if (modules.empty()) {
239 if (mode == ListModulesMode) {
240 // emulate toybox modprobe list with no pattern (list all)
241 modules.emplace_back("*");
242 } else {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700243 LOG(ERROR) << "No modules given.";
Steve Muckle64a55342019-07-30 11:53:15 -0700244 print_usage();
245 return EXIT_FAILURE;
246 }
247 }
Steve Muckle64a55342019-07-30 11:53:15 -0700248 if (parameter_count && modules.size() > 1) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700249 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
Steve Muckle64a55342019-07-30 11:53:15 -0700250 print_usage();
251 return EXIT_FAILURE;
252 }
253
Will McVicker87b2ef02021-03-12 11:11:37 -0800254 Modprobe m(mod_dirs, "modules.load", blocklist);
Steve Muckle64a55342019-07-30 11:53:15 -0700255
256 for (const auto& module : modules) {
257 switch (mode) {
258 case AddModulesMode:
259 if (!m.LoadWithAliases(module, true, module_parameters)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700260 PLOG(ERROR) << "Failed to load module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700261 rv = EXIT_FAILURE;
262 }
263 break;
264 case RemoveModulesMode:
265 if (!m.Remove(module)) {
Mark Salyzyn63368be2020-06-24 03:02:39 -0700266 PLOG(ERROR) << "Failed to remove module " << module;
Steve Muckle64a55342019-07-30 11:53:15 -0700267 rv = EXIT_FAILURE;
268 }
269 break;
270 case ListModulesMode: {
271 std::vector<std::string> list = m.ListModules(module);
Mark Salyzyn63368be2020-06-24 03:02:39 -0700272 LOG(INFO) << android::base::Join(list, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700273 break;
274 }
275 case ShowDependenciesMode: {
276 std::vector<std::string> pre_deps;
277 std::vector<std::string> deps;
278 std::vector<std::string> post_deps;
279 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
280 rv = EXIT_FAILURE;
281 break;
282 }
Mark Salyzyn63368be2020-06-24 03:02:39 -0700283 LOG(INFO) << "Dependencies for " << module << ":";
284 LOG(INFO) << "Soft pre-dependencies:";
285 LOG(INFO) << android::base::Join(pre_deps, "\n");
286 LOG(INFO) << "Hard dependencies:";
287 LOG(INFO) << android::base::Join(deps, "\n");
288 LOG(INFO) << "Soft post-dependencies:";
289 LOG(INFO) << android::base::Join(post_deps, "\n");
Steve Muckle64a55342019-07-30 11:53:15 -0700290 break;
291 }
292 default:
Mark Salyzyn63368be2020-06-24 03:02:39 -0700293 LOG(ERROR) << "Bad mode";
Steve Muckle64a55342019-07-30 11:53:15 -0700294 rv = EXIT_FAILURE;
295 }
296 }
297
298 return rv;
299}