libmodprobe: add support for dynamic module options
Hitherto libmodprobe was supporting modules.options which allows passing extra module options. This commit extends that support and beside `options`, modules.options can now contain `dyn_options` entries. Syntax example: dyn_options snd_sof_pci system "/vendor/bin/mod_options --sof-fw" This allows to determine extra module options, with help of external program, at runtime which can be useful for unibuilds, supporting various device flavors and therefore requiring different module options. Using external program to customize modules parameters are inspired by Android's external firmware handler: https://android.googlesource.com/platform/system/core/+/master/init/README.ueventd.md#firmware-loading and the same RunExternalHandler is reused for both dynamic module handling and firmware handling. Bug: 335619610 Test: Test audio on systems using snd_sof_pci module and verify correctness of custom module parameters. Change-Id: Ibd1a942d4ea022519676435ed525cd7c3e720d31
This commit is contained in:
parent
ac474ff7a0
commit
3063d84e9c
2 changed files with 62 additions and 0 deletions
|
|
@ -59,6 +59,7 @@ class Modprobe {
|
|||
bool ParseSoftdepCallback(const std::vector<std::string>& args);
|
||||
bool ParseLoadCallback(const std::vector<std::string>& args);
|
||||
bool ParseOptionsCallback(const std::vector<std::string>& args);
|
||||
bool ParseDynOptionsCallback(const std::vector<std::string>& args);
|
||||
bool ParseBlocklistCallback(const std::vector<std::string>& args);
|
||||
void ParseKernelCmdlineOptions();
|
||||
void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
#include <modprobe/modprobe.h>
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
|
@ -30,9 +33,12 @@
|
|||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#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<std::string>& args) {
|
|||
auto it = args.begin();
|
||||
const std::string& type = *it++;
|
||||
|
||||
if (type == "dyn_options") {
|
||||
return ParseDynOptionsCallback(std::vector<std::string>(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<std::string>& args) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Modprobe::ParseDynOptionsCallback(const std::vector<std::string>& 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<std::string>(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<std::string, std::string> 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<std::string>& args) {
|
||||
auto it = args.begin();
|
||||
const std::string& type = *it++;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue