From 373a3cadd7d0d221e1c3e1976a0fb0c6a1c34f9f Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Fri, 6 Dec 2019 17:08:09 -0800 Subject: [PATCH] libmodprobe: parse kernel command line for module options Bug: 145808811 Test: atest libmodprobe_tests, verify on flame Change-Id: I0b41b1610fe13ae526d38f029da888f6f0d8a02d --- libmodprobe/include/modprobe/modprobe.h | 4 ++ libmodprobe/libmodprobe.cpp | 75 +++++++++++++++++++++++++ libmodprobe/libmodprobe_ext.cpp | 9 +++ libmodprobe/libmodprobe_ext_test.cpp | 6 +- libmodprobe/libmodprobe_test.cpp | 30 +++++++--- libmodprobe/libmodprobe_test.h | 1 + 6 files changed, 116 insertions(+), 9 deletions(-) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 333fc5506..ee6ae7a45 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -44,6 +44,9 @@ class Modprobe { bool Rmmod(const std::string& module_name); std::vector 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& args); bool ParseAliasCallback(const std::vector& args); @@ -51,6 +54,7 @@ class Modprobe { bool ParseLoadCallback(const std::vector& args); bool ParseOptionsCallback(const std::vector& args); bool ParseBlacklistCallback(const std::vector& args); + void ParseKernelCmdlineOptions(); void ParseCfg(const std::string& cfg, std::function&)> f); std::vector> module_aliases_; diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 6b9107fd5..f22bbf18b 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -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& base_paths) { using namespace std::placeholders; @@ -261,6 +335,7 @@ Modprobe::Modprobe(const std::vector& base_paths) { ParseCfg(base_path + "/modules.blacklist", blacklist_callback); } + ParseKernelCmdlineOptions(); android::base::SetMinimumLogSeverity(android::base::INFO); } diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp index 8bebe4c9f..99472c14d 100644 --- a/libmodprobe/libmodprobe_ext.cpp +++ b/libmodprobe/libmodprobe_ext.cpp @@ -17,11 +17,20 @@ #include #include +#include #include #include #include +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))); diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp index 7d817b1f2..057dea3a0 100644 --- a/libmodprobe/libmodprobe_ext_test.cpp +++ b/libmodprobe/libmodprobe_ext_test.cpp @@ -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; } diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp index a711631da..879c7f2e9 100644 --- a/libmodprobe/libmodprobe_test.cpp +++ b/libmodprobe/libmodprobe_test.cpp @@ -31,7 +31,13 @@ std::vector test_modules; // Used by libmodprobe_ext_test to report which modules would have been loaded. std::vector 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 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", }; diff --git a/libmodprobe/libmodprobe_test.h b/libmodprobe/libmodprobe_test.h index a001b69a7..e7b949f6c 100644 --- a/libmodprobe/libmodprobe_test.h +++ b/libmodprobe/libmodprobe_test.h @@ -19,5 +19,6 @@ #include #include +extern std::string kernel_cmdline; extern std::vector test_modules; extern std::vector modules_loaded;