diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index d7a90c488..7b691b13a 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -59,6 +59,7 @@ class Modprobe { bool ParseSoftdepCallback(const std::vector& args); bool ParseLoadCallback(const std::vector& args); bool ParseOptionsCallback(const std::vector& args); + bool ParseDynOptionsCallback(const std::vector& args); bool ParseBlocklistCallback(const std::vector& args); void ParseKernelCmdlineOptions(); void ParseCfg(const std::string& cfg, std::function&)> f); diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 8cc0b9b2e..bdd114c4b 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -17,8 +17,11 @@ #include #include +#include +#include #include #include +#include #include #include @@ -30,9 +33,12 @@ #include #include #include +#include #include #include +#include "exthandler/exthandler.h" + std::string Modprobe::MakeCanonical(const std::string& module_path) { auto start = module_path.find_last_of('/'); if (start == std::string::npos) { @@ -164,6 +170,10 @@ bool Modprobe::ParseOptionsCallback(const std::vector& args) { auto it = args.begin(); const std::string& type = *it++; + if (type == "dyn_options") { + return ParseDynOptionsCallback(std::vector(it, args.end())); + } + if (type != "options") { LOG(ERROR) << "non-options line encountered in modules.options"; return false; @@ -197,6 +207,57 @@ bool Modprobe::ParseOptionsCallback(const std::vector& args) { return true; } +bool Modprobe::ParseDynOptionsCallback(const std::vector& args) { + auto it = args.begin(); + int arg_size = 3; + + if (args.size() < arg_size) { + LOG(ERROR) << "dyn_options lines in modules.options must have at least" << arg_size + << " entries, not " << args.size(); + return false; + } + + const std::string& module = *it++; + + const std::string& canonical_name = MakeCanonical(module); + if (canonical_name.empty()) { + return false; + } + + const std::string& pwnam = *it++; + passwd* pwd = getpwnam(pwnam.c_str()); + if (!pwd) { + LOG(ERROR) << "invalid handler uid'" << pwnam << "'"; + return false; + } + + std::string handler_with_args = + android::base::Join(std::vector(it, args.end()), ' '); + handler_with_args.erase(std::remove(handler_with_args.begin(), handler_with_args.end(), '\"'), + handler_with_args.end()); + + LOG(DEBUG) << "Launching external module options handler: '" << handler_with_args + << " for module: " << module; + + // There is no need to set envs for external module options handler - pass + // empty map. + std::unordered_map envs_map; + auto result = RunExternalHandler(handler_with_args, pwd->pw_uid, 0, envs_map); + if (!result.ok()) { + LOG(ERROR) << "External module handler failed: " << result.error(); + return false; + } + + LOG(INFO) << "Dynamic options for module: " << module << " are '" << *result << "'"; + + auto [unused, inserted] = this->module_options_.emplace(canonical_name, *result); + if (!inserted) { + LOG(ERROR) << "multiple options lines present for module " << module; + return false; + } + return true; +} + bool Modprobe::ParseBlocklistCallback(const std::vector& args) { auto it = args.begin(); const std::string& type = *it++;