blob: e0351a34b424d0b0135fa161d6610036fdd557f1 [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 Hughes51056c42017-05-18 09:13:15 -070033#include <sys/sysmacros.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070034#include <sys/time.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070035#include <sys/un.h>
Elliott Hughesf39f7f12016-08-31 14:41:51 -070036#include <sys/wait.h>
37#include <unistd.h>
38
Tom Cherry2e344f92017-04-04 17:53:45 -070039#include <algorithm>
James Hawkins588a2ca2016-02-18 14:52:46 -080040#include <memory>
Elliott Hughes290a2282016-11-14 17:08:47 -080041#include <thread>
James Hawkins588a2ca2016-02-18 14:52:46 -080042
Biao Ludc848562016-01-28 16:10:54 +080043#include <android-base/file.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070044#include <android-base/logging.h>
Rob Herring6de783a2016-05-06 10:06:59 -050045#include <android-base/stringprintf.h>
Tom Cherry2e344f92017-04-04 17:53:45 -070046#include <android-base/strings.h>
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +080047#include <android-base/unique_fd.h>
Vernon Tang3f582e92011-04-25 13:08:17 +100048#include <cutils/uevent.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070049#include <private/android_filesystem_config.h>
50#include <selinux/android.h>
Tom Cherry3f5eaae52017-04-06 16:30:22 -070051#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
Tom Cherrye7656b72017-05-01 17:10:09 -070058#ifdef _INIT_INIT_H
59#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
60#endif
61
62static selabel_handle* sehandle;
Stephen Smalleye46f9d52012-01-13 08:48:47 -050063
Sandeep Patil35403eb2017-02-08 20:27:12 -080064static android::base::unique_fd device_fd;
Colin Cross0dd7ca62010-04-13 19:25:51 -070065
Tom Cherrycc054c92017-04-05 17:55:46 -070066Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
67 : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
68 // If the first * is the last character, then we'll treat name_ as a prefix
69 // Otherwise, if a * is present, then we do a full fnmatch().
70 auto wildcard_position = name_.find('*');
71 if (wildcard_position == name_.length() - 1) {
72 prefix_ = true;
73 name_.pop_back();
74 } else if (wildcard_position != std::string::npos) {
75 wildcard_ = true;
Ting-Yuan Huang09bd41d2016-11-15 15:35:16 -080076 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070077}
78
Tom Cherrycc054c92017-04-05 17:55:46 -070079bool Permissions::Match(const std::string& path) const {
80 if (prefix_) {
81 return android::base::StartsWith(path, name_.c_str());
82 } else if (wildcard_) {
83 return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
Colin Cross43d537e2014-07-02 13:08:13 -070084 } else {
Tom Cherrycc054c92017-04-05 17:55:46 -070085 return path == name_;
Colin Cross43d537e2014-07-02 13:08:13 -070086 }
87
88 return false;
89}
90
Tom Cherrycc054c92017-04-05 17:55:46 -070091bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
92 const std::string& subsystem) const {
93 std::string path_basename = android::base::Basename(path);
94 if (name().find(subsystem) != std::string::npos) {
95 if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
96 if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
Rob Herring6de783a2016-05-06 10:06:59 -050097 }
Tom Cherrycc054c92017-04-05 17:55:46 -070098 return Match(path);
Rob Herring6de783a2016-05-06 10:06:59 -050099}
Rob Herringe5636a32016-05-06 12:28:48 -0500100
Tom Cherrycc054c92017-04-05 17:55:46 -0700101void SysfsPermissions::SetPermissions(const std::string& path) const {
102 std::string attribute_file = path + "/" + attribute_;
103 LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
104 << perm();
105 chown(attribute_file.c_str(), uid(), gid());
106 chmod(attribute_file.c_str(), perm());
107}
108
109// TODO: Move these to be member variables of a future devices class.
110std::vector<Permissions> dev_permissions;
111std::vector<SysfsPermissions> sysfs_permissions;
112
Tom Cherryfe062052017-04-24 16:59:05 -0700113bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
114 if (is_sysfs && args.size() != 5) {
115 *err = "/sys/ lines must have 5 entries";
116 return false;
117 }
118
119 if (!is_sysfs && args.size() != 4) {
120 *err = "/dev/ lines must have 4 entries";
121 return false;
122 }
123
124 auto it = args.begin();
125 const std::string& name = *it++;
126
127 std::string sysfs_attribute;
128 if (is_sysfs) sysfs_attribute = *it++;
129
130 // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
131 std::string& perm_string = *it++;
132 char* end_pointer = 0;
133 mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
134 if (end_pointer == nullptr || *end_pointer != '\0') {
135 *err = "invalid mode '" + perm_string + "'";
136 return false;
137 }
138
139 std::string& uid_string = *it++;
140 passwd* pwd = getpwnam(uid_string.c_str());
141 if (!pwd) {
142 *err = "invalid uid '" + uid_string + "'";
143 return false;
144 }
145 uid_t uid = pwd->pw_uid;
146
147 std::string& gid_string = *it++;
148 struct group* grp = getgrnam(gid_string.c_str());
149 if (!grp) {
150 *err = "invalid gid '" + gid_string + "'";
151 return false;
152 }
153 gid_t gid = grp->gr_gid;
154
155 if (is_sysfs) {
156 sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
157 } else {
158 dev_permissions.emplace_back(name, perm, uid, gid);
159 }
160 return true;
161}
162
163// TODO: Move this to be a member variable of a future devices class.
164static std::vector<Subsystem> subsystems;
165
166std::string Subsystem::ParseDevPath(uevent* uevent) const {
167 std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
168 ? uevent->device_name
169 : android::base::Basename(uevent->path);
170
171 return dir_name_ + "/" + devname;
172}
173
174bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
175 int line, std::string* err) {
176 if (args.size() != 2) {
177 *err = "subsystems must have exactly one name";
178 return false;
179 }
180
181 if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
182 *err = "ignoring duplicate subsystem entry";
183 return false;
184 }
185
186 subsystem_.name_ = args[1];
187
188 return true;
189}
190
191bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
192 if (args[1] == "uevent_devname") {
193 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
194 return true;
195 }
196 if (args[1] == "uevent_devpath") {
197 subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
198 return true;
199 }
200
201 *err = "invalid devname '" + args[1] + "'";
202 return false;
203}
204
205bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
206 if (args[1].front() != '/') {
207 *err = "dirname '" + args[1] + " ' does not start with '/'";
208 return false;
209 }
210
211 subsystem_.dir_name_ = args[1];
212 return true;
213}
214
215bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
216 using OptionParser =
217 bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
218 static class OptionParserMap : public KeywordMap<OptionParser> {
219 private:
220 const Map& map() const override {
221 // clang-format off
222 static const Map option_parsers = {
223 {"devname", {1, 1, &SubsystemParser::ParseDevName}},
224 {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
225 };
226 // clang-format on
227 return option_parsers;
228 }
229 } parser_map;
230
231 auto parser = parser_map.FindFunction(args, err);
232
233 if (!parser) {
234 return false;
235 }
236
237 return (this->*parser)(std::move(args), err);
238}
239
240void SubsystemParser::EndSection() {
241 subsystems.emplace_back(std::move(subsystem_));
242}
243
Tom Cherrycc054c92017-04-05 17:55:46 -0700244static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
Rob Herring6de783a2016-05-06 10:06:59 -0500245 // upaths omit the "/sys" that paths in this list
246 // contain, so we prepend it...
Tom Cherrycc054c92017-04-05 17:55:46 -0700247 std::string path = "/sys" + upath;
Rob Herring6de783a2016-05-06 10:06:59 -0500248
Tom Cherrycc054c92017-04-05 17:55:46 -0700249 for (const auto& s : sysfs_permissions) {
250 if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
Rob Herringe5636a32016-05-06 12:28:48 -0500251 }
252
253 if (access(path.c_str(), F_OK) == 0) {
Dmitry Shmidt7eed4742016-07-28 13:55:39 -0700254 LOG(VERBOSE) << "restorecon_recursive: " << path;
Tom Cherry482f36c2017-05-08 13:38:15 -0700255 selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
Rob Herringe5636a32016-05-06 12:28:48 -0500256 }
257}
258
Tom Cherrycc054c92017-04-05 17:55:46 -0700259static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
260 const std::string& path, const std::vector<std::string>& links) {
261 // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
262 for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
263 if (it->Match(path) || std::any_of(links.begin(), links.end(),
264 [it](const auto& link) { return it->Match(link); })) {
265 return {it->perm(), it->uid(), it->gid()};
Colin Cross43d537e2014-07-02 13:08:13 -0700266 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700267 }
Colin Cross44b65d02010-04-20 14:32:50 -0700268 /* Default if nothing found. */
Tom Cherrycc054c92017-04-05 17:55:46 -0700269 return {0600, 0, 0};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700270}
271
Tom Cherrye3e48212017-04-11 13:53:37 -0700272static void make_device(const std::string& path, int block, int major, int minor,
Tom Cherry2e344f92017-04-04 17:53:45 -0700273 const std::vector<std::string>& links) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700274 dev_t dev;
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500275 char *secontext = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700276
Tom Cherrycc054c92017-04-05 17:55:46 -0700277 auto [mode, uid, gid] = get_device_permissions(path, links);
278 mode |= (block ? S_IFBLK : S_IFCHR);
Kenny Rootb5982bf2012-10-16 23:07:05 -0700279
Sandeep Patilea239832017-02-03 07:51:55 -0800280 if (sehandle) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700281 std::vector<const char*> c_links;
282 for (const auto& link : links) {
283 c_links.emplace_back(link.c_str());
284 }
285 c_links.emplace_back(nullptr);
Tom Cherrye3e48212017-04-11 13:53:37 -0700286 if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
Sandeep Patilea239832017-02-03 07:51:55 -0800287 PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
288 return;
289 }
290 setfscreatecon(secontext);
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300291 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700292
Colin Cross17dcc5c2010-09-03 12:25:34 -0700293 dev = makedev(major, minor);
Nick Pelly6405c692010-01-21 18:13:39 -0800294 /* Temporarily change egid to avoid race condition setting the gid of the
295 * device node. Unforunately changing the euid would prevent creation of
296 * some device nodes, so the uid has to be set with chown() and is still
297 * racy. Fixing the gid race at least fixed the issue with system_server
298 * opening dynamic input devices under the AID_INPUT gid. */
Tom Cherry0506b182017-02-23 13:46:09 -0800299 if (setegid(gid)) {
300 PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
301 goto out;
302 }
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300303 /* If the node already exists update its SELinux label to handle cases when
304 * it was created with the wrong context during coldboot procedure. */
Tom Cherrye3e48212017-04-11 13:53:37 -0700305 if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
William Roberts397de142016-06-02 09:53:44 -0700306 char* fcon = nullptr;
Tom Cherrye3e48212017-04-11 13:53:37 -0700307 int rc = lgetfilecon(path.c_str(), &fcon);
William Roberts397de142016-06-02 09:53:44 -0700308 if (rc < 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700309 PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
William Roberts397de142016-06-02 09:53:44 -0700310 goto out;
311 }
312
313 bool different = strcmp(fcon, secontext) != 0;
314 freecon(fcon);
315
Tom Cherrye3e48212017-04-11 13:53:37 -0700316 if (different && lsetfilecon(path.c_str(), secontext)) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700317 PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
Mihai Serban24a3cbf2016-04-25 18:22:27 +0300318 }
319 }
William Roberts397de142016-06-02 09:53:44 -0700320
321out:
Tom Cherrye3e48212017-04-11 13:53:37 -0700322 chown(path.c_str(), uid, -1);
Tom Cherry0506b182017-02-23 13:46:09 -0800323 if (setegid(AID_ROOT)) {
324 PLOG(FATAL) << "setegid(AID_ROOT) failed";
325 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700326
Sandeep Patilea239832017-02-03 07:51:55 -0800327 if (secontext) {
328 freecon(secontext);
329 setfscreatecon(NULL);
330 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700331}
332
Tom Cherry1ab8f552017-04-06 14:41:30 -0700333// TODO: Move this to be a member variable of a future devices class.
334std::vector<std::string> platform_devices;
Dima Zavinf395c922013-03-06 16:23:57 -0800335
Tom Cherry1ab8f552017-04-06 14:41:30 -0700336// Given a path that may start with a platform device, find the length of the
337// platform device prefix. If it doesn't start with a platform device, return false
338bool find_platform_device(const std::string& path, std::string* out_path) {
339 out_path->clear();
340 // platform_devices is searched backwards, since parents are added before their children,
341 // and we want to match as deep of a child as we can.
342 for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
343 auto platform_device_path_length = it->length();
344 if (platform_device_path_length < path.length() &&
345 path[platform_device_path_length] == '/' &&
346 android::base::StartsWith(path, it->c_str())) {
347 *out_path = *it;
348 return true;
Colin Crossfadb85e2011-03-30 18:32:12 -0700349 }
350 }
Tom Cherry1ab8f552017-04-06 14:41:30 -0700351 return false;
Sandeep Patil35403eb2017-02-08 20:27:12 -0800352}
353
Andrew Boiea885d042013-09-13 17:41:20 -0700354/* Given a path that may start with a PCI device, populate the supplied buffer
355 * with the PCI domain/bus number and the peripheral ID and return 0.
356 * If it doesn't start with a PCI device, or there is some error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700357static bool find_pci_device_prefix(const std::string& path, std::string* result) {
358 result->clear();
Andrew Boiea885d042013-09-13 17:41:20 -0700359
Tom Cherry2e344f92017-04-04 17:53:45 -0700360 if (!android::base::StartsWith(path, "/devices/pci")) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700361
362 /* Beginning of the prefix is the initial "pci" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700363 std::string::size_type start = 9;
Andrew Boiea885d042013-09-13 17:41:20 -0700364
365 /* End of the prefix is two path '/' later, capturing the domain/bus number
366 * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700367 auto end = path.find('/', start);
368 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700369
Tom Cherry2e344f92017-04-04 17:53:45 -0700370 end = path.find('/', end + 1);
371 if (end == std::string::npos) return false;
Andrew Boiea885d042013-09-13 17:41:20 -0700372
Tom Cherry2e344f92017-04-04 17:53:45 -0700373 auto length = end - start;
374 if (length <= 4) {
375 // The minimum string that will get to this check is 'pci/', which is malformed,
376 // so return false
377 return false;
378 }
379
380 *result = path.substr(start, length);
381 return true;
Andrew Boiea885d042013-09-13 17:41:20 -0700382}
383
Jeremy Compostella937309d2017-03-03 16:27:29 +0100384/* Given a path that may start with a virtual block device, populate
385 * the supplied buffer with the virtual block device ID and return 0.
386 * If it doesn't start with a virtual block device, or there is some
387 * error, return -1 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700388static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
389 result->clear();
390
391 if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100392
393 /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
Tom Cherry2e344f92017-04-04 17:53:45 -0700394 std::string::size_type start = 13;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100395
396 /* End of the prefix is one path '/' later, capturing the
397 virtual block device ID. Example: 768 */
Tom Cherry2e344f92017-04-04 17:53:45 -0700398 auto end = path.find('/', start);
399 if (end == std::string::npos) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100400
Tom Cherry2e344f92017-04-04 17:53:45 -0700401 auto length = end - start;
402 if (length == 0) return false;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100403
Tom Cherry2e344f92017-04-04 17:53:45 -0700404 *result = path.substr(start, length);
405 return true;
Jeremy Compostella937309d2017-03-03 16:27:29 +0100406}
407
Tom Cherrye3e48212017-04-11 13:53:37 -0700408void parse_event(const char* msg, uevent* uevent) {
409 uevent->partition_num = -1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700410 uevent->major = -1;
411 uevent->minor = -1;
Tom Cherrye3e48212017-04-11 13:53:37 -0700412 // currently ignoring SEQNUM
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700413 while(*msg) {
414 if(!strncmp(msg, "ACTION=", 7)) {
415 msg += 7;
416 uevent->action = msg;
417 } else if(!strncmp(msg, "DEVPATH=", 8)) {
418 msg += 8;
419 uevent->path = msg;
420 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
421 msg += 10;
422 uevent->subsystem = msg;
423 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
424 msg += 9;
425 uevent->firmware = msg;
426 } else if(!strncmp(msg, "MAJOR=", 6)) {
427 msg += 6;
428 uevent->major = atoi(msg);
429 } else if(!strncmp(msg, "MINOR=", 6)) {
430 msg += 6;
431 uevent->minor = atoi(msg);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700432 } else if(!strncmp(msg, "PARTN=", 6)) {
433 msg += 6;
434 uevent->partition_num = atoi(msg);
435 } else if(!strncmp(msg, "PARTNAME=", 9)) {
436 msg += 9;
437 uevent->partition_name = msg;
Wei Zhongf97b8872012-03-23 14:15:34 -0700438 } else if(!strncmp(msg, "DEVNAME=", 8)) {
439 msg += 8;
440 uevent->device_name = msg;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700441 }
442
Tom Cherrye3e48212017-04-11 13:53:37 -0700443 // advance to after the next \0
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700444 while(*msg++)
445 ;
446 }
447
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800448 if (LOG_UEVENTS) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700449 LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
450 << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
451 << ", " << uevent->minor << " }";
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800452 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700453}
454
Tom Cherry2e344f92017-04-04 17:53:45 -0700455std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700456 std::string parent_device;
457 if (!find_platform_device(uevent->path, &parent_device)) return {};
Benoit Gobyd2278632010-08-03 14:36:02 -0700458
Tom Cherry1ab8f552017-04-06 14:41:30 -0700459 // skip path to the parent driver
460 std::string path = uevent->path.substr(parent_device.length());
Benoit Gobyd2278632010-08-03 14:36:02 -0700461
Tom Cherry1ab8f552017-04-06 14:41:30 -0700462 if (!android::base::StartsWith(path, "/usb")) return {};
Tom Cherry2e344f92017-04-04 17:53:45 -0700463
464 // skip root hub name and device. use device interface
465 // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
466 // then extract what comes between the 3rd and 4th slash
467 // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
468
469 std::string::size_type start = 0;
Tom Cherry1ab8f552017-04-06 14:41:30 -0700470 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700471 if (start == std::string::npos) return {};
472
Tom Cherry1ab8f552017-04-06 14:41:30 -0700473 start = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700474 if (start == std::string::npos) return {};
475
Tom Cherry1ab8f552017-04-06 14:41:30 -0700476 auto end = path.find('/', start + 1);
Tom Cherry2e344f92017-04-04 17:53:45 -0700477 if (end == std::string::npos) return {};
478
479 start++; // Skip the first '/'
480
481 auto length = end - start;
482 if (length == 0) return {};
483
Tom Cherry1ab8f552017-04-06 14:41:30 -0700484 auto name_string = path.substr(start, length);
Tom Cherry2e344f92017-04-04 17:53:45 -0700485
Tom Cherry2e344f92017-04-04 17:53:45 -0700486 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700487 links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
Tom Cherry2e344f92017-04-04 17:53:45 -0700488
489 mkdir("/dev/usb", 0755);
Benoit Gobyd2278632010-08-03 14:36:02 -0700490
491 return links;
Benoit Gobyd2278632010-08-03 14:36:02 -0700492}
493
Tom Cherryc44f6a42017-04-05 15:58:31 -0700494// replaces any unacceptable characters with '_', the
495// length of the resulting string is equal to the input string
Tom Cherry2e344f92017-04-04 17:53:45 -0700496void sanitize_partition_name(std::string* string) {
Tom Cherryc44f6a42017-04-05 15:58:31 -0700497 const char* accept =
498 "abcdefghijklmnopqrstuvwxyz"
499 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
500 "0123456789"
501 "_-.";
502
Tom Cherry2e344f92017-04-04 17:53:45 -0700503 if (!string) return;
Tom Cherryc44f6a42017-04-05 15:58:31 -0700504
Tom Cherry2e344f92017-04-04 17:53:45 -0700505 std::string::size_type pos = 0;
506 while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
507 (*string)[pos] = '_';
Tom Cherryc44f6a42017-04-05 15:58:31 -0700508 }
509}
510
Tom Cherry2e344f92017-04-04 17:53:45 -0700511std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
512 std::string device;
Tom Cherry2e344f92017-04-04 17:53:45 -0700513 std::string type;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700514
Tom Cherry1ab8f552017-04-06 14:41:30 -0700515 if (find_platform_device(uevent->path, &device)) {
516 // Skip /devices/platform or /devices/ if present
517 static const std::string devices_platform_prefix = "/devices/platform/";
518 static const std::string devices_prefix = "/devices/";
519
520 if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
521 device = device.substr(devices_platform_prefix.length());
522 } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
523 device = device.substr(devices_prefix.length());
524 }
525
Andrew Boiea885d042013-09-13 17:41:20 -0700526 type = "platform";
Tom Cherry2e344f92017-04-04 17:53:45 -0700527 } else if (find_pci_device_prefix(uevent->path, &device)) {
Andrew Boiea885d042013-09-13 17:41:20 -0700528 type = "pci";
Tom Cherry2e344f92017-04-04 17:53:45 -0700529 } else if (find_vbd_device_prefix(uevent->path, &device)) {
Jeremy Compostella937309d2017-03-03 16:27:29 +0100530 type = "vbd";
Andrew Boiea885d042013-09-13 17:41:20 -0700531 } else {
Tom Cherry2e344f92017-04-04 17:53:45 -0700532 return {};
Andrew Boiea885d042013-09-13 17:41:20 -0700533 }
Dima Zavinf395c922013-03-06 16:23:57 -0800534
Tom Cherry2e344f92017-04-04 17:53:45 -0700535 std::vector<std::string> links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700536
Sandeep Patil35403eb2017-02-08 20:27:12 -0800537 LOG(VERBOSE) << "found " << type << " device " << device;
Colin Crossfadb85e2011-03-30 18:32:12 -0700538
Tom Cherry2e344f92017-04-04 17:53:45 -0700539 auto link_path = "/dev/block/" + type + "/" + device;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700540
Tom Cherrye3e48212017-04-11 13:53:37 -0700541 if (!uevent->partition_name.empty()) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700542 std::string partition_name_sanitized(uevent->partition_name);
543 sanitize_partition_name(&partition_name_sanitized);
544 if (partition_name_sanitized != uevent->partition_name) {
545 LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
546 << partition_name_sanitized << "'";
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700547 }
Tom Cherry2e344f92017-04-04 17:53:45 -0700548 links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700549 }
550
551 if (uevent->partition_num >= 0) {
Tom Cherry2e344f92017-04-04 17:53:45 -0700552 links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700553 }
554
Tom Cherrye3e48212017-04-11 13:53:37 -0700555 auto last_slash = uevent->path.rfind('/');
556 links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
Colin Crossb0ab94b2010-04-08 16:16:20 -0700557
558 return links;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700559}
560
Tom Cherrye3e48212017-04-11 13:53:37 -0700561static void make_link_init(const std::string& oldpath, const std::string& newpath) {
Tom Cherrye7656b72017-05-01 17:10:09 -0700562 if (mkdir_recursive(dirname(newpath.c_str()), 0755, sehandle)) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700563 PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
564 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700565
Tom Cherrye3e48212017-04-11 13:53:37 -0700566 if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
567 PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
568 }
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700569}
570
Tom Cherrye3e48212017-04-11 13:53:37 -0700571static void remove_link(const std::string& oldpath, const std::string& newpath) {
572 std::string path;
573 if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
Elliott Hughesf39f7f12016-08-31 14:41:51 -0700574}
575
Tom Cherrye3e48212017-04-11 13:53:37 -0700576static void handle_device(const std::string& action, const std::string& devpath, int block,
Tom Cherry2e344f92017-04-04 17:53:45 -0700577 int major, int minor, const std::vector<std::string>& links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700578 if (action == "add") {
579 make_device(devpath, block, major, minor, links);
Tom Cherry2e344f92017-04-04 17:53:45 -0700580 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700581 make_link_init(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700582 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700583 }
584
Tom Cherrye3e48212017-04-11 13:53:37 -0700585 if (action == "remove") {
Tom Cherry2e344f92017-04-04 17:53:45 -0700586 for (const auto& link : links) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700587 remove_link(devpath, link);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700588 }
Tom Cherrye3e48212017-04-11 13:53:37 -0700589 unlink(devpath.c_str());
Colin Crossb0ab94b2010-04-08 16:16:20 -0700590 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700591}
592
Tom Cherry1ab8f552017-04-06 14:41:30 -0700593void handle_platform_device_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700594 if (uevent->action == "add") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700595 platform_devices.emplace_back(uevent->path);
Tom Cherrye3e48212017-04-11 13:53:37 -0700596 } else if (uevent->action == "remove") {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700597 auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
598 if (it != platform_devices.end()) platform_devices.erase(it);
Tom Cherrye3e48212017-04-11 13:53:37 -0700599 }
Colin Crossfadb85e2011-03-30 18:32:12 -0700600}
601
Tom Cherry3fa46732017-04-11 14:19:50 -0700602static void handle_block_device_event(uevent* uevent) {
603 // if it's not a /dev device, nothing to do
604 if (uevent->major < 0 || uevent->minor < 0) return;
Colin Crosseb5ba832011-03-30 17:37:17 -0700605
Tom Cherry3fa46732017-04-11 14:19:50 -0700606 const char* base = "/dev/block/";
Tom Cherrye7656b72017-05-01 17:10:09 -0700607 make_dir(base, 0755, sehandle);
Colin Crosseb5ba832011-03-30 17:37:17 -0700608
Tom Cherry3fa46732017-04-11 14:19:50 -0700609 std::string name = android::base::Basename(uevent->path);
610 std::string devpath = base + name;
611
Tom Cherry2e344f92017-04-04 17:53:45 -0700612 std::vector<std::string> links;
Tom Cherrye3e48212017-04-11 13:53:37 -0700613 if (android::base::StartsWith(uevent->path, "/devices")) {
Andrew Boiea885d042013-09-13 17:41:20 -0700614 links = get_block_device_symlinks(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700615 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700616
Tom Cherrye3e48212017-04-11 13:53:37 -0700617 handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700618}
619
Tom Cherry3fa46732017-04-11 14:19:50 -0700620static void handle_generic_device_event(uevent* uevent) {
621 // if it's not a /dev device, nothing to do
622 if (uevent->major < 0 || uevent->minor < 0) return;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800623
Tom Cherry3fa46732017-04-11 14:19:50 -0700624 std::string devpath;
Greg Hackmann3312aa82013-11-18 15:24:40 -0800625
Tom Cherryfe062052017-04-24 16:59:05 -0700626 if (android::base::StartsWith(uevent->subsystem, "usb")) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700627 if (uevent->subsystem == "usb") {
628 if (!uevent->device_name.empty()) {
629 devpath = "/dev/" + uevent->device_name;
Tom Cherry3fa46732017-04-11 14:19:50 -0700630 } else {
631 // This imitates the file system that would be created
632 // if we were using devfs instead.
633 // Minors are broken up into groups of 128, starting at "001"
634 int bus_id = uevent->minor / 128 + 1;
635 int device_id = uevent->minor % 128 + 1;
636 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
637 }
Tom Cherry3fa46732017-04-11 14:19:50 -0700638 } else {
639 // ignore other USB events
640 return;
641 }
Tom Cherryfe062052017-04-24 16:59:05 -0700642 } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
643 subsystem != subsystems.end()) {
644 devpath = subsystem->ParseDevPath(uevent);
Tom Cherry780a71e2017-04-04 16:30:40 -0700645 } else {
Tom Cherryfe062052017-04-24 16:59:05 -0700646 devpath = "/dev/" + android::base::Basename(uevent->path);
Tom Cherry780a71e2017-04-04 16:30:40 -0700647 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700648
Tom Cherrye7656b72017-05-01 17:10:09 -0700649 mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle);
Tom Cherryfe062052017-04-24 16:59:05 -0700650
Tom Cherry780a71e2017-04-04 16:30:40 -0700651 auto links = get_character_device_symlinks(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700652
Tom Cherrye3e48212017-04-11 13:53:37 -0700653 handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
Colin Crosseb5ba832011-03-30 17:37:17 -0700654}
655
656static void handle_device_event(struct uevent *uevent)
657{
Tom Cherrye3e48212017-04-11 13:53:37 -0700658 if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
Tom Cherrycc054c92017-04-05 17:55:46 -0700659 fixup_sys_permissions(uevent->path, uevent->subsystem);
Tom Cherrye3e48212017-04-11 13:53:37 -0700660 }
Colin Crosseb5ba832011-03-30 17:37:17 -0700661
Tom Cherrye3e48212017-04-11 13:53:37 -0700662 if (uevent->subsystem == "block") {
Colin Crosseb5ba832011-03-30 17:37:17 -0700663 handle_block_device_event(uevent);
Tom Cherrye3e48212017-04-11 13:53:37 -0700664 } else if (uevent->subsystem == "platform") {
Colin Crossfadb85e2011-03-30 18:32:12 -0700665 handle_platform_device_event(uevent);
Colin Crosseb5ba832011-03-30 17:37:17 -0700666 } else {
667 handle_generic_device_event(uevent);
668 }
669}
670
Elliott Hughes632e99a2016-11-12 11:44:16 -0800671static void load_firmware(uevent* uevent, const std::string& root,
672 int fw_fd, size_t fw_size,
673 int loading_fd, int data_fd) {
674 // Start transfer.
675 android::base::WriteFully(loading_fd, "1", 1);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700676
Elliott Hughes632e99a2016-11-12 11:44:16 -0800677 // Copy the firmware.
678 int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
679 if (rc == -1) {
680 PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700681 }
682
Elliott Hughes632e99a2016-11-12 11:44:16 -0800683 // Tell the firmware whether to abort or commit.
684 const char* response = (rc != -1) ? "0" : "-1";
685 android::base::WriteFully(loading_fd, response, strlen(response));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700686}
687
Elliott Hughes632e99a2016-11-12 11:44:16 -0800688static int is_booting() {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700689 return access("/dev/.booting", F_OK) == 0;
690}
691
Elliott Hughes632e99a2016-11-12 11:44:16 -0800692static void process_firmware_event(uevent* uevent) {
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700693 int booting = is_booting();
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700694
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700695 LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700696
Tom Cherrye3e48212017-04-11 13:53:37 -0700697 std::string root = "/sys" + uevent->path;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800698 std::string loading = root + "/loading";
699 std::string data = root + "/data";
700
701 android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
702 if (loading_fd == -1) {
703 PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700704 return;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800705 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700706
Elliott Hughes632e99a2016-11-12 11:44:16 -0800707 android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
708 if (data_fd == -1) {
709 PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
710 return;
711 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700712
Tom Cherrye3e48212017-04-11 13:53:37 -0700713 static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
714 "/firmware/image/"};
715
Brian Swetland8d48c8e2011-03-24 15:45:30 -0700716try_loading_again:
Elliott Hughes632e99a2016-11-12 11:44:16 -0800717 for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700718 std::string file = firmware_dirs[i] + uevent->firmware;
Elliott Hughes632e99a2016-11-12 11:44:16 -0800719 android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
720 struct stat sb;
721 if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
722 load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
723 return;
Benoit Goby609d8822010-11-09 18:10:24 -0800724 }
Brian Swetland02863b92010-09-19 03:36:39 -0700725 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700726
Elliott Hughes632e99a2016-11-12 11:44:16 -0800727 if (booting) {
728 // If we're not fully booted, we may be missing
729 // filesystems needed for firmware, wait and retry.
Elliott Hughes290a2282016-11-14 17:08:47 -0800730 std::this_thread::sleep_for(100ms);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800731 booting = is_booting();
732 goto try_loading_again;
733 }
734
735 LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
736
737 // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
738 write(loading_fd, "-1", 2);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700739}
740
Elliott Hughes632e99a2016-11-12 11:44:16 -0800741static void handle_firmware_event(uevent* uevent) {
Tom Cherrye3e48212017-04-11 13:53:37 -0700742 if (uevent->subsystem != "firmware" || uevent->action != "add") return;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700743
Elliott Hughes632e99a2016-11-12 11:44:16 -0800744 // Loading the firmware in a child means we can do that in parallel...
745 // (We ignore SIGCHLD rather than wait for our children.)
746 pid_t pid = fork();
747 if (pid == 0) {
748 Timer t;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700749 process_firmware_event(uevent);
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000750 LOG(INFO) << "loading " << uevent->path << " took " << t;
Kenny Root17baff42014-08-20 16:16:44 -0700751 _exit(EXIT_SUCCESS);
Elliott Hughes632e99a2016-11-12 11:44:16 -0800752 } else if (pid == -1) {
753 PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700754 }
755}
756
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800757static bool inline should_stop_coldboot(coldboot_action_t act)
758{
759 return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
760}
761
Ruchi Kandoic6037202014-06-23 11:22:09 -0700762#define UEVENT_MSG_LEN 2048
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800763
Sandeep Patil44a3ee22017-02-08 15:49:47 -0800764static inline coldboot_action_t handle_device_fd_with(
765 std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700766{
Vernon Tang3f582e92011-04-25 13:08:17 +1000767 char msg[UEVENT_MSG_LEN+2];
768 int n;
Nick Kralevich57de8b82011-05-11 14:58:24 -0700769 while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700770 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700771 continue;
772
773 msg[n] = '\0';
774 msg[n+1] = '\0';
775
Tom Cherrye3e48212017-04-11 13:53:37 -0700776 uevent uevent;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700777 parse_event(msg, &uevent);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800778 coldboot_action_t act = handle_uevent(&uevent);
779 if (should_stop_coldboot(act))
780 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700781 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800782
783 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700784}
785
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800786coldboot_action_t handle_device_fd(coldboot_callback fn)
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800787{
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800788 coldboot_action_t ret = handle_device_fd_with(
789 [&](uevent* uevent) -> coldboot_action_t {
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800790 // default is to always create the devices
791 coldboot_action_t act = COLDBOOT_CREATE;
792 if (fn) {
793 act = fn(uevent);
794 }
795
796 if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
797 handle_device_event(uevent);
798 handle_firmware_event(uevent);
799 }
800
801 return act;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800802 });
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800803
804 return ret;
Hung-ying Tyan99c4a8a2016-02-01 15:07:40 +0800805}
806
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700807/* Coldboot walks parts of the /sys tree and pokes the uevent files
808** to cause the kernel to regenerate device add events that happened
809** before init's device manager was started
810**
811** We drain any pending events from the netlink socket every time
812** we poke another uevent file to make sure we don't overrun the
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800813** socket's buffer.
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700814*/
815
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800816static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700817{
818 struct dirent *de;
819 int dfd, fd;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800820 coldboot_action_t act = COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700821
822 dfd = dirfd(d);
823
824 fd = openat(dfd, "uevent", O_WRONLY);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800825 if (fd >= 0) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700826 write(fd, "add\n", 4);
827 close(fd);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800828 act = handle_device_fd(fn);
829 if (should_stop_coldboot(act))
830 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700831 }
832
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800833 while (!should_stop_coldboot(act) && (de = readdir(d))) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700834 DIR *d2;
835
836 if(de->d_type != DT_DIR || de->d_name[0] == '.')
837 continue;
838
839 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
840 if(fd < 0)
841 continue;
842
843 d2 = fdopendir(fd);
844 if(d2 == 0)
845 close(fd);
846 else {
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800847 act = do_coldboot(d2, fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700848 closedir(d2);
849 }
850 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800851
852 // default is always to continue looking for uevents
853 return act;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700854}
855
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800856static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700857{
James Hawkins588a2ca2016-02-18 14:52:46 -0800858 std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800859 if (d) {
860 return do_coldboot(d.get(), fn);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700861 }
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800862
863 return COLDBOOT_CONTINUE;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700864}
865
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800866void device_init(const char* path, coldboot_callback fn) {
Sandeep Patil971a4602017-02-15 13:37:52 -0800867 if (!sehandle) {
868 sehandle = selinux_android_file_context_handle();
Elliott Hughes56a06562015-03-28 11:23:32 -0700869 }
Sandeep Patil971a4602017-02-15 13:37:52 -0800870 // open uevent socket and selinux status only if it hasn't been
871 // done before
872 if (device_fd == -1) {
873 /* is 256K enough? udev uses 16MB! */
874 device_fd.reset(uevent_open_socket(256 * 1024, true));
875 if (device_fd == -1) {
876 return;
877 }
878 fcntl(device_fd, F_SETFL, O_NONBLOCK);
Sandeep Patil971a4602017-02-15 13:37:52 -0800879 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700880
Elliott Hughes56a06562015-03-28 11:23:32 -0700881 if (access(COLDBOOT_DONE, F_OK) == 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700882 LOG(VERBOSE) << "Skipping coldboot, already done!";
Elliott Hughes56a06562015-03-28 11:23:32 -0700883 return;
Colin Crossf83d0b92010-04-21 12:04:20 -0700884 }
Elliott Hughes56a06562015-03-28 11:23:32 -0700885
886 Timer t;
Sandeep Patil957e4ab2017-02-07 18:11:37 -0800887 coldboot_action_t act;
888 if (!path) {
889 act = coldboot("/sys/class", fn);
890 if (!should_stop_coldboot(act)) {
891 act = coldboot("/sys/block", fn);
892 if (!should_stop_coldboot(act)) {
893 act = coldboot("/sys/devices", fn);
894 }
895 }
896 } else {
897 act = coldboot(path, fn);
898 }
899
900 // If we have a callback, then do as it says. If no, then the default is
901 // to always create COLDBOOT_DONE file.
902 if (!fn || (act == COLDBOOT_FINISH)) {
903 close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
904 }
905
Elliott Hughes331cf2f2016-11-29 19:20:58 +0000906 LOG(INFO) << "Coldboot took " << t;
Colin Cross0dd7ca62010-04-13 19:25:51 -0700907}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700908
Sandeep Patil35403eb2017-02-08 20:27:12 -0800909void device_close() {
Tom Cherry1ab8f552017-04-06 14:41:30 -0700910 platform_devices.clear();
Sandeep Patil35403eb2017-02-08 20:27:12 -0800911 device_fd.reset();
912}
913
Elliott Hughes632e99a2016-11-12 11:44:16 -0800914int get_device_fd() {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700915 return device_fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700916}