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