1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <sys/stat.h>
18 #include <sys/syscall.h>
19 
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/unique_fd.h>
23 
24 #include <modprobe/modprobe.h>
25 
GetKernelCmdline(void)26 std::string Modprobe::GetKernelCmdline(void) {
27     std::string cmdline;
28     if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
29         return "";
30     }
31     return cmdline;
32 }
33 
Insmod(const std::string & path_name,const std::string & parameters)34 bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
35     android::base::unique_fd fd(
36             TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
37     if (fd == -1) {
38         PLOG(ERROR) << "Could not open module '" << path_name << "'";
39         return false;
40     }
41 
42     auto canonical_name = MakeCanonical(path_name);
43     std::string options = "";
44     auto options_iter = module_options_.find(canonical_name);
45     if (options_iter != module_options_.end()) {
46         options = options_iter->second;
47     }
48     if (!parameters.empty()) {
49         options = options + " " + parameters;
50     }
51 
52     LOG(INFO) << "Loading module " << path_name << " with args '" << options << "'";
53     int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
54     if (ret != 0) {
55         if (errno == EEXIST) {
56             // Module already loaded
57             module_loaded_.emplace(canonical_name);
58             return true;
59         }
60         PLOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
61         return false;
62     }
63 
64     LOG(INFO) << "Loaded kernel module " << path_name;
65     module_loaded_.emplace(canonical_name);
66     module_count_++;
67     return true;
68 }
69 
Rmmod(const std::string & module_name)70 bool Modprobe::Rmmod(const std::string& module_name) {
71     auto canonical_name = MakeCanonical(module_name);
72     int ret = syscall(__NR_delete_module, canonical_name.c_str(), O_NONBLOCK);
73     if (ret != 0) {
74         PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
75         return false;
76     }
77     module_loaded_.erase(canonical_name);
78     return true;
79 }
80 
ModuleExists(const std::string & module_name)81 bool Modprobe::ModuleExists(const std::string& module_name) {
82     struct stat fileStat;
83     if (blocklist_enabled && module_blocklist_.count(module_name)) {
84         LOG(INFO) << "module " << module_name << " is blocklisted";
85         return false;
86     }
87     auto deps = GetDependencies(module_name);
88     if (deps.empty()) {
89         // missing deps can happen in the case of an alias
90         return false;
91     }
92     if (stat(deps.front().c_str(), &fileStat)) {
93         LOG(INFO) << "module " << module_name << " does not exist";
94         return false;
95     }
96     if (!S_ISREG(fileStat.st_mode)) {
97         LOG(INFO) << "module " << module_name << " is not a regular file";
98         return false;
99     }
100     return true;
101 }
102