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 <modprobe/modprobe.h>
27
28 namespace {
29
30 enum modprobe_mode {
31 AddModulesMode,
32 RemoveModulesMode,
33 ListModulesMode,
34 ShowDependenciesMode,
35 };
36
print_usage(void)37 void print_usage(void) {
38 LOG(INFO) << "Usage:";
39 LOG(INFO);
40 // -d option is required on Android
41 LOG(INFO) << " modprobe [options] -d DIR [--all=FILE|MODULE]...";
42 LOG(INFO) << " modprobe [options] -d DIR MODULE [symbol=value]...";
43 LOG(INFO);
44 LOG(INFO) << "Options:";
45 LOG(INFO) << " --all=FILE: FILE to acquire module names from";
46 LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
47 LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
48 LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
49 LOG(INFO) << " -h, --help: Print this help";
50 LOG(INFO) << " -l, --list: List modules matching pattern";
51 LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
52 LOG(INFO) << " -s, --syslog: print to syslog also";
53 LOG(INFO) << " -q, --quiet: disable messages";
54 LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
55 LOG(INFO);
56 }
57
58 #define check_mode() \
59 if (mode != AddModulesMode) { \
60 LOG(ERROR) << "multiple mode flags specified"; \
61 print_usage(); \
62 return EXIT_FAILURE; \
63 }
64
stripComments(const std::string & str)65 std::string stripComments(const std::string& str) {
66 for (std::string rv = str;;) {
67 auto comment = rv.find('#');
68 if (comment == std::string::npos) return rv;
69 auto end = rv.find('\n', comment);
70 if (end != std::string::npos) end = end - comment;
71 rv.erase(comment, end);
72 }
73 /* NOTREACHED */
74 }
75
76 auto syslog = false;
77
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)78 void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
79 const char* file, unsigned int line, const char* message) {
80 android::base::StdioLogger(id, severity, tag, file, line, message);
81 if (syslog && message[0]) {
82 android::base::KernelLogger(id, severity, tag, file, line, message);
83 }
84 }
85
86 } // anonymous namespace
87
modprobe_main(int argc,char ** argv)88 extern "C" int modprobe_main(int argc, char** argv) {
89 android::base::InitLogging(argv, MyLogger);
90 android::base::SetMinimumLogSeverity(android::base::INFO);
91
92 std::vector<std::string> modules;
93 std::string module_parameters;
94 std::string mods;
95 std::vector<std::string> mod_dirs;
96 modprobe_mode mode = AddModulesMode;
97 bool blocklist = false;
98 int rv = EXIT_SUCCESS;
99
100 int opt;
101 int option_index = 0;
102 // NB: We have non-standard short options -l and -D to make it easier for
103 // OEMs to transition from toybox.
104 // clang-format off
105 static struct option long_options[] = {
106 { "all", optional_argument, 0, 'a' },
107 { "use-blocklist", no_argument, 0, 'b' },
108 { "dirname", required_argument, 0, 'd' },
109 { "show-depends", no_argument, 0, 'D' },
110 { "help", no_argument, 0, 'h' },
111 { "list", no_argument, 0, 'l' },
112 { "quiet", no_argument, 0, 'q' },
113 { "remove", no_argument, 0, 'r' },
114 { "syslog", no_argument, 0, 's' },
115 { "verbose", no_argument, 0, 'v' },
116 };
117 // clang-format on
118 while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
119 switch (opt) {
120 case 'a':
121 // toybox modprobe supported -a to load multiple modules, this
122 // is supported here by default, ignore flag if no argument.
123 check_mode();
124 if (optarg == NULL) break;
125 if (!android::base::ReadFileToString(optarg, &mods)) {
126 PLOG(ERROR) << "Failed to open " << optarg;
127 rv = EXIT_FAILURE;
128 }
129 for (auto mod : android::base::Split(stripComments(mods), "\n")) {
130 mod = android::base::Trim(mod);
131 if (mod == "") continue;
132 if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
133 modules.emplace_back(mod);
134 }
135 break;
136 case 'b':
137 blocklist = true;
138 break;
139 case 'd':
140 mod_dirs.emplace_back(optarg);
141 break;
142 case 'D':
143 check_mode();
144 mode = ShowDependenciesMode;
145 break;
146 case 'h':
147 android::base::SetMinimumLogSeverity(android::base::INFO);
148 print_usage();
149 return rv;
150 case 'l':
151 check_mode();
152 mode = ListModulesMode;
153 break;
154 case 'q':
155 android::base::SetMinimumLogSeverity(android::base::WARNING);
156 break;
157 case 'r':
158 check_mode();
159 mode = RemoveModulesMode;
160 break;
161 case 's':
162 syslog = true;
163 break;
164 case 'v':
165 if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
166 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
167 } else {
168 android::base::SetMinimumLogSeverity(android::base::DEBUG);
169 }
170 break;
171 default:
172 LOG(ERROR) << "Unrecognized option: " << opt;
173 print_usage();
174 return EXIT_FAILURE;
175 }
176 }
177
178 int parameter_count = 0;
179 for (opt = optind; opt < argc; opt++) {
180 if (!strchr(argv[opt], '=')) {
181 modules.emplace_back(argv[opt]);
182 } else {
183 parameter_count++;
184 if (module_parameters.empty()) {
185 module_parameters = argv[opt];
186 } else {
187 module_parameters = module_parameters + " " + argv[opt];
188 }
189 }
190 }
191
192 LOG(DEBUG) << "mode is " << mode;
193 LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
194 LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
195 LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
196
197 if (modules.empty()) {
198 if (mode == ListModulesMode) {
199 // emulate toybox modprobe list with no pattern (list all)
200 modules.emplace_back("*");
201 } else {
202 LOG(ERROR) << "No modules given.";
203 print_usage();
204 return EXIT_FAILURE;
205 }
206 }
207 if (mod_dirs.empty()) {
208 LOG(ERROR) << "No module configuration directories given.";
209 print_usage();
210 return EXIT_FAILURE;
211 }
212 if (parameter_count && modules.size() > 1) {
213 LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
214 print_usage();
215 return EXIT_FAILURE;
216 }
217
218 Modprobe m(mod_dirs, "modules.load", blocklist);
219
220 for (const auto& module : modules) {
221 switch (mode) {
222 case AddModulesMode:
223 if (!m.LoadWithAliases(module, true, module_parameters)) {
224 PLOG(ERROR) << "Failed to load module " << module;
225 rv = EXIT_FAILURE;
226 }
227 break;
228 case RemoveModulesMode:
229 if (!m.Remove(module)) {
230 PLOG(ERROR) << "Failed to remove module " << module;
231 rv = EXIT_FAILURE;
232 }
233 break;
234 case ListModulesMode: {
235 std::vector<std::string> list = m.ListModules(module);
236 LOG(INFO) << android::base::Join(list, "\n");
237 break;
238 }
239 case ShowDependenciesMode: {
240 std::vector<std::string> pre_deps;
241 std::vector<std::string> deps;
242 std::vector<std::string> post_deps;
243 if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
244 rv = EXIT_FAILURE;
245 break;
246 }
247 LOG(INFO) << "Dependencies for " << module << ":";
248 LOG(INFO) << "Soft pre-dependencies:";
249 LOG(INFO) << android::base::Join(pre_deps, "\n");
250 LOG(INFO) << "Hard dependencies:";
251 LOG(INFO) << android::base::Join(deps, "\n");
252 LOG(INFO) << "Soft post-dependencies:";
253 LOG(INFO) << android::base::Join(post_deps, "\n");
254 break;
255 }
256 default:
257 LOG(ERROR) << "Bad mode";
258 rv = EXIT_FAILURE;
259 }
260 }
261
262 return rv;
263 }
264