From 4104b8803a53638100d2aa751102358117052741 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Tue, 30 Jul 2019 11:55:49 -0700 Subject: [PATCH 1/9] libmodprobe: make available in vendor A toolbox implementation of modprobe will require libmodprobe. Change-Id: I7790576b828ad8cd5fae0c51926d8da9fb540d30 --- libmodprobe/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp index a2824d124..78da46cf1 100644 --- a/libmodprobe/Android.bp +++ b/libmodprobe/Android.bp @@ -3,6 +3,7 @@ cc_library_static { cflags: [ "-Werror", ], + vendor_available: true, recovery_available: true, srcs: [ "libmodprobe.cpp", From 73b2928b9400ce679a1482fc072be9fa55d7a8cd Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Tue, 30 Jul 2019 16:03:44 -0700 Subject: [PATCH 2/9] libmodprobe: make name canonical in LoadWithAliases Make the module name canonical in LoadWithAliases so it may be used more easily from outside the class. Change-Id: I7ee496b77a2beea43a6b68daed7af42660747559 --- libmodprobe/libmodprobe.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 01cf2e354..eb6c4ab26 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -256,11 +256,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name) { // load module dependencies in reverse order for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) { - const std::string& canonical_name = MakeCanonical(*dep); - if (canonical_name.empty()) { - return false; - } - if (!LoadWithAliases(canonical_name, true)) { + if (!LoadWithAliases(*dep, true)) { return false; } } @@ -288,7 +284,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name) { } bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) { - std::set modules_to_load = {module_name}; + std::set modules_to_load = {MakeCanonical(module_name)}; bool module_loaded = false; // use aliases to expand list of modules to load (multiple modules From bb58b0157454ad4f066f098977e37d0494312bae Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Tue, 30 Jul 2019 11:58:11 -0700 Subject: [PATCH 3/9] libmodprobe: add support to remove modules Add a remove method which will unload a given module from the kernel, along with any modules it depended on, assuming those modules are now unused. Change-Id: Ie66dc153ef1771f50e26421d38d3656e95954780 --- libmodprobe/include/modprobe/modprobe.h | 2 ++ libmodprobe/libmodprobe.cpp | 15 +++++++++++++++ libmodprobe/libmodprobe_ext.cpp | 9 +++++++++ libmodprobe/libmodprobe_ext_test.cpp | 10 ++++++++++ libmodprobe/libmodprobe_test.cpp | 22 ++++++++++++++++++++++ 5 files changed, 58 insertions(+) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 0ec766aaa..5003e6b00 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -26,11 +26,13 @@ class Modprobe { bool LoadListedModules(); bool LoadWithAliases(const std::string& module_name, bool strict); + bool Remove(const std::string& module_name); private: std::string MakeCanonical(const std::string& module_path); bool InsmodWithDeps(const std::string& module_name); bool Insmod(const std::string& path_name); + bool Rmmod(const std::string& module_name); std::vector GetDependencies(const std::string& module); bool ModuleExists(const std::string& module_name); diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index eb6c4ab26..4df458ffe 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -315,3 +315,18 @@ bool Modprobe::LoadListedModules() { } return true; } + +bool Modprobe::Remove(const std::string& module_name) { + auto dependencies = GetDependencies(MakeCanonical(module_name)); + if (dependencies.empty()) { + LOG(ERROR) << "Empty dependencies for module " << module_name; + return false; + } + if (!Rmmod(dependencies[0])) { + return false; + } + for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) { + Rmmod(*dep); + } + return true; +} diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp index 5f3a04da8..8cbd08c5a 100644 --- a/libmodprobe/libmodprobe_ext.cpp +++ b/libmodprobe/libmodprobe_ext.cpp @@ -51,6 +51,15 @@ bool Modprobe::Insmod(const std::string& path_name) { return true; } +bool Modprobe::Rmmod(const std::string& module_name) { + int ret = syscall(__NR_delete_module, MakeCanonical(module_name).c_str(), O_NONBLOCK); + if (ret != 0) { + PLOG(ERROR) << "Failed to remove module '" << module_name << "'"; + return false; + } + return true; +} + bool Modprobe::ModuleExists(const std::string& module_name) { struct stat fileStat; auto deps = GetDependencies(module_name); diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp index 0f073cb5c..ca1c081c8 100644 --- a/libmodprobe/libmodprobe_ext_test.cpp +++ b/libmodprobe/libmodprobe_ext_test.cpp @@ -51,6 +51,16 @@ bool Modprobe::Insmod(const std::string& path_name) { return true; } +bool Modprobe::Rmmod(const std::string& module_name) { + for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) { + if (*it == module_name) { + modules_loaded.erase(it); + return true; + } + } + return false; +} + bool Modprobe::ModuleExists(const std::string& module_name) { auto deps = GetDependencies(module_name); if (deps.empty()) { diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp index 481658d14..d5a7d6f15 100644 --- a/libmodprobe/libmodprobe_test.cpp +++ b/libmodprobe/libmodprobe_test.cpp @@ -56,6 +56,14 @@ TEST(libmodprobe, Test) { "/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", + "/test13.ko", + }; + const std::string modules_dep = "test1.ko:\n" "test2.ko:\n" @@ -131,4 +139,18 @@ TEST(libmodprobe, Test) { } EXPECT_TRUE(modules_loaded == expected_modules_loaded); + + EXPECT_TRUE(m.Remove("test4")); + + GTEST_LOG_(INFO) << "Expected modules loaded after removing test4 (in order):"; + for (auto i = expected_after_remove.begin(); i != expected_after_remove.end(); ++i) { + *i = dir.path + *i; + GTEST_LOG_(INFO) << "\"" << *i << "\""; + } + GTEST_LOG_(INFO) << "Actual modules loaded after removing test4 (in order):"; + for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) { + GTEST_LOG_(INFO) << "\"" << *i << "\""; + } + + EXPECT_TRUE(modules_loaded == expected_after_remove); } From 13700a69d382d7b4482f63317b6eaf2e3214e012 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 31 Jul 2019 09:59:48 -0700 Subject: [PATCH 4/9] libmodprobe: support parameters in LoadWithAliases Add support to specify module parameters in LoadWithAliases. These parameters will be appended to any which are specified for the module in modules.options. Change-Id: I9aff1656ea397826f815b658b3b52c1892748601 --- libmodprobe/include/modprobe/modprobe.h | 7 ++++--- libmodprobe/libmodprobe.cpp | 9 +++++---- libmodprobe/libmodprobe_ext.cpp | 5 ++++- libmodprobe/libmodprobe_ext_test.cpp | 6 +++++- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 5003e6b00..05136bda4 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -25,13 +25,14 @@ class Modprobe { Modprobe(const std::vector&); bool LoadListedModules(); - bool LoadWithAliases(const std::string& module_name, bool strict); + bool LoadWithAliases(const std::string& module_name, bool strict, + const std::string& parameters = ""); bool Remove(const std::string& module_name); private: std::string MakeCanonical(const std::string& module_path); - bool InsmodWithDeps(const std::string& module_name); - bool Insmod(const std::string& path_name); + bool InsmodWithDeps(const std::string& module_name, const std::string& parameters); + bool Insmod(const std::string& path_name, const std::string& parameters); bool Rmmod(const std::string& module_name); std::vector GetDependencies(const std::string& module); bool ModuleExists(const std::string& module_name); diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 4df458ffe..37ac32851 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -242,7 +242,7 @@ std::vector Modprobe::GetDependencies(const std::string& module) { return it->second; } -bool Modprobe::InsmodWithDeps(const std::string& module_name) { +bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& parameters) { if (module_name.empty()) { LOG(ERROR) << "Need valid module name, given: " << module_name; return false; @@ -269,7 +269,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name) { } // load target module itself with args - if (!Insmod(dependencies[0])) { + if (!Insmod(dependencies[0], parameters)) { return false; } @@ -283,7 +283,8 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name) { return true; } -bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) { +bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict, + const std::string& parameters) { std::set modules_to_load = {MakeCanonical(module_name)}; bool module_loaded = false; @@ -297,7 +298,7 @@ bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) { // attempt to load all modules aliased to this name for (const auto& module : modules_to_load) { if (!ModuleExists(module)) continue; - if (InsmodWithDeps(module)) module_loaded = true; + if (InsmodWithDeps(module, parameters)) module_loaded = true; } if (strict && !module_loaded) { diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp index 8cbd08c5a..52e308fab 100644 --- a/libmodprobe/libmodprobe_ext.cpp +++ b/libmodprobe/libmodprobe_ext.cpp @@ -22,7 +22,7 @@ #include -bool Modprobe::Insmod(const std::string& path_name) { +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))); if (fd == -1) { @@ -35,6 +35,9 @@ bool Modprobe::Insmod(const std::string& path_name) { if (options_iter != module_options_.end()) { options = options_iter->second; } + if (!parameters.empty()) { + options = options + " " + parameters; + } LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\""; int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0); diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp index ca1c081c8..204809fa5 100644 --- a/libmodprobe/libmodprobe_ext_test.cpp +++ b/libmodprobe/libmodprobe_ext_test.cpp @@ -29,7 +29,7 @@ #include "libmodprobe_test.h" -bool Modprobe::Insmod(const std::string& path_name) { +bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) { auto deps = GetDependencies(MakeCanonical(path_name)); if (deps.empty()) { return false; @@ -47,6 +47,10 @@ bool Modprobe::Insmod(const std::string& path_name) { if (options_iter != module_options_.end()) { options = " " + options_iter->second; } + if (!parameters.empty()) { + options = options + " " + parameters; + } + modules_loaded.emplace_back(path_name + options); return true; } From e31f840a0a081cca2ab2a418c9611a01e3b54a98 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 31 Jul 2019 14:34:52 -0700 Subject: [PATCH 5/9] libmodprobe: add support for a blacklist If the blacklist is enabled, blacklisted modules are treated as though they are not present. Change-Id: Ie8712f24298e78f92d5028b1ca3a8a3e07a9190a --- libmodprobe/include/modprobe/modprobe.h | 5 ++++ libmodprobe/libmodprobe.cpp | 32 +++++++++++++++++++++++++ libmodprobe/libmodprobe_ext.cpp | 3 +++ libmodprobe/libmodprobe_ext_test.cpp | 3 +++ libmodprobe/libmodprobe_test.cpp | 30 +++++++++++++++-------- 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 05136bda4..03fb0a3ac 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -28,6 +29,7 @@ class Modprobe { bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); bool Remove(const std::string& module_name); + void EnableBlacklist(bool enable); private: std::string MakeCanonical(const std::string& module_path); @@ -42,6 +44,7 @@ class Modprobe { bool ParseSoftdepCallback(const std::vector& args); bool ParseLoadCallback(const std::vector& args); bool ParseOptionsCallback(const std::vector& args); + bool ParseBlacklistCallback(const std::vector& args); void ParseCfg(const std::string& cfg, std::function&)> f); std::vector> module_aliases_; @@ -50,4 +53,6 @@ class Modprobe { std::vector> module_post_softdep_; std::vector module_load_; std::unordered_map module_options_; + std::set module_blacklist_; + bool blacklist_enabled = false; }; diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 37ac32851..354ec7bce 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -194,6 +194,31 @@ bool Modprobe::ParseOptionsCallback(const std::vector& args) { return true; } +bool Modprobe::ParseBlacklistCallback(const std::vector& args) { + auto it = args.begin(); + const std::string& type = *it++; + + if (type != "blacklist") { + LOG(ERROR) << "non-blacklist line encountered in modules.blacklist"; + return false; + } + + if (args.size() != 2) { + LOG(ERROR) << "lines in modules.blacklist must have exactly 2 entries, not " << args.size(); + return false; + } + + const std::string& module = *it++; + + const std::string& canonical_name = MakeCanonical(module); + if (canonical_name.empty()) { + return false; + } + this->module_blacklist_.emplace(canonical_name); + + return true; +} + void Modprobe::ParseCfg(const std::string& cfg, std::function&)> f) { std::string cfg_contents; @@ -231,9 +256,16 @@ Modprobe::Modprobe(const std::vector& base_paths) { auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1); ParseCfg(base_path + "/modules.options", options_callback); + + auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1); + ParseCfg(base_path + "/modules.blacklist", blacklist_callback); } } +void Modprobe::EnableBlacklist(bool enable) { + blacklist_enabled = enable; +} + std::vector Modprobe::GetDependencies(const std::string& module) { auto it = module_deps_.find(module); if (it == module_deps_.end()) { diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp index 52e308fab..3e9ff70cb 100644 --- a/libmodprobe/libmodprobe_ext.cpp +++ b/libmodprobe/libmodprobe_ext.cpp @@ -65,6 +65,9 @@ bool Modprobe::Rmmod(const std::string& module_name) { bool Modprobe::ModuleExists(const std::string& module_name) { struct stat fileStat; + if (blacklist_enabled && module_blacklist_.count(module_name)) { + return false; + } auto deps = GetDependencies(module_name); if (deps.empty()) { // missing deps can happen in the case of an alias diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp index 204809fa5..7d817b1f2 100644 --- a/libmodprobe/libmodprobe_ext_test.cpp +++ b/libmodprobe/libmodprobe_ext_test.cpp @@ -67,6 +67,9 @@ bool Modprobe::Rmmod(const std::string& module_name) { bool Modprobe::ModuleExists(const std::string& module_name) { auto deps = GetDependencies(module_name); + if (blacklist_enabled && module_blacklist_.count(module_name)) { + return false; + } if (deps.empty()) { // missing deps can happen in the case of an alias return false; diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp index d5a7d6f15..a711631da 100644 --- a/libmodprobe/libmodprobe_test.cpp +++ b/libmodprobe/libmodprobe_test.cpp @@ -99,6 +99,10 @@ TEST(libmodprobe, Test) { "options test9.ko param_x=1 param_y=2 param_z=3\n" "options test100.ko param_1=1\n"; + const std::string modules_blacklist = + "blacklist test9.ko\n" + "blacklist test3.ko\n"; + const std::string modules_load = "test4.ko\n" "test1.ko\n" @@ -109,17 +113,20 @@ TEST(libmodprobe, Test) { "test11.ko\n"; TemporaryDir dir; - ASSERT_TRUE(android::base::WriteStringToFile( - modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid())); + auto dir_path = std::string(dir.path); + ASSERT_TRUE(android::base::WriteStringToFile(modules_alias, dir_path + "/modules.alias", 0600, + getuid(), getgid())); - ASSERT_TRUE(android::base::WriteStringToFile( - modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid())); - ASSERT_TRUE(android::base::WriteStringToFile( - modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid())); - ASSERT_TRUE(android::base::WriteStringToFile( - modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid())); - ASSERT_TRUE(android::base::WriteStringToFile( - modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid())); + ASSERT_TRUE(android::base::WriteStringToFile(modules_dep, dir_path + "/modules.dep", 0600, + getuid(), getgid())); + ASSERT_TRUE(android::base::WriteStringToFile(modules_softdep, dir_path + "/modules.softdep", + 0600, getuid(), getgid())); + ASSERT_TRUE(android::base::WriteStringToFile(modules_options, dir_path + "/modules.options", + 0600, getuid(), getgid())); + ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + "/modules.load", 0600, + getuid(), getgid())); + ASSERT_TRUE(android::base::WriteStringToFile(modules_blacklist, dir_path + "/modules.blacklist", + 0600, getuid(), getgid())); for (auto i = test_modules.begin(); i != test_modules.end(); ++i) { *i = dir.path + *i; @@ -153,4 +160,7 @@ TEST(libmodprobe, Test) { } EXPECT_TRUE(modules_loaded == expected_after_remove); + + m.EnableBlacklist(true); + EXPECT_FALSE(m.LoadWithAliases("test4", true)); } From 012cfa19b0a3105089ba932f32b286e4129fe79a Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 31 Jul 2019 15:55:00 -0700 Subject: [PATCH 6/9] libmodprobe: add support to list modules List the known modules with a name matching a given pattern. Change-Id: I7f6bd1f09a688c66682f94c5837e61d7dc61c1f7 --- libmodprobe/include/modprobe/modprobe.h | 1 + libmodprobe/libmodprobe.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 03fb0a3ac..913a7870b 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -29,6 +29,7 @@ class Modprobe { bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); bool Remove(const std::string& module_name); + std::vector ListModules(const std::string& pattern); void EnableBlacklist(bool enable); private: diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 354ec7bce..010624240 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -363,3 +363,16 @@ bool Modprobe::Remove(const std::string& module_name) { } return true; } + +std::vector Modprobe::ListModules(const std::string& pattern) { + std::vector rv; + for (const auto& [module, deps] : module_deps_) { + // Attempt to match both the canonical module name and the module filename. + if (!fnmatch(pattern.c_str(), module.c_str(), 0)) { + rv.emplace_back(module); + } else if (!fnmatch(pattern.c_str(), basename(deps[0].c_str()), 0)) { + rv.emplace_back(deps[0]); + } + } + return rv; +} From 781aa78ee25fca2bb27a9b724ae111f9581e63f7 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Thu, 1 Aug 2019 14:55:07 -0700 Subject: [PATCH 7/9] libmodprobe: add GetAllDependencies Add a method to retrieve the dependencies (both hard and soft) of a module. Change-Id: Ie44ceb3e36856bb1a3e68c5d3c0d55a38deb0ef9 --- libmodprobe/include/modprobe/modprobe.h | 3 +++ libmodprobe/libmodprobe.cpp | 33 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 913a7870b..0599ec538 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -30,6 +30,9 @@ class Modprobe { const std::string& parameters = ""); bool Remove(const std::string& module_name); std::vector ListModules(const std::string& pattern); + bool GetAllDependencies(const std::string& module, std::vector* pre_dependencies, + std::vector* dependencies, + std::vector* post_dependencies); void EnableBlacklist(bool enable); private: diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 010624240..3e9001a0e 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -376,3 +376,36 @@ std::vector Modprobe::ListModules(const std::string& pattern) { } return rv; } + +bool Modprobe::GetAllDependencies(const std::string& module, + std::vector* pre_dependencies, + std::vector* dependencies, + std::vector* post_dependencies) { + std::string canonical_name = MakeCanonical(module); + if (pre_dependencies) { + pre_dependencies->clear(); + for (const auto& [it_module, it_softdep] : module_pre_softdep_) { + if (canonical_name == it_module) { + pre_dependencies->emplace_back(it_softdep); + } + } + } + if (dependencies) { + dependencies->clear(); + auto hard_deps = GetDependencies(canonical_name); + if (hard_deps.empty()) { + return false; + } + for (auto dep = hard_deps.rbegin(); dep != hard_deps.rend(); dep++) { + dependencies->emplace_back(*dep); + } + } + if (post_dependencies) { + for (const auto& [it_module, it_softdep] : module_post_softdep_) { + if (canonical_name == it_module) { + post_dependencies->emplace_back(it_softdep); + } + } + } + return true; +} From ded44c06be23dc7447fb3e3f33f7d998c418657e Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Thu, 1 Aug 2019 16:03:02 -0700 Subject: [PATCH 8/9] libmodprobe: add verbose mode Change-Id: I2be18320461cd712a4828400b8f29bb5f07c801f --- libmodprobe/include/modprobe/modprobe.h | 1 + libmodprobe/libmodprobe.cpp | 16 +++++++++++++++- libmodprobe/libmodprobe_ext.cpp | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h index 0599ec538..dcb4ffbef 100644 --- a/libmodprobe/include/modprobe/modprobe.h +++ b/libmodprobe/include/modprobe/modprobe.h @@ -34,6 +34,7 @@ class Modprobe { std::vector* dependencies, std::vector* post_dependencies); void EnableBlacklist(bool enable); + void EnableVerbose(bool enable); private: std::string MakeCanonical(const std::string& module_path); diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp index 3e9001a0e..73ae15b07 100644 --- a/libmodprobe/libmodprobe.cpp +++ b/libmodprobe/libmodprobe.cpp @@ -260,12 +260,22 @@ Modprobe::Modprobe(const std::vector& base_paths) { auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1); ParseCfg(base_path + "/modules.blacklist", blacklist_callback); } + + android::base::SetMinimumLogSeverity(android::base::INFO); } void Modprobe::EnableBlacklist(bool enable) { blacklist_enabled = enable; } +void Modprobe::EnableVerbose(bool enable) { + if (enable) { + android::base::SetMinimumLogSeverity(android::base::VERBOSE); + } else { + android::base::SetMinimumLogSeverity(android::base::INFO); + } +} + std::vector Modprobe::GetDependencies(const std::string& module) { auto it = module_deps_.find(module); if (it == module_deps_.end()) { @@ -288,6 +298,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& // load module dependencies in reverse order for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) { + LOG(VERBOSE) << "Loading hard dep for '" << module_name << "': " << *dep; if (!LoadWithAliases(*dep, true)) { return false; } @@ -296,6 +307,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& // try to load soft pre-dependencies for (const auto& [module, softdep] : module_pre_softdep_) { if (module_name == module) { + LOG(VERBOSE) << "Loading soft pre-dep for '" << module << "': " << softdep; LoadWithAliases(softdep, false); } } @@ -308,6 +320,7 @@ bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& // try to load soft post-dependencies for (const auto& [module, softdep] : module_post_softdep_) { if (module_name == module) { + LOG(VERBOSE) << "Loading soft post-dep for '" << module << "': " << softdep; LoadWithAliases(softdep, false); } } @@ -324,6 +337,7 @@ bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict, // may alias themselves to the requested name) for (const auto& [alias, aliased_module] : module_aliases_) { if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue; + LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module; modules_to_load.emplace(aliased_module); } @@ -334,7 +348,7 @@ bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict, } if (strict && !module_loaded) { - LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name; + LOG(ERROR) << "LoadWithAliases was unable to load " << module_name; return false; } return true; diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp index 3e9ff70cb..2efcac290 100644 --- a/libmodprobe/libmodprobe_ext.cpp +++ b/libmodprobe/libmodprobe_ext.cpp @@ -66,6 +66,7 @@ bool Modprobe::Rmmod(const std::string& module_name) { bool Modprobe::ModuleExists(const std::string& module_name) { struct stat fileStat; if (blacklist_enabled && module_blacklist_.count(module_name)) { + LOG(INFO) << "module " << module_name << " is blacklisted"; return false; } auto deps = GetDependencies(module_name); @@ -74,9 +75,11 @@ bool Modprobe::ModuleExists(const std::string& module_name) { return false; } if (stat(deps.front().c_str(), &fileStat)) { + LOG(INFO) << "module " << module_name << " does not exist"; return false; } if (!S_ISREG(fileStat.st_mode)) { + LOG(INFO) << "module " << module_name << " is not a regular file"; return false; } return true; From 64a553451a5890800978cad84f75d85e185a6355 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Tue, 30 Jul 2019 11:53:15 -0700 Subject: [PATCH 9/9] toolbox: add modprobe Add an implementation of modprobe based on libmodprobe. Change-Id: I85ba440766406fe6ca7e90bec204d06632785a66 --- toolbox/Android.bp | 7 +- toolbox/modprobe.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++ toolbox/tools.h | 1 + 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 toolbox/modprobe.cpp 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)