blob: 1734a7e0fec32223a22aea6cd276be30d7a9d7a0 [file] [log] [blame]
Andrew F. Davis99638472018-07-09 13:12:00 -05001/*
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
33namespace android {
34namespace init {
35
36Result<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
64Result<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
83ModaliasHandler::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
103Result<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
121Result<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
142void ModaliasHandler::HandleModaliasEvent(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