blob: 07d28d0bc9c806b8514af7e1a6a9762ca4719b20 [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001/*
Mark Salyzyn154f4602014-02-20 14:59:07 -08002 * Copyright (C) 2007-2014 The Android Open Source Project
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07003 *
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
Tom Cherrycc054c92017-04-05 17:55:46 -070017#include "devices.h"
18
Elliott Hughesf39f7f12016-08-31 14:41:51 -070019#include <dirent.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070020#include <errno.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070021#include <fcntl.h>
Daniel Leungc0c1ffe2012-07-02 11:32:30 -070022#include <fnmatch.h>
Tom Cherryfe062052017-04-24 16:59:05 -070023#include <grp.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070024#include <libgen.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070025#include <linux/netlink.h>
Tom Cherryfe062052017-04-24 16:59:05 -070026#include <pwd.h>
Olivier Baillyb93e5812010-11-17 11:47:23 -080027#include <stddef.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070028#include <stdio.h>
29#include <stdlib.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070030#include <string.h>
Elliott Hughes632e99a2016-11-12 11:44:16 -080031#include <sys/sendfile.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070032#include <sys/socket.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070033#include <sys/time.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070034#include <sys/un.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070035#include <sys/wait.h>
36#include <unistd.h>
37
Tom Cherry2e344f92017-04-04 17:53:45 -070038#include <algorithm>
James Hawkins588a2ca2016-02-18 14:52:46 -080039#include <memory>
Elliott Hughes290a2282016-11-14 17:08:47 -080040#include <thread>
James Hawkins588a2ca2016-02-18 14:52:46 -080041
Biao Ludc848562016-01-28 16:10:54 +080042#include <android-base/file.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070043#include <android-base/logging.h>
Rob Herring6de783a2016-05-06 10:06:59 -050044#include <android-base/stringprintf.h>
Tom Cherry2e344f92017-04-04 17:53:45 -070045#include <android-base/strings.h>
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +080046#include <android-base/unique_fd.h>
Vernon Tang3f582e92011-04-25 13:08:17 +100047#include <cutils/uevent.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070048#include <private/android_filesystem_config.h>
49#include <selinux/android.h>
50#include <selinux/avc.h>
51#include <selinux/label.h>
52#include <selinux/selinux.h>
Vernon Tang3f582e92011-04-25 13:08:17 +100053
Tom Cherryfe062052017-04-24 16:59:05 -070054#include "keyword_map.h"
55#include "ueventd.h"
Colin Crossb0ab94b2010-04-08 16:16:20 -070056#include "util.h"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070057
Stephen Smalleye096e362012-06-11 13:37:39 -040058extern struct selabel_handle *sehandle;
Stephen Smalleye46f9d52012-01-13 08:48:47 -050059
Sandeep Patil35403eb2017-02-08 20:27:12 -080060static android::base::unique_fd device_fd;
Colin Cross0dd7ca62010-04-13 19:25:51 -070061
Tom Cherrycc054c92017-04-05 17:55:46 -070062Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
63 : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
64 // If the first * is the last character, then we'll treat name_ as a prefix
65 // Otherwise, if a * is present, then we do a full fnmatch().
66 auto wildcard_position = name_.find('*');
67 if (wildcard_position == name_.length() - 1) {
68 prefix_ = true;
69 name_.pop_back();
70 } else if (wildcard_position != std::string::npos) {
71 wildcard_ = true;
Ting-Yuan Huang09bd41d2016-11-15 15:35:16 -080072 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070073}
74
Tom Cherrycc054c92017-04-05 17:55:46 -070075bool Permissions::Match(const std::string& path) const {
76 if (prefix_) {
77 return android::base::StartsWith(path, name_.c_str());
78 } else if (wildcard_) {
79 return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
Colin Cross43d537e2014-07-02 13:08:13 -070080 } else {
Tom Cherrycc054c92017-04-05 17:55:46 -070081 return path == name_;
Colin Cross43d537e2014-07-02 13:08:13 -070082 }
83
84 return false;
85}
86
Tom Cherrycc054c92017-04-05 17:55:46 -070087bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
88 const std::string& subsystem) const {
89 std::string path_basename = android::base::Basename(path);
90 if (name().find(subsystem) != std::string::npos) {
91 if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
92 if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
Rob Herring6de783a2016-05-06 10:06:59 -050093 }
Tom Cherrycc054c92017-04-05 17:55:46 -070094 return Match(path);
Rob Herring6de783a2016-05-06 10:06:59 -050095}
Rob Herringe5636a32016-05-06 12:28:48 -050096
Tom Cherrycc054c92017-04-05 17:55:46 -070097void SysfsPermissions::SetPermissions(const std::string& path) const {
98 std::string attribute_file = path + "/" + attribute_;
99 LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
100 << perm();
101 chown(attribute_file.c_str(), uid(), gid());
102 chmod(attribute_file.c_str(), perm());
103}
104
105// TODO: Move these to be member variables of a future devices class.
106std::vector<Permissions> dev_permissions;
107std::vector<SysfsPermissions> sysfs_permissions;
108
Tom Cherryfe062052017-04-24 16:59:05 -0700109bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
110 if (is_sysfs && args.size() != 5) {
111 *err = "/sys/ lines must have 5 entries";
112 return false;
113 }
114
115 if (!is_sysfs && args.size() != 4) {
116 *err = "/dev/ lines must have 4 entries";
117 return false;
118 }
119
120 auto it = args.begin();
121 const std::string& name = *it++;
122
123 std::string sysfs_attribute;
124 if (is_sysfs) sysfs_attribute = *it++;
125
126 // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
127 std::string& perm_string = *it++;
128 char* end_pointer = 0;
129 mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
130 if (end_pointer == nullptr || *end_pointer != '\0') {
131 *err = "invalid mode '" + perm_string + "'";
132 return false;
133 }
134
135 std::string& uid_string = *it++;
136 passwd* pwd = getpwnam(uid_string.c_str());
137 if (!pwd) {
138 *err = "invalid uid '" + uid_string + "'";
139 return false;
140 }
141 uid_t uid = pwd->pw_uid;
142
143 std::string& gid_string = *it++;
144 struct group* grp = getgrnam(gid_string.c_str());
145 if (!grp) {
146 *err = "invalid gid '" + gid_string + "'";
147 return false;
148 }
149 gid_t gid = grp->gr_gid;
150
151 if (is_sysfs) {
152 sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
153 } else {
154 dev_permissions.emplace_back(name, perm, uid, gid);
155 }
156 return true;
157}
158
159// TODO: Move this to be a member variable of a future devices class.
160static std::vector<Subsystem> subsystems;
161
162std::string Subsystem::ParseDevPath(uevent* uevent) const {
163 std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
164 ? uevent->device_name
165 : android::base::Basename(uevent->path);
166
167 return dir_name_ + "/" + devname;
168}
169
170bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
171 int line, std::string* err) {
172 if (args.size() != 2) {
173 *err = "subsystems must have exactly one name";
174 return false;
175 }
176
177 if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
178 *err = "ignoring duplicate subsystem entry";
179 return false;
180 }
181
182 subsystem_.name_ = args[1];
183
184 return true;
185}
186
187bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
188 if (args[1] == "uevent_devname") {
189 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
190 return true;
191 }
192 if (args[1] == "uevent_devpath") {
193 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
194 return true;
195 }
196
197 *err = "invalid devname '" + args[1] + "'";
198 return false;
199}
200
201bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
202 if (args[1].front() != '/') {
203 *err = "dirname '" + args[1] + " ' does not start with '/'";
204 return false;
205 }
206
207 subsystem_.dir_name_ = args[1];
208 return true;
209}
210
211bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
212 using OptionParser =
213 bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
214 static class OptionParserMap : public KeywordMap<OptionParser> {
215 private:
216 const Map& map() const override {
217 // clang-format off
218 static const Map option_parsers = {
219 {"devname", {1, 1, &SubsystemParser::ParseDevName}},
220 {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
221 };
222 // clang-format on
223 return option_parsers;
224 }
225 } parser_map;
226
227 auto parser = parser_map.FindFunction(args, err);
228
229 if (!parser) {
230 return false;
231 }
232
233 return (this->*parser)(std::move(args), err);
234}
235
236void SubsystemParser::EndSection() {
237 subsystems.emplace_back(std::move(subsystem_));
238}
239
Tom Cherrycc054c92017-04-05 17:55:46 -0700240static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
Rob Herring6de783a2016-05-06 10:06:59 -0500241 // upaths omit the "/sys" that paths in this list
242 // contain, so we prepend it...
Tom Cherrycc054c92017-04-05 17:55:46 -0700243 std::string path = "/sys" + upath;
Rob Herring6de783a2016-05-06 10:06:59 -0500244
Tom Cherrycc054c92017-04-05 17:55:46 -0700245 for (const auto& s : sysfs_permissions) {
246 if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
Rob Herringe5636a32016-05-06 12:28:48 -0500247 }
248
249 if (access(path.c_str(), F_OK) == 0) {
Dmitry Shmidt7eed4742016-07-28 13:55:39 -0700250 LOG(VERBOSE) << "restorecon_recursive: " << path;
Paul Lawrencea8d84342016-11-14 15:40:18 -0800251 restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
Rob Herringe5636a32016-05-06 12:28:48 -0500252 }
253}
254
Tom Cherrycc054c92017-04-05 17:55:46 -0700255static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
256 const std::string& path, const std::vector<std::string>& links) {
257 // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
258 for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
259 if (it->Match(path) || std::any_of(links.begin(), links.end(),
260 [it](const auto& link) { return it->Match(link); })) {
261 return {it->perm(), it->uid(), it->gid()};
Colin Cross43d537e2014-07-02 13:08:13 -0700262 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700263 }
Colin Cross44b65d02010-04-20 14:32:50 -0700264 /* Default if nothing found. */
Tom Cherrycc054c92017-04-05 17:55:46 -0700265 return {0600, 0, 0};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700266}
267
Tom Cherrye3e48212017-04-11 13:53:37 -0700268static void make_device(const std::string& path, int block, int major, int minor,
Tom Cherry2e344f92017-04-04 17:53:45 -0700269 const std::vector<std::string>& links) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700270 dev_t dev;
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500271 char *secontext = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700272
Tom Cherrycc054c92017-04-05 17:55:46 -0700273 auto [mode, uid, gid] = get_device_permissions(path, links);
274 mode |= (block ? S_IFBLK : S_IFCHR);
Kenny Rootb5982bf2012-10-16 23:07:05 -0700275
Sandeep Patilea239832017-02-03 07:51:55 -0800276 if (sehandle) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700277 std::vector<const char*> c_links;
278 for (const auto& link : links) {
279 c_links.emplace_back(link.c_str());
280 }
281 c_links.emplace_back(nullptr);
Tom Cherrye3e48212017-04-11 13:53:37 -0700282 if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
Sandeep Patilea239832017-02-03 07:51:55 -0800283 PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
284 return;
285 }
286 setfscreatecon(secontext);
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300287 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700288
Colin Cross17dcc5c2010-09-03 12:25:34 -0700289 dev = makedev(major, minor);
Nick Pelly6405c692010-01-21 18:13:39 -0800290 /* Temporarily change egid to avoid race condition setting the gid of the
291 * device node. Unforunately changing the euid would prevent creation of
292 * some device nodes, so the uid has to be set with chown() and is still
293 * racy. Fixing the gid race at least fixed the issue with system_server
294 * opening dynamic input devices under the AID_INPUT gid. */
Tom Cherry0506b182017-02-23 13:46:09 -0800295 if (setegid(gid)) {
296 PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
297 goto out;
298 }
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300299 /* If the node already exists update its SELinux label to handle cases when
300 * it was created with the wrong context during coldboot procedure. */
Tom Cherrye3e48212017-04-11 13:53:37 -0700301 if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
William Roberts397de142016-06-02 09:53:44 -0700302 char* fcon = nullptr;
Tom Cherrye3e48212017-04-11 13:53:37 -0700303 int rc = lgetfilecon(path.c_str(), &fcon);
William Roberts397de142016-06-02 09:53:44 -0700304 if (rc < 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700305 PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
William Roberts397de142016-06-02 09:53:44 -0700306 goto out;
307 }
308
309 bool different = strcmp(fcon, secontext) != 0;
310 freecon(fcon);
311
Tom Cherrye3e48212017-04-11 13:53:37 -0700312 if (different && lsetfilecon(path.c_str(), secontext)) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700313 PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300314 }
315 }
William Roberts397de142016-06-02 09:53:44 -0700316
317out:
Tom Cherrye3e48212017-04-11 13:53:37 -0700318 chown(path.c_str(), uid, -1);
Tom Cherry0506b182017-02-23 13:46:09 -0800319 if (setegid(AID_ROOT)) {
320 PLOG(FATAL) << "setegid(AID_ROOT) failed";
321 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700322
Sandeep Patilea239832017-02-03 07:51:55 -0800323 if (secontext) {
324 freecon(secontext);
325 setfscreatecon(NULL);
326 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700327}
328
Tom Cherry1ab8f552017-04-06 14:41:30 -0700329// TODO: Move this to be a member variable of a future devices class.
330std::vector<std::string> platform_devices;
Dima Zavinf395c922013-03-06 16:23:57 -0800331
Tom Cherry1ab8f552017-04-06 14:41:30 -0700332// Given a path that may start with a platform device, find the length of the
333// platform device prefix. If it doesn't start with a platform device, return false
334bool find_platform_device(const std::string& path, std::string* out_path) {
335 out_path->clear();
336 // platform_devices is searched backwards, since parents are added before their children,
337 // and we want to match as deep of a child as we can.
338 for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
339 auto platform_device_path_length = it->length();
340 if (platform_device_path_length < path.length() &&
341 path[platform_device_path_length] == '/' &&
342 android::base::StartsWith(path, it->c_str())) {
343 *out_path = *it;
344 return true;
Colin Crossfadb85e2011-03-30 18:32:12 -0700345 }
346 }
Tom Cherry1ab8f552017-04-06 14:41:30 -0700347 return false;
Sandeep Patil35403eb2017-02-08 20:27:12 -0800348}
349
Andrew Boiea885d042013-09-13 17:41:20 -0700350/* Given a path that may start with a PCI device, populate the supplied buffer
351 * with the PCI domain/bus number and the peripheral ID and return 0.
352 * If it doesn't start with a PCI device, or there is some error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700353static bool find_pci_device_prefix(const std::string& path, std::string* result) {
354 result->clear();
Andrew Boiea885d042013-09-13 17:41:20 -0700355
Tom Cherry2e344f92017-04-04 17:53:45 -0700356 if (!android::base::StartsWith(path, "/devices/pci")) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700357
358 /* Beginning of the prefix is the initial "pci" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700359 std::string::size_type start = 9;
Andrew Boiea885d042013-09-13 17:41:20 -0700360
361 /* End of the prefix is two path '/' later, capturing the domain/bus number
362 * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700363 auto end = path.find('/', start);
364 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700365
Tom Cherry2e344f92017-04-04 17:53:45 -0700366 end = path.find('/', end + 1);
367 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700368
Tom Cherry2e344f92017-04-04 17:53:45 -0700369 auto length = end - start;
370 if (length <= 4) {
371 // The minimum string that will get to this check is 'pci/', which is malformed,
372 // so return false
373 return false;
374 }
375
376 *result = path.substr(start, length);
377 return true;
Andrew Boiea885d042013-09-13 17:41:20 -0700378}
379
Jeremy Compostella937309d2017-03-03 16:27:29 +0100380/* Given a path that may start with a virtual block device, populate
381 * the supplied buffer with the virtual block device ID and return 0.
382 * If it doesn't start with a virtual block device, or there is some
383 * error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700384static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
385 result->clear();
386
387 if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100388
389 /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700390 std::string::size_type start = 13;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100391
392 /* End of the prefix is one path '/' later, capturing the
393 virtual block device ID. Example: 768 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700394 auto end = path.find('/', start);
395 if (end == std::string::npos) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100396
Tom Cherry2e344f92017-04-04 17:53:45 -0700397 auto length = end - start;
398 if (length == 0) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100399
Tom Cherry2e344f92017-04-04 17:53:45 -0700400 *result = path.substr(start, length);
401 return true;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100402}
403
Tom Cherrye3e48212017-04-11 13:53:37 -0700404void parse_event(const char* msg, uevent* uevent) {
405 uevent->partition_num = -1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700406 uevent->major = -1;
407 uevent->minor = -1;
Tom Cherrye3e48212017-04-11 13:53:37 -0700408 // currently ignoring SEQNUM
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700409 while(*msg) {
410 if(!strncmp(msg, "ACTION=", 7)) {
411 msg += 7;
412 uevent->action = msg;
413 } else if(!strncmp(msg, "DEVPATH=", 8)) {
414 msg += 8;
415 uevent->path = msg;
416 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
417 msg += 10;
418 uevent->subsystem = msg;
419 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
420 msg += 9;
421 uevent->firmware = msg;
422 } else if(!strncmp(msg, "MAJOR=", 6)) {
423 msg += 6;
424 uevent->major = atoi(msg);
425 } else if(!strncmp(msg, "MINOR=", 6)) {
426 msg += 6;
427 uevent->minor = atoi(msg);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700428 } else if(!strncmp(msg, "PARTN=", 6)) {
429 msg += 6;
430 uevent->partition_num = atoi(msg);
431 } else if(!strncmp(msg, "PARTNAME=", 9)) {
432 msg += 9;
433 uevent->partition_name = msg;
Wei Zhongf97b8872012-03-23 14:15:34 -0700434 } else if(!strncmp(msg, "DEVNAME=", 8)) {
435 msg += 8;
436 uevent->device_name = msg;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700437 }
438
Tom Cherrye3e48212017-04-11 13:53:37 -0700439 // advance to after the next \0
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700440 while(*msg++)
441 ;
442 }
443
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800444 if (LOG_UEVENTS) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700445 LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
446 << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
447 << ", " << uevent->minor << " }";
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800448 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700449}
450
Tom Cherry2e344f92017-04-04 17:53:45 -0700451std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700452 std::string parent_device;
453 if (!find_platform_device(uevent->path, &parent_device)) return {};
Benoit Gobyd2278632010-08-03 14:36:02 -0700454
Tom Cherry1ab8f552017-04-06 14:41:30 -0700455 // skip path to the parent driver
456 std::string path = uevent->path.substr(parent_device.length());
Benoit Gobyd2278632010-08-03 14:36:02 -0700457
Tom Cherry1ab8f552017-04-06 14:41:30 -0700458 if (!android::base::StartsWith(path, "/usb")) return {};
Tom Cherry2e344f92017-04-04 17:53:45 -0700459
460 // skip root hub name and device. use device interface
461 // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
462 // then extract what comes between the 3rd and 4th slash
463 // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
464
465 std::string::size_type start = 0;
Tom Cherry1ab8f552017-04-06 14:41:30 -0700466 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700467 if (start == std::string::npos) return {};
468
Tom Cherry1ab8f552017-04-06 14:41:30 -0700469 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700470 if (start == std::string::npos) return {};
471
Tom Cherry1ab8f552017-04-06 14:41:30 -0700472 auto end = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700473 if (end == std::string::npos) return {};
474
475 start++; // Skip the first '/'
476
477 auto length = end - start;
478 if (length == 0) return {};
479
Tom Cherry1ab8f552017-04-06 14:41:30 -0700480 auto name_string = path.substr(start, length);
Tom Cherry2e344f92017-04-04 17:53:45 -0700481
Tom Cherry2e344f92017-04-04 17:53:45 -0700482 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700483 links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
Tom Cherry2e344f92017-04-04 17:53:45 -0700484
485 mkdir("/dev/usb", 0755);
Benoit Gobyd2278632010-08-03 14:36:02 -0700486
487 return links;
Benoit Gobyd2278632010-08-03 14:36:02 -0700488}
489
Tom Cherryc44f6a42017-04-05 15:58:31 -0700490// replaces any unacceptable characters with '_', the
491// length of the resulting string is equal to the input string
Tom Cherry2e344f92017-04-04 17:53:45 -0700492void sanitize_partition_name(std::string* string) {
Tom Cherryc44f6a42017-04-05 15:58:31 -0700493 const char* accept =
494 "abcdefghijklmnopqrstuvwxyz"
495 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
496 "0123456789"
497 "_-.";
498
Tom Cherry2e344f92017-04-04 17:53:45 -0700499 if (!string) return;
Tom Cherryc44f6a42017-04-05 15:58:31 -0700500
Tom Cherry2e344f92017-04-04 17:53:45 -0700501 std::string::size_type pos = 0;
502 while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
503 (*string)[pos] = '_';
Tom Cherryc44f6a42017-04-05 15:58:31 -0700504 }
505}
506
Tom Cherry2e344f92017-04-04 17:53:45 -0700507std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
508 std::string device;
Tom Cherry2e344f92017-04-04 17:53:45 -0700509 std::string type;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700510
Tom Cherry1ab8f552017-04-06 14:41:30 -0700511 if (find_platform_device(uevent->path, &device)) {
512 // Skip /devices/platform or /devices/ if present
513 static const std::string devices_platform_prefix = "/devices/platform/";
514 static const std::string devices_prefix = "/devices/";
515
516 if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
517 device = device.substr(devices_platform_prefix.length());
518 } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
519 device = device.substr(devices_prefix.length());
520 }
521
Andrew Boiea885d042013-09-13 17:41:20 -0700522 type = "platform";
Tom Cherry2e344f92017-04-04 17:53:45 -0700523 } else if (find_pci_device_prefix(uevent->path, &device)) {
Andrew Boiea885d042013-09-13 17:41:20 -0700524 type = "pci";
Tom Cherry2e344f92017-04-04 17:53:45 -0700525 } else if (find_vbd_device_prefix(uevent->path, &device)) {
Jeremy Compostella937309d2017-03-03 16:27:29 +0100526 type = "vbd";
Andrew Boiea885d042013-09-13 17:41:20 -0700527 } else {
Tom Cherry2e344f92017-04-04 17:53:45 -0700528 return {};
Andrew Boiea885d042013-09-13 17:41:20 -0700529 }
Dima Zavinf395c922013-03-06 16:23:57 -0800530
Tom Cherry2e344f92017-04-04 17:53:45 -0700531 std::vector<std::string> links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700532
Sandeep Patil35403eb2017-02-08 20:27:12 -0800533 LOG(VERBOSE) << "found " << type << " device " << device;
Colin Crossfadb85e2011-03-30 18:32:12 -0700534
Tom Cherry2e344f92017-04-04 17:53:45 -0700535 auto link_path = "/dev/block/" + type + "/" + device;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700536
Tom Cherrye3e48212017-04-11 13:53:37 -0700537 if (!uevent->partition_name.empty()) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700538 std::string partition_name_sanitized(uevent->partition_name);
539 sanitize_partition_name(&partition_name_sanitized);
540 if (partition_name_sanitized != uevent->partition_name) {
541 LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
542 << partition_name_sanitized << "'";
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700543 }
Tom Cherry2e344f92017-04-04 17:53:45 -0700544 links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700545 }
546
547 if (uevent->partition_num >= 0) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700548 links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700549 }
550
Tom Cherrye3e48212017-04-11 13:53:37 -0700551 auto last_slash = uevent->path.rfind('/');
552 links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700553
554 return links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700555}
556
Tom Cherrye3e48212017-04-11 13:53:37 -0700557static void make_link_init(const std::string& oldpath, const std::string& newpath) {
558 if (mkdir_recursive(dirname(newpath.c_str()), 0755)) {
559 PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
560 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700561
Tom Cherrye3e48212017-04-11 13:53:37 -0700562 if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
563 PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
564 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700565}
566
Tom Cherrye3e48212017-04-11 13:53:37 -0700567static void remove_link(const std::string& oldpath, const std::string& newpath) {
568 std::string path;
569 if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700570}
571
Tom Cherrye3e48212017-04-11 13:53:37 -0700572static void handle_device(const std::string& action, const std::string& devpath, int block,
Tom Cherry2e344f92017-04-04 17:53:45 -0700573 int major, int minor, const std::vector<std::string>& links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700574 if (action == "add") {
575 make_device(devpath, block, major, minor, links);
Tom Cherry2e344f92017-04-04 17:53:45 -0700576 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700577 make_link_init(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700578 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700579 }
580
Tom Cherrye3e48212017-04-11 13:53:37 -0700581 if (action == "remove") {
Tom Cherry2e344f92017-04-04 17:53:45 -0700582 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700583 remove_link(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700584 }
Tom Cherrye3e48212017-04-11 13:53:37 -0700585 unlink(devpath.c_str());
Colin Crossb0ab94b2010-04-08 16:16:20 -0700586 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700587}
588
Tom Cherry1ab8f552017-04-06 14:41:30 -0700589void handle_platform_device_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700590 if (uevent->action == "add") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700591 platform_devices.emplace_back(uevent->path);
Tom Cherrye3e48212017-04-11 13:53:37 -0700592 } else if (uevent->action == "remove") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700593 auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
594 if (it != platform_devices.end()) platform_devices.erase(it);
Tom Cherrye3e48212017-04-11 13:53:37 -0700595 }
Colin Crossfadb85e2011-03-30 18:32:12 -0700596}
597
Tom Cherry3fa46732017-04-11 14:19:50 -0700598static void handle_block_device_event(uevent* uevent) {
599 // if it's not a /dev device, nothing to do
600 if (uevent->major < 0 || uevent->minor < 0) return;
Colin Crosseb5ba832011-03-30 17:37:17 -0700601
Tom Cherry3fa46732017-04-11 14:19:50 -0700602 const char* base = "/dev/block/";
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500603 make_dir(base, 0755);
Colin Crosseb5ba832011-03-30 17:37:17 -0700604
Tom Cherry3fa46732017-04-11 14:19:50 -0700605 std::string name = android::base::Basename(uevent->path);
606 std::string devpath = base + name;
607
Tom Cherry2e344f92017-04-04 17:53:45 -0700608 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700609 if (android::base::StartsWith(uevent->path, "/devices")) {
Andrew Boiea885d042013-09-13 17:41:20 -0700610 links = get_block_device_symlinks(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700611 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700612
Tom Cherrye3e48212017-04-11 13:53:37 -0700613 handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700614}
615
Tom Cherry3fa46732017-04-11 14:19:50 -0700616static void handle_generic_device_event(uevent* uevent) {
617 // if it's not a /dev device, nothing to do
618 if (uevent->major < 0 || uevent->minor < 0) return;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800619
Tom Cherry3fa46732017-04-11 14:19:50 -0700620 std::string devpath;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800621
Tom Cherryfe062052017-04-24 16:59:05 -0700622 if (android::base::StartsWith(uevent->subsystem, "usb")) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700623 if (uevent->subsystem == "usb") {
624 if (!uevent->device_name.empty()) {
625 devpath = "/dev/" + uevent->device_name;
Tom Cherry3fa46732017-04-11 14:19:50 -0700626 } else {
627 // This imitates the file system that would be created
628 // if we were using devfs instead.
629 // Minors are broken up into groups of 128, starting at "001"
630 int bus_id = uevent->minor / 128 + 1;
631 int device_id = uevent->minor % 128 + 1;
632 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
633 }
Tom Cherry3fa46732017-04-11 14:19:50 -0700634 } else {
635 // ignore other USB events
636 return;
637 }
Tom Cherryfe062052017-04-24 16:59:05 -0700638 } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
639 subsystem != subsystems.end()) {
640 devpath = subsystem->ParseDevPath(uevent);
Tom Cherry780a71e2017-04-04 16:30:40 -0700641 } else {
Tom Cherryfe062052017-04-24 16:59:05 -0700642 devpath = "/dev/" + android::base::Basename(uevent->path);
Tom Cherry780a71e2017-04-04 16:30:40 -0700643 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700644
Tom Cherryfe062052017-04-24 16:59:05 -0700645 mkdir_recursive(android::base::Dirname(devpath), 0755);
646
Tom Cherry780a71e2017-04-04 16:30:40 -0700647 auto links = get_character_device_symlinks(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700648
Tom Cherrye3e48212017-04-11 13:53:37 -0700649 handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700650}
651
652static void handle_device_event(struct uevent *uevent)
653{
Tom Cherrye3e48212017-04-11 13:53:37 -0700654 if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
Tom Cherrycc054c92017-04-05 17:55:46 -0700655 fixup_sys_permissions(uevent->path, uevent->subsystem);
Tom Cherrye3e48212017-04-11 13:53:37 -0700656 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700657
Tom Cherrye3e48212017-04-11 13:53:37 -0700658 if (uevent->subsystem == "block") {
Colin Crosseb5ba832011-03-30 17:37:17 -0700659 handle_block_device_event(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700660 } else if (uevent->subsystem == "platform") {
Colin Crossfadb85e2011-03-30 18:32:12 -0700661 handle_platform_device_event(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700662 } else {
663 handle_generic_device_event(uevent);
664 }
665}
666
Elliott Hughes632e99a2016-11-12 11:44:16 -0800667static void load_firmware(uevent* uevent, const std::string& root,
668 int fw_fd, size_t fw_size,
669 int loading_fd, int data_fd) {
670 // Start transfer.
671 android::base::WriteFully(loading_fd, "1", 1);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700672
Elliott Hughes632e99a2016-11-12 11:44:16 -0800673 // Copy the firmware.
674 int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
675 if (rc == -1) {
676 PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700677 }
678
Elliott Hughes632e99a2016-11-12 11:44:16 -0800679 // Tell the firmware whether to abort or commit.
680 const char* response = (rc != -1) ? "0" : "-1";
681 android::base::WriteFully(loading_fd, response, strlen(response));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700682}
683
Elliott Hughes632e99a2016-11-12 11:44:16 -0800684static int is_booting() {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700685 return access("/dev/.booting", F_OK) == 0;
686}
687
Elliott Hughes632e99a2016-11-12 11:44:16 -0800688static void process_firmware_event(uevent* uevent) {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700689 int booting = is_booting();
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700690
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700691 LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700692
Tom Cherrye3e48212017-04-11 13:53:37 -0700693 std::string root = "/sys" + uevent->path;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800694 std::string loading = root + "/loading";
695 std::string data = root + "/data";
696
697 android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
698 if (loading_fd == -1) {
699 PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700700 return;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800701 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700702
Elliott Hughes632e99a2016-11-12 11:44:16 -0800703 android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
704 if (data_fd == -1) {
705 PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
706 return;
707 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700708
Tom Cherrye3e48212017-04-11 13:53:37 -0700709 static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
710 "/firmware/image/"};
711
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700712try_loading_again:
Elliott Hughes632e99a2016-11-12 11:44:16 -0800713 for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700714 std::string file = firmware_dirs[i] + uevent->firmware;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800715 android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
716 struct stat sb;
717 if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
718 load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
719 return;
Benoit Goby609d8822010-11-09 18:10:24 -0800720 }
Brian Swetland02863b92010-09-19 03:36:39 -0700721 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700722
Elliott Hughes632e99a2016-11-12 11:44:16 -0800723 if (booting) {
724 // If we're not fully booted, we may be missing
725 // filesystems needed for firmware, wait and retry.
Elliott Hughes290a2282016-11-14 17:08:47 -0800726 std::this_thread::sleep_for(100ms);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800727 booting = is_booting();
728 goto try_loading_again;
729 }
730
731 LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
732
733 // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
734 write(loading_fd, "-1", 2);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700735}
736
Elliott Hughes632e99a2016-11-12 11:44:16 -0800737static void handle_firmware_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700738 if (uevent->subsystem != "firmware" || uevent->action != "add") return;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700739
Elliott Hughes632e99a2016-11-12 11:44:16 -0800740 // Loading the firmware in a child means we can do that in parallel...
741 // (We ignore SIGCHLD rather than wait for our children.)
742 pid_t pid = fork();
743 if (pid == 0) {
744 Timer t;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700745 process_firmware_event(uevent);
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000746 LOG(INFO) << "loading " << uevent->path << " took " << t;
Kenny Root17baff42014-08-20 16:16:44 -0700747 _exit(EXIT_SUCCESS);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800748 } else if (pid == -1) {
749 PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700750 }
751}
752
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800753static bool inline should_stop_coldboot(coldboot_action_t act)
754{
755 return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
756}
757
Ruchi Kandoic6037202014-06-23 11:22:09 -0700758#define UEVENT_MSG_LEN 2048
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800759
Sandeep Patil44a3ee22017-02-08 15:49:47 -0800760static inline coldboot_action_t handle_device_fd_with(
761 std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700762{
Vernon Tang3f582e92011-04-25 13:08:17 +1000763 char msg[UEVENT_MSG_LEN+2];
764 int n;
Nick Kralevich57de8b82011-05-11 14:58:24 -0700765 while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700766 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700767 continue;
768
769 msg[n] = '\0';
770 msg[n+1] = '\0';
771
Tom Cherrye3e48212017-04-11 13:53:37 -0700772 uevent uevent;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700773 parse_event(msg, &uevent);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800774 coldboot_action_t act = handle_uevent(&uevent);
775 if (should_stop_coldboot(act))
776 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700777 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800778
779 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700780}
781
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800782coldboot_action_t handle_device_fd(coldboot_callback fn)
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800783{
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800784 coldboot_action_t ret = handle_device_fd_with(
785 [&](uevent* uevent) -> coldboot_action_t {
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800786 if (selinux_status_updated() > 0) {
787 struct selabel_handle *sehandle2;
788 sehandle2 = selinux_android_file_context_handle();
789 if (sehandle2) {
790 selabel_close(sehandle);
791 sehandle = sehandle2;
792 }
793 }
794
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800795 // default is to always create the devices
796 coldboot_action_t act = COLDBOOT_CREATE;
797 if (fn) {
798 act = fn(uevent);
799 }
800
801 if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
802 handle_device_event(uevent);
803 handle_firmware_event(uevent);
804 }
805
806 return act;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800807 });
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800808
809 return ret;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800810}
811
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700812/* Coldboot walks parts of the /sys tree and pokes the uevent files
813** to cause the kernel to regenerate device add events that happened
814** before init's device manager was started
815**
816** We drain any pending events from the netlink socket every time
817** we poke another uevent file to make sure we don't overrun the
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800818** socket's buffer.
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700819*/
820
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800821static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700822{
823 struct dirent *de;
824 int dfd, fd;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800825 coldboot_action_t act = COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700826
827 dfd = dirfd(d);
828
829 fd = openat(dfd, "uevent", O_WRONLY);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800830 if (fd >= 0) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700831 write(fd, "add\n", 4);
832 close(fd);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800833 act = handle_device_fd(fn);
834 if (should_stop_coldboot(act))
835 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700836 }
837
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800838 while (!should_stop_coldboot(act) && (de = readdir(d))) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700839 DIR *d2;
840
841 if(de->d_type != DT_DIR || de->d_name[0] == '.')
842 continue;
843
844 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
845 if(fd < 0)
846 continue;
847
848 d2 = fdopendir(fd);
849 if(d2 == 0)
850 close(fd);
851 else {
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800852 act = do_coldboot(d2, fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700853 closedir(d2);
854 }
855 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800856
857 // default is always to continue looking for uevents
858 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700859}
860
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800861static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700862{
James Hawkins588a2ca2016-02-18 14:52:46 -0800863 std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800864 if (d) {
865 return do_coldboot(d.get(), fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700866 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800867
868 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700869}
870
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800871void device_init(const char* path, coldboot_callback fn) {
Sandeep Patil971a4602017-02-15 13:37:52 -0800872 if (!sehandle) {
873 sehandle = selinux_android_file_context_handle();
Elliott Hughes56a06562015-03-28 11:23:32 -0700874 }
Sandeep Patil971a4602017-02-15 13:37:52 -0800875 // open uevent socket and selinux status only if it hasn't been
876 // done before
877 if (device_fd == -1) {
878 /* is 256K enough? udev uses 16MB! */
879 device_fd.reset(uevent_open_socket(256 * 1024, true));
880 if (device_fd == -1) {
881 return;
882 }
883 fcntl(device_fd, F_SETFL, O_NONBLOCK);
884 selinux_status_open(true);
885 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700886
Elliott Hughes56a06562015-03-28 11:23:32 -0700887 if (access(COLDBOOT_DONE, F_OK) == 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700888 LOG(VERBOSE) << "Skipping coldboot, already done!";
Elliott Hughes56a06562015-03-28 11:23:32 -0700889 return;
Colin Crossf83d0b92010-04-21 12:04:20 -0700890 }
Elliott Hughes56a06562015-03-28 11:23:32 -0700891
892 Timer t;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800893 coldboot_action_t act;
894 if (!path) {
895 act = coldboot("/sys/class", fn);
896 if (!should_stop_coldboot(act)) {
897 act = coldboot("/sys/block", fn);
898 if (!should_stop_coldboot(act)) {
899 act = coldboot("/sys/devices", fn);
900 }
901 }
902 } else {
903 act = coldboot(path, fn);
904 }
905
906 // If we have a callback, then do as it says. If no, then the default is
907 // to always create COLDBOOT_DONE file.
908 if (!fn || (act == COLDBOOT_FINISH)) {
909 close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
910 }
911
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000912 LOG(INFO) << "Coldboot took " << t;
Colin Cross0dd7ca62010-04-13 19:25:51 -0700913}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700914
Sandeep Patil35403eb2017-02-08 20:27:12 -0800915void device_close() {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700916 platform_devices.clear();
Sandeep Patil35403eb2017-02-08 20:27:12 -0800917 device_fd.reset();
Sandeep Patil971a4602017-02-15 13:37:52 -0800918 selinux_status_close();
Sandeep Patil35403eb2017-02-08 20:27:12 -0800919}
920
Elliott Hughes632e99a2016-11-12 11:44:16 -0800921int get_device_fd() {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700922 return device_fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700923}