• 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/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