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/strings.h>
27 #include <android-base/stringprintf.h>
28 #include <modprobe/modprobe.h>
29
30 #include <sys/utsname.h>
31
32 namespace {
33
34 enum modprobe_mode {
35 AddModulesMode,
36 RemoveModulesMode,
37 ListModulesMode,
38 ShowDependenciesMode,
39 };
40
print_usage(void)41 void print_usage(void) {
42 LOG(INFO) << "Usage:";
43 LOG(INFO);
44 LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
45 LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
46 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);
59 }
60
61 #define check_mode() \
62 if (mode != AddModulesMode) { \
63 LOG(ERROR) << "multiple mode flags specified"; \
64 print_usage(); \
65 return EXIT_FAILURE; \
66 }
67
stripComments(const std::string & str)68 std::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
79 auto syslog = false;
80
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)81 void 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
ModDirMatchesKernelPageSize(const char * mod_dir)89 static 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
103 // Find directories in format of "/lib/modules/x.y.z-*".
KernelVersionNameFilter(const dirent * de)104 static 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)) {
118 return ModDirMatchesKernelPageSize(de->d_name);
119 }
120 return 0;
121 }
122
123 } // anonymous namespace
124
modprobe_main(int argc,char ** argv)125 extern "C" int modprobe_main(int argc, char** argv) {
126 android::base::InitLogging(argv, MyLogger);
127 android::base::SetMinimumLogSeverity(android::base::INFO);
128
129 std::vector<std::string> modules;
130 std::string modules_load_file;
131 std::string module_parameters;
132 std::string mods;
133 std::vector<std::string> mod_dirs;
134 modprobe_mode mode = AddModulesMode;
135 bool blocklist = false;
136 int rv = EXIT_SUCCESS;
137
138 int opt, fd;
139 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[] = {
144 { "all", optional_argument, 0, 'a' },
145 { "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' },
152 { "syslog", no_argument, 0, 's' },
153 { "verbose", no_argument, 0, 'v' },
154 };
155 // clang-format on
156 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
157 switch (opt) {
158 case 'a':
159 // toybox modprobe supported -a to load multiple modules, this
160 // is supported here by default, ignore flag if no argument.
161 check_mode();
162 if (optarg == NULL) break;
163
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) {
169 PLOG(ERROR) << "Failed to open " << optarg;
170 return EXIT_FAILURE;
171 }
172 close(fd);
173
174 mod_dirs.emplace_back(android::base::Dirname(optarg));
175 modules_load_file = android::base::Basename(optarg);
176 break;
177 case 'b':
178 blocklist = true;
179 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':
188 android::base::SetMinimumLogSeverity(android::base::INFO);
189 print_usage();
190 return rv;
191 case 'l':
192 check_mode();
193 mode = ListModulesMode;
194 break;
195 case 'q':
196 android::base::SetMinimumLogSeverity(android::base::WARNING);
197 break;
198 case 'r':
199 check_mode();
200 mode = RemoveModulesMode;
201 break;
202 case 's':
203 syslog = true;
204 break;
205 case 'v':
206 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
207 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
208 } else {
209 android::base::SetMinimumLogSeverity(android::base::DEBUG);
210 }
211 break;
212 default:
213 LOG(ERROR) << "Unrecognized option: " << opt;
214 print_usage();
215 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
233 if (mod_dirs.empty()) {
234 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
248 if (mod_dirs.empty() || getpagesize() == 4096) {
249 // Allow modules to be directly inside /lib/modules
250 mod_dirs.emplace_back(LIB_MODULES_PREFIX);
251 }
252 }
253
254 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, " ");
257 LOG(DEBUG) << "modules load file is: " << modules_load_file;
258 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
259
260 if (modules.empty()) {
261 if (mode == ListModulesMode) {
262 // emulate toybox modprobe list with no pattern (list all)
263 modules.emplace_back("*");
264 } else if (modules_load_file.empty()) {
265 LOG(ERROR) << "No modules given.";
266 print_usage();
267 return EXIT_FAILURE;
268 }
269 }
270 if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
271 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
272 print_usage();
273 return EXIT_FAILURE;
274 }
275
276 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 }
284
285 for (const auto& module : modules) {
286 switch (mode) {
287 case AddModulesMode:
288 if (!m.LoadWithAliases(module, true, module_parameters)) {
289 if (m.IsBlocklisted(module)) continue;
290 PLOG(ERROR) << "Failed to load module " << module;
291 rv = EXIT_FAILURE;
292 }
293 break;
294 case RemoveModulesMode:
295 if (!m.Remove(module)) {
296 PLOG(ERROR) << "Failed to remove module " << module;
297 rv = EXIT_FAILURE;
298 }
299 break;
300 case ListModulesMode: {
301 std::vector<std::string> list = m.ListModules(module);
302 LOG(INFO) << android::base::Join(list, "\n");
303 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 }
313 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");
320 break;
321 }
322 default:
323 LOG(ERROR) << "Bad mode";
324 rv = EXIT_FAILURE;
325 }
326 }
327
328 return rv;
329 }
330