• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/macros.h>
27 #include <android-base/strings.h>
28 #include <android-base/stringprintf.h>
29 #include <modprobe/modprobe.h>
30 
31 #include <sys/utsname.h>
32 
33 namespace {
34 
35 enum modprobe_mode {
36     AddModulesMode,
37     RemoveModulesMode,
38     ListModulesMode,
39     ShowDependenciesMode,
40 };
41 
print_usage(void)42 void print_usage(void) {
43     LOG(INFO) << "Usage:";
44     LOG(INFO);
45     LOG(INFO) << "  modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
46     LOG(INFO) << "  modprobe [options] [-d DIR] MODULE [symbol=value]...";
47     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);
60 }
61 
62 #define check_mode()                                   \
63     if (mode != AddModulesMode) {                      \
64         LOG(ERROR) << "multiple mode flags specified"; \
65         print_usage();                                 \
66         return EXIT_FAILURE;                           \
67     }
68 
stripComments(const std::string & str)69 std::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 
80 auto syslog = false;
81 
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)82 void 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 
ModDirMatchesKernelPageSize(const char * mod_dir)90 static bool ModDirMatchesKernelPageSize(const char* mod_dir) {
91     static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;
92     unsigned int mod_pgsize_kb = 16;  // 16k default since android15-6.6
93 
94     if (mod_dir && strstr(mod_dir, "-4k") != NULL) {
95         mod_pgsize_kb = 4;
96     }
97 
98     return kernel_pgsize_kb == mod_pgsize_kb;
99 }
100 
ModDirMatchesKernelPageSizeLegacy(const char * mod_dir)101 static bool ModDirMatchesKernelPageSizeLegacy(const char* mod_dir) {
102     static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;
103     const char* mod_sfx = strrchr(mod_dir, '_');
104     unsigned int mod_pgsize_kb;
105     int mod_sfx_len;
106 
107     if (mod_sfx == NULL || sscanf(mod_sfx, "_%uk%n", &mod_pgsize_kb, &mod_sfx_len) != 1 ||
108         strlen(mod_sfx) != mod_sfx_len) {
109         mod_pgsize_kb = 4;
110     }
111 
112     return kernel_pgsize_kb == mod_pgsize_kb;
113 }
114 
115 // Find directories in format of "/lib/modules/x.y.z-*".
KernelVersionNameFilter(const dirent * de)116 static int KernelVersionNameFilter(const dirent* de) {
117     static unsigned int major, minor;
118     static std::string kernel_version;
119     utsname uts;
120 
121     if (kernel_version.empty()) {
122         if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
123             LOG(ERROR) << "Could not parse the kernel version from uname";
124             return 0;
125         }
126         kernel_version = android::base::StringPrintf("%u.%u", major, minor);
127     }
128 
129     if (android::base::StartsWith(de->d_name, kernel_version)) {
130         // Check for GKI to avoid breaking non-GKI Android devices.
131         if (UNLIKELY(strstr(de->d_name, "-android") == NULL)) {
132             // For non-GKI, just match when the major and minor versions match.
133             return 1;
134         }
135 
136         // For android15-6.6 and later, GKI adds `-4k` to the UTS release
137         // string to identify 4kb page size kernels. If there is no page size
138         // suffix, then the kernel page size is 16kb.
139         if (major > 6 || (major == 6 && minor >= 6)) {
140             return ModDirMatchesKernelPageSize(de->d_name);
141         } else {
142             return ModDirMatchesKernelPageSizeLegacy(de->d_name);
143         }
144     }
145     return 0;
146 }
147 
148 }  // anonymous namespace
149 
modprobe_main(int argc,char ** argv)150 extern "C" int modprobe_main(int argc, char** argv) {
151     android::base::InitLogging(argv, MyLogger);
152     android::base::SetMinimumLogSeverity(android::base::INFO);
153 
154     std::vector<std::string> modules;
155     std::string modules_load_file;
156     std::string module_parameters;
157     std::string mods;
158     std::vector<std::string> mod_dirs;
159     modprobe_mode mode = AddModulesMode;
160     bool blocklist = false;
161     int rv = EXIT_SUCCESS;
162 
163     int opt, fd;
164     int option_index = 0;
165     // NB: We have non-standard short options -l and -D to make it easier for
166     // OEMs to transition from toybox.
167     // clang-format off
168     static struct option long_options[] = {
169         { "all",                 optional_argument, 0, 'a' },
170         { "use-blocklist",       no_argument,       0, 'b' },
171         { "dirname",             required_argument, 0, 'd' },
172         { "show-depends",        no_argument,       0, 'D' },
173         { "help",                no_argument,       0, 'h' },
174         { "list",                no_argument,       0, 'l' },
175         { "quiet",               no_argument,       0, 'q' },
176         { "remove",              no_argument,       0, 'r' },
177         { "syslog",              no_argument,       0, 's' },
178         { "verbose",             no_argument,       0, 'v' },
179     };
180     // clang-format on
181     while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
182         switch (opt) {
183             case 'a':
184                 // toybox modprobe supported -a to load multiple modules, this
185                 // is supported here by default, ignore flag if no argument.
186                 check_mode();
187                 if (optarg == NULL) break;
188 
189                 // Since libmodprobe doesn't fail when the modules load file
190                 // doesn't exist, let's check that here so that we don't
191                 // silently fail.
192                 fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
193                 if (fd == -1) {
194                     PLOG(ERROR) << "Failed to open " << optarg;
195                     return EXIT_FAILURE;
196                 }
197                 close(fd);
198 
199                 mod_dirs.emplace_back(android::base::Dirname(optarg));
200                 modules_load_file = android::base::Basename(optarg);
201                 break;
202             case 'b':
203                 blocklist = true;
204                 break;
205             case 'd':
206                 mod_dirs.emplace_back(optarg);
207                 break;
208             case 'D':
209                 check_mode();
210                 mode = ShowDependenciesMode;
211                 break;
212             case 'h':
213                 android::base::SetMinimumLogSeverity(android::base::INFO);
214                 print_usage();
215                 return rv;
216             case 'l':
217                 check_mode();
218                 mode = ListModulesMode;
219                 break;
220             case 'q':
221                 android::base::SetMinimumLogSeverity(android::base::WARNING);
222                 break;
223             case 'r':
224                 check_mode();
225                 mode = RemoveModulesMode;
226                 break;
227             case 's':
228                 syslog = true;
229                 break;
230             case 'v':
231                 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
232                     android::base::SetMinimumLogSeverity(android::base::VERBOSE);
233                 } else {
234                     android::base::SetMinimumLogSeverity(android::base::DEBUG);
235                 }
236                 break;
237             default:
238                 LOG(ERROR) << "Unrecognized option: " << opt;
239                 print_usage();
240                 return EXIT_FAILURE;
241         }
242     }
243 
244     int parameter_count = 0;
245     for (opt = optind; opt < argc; opt++) {
246         if (!strchr(argv[opt], '=')) {
247             modules.emplace_back(argv[opt]);
248         } else {
249             parameter_count++;
250             if (module_parameters.empty()) {
251                 module_parameters = argv[opt];
252             } else {
253                 module_parameters = module_parameters + " " + argv[opt];
254             }
255         }
256     }
257 
258     if (mod_dirs.empty()) {
259         static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
260         dirent** kernel_dirs = NULL;
261 
262         int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
263         if (n == -1) {
264             PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
265             return EXIT_FAILURE;
266         } else if (n > 0) {
267             while (n--) {
268                 mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
269             }
270         }
271         free(kernel_dirs);
272 
273         if (mod_dirs.empty() || getpagesize() == 4096) {
274             // Allow modules to be directly inside /lib/modules
275             mod_dirs.emplace_back(LIB_MODULES_PREFIX);
276         }
277     }
278 
279     LOG(DEBUG) << "mode is " << mode;
280     LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
281     LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
282     LOG(DEBUG) << "modules load file is: " << modules_load_file;
283     LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
284 
285     if (modules.empty()) {
286         if (mode == ListModulesMode) {
287             // emulate toybox modprobe list with no pattern (list all)
288             modules.emplace_back("*");
289         } else if (modules_load_file.empty()) {
290             LOG(ERROR) << "No modules given.";
291             print_usage();
292             return EXIT_FAILURE;
293         }
294     }
295     if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
296         LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
297         print_usage();
298         return EXIT_FAILURE;
299     }
300 
301     Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
302     if (mode == AddModulesMode && !modules_load_file.empty()) {
303         if (!m.LoadListedModules(false)) {
304             PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
305             return EXIT_FAILURE;
306         }
307         /* Fall-through to load modules provided on the command line (if any)*/
308     }
309 
310     for (const auto& module : modules) {
311         switch (mode) {
312             case AddModulesMode:
313                 if (!m.LoadWithAliases(module, true, module_parameters)) {
314                     if (m.IsBlocklisted(module)) continue;
315                     PLOG(ERROR) << "Failed to load module " << module;
316                     rv = EXIT_FAILURE;
317                 }
318                 break;
319             case RemoveModulesMode:
320                 if (!m.Remove(module)) {
321                     PLOG(ERROR) << "Failed to remove module " << module;
322                     rv = EXIT_FAILURE;
323                 }
324                 break;
325             case ListModulesMode: {
326                 std::vector<std::string> list = m.ListModules(module);
327                 LOG(INFO) << android::base::Join(list, "\n");
328                 break;
329             }
330             case ShowDependenciesMode: {
331                 std::vector<std::string> pre_deps;
332                 std::vector<std::string> deps;
333                 std::vector<std::string> post_deps;
334                 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
335                     rv = EXIT_FAILURE;
336                     break;
337                 }
338                 LOG(INFO) << "Dependencies for " << module << ":";
339                 LOG(INFO) << "Soft pre-dependencies:";
340                 LOG(INFO) << android::base::Join(pre_deps, "\n");
341                 LOG(INFO) << "Hard dependencies:";
342                 LOG(INFO) << android::base::Join(deps, "\n");
343                 LOG(INFO) << "Soft post-dependencies:";
344                 LOG(INFO) << android::base::Join(post_deps, "\n");
345                 break;
346             }
347             default:
348                 LOG(ERROR) << "Bad mode";
349                 rv = EXIT_FAILURE;
350         }
351     }
352 
353     return rv;
354 }
355