• 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 
21 #include <string>
22 
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <android-base/strings.h>
26 #include <android-base/stringprintf.h>
27 #include <modprobe/modprobe.h>
28 
29 #include <sys/utsname.h>
30 
31 namespace {
32 
33 enum modprobe_mode {
34     AddModulesMode,
35     RemoveModulesMode,
36     ListModulesMode,
37     ShowDependenciesMode,
38 };
39 
print_usage(void)40 void print_usage(void) {
41     LOG(INFO) << "Usage:";
42     LOG(INFO);
43     LOG(INFO) << "  modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
44     LOG(INFO) << "  modprobe [options] [-d DIR] MODULE [symbol=value]...";
45     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);
58 }
59 
60 #define check_mode()                                   \
61     if (mode != AddModulesMode) {                      \
62         LOG(ERROR) << "multiple mode flags specified"; \
63         print_usage();                                 \
64         return EXIT_FAILURE;                           \
65     }
66 
stripComments(const std::string & str)67 std::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 
78 auto syslog = false;
79 
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)80 void 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 
88 // Find directories in format of "/lib/modules/x.y.z-*".
KernelVersionNameFilter(const dirent * de)89 static 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 
108 }  // anonymous namespace
109 
modprobe_main(int argc,char ** argv)110 extern "C" int modprobe_main(int argc, char** argv) {
111     android::base::InitLogging(argv, MyLogger);
112     android::base::SetMinimumLogSeverity(android::base::INFO);
113 
114     std::vector<std::string> modules;
115     std::string module_parameters;
116     std::string mods;
117     std::vector<std::string> mod_dirs;
118     modprobe_mode mode = AddModulesMode;
119     bool blocklist = false;
120     int rv = EXIT_SUCCESS;
121 
122     int opt;
123     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[] = {
128         { "all",                 optional_argument, 0, 'a' },
129         { "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' },
136         { "syslog",              no_argument,       0, 's' },
137         { "verbose",             no_argument,       0, 'v' },
138     };
139     // clang-format on
140     while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
141         switch (opt) {
142             case 'a':
143                 // toybox modprobe supported -a to load multiple modules, this
144                 // is supported here by default, ignore flag if no argument.
145                 check_mode();
146                 if (optarg == NULL) break;
147                 if (!android::base::ReadFileToString(optarg, &mods)) {
148                     PLOG(ERROR) << "Failed to open " << optarg;
149                     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                 }
157                 break;
158             case 'b':
159                 blocklist = true;
160                 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':
169                 android::base::SetMinimumLogSeverity(android::base::INFO);
170                 print_usage();
171                 return rv;
172             case 'l':
173                 check_mode();
174                 mode = ListModulesMode;
175                 break;
176             case 'q':
177                 android::base::SetMinimumLogSeverity(android::base::WARNING);
178                 break;
179             case 'r':
180                 check_mode();
181                 mode = RemoveModulesMode;
182                 break;
183             case 's':
184                 syslog = true;
185                 break;
186             case 'v':
187                 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
188                     android::base::SetMinimumLogSeverity(android::base::VERBOSE);
189                 } else {
190                     android::base::SetMinimumLogSeverity(android::base::DEBUG);
191                 }
192                 break;
193             default:
194                 LOG(ERROR) << "Unrecognized option: " << opt;
195                 print_usage();
196                 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 
214     if (mod_dirs.empty()) {
215         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);
231     }
232 
233     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, " ");
237 
238     if (modules.empty()) {
239         if (mode == ListModulesMode) {
240             // emulate toybox modprobe list with no pattern (list all)
241             modules.emplace_back("*");
242         } else {
243             LOG(ERROR) << "No modules given.";
244             print_usage();
245             return EXIT_FAILURE;
246         }
247     }
248     if (parameter_count && modules.size() > 1) {
249         LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
250         print_usage();
251         return EXIT_FAILURE;
252     }
253 
254     Modprobe m(mod_dirs, "modules.load", blocklist);
255 
256     for (const auto& module : modules) {
257         switch (mode) {
258             case AddModulesMode:
259                 if (!m.LoadWithAliases(module, true, module_parameters)) {
260                     PLOG(ERROR) << "Failed to load module " << module;
261                     rv = EXIT_FAILURE;
262                 }
263                 break;
264             case RemoveModulesMode:
265                 if (!m.Remove(module)) {
266                     PLOG(ERROR) << "Failed to remove module " << module;
267                     rv = EXIT_FAILURE;
268                 }
269                 break;
270             case ListModulesMode: {
271                 std::vector<std::string> list = m.ListModules(module);
272                 LOG(INFO) << android::base::Join(list, "\n");
273                 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                 }
283                 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");
290                 break;
291             }
292             default:
293                 LOG(ERROR) << "Bad mode";
294                 rv = EXIT_FAILURE;
295         }
296     }
297 
298     return rv;
299 }
300