blob: 11687f06cf57d5fefe5f261b6b2af0cfdfbed29d [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>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070050#include <selinux/label.h>
51#include <selinux/selinux.h>
Vernon Tang3f582e92011-04-25 13:08:17 +100052
Tom Cherryfe062052017-04-24 16:59:05 -070053#include "keyword_map.h"
54#include "ueventd.h"
Colin Crossb0ab94b2010-04-08 16:16:20 -070055#include "util.h"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070056
Stephen Smalleye096e362012-06-11 13:37:39 -040057extern struct selabel_handle *sehandle;
Stephen Smalleye46f9d52012-01-13 08:48:47 -050058
Sandeep Patil35403eb2017-02-08 20:27:12 -080059static android::base::unique_fd device_fd;
Colin Cross0dd7ca62010-04-13 19:25:51 -070060
Tom Cherrycc054c92017-04-05 17:55:46 -070061Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
62 : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
63 // If the first * is the last character, then we'll treat name_ as a prefix
64 // Otherwise, if a * is present, then we do a full fnmatch().
65 auto wildcard_position = name_.find('*');
66 if (wildcard_position == name_.length() - 1) {
67 prefix_ = true;
68 name_.pop_back();
69 } else if (wildcard_position != std::string::npos) {
70 wildcard_ = true;
Ting-Yuan Huang09bd41d2016-11-15 15:35:16 -080071 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070072}
73
Tom Cherrycc054c92017-04-05 17:55:46 -070074bool Permissions::Match(const std::string& path) const {
75 if (prefix_) {
76 return android::base::StartsWith(path, name_.c_str());
77 } else if (wildcard_) {
78 return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
Colin Cross43d537e2014-07-02 13:08:13 -070079 } else {
Tom Cherrycc054c92017-04-05 17:55:46 -070080 return path == name_;
Colin Cross43d537e2014-07-02 13:08:13 -070081 }
82
83 return false;
84}
85
Tom Cherrycc054c92017-04-05 17:55:46 -070086bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
87 const std::string& subsystem) const {
88 std::string path_basename = android::base::Basename(path);
89 if (name().find(subsystem) != std::string::npos) {
90 if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
91 if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
Rob Herring6de783a2016-05-06 10:06:59 -050092 }
Tom Cherrycc054c92017-04-05 17:55:46 -070093 return Match(path);
Rob Herring6de783a2016-05-06 10:06:59 -050094}
Rob Herringe5636a32016-05-06 12:28:48 -050095
Tom Cherrycc054c92017-04-05 17:55:46 -070096void SysfsPermissions::SetPermissions(const std::string& path) const {
97 std::string attribute_file = path + "/" + attribute_;
98 LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
99 << perm();
100 chown(attribute_file.c_str(), uid(), gid());
101 chmod(attribute_file.c_str(), perm());
102}
103
104// TODO: Move these to be member variables of a future devices class.
105std::vector<Permissions> dev_permissions;
106std::vector<SysfsPermissions> sysfs_permissions;
107
Tom Cherryfe062052017-04-24 16:59:05 -0700108bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
109 if (is_sysfs && args.size() != 5) {
110 *err = "/sys/ lines must have 5 entries";
111 return false;
112 }
113
114 if (!is_sysfs && args.size() != 4) {
115 *err = "/dev/ lines must have 4 entries";
116 return false;
117 }
118
119 auto it = args.begin();
120 const std::string& name = *it++;
121
122 std::string sysfs_attribute;
123 if (is_sysfs) sysfs_attribute = *it++;
124
125 // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
126 std::string& perm_string = *it++;
127 char* end_pointer = 0;
128 mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
129 if (end_pointer == nullptr || *end_pointer != '\0') {
130 *err = "invalid mode '" + perm_string + "'";
131 return false;
132 }
133
134 std::string& uid_string = *it++;
135 passwd* pwd = getpwnam(uid_string.c_str());
136 if (!pwd) {
137 *err = "invalid uid '" + uid_string + "'";
138 return false;
139 }
140 uid_t uid = pwd->pw_uid;
141
142 std::string& gid_string = *it++;
143 struct group* grp = getgrnam(gid_string.c_str());
144 if (!grp) {
145 *err = "invalid gid '" + gid_string + "'";
146 return false;
147 }
148 gid_t gid = grp->gr_gid;
149
150 if (is_sysfs) {
151 sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
152 } else {
153 dev_permissions.emplace_back(name, perm, uid, gid);
154 }
155 return true;
156}
157
158// TODO: Move this to be a member variable of a future devices class.
159static std::vector<Subsystem> subsystems;
160
161std::string Subsystem::ParseDevPath(uevent* uevent) const {
162 std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
163 ? uevent->device_name
164 : android::base::Basename(uevent->path);
165
166 return dir_name_ + "/" + devname;
167}
168
169bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
170 int line, std::string* err) {
171 if (args.size() != 2) {
172 *err = "subsystems must have exactly one name";
173 return false;
174 }
175
176 if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
177 *err = "ignoring duplicate subsystem entry";
178 return false;
179 }
180
181 subsystem_.name_ = args[1];
182
183 return true;
184}
185
186bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
187 if (args[1] == "uevent_devname") {
188 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
189 return true;
190 }
191 if (args[1] == "uevent_devpath") {
192 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
193 return true;
194 }
195
196 *err = "invalid devname '" + args[1] + "'";
197 return false;
198}
199
200bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
201 if (args[1].front() != '/') {
202 *err = "dirname '" + args[1] + " ' does not start with '/'";
203 return false;
204 }
205
206 subsystem_.dir_name_ = args[1];
207 return true;
208}
209
210bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
211 using OptionParser =
212 bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
213 static class OptionParserMap : public KeywordMap<OptionParser> {
214 private:
215 const Map& map() const override {
216 // clang-format off
217 static const Map option_parsers = {
218 {"devname", {1, 1, &SubsystemParser::ParseDevName}},
219 {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
220 };
221 // clang-format on
222 return option_parsers;
223 }
224 } parser_map;
225
226 auto parser = parser_map.FindFunction(args, err);
227
228 if (!parser) {
229 return false;
230 }
231
232 return (this->*parser)(std::move(args), err);
233}
234
235void SubsystemParser::EndSection() {
236 subsystems.emplace_back(std::move(subsystem_));
237}
238
Tom Cherrycc054c92017-04-05 17:55:46 -0700239static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
Rob Herring6de783a2016-05-06 10:06:59 -0500240 // upaths omit the "/sys" that paths in this list
241 // contain, so we prepend it...
Tom Cherrycc054c92017-04-05 17:55:46 -0700242 std::string path = "/sys" + upath;
Rob Herring6de783a2016-05-06 10:06:59 -0500243
Tom Cherrycc054c92017-04-05 17:55:46 -0700244 for (const auto& s : sysfs_permissions) {
245 if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
Rob Herringe5636a32016-05-06 12:28:48 -0500246 }
247
248 if (access(path.c_str(), F_OK) == 0) {
Dmitry Shmidt7eed4742016-07-28 13:55:39 -0700249 LOG(VERBOSE) << "restorecon_recursive: " << path;
Paul Lawrencea8d84342016-11-14 15:40:18 -0800250 restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
Rob Herringe5636a32016-05-06 12:28:48 -0500251 }
252}
253
Tom Cherrycc054c92017-04-05 17:55:46 -0700254static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
255 const std::string& path, const std::vector<std::string>& links) {
256 // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
257 for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
258 if (it->Match(path) || std::any_of(links.begin(), links.end(),
259 [it](const auto& link) { return it->Match(link); })) {
260 return {it->perm(), it->uid(), it->gid()};
Colin Cross43d537e2014-07-02 13:08:13 -0700261 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700262 }
Colin Cross44b65d02010-04-20 14:32:50 -0700263 /* Default if nothing found. */
Tom Cherrycc054c92017-04-05 17:55:46 -0700264 return {0600, 0, 0};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700265}
266
Tom Cherrye3e48212017-04-11 13:53:37 -0700267static void make_device(const std::string& path, int block, int major, int minor,
Tom Cherry2e344f92017-04-04 17:53:45 -0700268 const std::vector<std::string>& links) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700269 dev_t dev;
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500270 char *secontext = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700271
Tom Cherrycc054c92017-04-05 17:55:46 -0700272 auto [mode, uid, gid] = get_device_permissions(path, links);
273 mode |= (block ? S_IFBLK : S_IFCHR);
Kenny Rootb5982bf2012-10-16 23:07:05 -0700274
Sandeep Patilea239832017-02-03 07:51:55 -0800275 if (sehandle) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700276 std::vector<const char*> c_links;
277 for (const auto& link : links) {
278 c_links.emplace_back(link.c_str());
279 }
280 c_links.emplace_back(nullptr);
Tom Cherrye3e48212017-04-11 13:53:37 -0700281 if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
Sandeep Patilea239832017-02-03 07:51:55 -0800282 PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
283 return;
284 }
285 setfscreatecon(secontext);
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300286 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700287
Colin Cross17dcc5c2010-09-03 12:25:34 -0700288 dev = makedev(major, minor);
Nick Pelly6405c692010-01-21 18:13:39 -0800289 /* Temporarily change egid to avoid race condition setting the gid of the
290 * device node. Unforunately changing the euid would prevent creation of
291 * some device nodes, so the uid has to be set with chown() and is still
292 * racy. Fixing the gid race at least fixed the issue with system_server
293 * opening dynamic input devices under the AID_INPUT gid. */
Tom Cherry0506b182017-02-23 13:46:09 -0800294 if (setegid(gid)) {
295 PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
296 goto out;
297 }
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300298 /* If the node already exists update its SELinux label to handle cases when
299 * it was created with the wrong context during coldboot procedure. */
Tom Cherrye3e48212017-04-11 13:53:37 -0700300 if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
William Roberts397de142016-06-02 09:53:44 -0700301 char* fcon = nullptr;
Tom Cherrye3e48212017-04-11 13:53:37 -0700302 int rc = lgetfilecon(path.c_str(), &fcon);
William Roberts397de142016-06-02 09:53:44 -0700303 if (rc < 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700304 PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
William Roberts397de142016-06-02 09:53:44 -0700305 goto out;
306 }
307
308 bool different = strcmp(fcon, secontext) != 0;
309 freecon(fcon);
310
Tom Cherrye3e48212017-04-11 13:53:37 -0700311 if (different && lsetfilecon(path.c_str(), secontext)) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700312 PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300313 }
314 }
William Roberts397de142016-06-02 09:53:44 -0700315
316out:
Tom Cherrye3e48212017-04-11 13:53:37 -0700317 chown(path.c_str(), uid, -1);
Tom Cherry0506b182017-02-23 13:46:09 -0800318 if (setegid(AID_ROOT)) {
319 PLOG(FATAL) << "setegid(AID_ROOT) failed";
320 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700321
Sandeep Patilea239832017-02-03 07:51:55 -0800322 if (secontext) {
323 freecon(secontext);
324 setfscreatecon(NULL);
325 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700326}
327
Tom Cherry1ab8f552017-04-06 14:41:30 -0700328// TODO: Move this to be a member variable of a future devices class.
329std::vector<std::string> platform_devices;
Dima Zavinf395c922013-03-06 16:23:57 -0800330
Tom Cherry1ab8f552017-04-06 14:41:30 -0700331// Given a path that may start with a platform device, find the length of the
332// platform device prefix. If it doesn't start with a platform device, return false
333bool find_platform_device(const std::string& path, std::string* out_path) {
334 out_path->clear();
335 // platform_devices is searched backwards, since parents are added before their children,
336 // and we want to match as deep of a child as we can.
337 for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
338 auto platform_device_path_length = it->length();
339 if (platform_device_path_length < path.length() &&
340 path[platform_device_path_length] == '/' &&
341 android::base::StartsWith(path, it->c_str())) {
342 *out_path = *it;
343 return true;
Colin Crossfadb85e2011-03-30 18:32:12 -0700344 }
345 }
Tom Cherry1ab8f552017-04-06 14:41:30 -0700346 return false;
Sandeep Patil35403eb2017-02-08 20:27:12 -0800347}
348
Andrew Boiea885d042013-09-13 17:41:20 -0700349/* Given a path that may start with a PCI device, populate the supplied buffer
350 * with the PCI domain/bus number and the peripheral ID and return 0.
351 * If it doesn't start with a PCI device, or there is some error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700352static bool find_pci_device_prefix(const std::string& path, std::string* result) {
353 result->clear();
Andrew Boiea885d042013-09-13 17:41:20 -0700354
Tom Cherry2e344f92017-04-04 17:53:45 -0700355 if (!android::base::StartsWith(path, "/devices/pci")) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700356
357 /* Beginning of the prefix is the initial "pci" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700358 std::string::size_type start = 9;
Andrew Boiea885d042013-09-13 17:41:20 -0700359
360 /* End of the prefix is two path '/' later, capturing the domain/bus number
361 * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700362 auto end = path.find('/', start);
363 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700364
Tom Cherry2e344f92017-04-04 17:53:45 -0700365 end = path.find('/', end + 1);
366 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700367
Tom Cherry2e344f92017-04-04 17:53:45 -0700368 auto length = end - start;
369 if (length <= 4) {
370 // The minimum string that will get to this check is 'pci/', which is malformed,
371 // so return false
372 return false;
373 }
374
375 *result = path.substr(start, length);
376 return true;
Andrew Boiea885d042013-09-13 17:41:20 -0700377}
378
Jeremy Compostella937309d2017-03-03 16:27:29 +0100379/* Given a path that may start with a virtual block device, populate
380 * the supplied buffer with the virtual block device ID and return 0.
381 * If it doesn't start with a virtual block device, or there is some
382 * error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700383static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
384 result->clear();
385
386 if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100387
388 /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700389 std::string::size_type start = 13;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100390
391 /* End of the prefix is one path '/' later, capturing the
392 virtual block device ID. Example: 768 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700393 auto end = path.find('/', start);
394 if (end == std::string::npos) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100395
Tom Cherry2e344f92017-04-04 17:53:45 -0700396 auto length = end - start;
397 if (length == 0) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100398
Tom Cherry2e344f92017-04-04 17:53:45 -0700399 *result = path.substr(start, length);
400 return true;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100401}
402
Tom Cherrye3e48212017-04-11 13:53:37 -0700403void parse_event(const char* msg, uevent* uevent) {
404 uevent->partition_num = -1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700405 uevent->major = -1;
406 uevent->minor = -1;
Tom Cherrye3e48212017-04-11 13:53:37 -0700407 // currently ignoring SEQNUM
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700408 while(*msg) {
409 if(!strncmp(msg, "ACTION=", 7)) {
410 msg += 7;
411 uevent->action = msg;
412 } else if(!strncmp(msg, "DEVPATH=", 8)) {
413 msg += 8;
414 uevent->path = msg;
415 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
416 msg += 10;
417 uevent->subsystem = msg;
418 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
419 msg += 9;
420 uevent->firmware = msg;
421 } else if(!strncmp(msg, "MAJOR=", 6)) {
422 msg += 6;
423 uevent->major = atoi(msg);
424 } else if(!strncmp(msg, "MINOR=", 6)) {
425 msg += 6;
426 uevent->minor = atoi(msg);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700427 } else if(!strncmp(msg, "PARTN=", 6)) {
428 msg += 6;
429 uevent->partition_num = atoi(msg);
430 } else if(!strncmp(msg, "PARTNAME=", 9)) {
431 msg += 9;
432 uevent->partition_name = msg;
Wei Zhongf97b8872012-03-23 14:15:34 -0700433 } else if(!strncmp(msg, "DEVNAME=", 8)) {
434 msg += 8;
435 uevent->device_name = msg;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700436 }
437
Tom Cherrye3e48212017-04-11 13:53:37 -0700438 // advance to after the next \0
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700439 while(*msg++)
440 ;
441 }
442
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800443 if (LOG_UEVENTS) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700444 LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
445 << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
446 << ", " << uevent->minor << " }";
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800447 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700448}
449
Tom Cherry2e344f92017-04-04 17:53:45 -0700450std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700451 std::string parent_device;
452 if (!find_platform_device(uevent->path, &parent_device)) return {};
Benoit Gobyd2278632010-08-03 14:36:02 -0700453
Tom Cherry1ab8f552017-04-06 14:41:30 -0700454 // skip path to the parent driver
455 std::string path = uevent->path.substr(parent_device.length());
Benoit Gobyd2278632010-08-03 14:36:02 -0700456
Tom Cherry1ab8f552017-04-06 14:41:30 -0700457 if (!android::base::StartsWith(path, "/usb")) return {};
Tom Cherry2e344f92017-04-04 17:53:45 -0700458
459 // skip root hub name and device. use device interface
460 // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
461 // then extract what comes between the 3rd and 4th slash
462 // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
463
464 std::string::size_type start = 0;
Tom Cherry1ab8f552017-04-06 14:41:30 -0700465 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700466 if (start == std::string::npos) return {};
467
Tom Cherry1ab8f552017-04-06 14:41:30 -0700468 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700469 if (start == std::string::npos) return {};
470
Tom Cherry1ab8f552017-04-06 14:41:30 -0700471 auto end = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700472 if (end == std::string::npos) return {};
473
474 start++; // Skip the first '/'
475
476 auto length = end - start;
477 if (length == 0) return {};
478
Tom Cherry1ab8f552017-04-06 14:41:30 -0700479 auto name_string = path.substr(start, length);
Tom Cherry2e344f92017-04-04 17:53:45 -0700480
Tom Cherry2e344f92017-04-04 17:53:45 -0700481 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700482 links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
Tom Cherry2e344f92017-04-04 17:53:45 -0700483
484 mkdir("/dev/usb", 0755);
Benoit Gobyd2278632010-08-03 14:36:02 -0700485
486 return links;
Benoit Gobyd2278632010-08-03 14:36:02 -0700487}
488
Tom Cherryc44f6a42017-04-05 15:58:31 -0700489// replaces any unacceptable characters with '_', the
490// length of the resulting string is equal to the input string
Tom Cherry2e344f92017-04-04 17:53:45 -0700491void sanitize_partition_name(std::string* string) {
Tom Cherryc44f6a42017-04-05 15:58:31 -0700492 const char* accept =
493 "abcdefghijklmnopqrstuvwxyz"
494 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
495 "0123456789"
496 "_-.";
497
Tom Cherry2e344f92017-04-04 17:53:45 -0700498 if (!string) return;
Tom Cherryc44f6a42017-04-05 15:58:31 -0700499
Tom Cherry2e344f92017-04-04 17:53:45 -0700500 std::string::size_type pos = 0;
501 while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
502 (*string)[pos] = '_';
Tom Cherryc44f6a42017-04-05 15:58:31 -0700503 }
504}
505
Tom Cherry2e344f92017-04-04 17:53:45 -0700506std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
507 std::string device;
Tom Cherry2e344f92017-04-04 17:53:45 -0700508 std::string type;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700509
Tom Cherry1ab8f552017-04-06 14:41:30 -0700510 if (find_platform_device(uevent->path, &device)) {
511 // Skip /devices/platform or /devices/ if present
512 static const std::string devices_platform_prefix = "/devices/platform/";
513 static const std::string devices_prefix = "/devices/";
514
515 if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
516 device = device.substr(devices_platform_prefix.length());
517 } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
518 device = device.substr(devices_prefix.length());
519 }
520
Andrew Boiea885d042013-09-13 17:41:20 -0700521 type = "platform";
Tom Cherry2e344f92017-04-04 17:53:45 -0700522 } else if (find_pci_device_prefix(uevent->path, &device)) {
Andrew Boiea885d042013-09-13 17:41:20 -0700523 type = "pci";
Tom Cherry2e344f92017-04-04 17:53:45 -0700524 } else if (find_vbd_device_prefix(uevent->path, &device)) {
Jeremy Compostella937309d2017-03-03 16:27:29 +0100525 type = "vbd";
Andrew Boiea885d042013-09-13 17:41:20 -0700526 } else {
Tom Cherry2e344f92017-04-04 17:53:45 -0700527 return {};
Andrew Boiea885d042013-09-13 17:41:20 -0700528 }
Dima Zavinf395c922013-03-06 16:23:57 -0800529
Tom Cherry2e344f92017-04-04 17:53:45 -0700530 std::vector<std::string> links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700531
Sandeep Patil35403eb2017-02-08 20:27:12 -0800532 LOG(VERBOSE) << "found " << type << " device " << device;
Colin Crossfadb85e2011-03-30 18:32:12 -0700533
Tom Cherry2e344f92017-04-04 17:53:45 -0700534 auto link_path = "/dev/block/" + type + "/" + device;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700535
Tom Cherrye3e48212017-04-11 13:53:37 -0700536 if (!uevent->partition_name.empty()) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700537 std::string partition_name_sanitized(uevent->partition_name);
538 sanitize_partition_name(&partition_name_sanitized);
539 if (partition_name_sanitized != uevent->partition_name) {
540 LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
541 << partition_name_sanitized << "'";
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700542 }
Tom Cherry2e344f92017-04-04 17:53:45 -0700543 links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700544 }
545
546 if (uevent->partition_num >= 0) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700547 links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700548 }
549
Tom Cherrye3e48212017-04-11 13:53:37 -0700550 auto last_slash = uevent->path.rfind('/');
551 links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700552
553 return links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700554}
555
Tom Cherrye3e48212017-04-11 13:53:37 -0700556static void make_link_init(const std::string& oldpath, const std::string& newpath) {
557 if (mkdir_recursive(dirname(newpath.c_str()), 0755)) {
558 PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
559 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700560
Tom Cherrye3e48212017-04-11 13:53:37 -0700561 if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
562 PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
563 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700564}
565
Tom Cherrye3e48212017-04-11 13:53:37 -0700566static void remove_link(const std::string& oldpath, const std::string& newpath) {
567 std::string path;
568 if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700569}
570
Tom Cherrye3e48212017-04-11 13:53:37 -0700571static void handle_device(const std::string& action, const std::string& devpath, int block,
Tom Cherry2e344f92017-04-04 17:53:45 -0700572 int major, int minor, const std::vector<std::string>& links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700573 if (action == "add") {
574 make_device(devpath, block, major, minor, links);
Tom Cherry2e344f92017-04-04 17:53:45 -0700575 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700576 make_link_init(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700577 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700578 }
579
Tom Cherrye3e48212017-04-11 13:53:37 -0700580 if (action == "remove") {
Tom Cherry2e344f92017-04-04 17:53:45 -0700581 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700582 remove_link(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700583 }
Tom Cherrye3e48212017-04-11 13:53:37 -0700584 unlink(devpath.c_str());
Colin Crossb0ab94b2010-04-08 16:16:20 -0700585 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700586}
587
Tom Cherry1ab8f552017-04-06 14:41:30 -0700588void handle_platform_device_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700589 if (uevent->action == "add") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700590 platform_devices.emplace_back(uevent->path);
Tom Cherrye3e48212017-04-11 13:53:37 -0700591 } else if (uevent->action == "remove") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700592 auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
593 if (it != platform_devices.end()) platform_devices.erase(it);
Tom Cherrye3e48212017-04-11 13:53:37 -0700594 }
Colin Crossfadb85e2011-03-30 18:32:12 -0700595}
596
Tom Cherry3fa46732017-04-11 14:19:50 -0700597static void handle_block_device_event(uevent* uevent) {
598 // if it's not a /dev device, nothing to do
599 if (uevent->major < 0 || uevent->minor < 0) return;
Colin Crosseb5ba832011-03-30 17:37:17 -0700600
Tom Cherry3fa46732017-04-11 14:19:50 -0700601 const char* base = "/dev/block/";
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500602 make_dir(base, 0755);
Colin Crosseb5ba832011-03-30 17:37:17 -0700603
Tom Cherry3fa46732017-04-11 14:19:50 -0700604 std::string name = android::base::Basename(uevent->path);
605 std::string devpath = base + name;
606
Tom Cherry2e344f92017-04-04 17:53:45 -0700607 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700608 if (android::base::StartsWith(uevent->path, "/devices")) {
Andrew Boiea885d042013-09-13 17:41:20 -0700609 links = get_block_device_symlinks(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700610 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700611
Tom Cherrye3e48212017-04-11 13:53:37 -0700612 handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700613}
614
Tom Cherry3fa46732017-04-11 14:19:50 -0700615static void handle_generic_device_event(uevent* uevent) {
616 // if it's not a /dev device, nothing to do
617 if (uevent->major < 0 || uevent->minor < 0) return;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800618
Tom Cherry3fa46732017-04-11 14:19:50 -0700619 std::string devpath;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800620
Tom Cherryfe062052017-04-24 16:59:05 -0700621 if (android::base::StartsWith(uevent->subsystem, "usb")) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700622 if (uevent->subsystem == "usb") {
623 if (!uevent->device_name.empty()) {
624 devpath = "/dev/" + uevent->device_name;
Tom Cherry3fa46732017-04-11 14:19:50 -0700625 } else {
626 // This imitates the file system that would be created
627 // if we were using devfs instead.
628 // Minors are broken up into groups of 128, starting at "001"
629 int bus_id = uevent->minor / 128 + 1;
630 int device_id = uevent->minor % 128 + 1;
631 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
632 }
Tom Cherry3fa46732017-04-11 14:19:50 -0700633 } else {
634 // ignore other USB events
635 return;
636 }
Tom Cherryfe062052017-04-24 16:59:05 -0700637 } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
638 subsystem != subsystems.end()) {
639 devpath = subsystem->ParseDevPath(uevent);
Tom Cherry780a71e2017-04-04 16:30:40 -0700640 } else {
Tom Cherryfe062052017-04-24 16:59:05 -0700641 devpath = "/dev/" + android::base::Basename(uevent->path);
Tom Cherry780a71e2017-04-04 16:30:40 -0700642 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700643
Tom Cherryfe062052017-04-24 16:59:05 -0700644 mkdir_recursive(android::base::Dirname(devpath), 0755);
645
Tom Cherry780a71e2017-04-04 16:30:40 -0700646 auto links = get_character_device_symlinks(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700647
Tom Cherrye3e48212017-04-11 13:53:37 -0700648 handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700649}
650
651static void handle_device_event(struct uevent *uevent)
652{
Tom Cherrye3e48212017-04-11 13:53:37 -0700653 if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
Tom Cherrycc054c92017-04-05 17:55:46 -0700654 fixup_sys_permissions(uevent->path, uevent->subsystem);
Tom Cherrye3e48212017-04-11 13:53:37 -0700655 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700656
Tom Cherrye3e48212017-04-11 13:53:37 -0700657 if (uevent->subsystem == "block") {
Colin Crosseb5ba832011-03-30 17:37:17 -0700658 handle_block_device_event(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700659 } else if (uevent->subsystem == "platform") {
Colin Crossfadb85e2011-03-30 18:32:12 -0700660 handle_platform_device_event(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700661 } else {
662 handle_generic_device_event(uevent);
663 }
664}
665
Elliott Hughes632e99a2016-11-12 11:44:16 -0800666static void load_firmware(uevent* uevent, const std::string& root,
667 int fw_fd, size_t fw_size,
668 int loading_fd, int data_fd) {
669 // Start transfer.
670 android::base::WriteFully(loading_fd, "1", 1);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700671
Elliott Hughes632e99a2016-11-12 11:44:16 -0800672 // Copy the firmware.
673 int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
674 if (rc == -1) {
675 PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700676 }
677
Elliott Hughes632e99a2016-11-12 11:44:16 -0800678 // Tell the firmware whether to abort or commit.
679 const char* response = (rc != -1) ? "0" : "-1";
680 android::base::WriteFully(loading_fd, response, strlen(response));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700681}
682
Elliott Hughes632e99a2016-11-12 11:44:16 -0800683static int is_booting() {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700684 return access("/dev/.booting", F_OK) == 0;
685}
686
Elliott Hughes632e99a2016-11-12 11:44:16 -0800687static void process_firmware_event(uevent* uevent) {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700688 int booting = is_booting();
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700689
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700690 LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700691
Tom Cherrye3e48212017-04-11 13:53:37 -0700692 std::string root = "/sys" + uevent->path;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800693 std::string loading = root + "/loading";
694 std::string data = root + "/data";
695
696 android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
697 if (loading_fd == -1) {
698 PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700699 return;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800700 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700701
Elliott Hughes632e99a2016-11-12 11:44:16 -0800702 android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
703 if (data_fd == -1) {
704 PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
705 return;
706 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700707
Tom Cherrye3e48212017-04-11 13:53:37 -0700708 static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
709 "/firmware/image/"};
710
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700711try_loading_again:
Elliott Hughes632e99a2016-11-12 11:44:16 -0800712 for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700713 std::string file = firmware_dirs[i] + uevent->firmware;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800714 android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
715 struct stat sb;
716 if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
717 load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
718 return;
Benoit Goby609d8822010-11-09 18:10:24 -0800719 }
Brian Swetland02863b92010-09-19 03:36:39 -0700720 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700721
Elliott Hughes632e99a2016-11-12 11:44:16 -0800722 if (booting) {
723 // If we're not fully booted, we may be missing
724 // filesystems needed for firmware, wait and retry.
Elliott Hughes290a2282016-11-14 17:08:47 -0800725 std::this_thread::sleep_for(100ms);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800726 booting = is_booting();
727 goto try_loading_again;
728 }
729
730 LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
731
732 // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
733 write(loading_fd, "-1", 2);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700734}
735
Elliott Hughes632e99a2016-11-12 11:44:16 -0800736static void handle_firmware_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700737 if (uevent->subsystem != "firmware" || uevent->action != "add") return;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700738
Elliott Hughes632e99a2016-11-12 11:44:16 -0800739 // Loading the firmware in a child means we can do that in parallel...
740 // (We ignore SIGCHLD rather than wait for our children.)
741 pid_t pid = fork();
742 if (pid == 0) {
743 Timer t;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700744 process_firmware_event(uevent);
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000745 LOG(INFO) << "loading " << uevent->path << " took " << t;
Kenny Root17baff42014-08-20 16:16:44 -0700746 _exit(EXIT_SUCCESS);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800747 } else if (pid == -1) {
748 PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700749 }
750}
751
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800752static bool inline should_stop_coldboot(coldboot_action_t act)
753{
754 return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
755}
756
Ruchi Kandoic6037202014-06-23 11:22:09 -0700757#define UEVENT_MSG_LEN 2048
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800758
Sandeep Patil44a3ee22017-02-08 15:49:47 -0800759static inline coldboot_action_t handle_device_fd_with(
760 std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700761{
Vernon Tang3f582e92011-04-25 13:08:17 +1000762 char msg[UEVENT_MSG_LEN+2];
763 int n;
Nick Kralevich57de8b82011-05-11 14:58:24 -0700764 while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700765 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700766 continue;
767
768 msg[n] = '\0';
769 msg[n+1] = '\0';
770
Tom Cherrye3e48212017-04-11 13:53:37 -0700771 uevent uevent;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700772 parse_event(msg, &uevent);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800773 coldboot_action_t act = handle_uevent(&uevent);
774 if (should_stop_coldboot(act))
775 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700776 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800777
778 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700779}
780
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800781coldboot_action_t handle_device_fd(coldboot_callback fn)
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800782{
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800783 coldboot_action_t ret = handle_device_fd_with(
784 [&](uevent* uevent) -> coldboot_action_t {
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800785 // default is to always create the devices
786 coldboot_action_t act = COLDBOOT_CREATE;
787 if (fn) {
788 act = fn(uevent);
789 }
790
791 if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
792 handle_device_event(uevent);
793 handle_firmware_event(uevent);
794 }
795
796 return act;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800797 });
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800798
799 return ret;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800800}
801
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700802/* Coldboot walks parts of the /sys tree and pokes the uevent files
803** to cause the kernel to regenerate device add events that happened
804** before init's device manager was started
805**
806** We drain any pending events from the netlink socket every time
807** we poke another uevent file to make sure we don't overrun the
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800808** socket's buffer.
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700809*/
810
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800811static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700812{
813 struct dirent *de;
814 int dfd, fd;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800815 coldboot_action_t act = COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700816
817 dfd = dirfd(d);
818
819 fd = openat(dfd, "uevent", O_WRONLY);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800820 if (fd >= 0) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700821 write(fd, "add\n", 4);
822 close(fd);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800823 act = handle_device_fd(fn);
824 if (should_stop_coldboot(act))
825 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700826 }
827
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800828 while (!should_stop_coldboot(act) && (de = readdir(d))) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700829 DIR *d2;
830
831 if(de->d_type != DT_DIR || de->d_name[0] == '.')
832 continue;
833
834 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
835 if(fd < 0)
836 continue;
837
838 d2 = fdopendir(fd);
839 if(d2 == 0)
840 close(fd);
841 else {
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800842 act = do_coldboot(d2, fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700843 closedir(d2);
844 }
845 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800846
847 // default is always to continue looking for uevents
848 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700849}
850
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800851static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700852{
James Hawkins588a2ca2016-02-18 14:52:46 -0800853 std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800854 if (d) {
855 return do_coldboot(d.get(), fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700856 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800857
858 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700859}
860
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800861void device_init(const char* path, coldboot_callback fn) {
Sandeep Patil971a4602017-02-15 13:37:52 -0800862 if (!sehandle) {
863 sehandle = selinux_android_file_context_handle();
Elliott Hughes56a06562015-03-28 11:23:32 -0700864 }
Sandeep Patil971a4602017-02-15 13:37:52 -0800865 // open uevent socket and selinux status only if it hasn't been
866 // done before
867 if (device_fd == -1) {
868 /* is 256K enough? udev uses 16MB! */
869 device_fd.reset(uevent_open_socket(256 * 1024, true));
870 if (device_fd == -1) {
871 return;
872 }
873 fcntl(device_fd, F_SETFL, O_NONBLOCK);
Sandeep Patil971a4602017-02-15 13:37:52 -0800874 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700875
Elliott Hughes56a06562015-03-28 11:23:32 -0700876 if (access(COLDBOOT_DONE, F_OK) == 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700877 LOG(VERBOSE) << "Skipping coldboot, already done!";
Elliott Hughes56a06562015-03-28 11:23:32 -0700878 return;
Colin Crossf83d0b92010-04-21 12:04:20 -0700879 }
Elliott Hughes56a06562015-03-28 11:23:32 -0700880
881 Timer t;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800882 coldboot_action_t act;
883 if (!path) {
884 act = coldboot("/sys/class", fn);
885 if (!should_stop_coldboot(act)) {
886 act = coldboot("/sys/block", fn);
887 if (!should_stop_coldboot(act)) {
888 act = coldboot("/sys/devices", fn);
889 }
890 }
891 } else {
892 act = coldboot(path, fn);
893 }
894
895 // If we have a callback, then do as it says. If no, then the default is
896 // to always create COLDBOOT_DONE file.
897 if (!fn || (act == COLDBOOT_FINISH)) {
898 close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
899 }
900
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000901 LOG(INFO) << "Coldboot took " << t;
Colin Cross0dd7ca62010-04-13 19:25:51 -0700902}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700903
Sandeep Patil35403eb2017-02-08 20:27:12 -0800904void device_close() {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700905 platform_devices.clear();
Sandeep Patil35403eb2017-02-08 20:27:12 -0800906 device_fd.reset();
907}
908
Elliott Hughes632e99a2016-11-12 11:44:16 -0800909int get_device_fd() {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700910 return device_fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700911}