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
This commit is contained in:
Steve Muckle 2019-07-30 11:58:11 -07:00
parent 73b2928b94
commit bb58b01574
5 changed files with 58 additions and 0 deletions

View file

@ -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<std::string> GetDependencies(const std::string& module);
bool ModuleExists(const std::string& module_name);

View file

@ -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;
}

View file

@ -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);

View file

@ -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()) {

View file

@ -56,6 +56,14 @@ TEST(libmodprobe, Test) {
"/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",
"/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);
}