diff --git a/toolbox/Android.bp b/toolbox/Android.bp index 0cc603a41..4ca5f5a0e 100644 --- a/toolbox/Android.bp +++ b/toolbox/Android.bp @@ -24,6 +24,7 @@ cc_defaults { "toolbox.c", "getevent.c", "getprop.cpp", + "modprobe.cpp", "setprop.cpp", "start.cpp", ], @@ -33,11 +34,15 @@ cc_defaults { shared_libs: [ "libbase", ], - static_libs: ["libpropertyinfoparser"], + static_libs: [ + "libmodprobe", + "libpropertyinfoparser", + ], symlinks: [ "getevent", "getprop", + "modprobe", "setprop", "start", "stop", diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp new file mode 100644 index 000000000..1b5f54e8f --- /dev/null +++ b/toolbox/modprobe.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +enum modprobe_mode { + AddModulesMode, + RemoveModulesMode, + ListModulesMode, + ShowDependenciesMode, +}; + +static void print_usage(void) { + std::cerr << "Usage:" << std::endl; + std::cerr << std::endl; + std::cerr << " modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl; + std::cerr << " modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl; + std::cerr << std::endl; + std::cerr << "Options:" << std::endl; + std::cerr << " -b: Apply blacklist to module names too" << std::endl; + std::cerr << " -d: Load modules from DIR, option may be used multiple times" << std::endl; + std::cerr << " -D: Print dependencies for modules only, do not load"; + std::cerr << " -h: Print this help" << std::endl; + std::cerr << " -l: List modules matching pattern" << std::endl; + std::cerr << " -r: Remove MODULE (multiple modules may be specified)" << std::endl; + std::cerr << " -q: Quiet" << std::endl; + std::cerr << " -v: Verbose" << std::endl; + std::cerr << std::endl; +} + +#define check_mode() \ + if (mode != AddModulesMode) { \ + std::cerr << "Error, multiple mode flags specified" << std::endl; \ + print_usage(); \ + return EXIT_FAILURE; \ + } + +extern "C" int modprobe_main(int argc, char** argv) { + std::vector modules; + std::string module_parameters; + std::vector mod_dirs; + modprobe_mode mode = AddModulesMode; + bool blacklist = false; + bool verbose = false; + int rv = EXIT_SUCCESS; + + int opt; + while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) { + switch (opt) { + case 'a': + // toybox modprobe supported -a to load multiple modules, this + // is supported here by default, ignore flag + check_mode(); + break; + case 'b': + blacklist = true; + break; + case 'd': + mod_dirs.emplace_back(optarg); + break; + case 'D': + check_mode(); + mode = ShowDependenciesMode; + break; + case 'h': + print_usage(); + return EXIT_SUCCESS; + case 'l': + check_mode(); + mode = ListModulesMode; + break; + case 'q': + verbose = false; + break; + case 'r': + check_mode(); + mode = RemoveModulesMode; + break; + case 'v': + verbose = true; + break; + default: + std::cerr << "Unrecognized option: " << opt << std::endl; + return EXIT_FAILURE; + } + } + + int parameter_count = 0; + for (opt = optind; opt < argc; opt++) { + if (!strchr(argv[opt], '=')) { + modules.emplace_back(argv[opt]); + } else { + parameter_count++; + if (module_parameters.empty()) { + module_parameters = argv[opt]; + } else { + module_parameters = module_parameters + " " + argv[opt]; + } + } + } + + if (verbose) { + std::cout << "mode is " << mode << std::endl; + std::cout << "verbose is " << verbose << std::endl; + std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl; + std::cout << "modules is: " << android::base::Join(modules, "") << std::endl; + std::cout << "module parameters is: " << android::base::Join(module_parameters, "") + << std::endl; + } + + if (modules.empty()) { + if (mode == ListModulesMode) { + // emulate toybox modprobe list with no pattern (list all) + modules.emplace_back("*"); + } else { + std::cerr << "No modules given." << std::endl; + print_usage(); + return EXIT_FAILURE; + } + } + if (mod_dirs.empty()) { + std::cerr << "No module configuration directories given." << std::endl; + print_usage(); + return EXIT_FAILURE; + } + if (parameter_count && modules.size() > 1) { + std::cerr << "Only one module may be loaded when specifying module parameters." + << std::endl; + print_usage(); + return EXIT_FAILURE; + } + + Modprobe m(mod_dirs); + m.EnableVerbose(verbose); + if (blacklist) { + m.EnableBlacklist(true); + } + + for (const auto& module : modules) { + switch (mode) { + case AddModulesMode: + if (!m.LoadWithAliases(module, true, module_parameters)) { + std::cerr << "Failed to load module " << module; + rv = EXIT_FAILURE; + } + break; + case RemoveModulesMode: + if (!m.Remove(module)) { + std::cerr << "Failed to remove module " << module; + rv = EXIT_FAILURE; + } + break; + case ListModulesMode: { + std::vector list = m.ListModules(module); + std::cout << android::base::Join(list, "\n") << std::endl; + break; + } + case ShowDependenciesMode: { + std::vector pre_deps; + std::vector deps; + std::vector post_deps; + if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) { + rv = EXIT_FAILURE; + break; + } + std::cout << "Dependencies for " << module << ":" << std::endl; + std::cout << "Soft pre-dependencies:" << std::endl; + std::cout << android::base::Join(pre_deps, "\n") << std::endl; + std::cout << "Hard dependencies:" << std::endl; + std::cout << android::base::Join(deps, "\n") << std::endl; + std::cout << "Soft post-dependencies:" << std::endl; + std::cout << android::base::Join(post_deps, "\n") << std::endl; + break; + } + default: + std::cerr << "Bad mode"; + rv = EXIT_FAILURE; + } + } + + return rv; +} diff --git a/toolbox/tools.h b/toolbox/tools.h index 9a7ebd228..bb57e6778 100644 --- a/toolbox/tools.h +++ b/toolbox/tools.h @@ -1,5 +1,6 @@ TOOL(getevent) TOOL(getprop) +TOOL(modprobe) TOOL(setprop) TOOL(start) TOOL(stop)