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 "modalias_handler.h"
18
19 #include <fnmatch.h>
20 #include <sys/syscall.h>
21
22 #include <algorithm>
23 #include <functional>
24 #include <string>
25 #include <vector>
26
27 #include <android-base/chrono_utils.h>
28 #include <android-base/logging.h>
29 #include <android-base/unique_fd.h>
30
31 #include "parser.h"
32
33 namespace android {
34 namespace init {
35
ParseDepCallback(std::vector<std::string> && args)36 Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
37 std::vector<std::string> deps;
38
39 // Set first item as our modules path
40 std::string::size_type pos = args[0].find(':');
41 if (pos != std::string::npos) {
42 deps.emplace_back(args[0].substr(0, pos));
43 } else {
44 return Error() << "dependency lines must start with name followed by ':'";
45 }
46
47 // Remaining items are dependencies of our module
48 for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
49 deps.push_back(*arg);
50 }
51
52 // Key is striped module name to match names in alias file
53 std::size_t start = args[0].find_last_of('/');
54 std::size_t end = args[0].find(".ko:");
55 if ((end - start) <= 1) return Error() << "malformed dependency line";
56 auto mod_name = args[0].substr(start + 1, (end - start) - 1);
57 // module names can have '-', but their file names will have '_'
58 std::replace(mod_name.begin(), mod_name.end(), '-', '_');
59 this->module_deps_[mod_name] = deps;
60
61 return Success();
62 }
63
ParseAliasCallback(std::vector<std::string> && args)64 Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
65 auto it = args.begin();
66 const std::string& type = *it++;
67
68 if (type != "alias") {
69 return Error() << "we only handle alias lines, got: " << type;
70 }
71
72 if (args.size() != 3) {
73 return Error() << "alias lines must have 3 entries";
74 }
75
76 std::string& alias = *it++;
77 std::string& module_name = *it++;
78 this->module_aliases_.emplace_back(alias, module_name);
79
80 return Success();
81 }
82
ModaliasHandler()83 ModaliasHandler::ModaliasHandler() {
84 using namespace std::placeholders;
85
86 static const std::string base_paths[] = {
87 "/vendor/lib/modules/",
88 "/lib/modules/",
89 "/odm/lib/modules/",
90 };
91
92 Parser alias_parser;
93 auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
94 alias_parser.AddSingleLineParser("alias", alias_callback);
95 for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
96
97 Parser dep_parser;
98 auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
99 dep_parser.AddSingleLineParser("", dep_callback);
100 for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
101 }
102
Insmod(const std::string & path_name,const std::string & args)103 Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
104 base::unique_fd fd(
105 TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
106 if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
107
108 int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
109 if (ret != 0) {
110 if (errno == EEXIST) {
111 // Module already loaded
112 return Success();
113 }
114 return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
115 }
116
117 LOG(INFO) << "Loaded kernel module " << path_name;
118 return Success();
119 }
120
InsmodWithDeps(const std::string & module_name,const std::string & args)121 Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
122 const std::string& args) {
123 if (module_name.empty()) {
124 return Error() << "Need valid module name";
125 }
126
127 auto it = module_deps_.find(module_name);
128 if (it == module_deps_.end()) {
129 return Error() << "Module '" << module_name << "' not in dependency file";
130 }
131 auto& dependencies = it->second;
132
133 // load module dependencies in reverse order
134 for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
135 if (auto result = Insmod(*dep, ""); !result) return result;
136 }
137
138 // load target module itself with args
139 return Insmod(dependencies[0], args);
140 }
141
HandleUevent(const Uevent & uevent)142 void ModaliasHandler::HandleUevent(const Uevent& uevent) {
143 if (uevent.modalias.empty()) return;
144
145 for (const auto& [alias, module] : module_aliases_) {
146 if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue; // Keep looking
147
148 LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
149 << "'";
150
151 if (auto result = InsmodWithDeps(module, ""); !result) {
152 LOG(ERROR) << "Cannot load module: " << result.error();
153 // try another one since there may be another match
154 continue;
155 }
156
157 // loading was successful
158 return;
159 }
160 }
161
162 } // namespace init
163 } // namespace android
164