libmodprobe: parse kernel command line for module options

Bug: 145808811
Test: atest libmodprobe_tests, verify on flame
Change-Id: I0b41b1610fe13ae526d38f029da888f6f0d8a02d
This commit is contained in:
Steve Muckle 2019-12-06 17:08:09 -08:00
parent ee08c978d2
commit 373a3cadd7
6 changed files with 116 additions and 9 deletions

View file

@ -44,6 +44,9 @@ class Modprobe {
bool Rmmod(const std::string& module_name);
std::vector<std::string> GetDependencies(const std::string& module);
bool ModuleExists(const std::string& module_name);
void AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value);
std::string GetKernelCmdline();
bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
bool ParseAliasCallback(const std::vector<std::string>& args);
@ -51,6 +54,7 @@ class Modprobe {
bool ParseLoadCallback(const std::vector<std::string>& args);
bool ParseOptionsCallback(const std::vector<std::string>& args);
bool ParseBlacklistCallback(const std::vector<std::string>& args);
void ParseKernelCmdlineOptions();
void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
std::vector<std::pair<std::string, std::string>> module_aliases_;

View file

@ -238,6 +238,80 @@ void Modprobe::ParseCfg(const std::string& cfg,
return;
}
void Modprobe::AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value) {
auto canonical_name = MakeCanonical(module_name);
auto options_iter = module_options_.find(canonical_name);
auto option_str = option_name + "=" + value;
if (options_iter != module_options_.end()) {
options_iter->second = options_iter->second + " " + option_str;
} else {
module_options_.emplace(canonical_name, option_str);
}
}
void Modprobe::ParseKernelCmdlineOptions(void) {
std::string cmdline = GetKernelCmdline();
std::string module_name = "";
std::string option_name = "";
std::string value = "";
bool in_module = true;
bool in_option = false;
bool in_value = false;
bool in_quotes = false;
int start = 0;
for (int i = 0; i < cmdline.size(); i++) {
if (cmdline[i] == '"') {
in_quotes = !in_quotes;
}
if (in_quotes) continue;
if (cmdline[i] == ' ') {
if (in_value) {
value = cmdline.substr(start, i - start);
if (!module_name.empty() && !option_name.empty()) {
AddOption(module_name, option_name, value);
}
}
module_name = "";
option_name = "";
value = "";
in_value = false;
start = i + 1;
in_module = true;
continue;
}
if (cmdline[i] == '.') {
if (in_module) {
module_name = cmdline.substr(start, i - start);
start = i + 1;
in_module = false;
}
in_option = true;
continue;
}
if (cmdline[i] == '=') {
if (in_option) {
option_name = cmdline.substr(start, i - start);
start = i + 1;
in_option = false;
}
in_value = true;
continue;
}
}
if (in_value && !in_quotes) {
value = cmdline.substr(start, cmdline.size() - start);
if (!module_name.empty() && !option_name.empty()) {
AddOption(module_name, option_name, value);
}
}
}
Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
using namespace std::placeholders;
@ -261,6 +335,7 @@ Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
}
ParseKernelCmdlineOptions();
android::base::SetMinimumLogSeverity(android::base::INFO);
}

View file

@ -17,11 +17,20 @@
#include <sys/stat.h>
#include <sys/syscall.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <modprobe/modprobe.h>
std::string Modprobe::GetKernelCmdline(void) {
std::string cmdline;
if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
return "";
}
return cmdline;
}
bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));

View file

@ -29,6 +29,10 @@
#include "libmodprobe_test.h"
std::string Modprobe::GetKernelCmdline(void) {
return kernel_cmdline;
}
bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
auto deps = GetDependencies(MakeCanonical(path_name));
if (deps.empty()) {
@ -57,7 +61,7 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter
bool Modprobe::Rmmod(const std::string& module_name) {
for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
if (*it == module_name) {
if (*it == module_name || android::base::StartsWith(*it, module_name + " ")) {
modules_loaded.erase(it);
return true;
}

View file

@ -31,7 +31,13 @@ std::vector<std::string> test_modules;
// Used by libmodprobe_ext_test to report which modules would have been loaded.
std::vector<std::string> modules_loaded;
// Used by libmodprobe_ext_test to fake a kernel commandline
std::string kernel_cmdline;
TEST(libmodprobe, Test) {
kernel_cmdline =
"flag1 flag2 test1.option1=50 test4.option3=\"set x\" test1.option2=60 "
"test8. test5.option1= test10.option1=1";
test_modules = {
"/test1.ko", "/test2.ko", "/test3.ko", "/test4.ko", "/test5.ko",
"/test6.ko", "/test7.ko", "/test8.ko", "/test9.ko", "/test10.ko",
@ -42,25 +48,33 @@ TEST(libmodprobe, Test) {
"/test14.ko",
"/test15.ko",
"/test3.ko",
"/test4.ko",
"/test1.ko",
"/test4.ko option3=\"set x\"",
"/test1.ko option1=50 option2=60",
"/test6.ko",
"/test2.ko",
"/test5.ko",
"/test5.ko option1=",
"/test8.ko",
"/test7.ko param1=4",
"/test9.ko param_x=1 param_y=2 param_z=3",
"/test10.ko",
"/test10.ko option1=1",
"/test12.ko",
"/test11.ko",
"/test13.ko",
};
std::vector<std::string> expected_after_remove = {
"/test14.ko", "/test15.ko", "/test1.ko",
"/test6.ko", "/test2.ko", "/test5.ko",
"/test8.ko", "/test7.ko param1=4", "/test9.ko param_x=1 param_y=2 param_z=3",
"/test10.ko", "/test12.ko", "/test11.ko",
"/test14.ko",
"/test15.ko",
"/test1.ko option1=50 option2=60",
"/test6.ko",
"/test2.ko",
"/test5.ko option1=",
"/test8.ko",
"/test7.ko param1=4",
"/test9.ko param_x=1 param_y=2 param_z=3",
"/test10.ko option1=1",
"/test12.ko",
"/test11.ko",
"/test13.ko",
};

View file

@ -19,5 +19,6 @@
#include <string>
#include <vector>
extern std::string kernel_cmdline;
extern std::vector<std::string> test_modules;
extern std::vector<std::string> modules_loaded;