blob: 24b14c44dc4c82a7db3dbee65cfaad09461e8d6d [file] [log] [blame]
Tom Cherryed506f72017-05-25 15:58:59 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "uevent_listener.h"
18
19#include <fcntl.h>
20#include <poll.h>
21#include <string.h>
22#include <unistd.h>
23
24#include <memory>
25
26#include <android-base/logging.h>
27#include <cutils/uevent.h>
28
Tom Cherry81f5d3e2017-06-22 12:53:17 -070029namespace android {
30namespace init {
31
Tom Cherryed506f72017-05-25 15:58:59 -070032static void ParseEvent(const char* msg, Uevent* uevent) {
33 uevent->partition_num = -1;
34 uevent->major = -1;
35 uevent->minor = -1;
36 uevent->action.clear();
37 uevent->path.clear();
38 uevent->subsystem.clear();
39 uevent->firmware.clear();
40 uevent->partition_name.clear();
41 uevent->device_name.clear();
42 // currently ignoring SEQNUM
43 while (*msg) {
44 if (!strncmp(msg, "ACTION=", 7)) {
45 msg += 7;
46 uevent->action = msg;
47 } else if (!strncmp(msg, "DEVPATH=", 8)) {
48 msg += 8;
49 uevent->path = msg;
50 } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
51 msg += 10;
52 uevent->subsystem = msg;
53 } else if (!strncmp(msg, "FIRMWARE=", 9)) {
54 msg += 9;
55 uevent->firmware = msg;
56 } else if (!strncmp(msg, "MAJOR=", 6)) {
57 msg += 6;
58 uevent->major = atoi(msg);
59 } else if (!strncmp(msg, "MINOR=", 6)) {
60 msg += 6;
61 uevent->minor = atoi(msg);
62 } else if (!strncmp(msg, "PARTN=", 6)) {
63 msg += 6;
64 uevent->partition_num = atoi(msg);
65 } else if (!strncmp(msg, "PARTNAME=", 9)) {
66 msg += 9;
67 uevent->partition_name = msg;
68 } else if (!strncmp(msg, "DEVNAME=", 8)) {
69 msg += 8;
70 uevent->device_name = msg;
71 }
72
73 // advance to after the next \0
74 while (*msg++)
75 ;
76 }
77
78 if (LOG_UEVENTS) {
79 LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
80 << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
81 << ", " << uevent->minor << " }";
82 }
83}
84
85UeventListener::UeventListener() {
Daniel Mentzbd93ad52018-03-07 20:54:47 -080086 // is 2MB enough? udev uses 128MB!
87 device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
Tom Cherryed506f72017-05-25 15:58:59 -070088 if (device_fd_ == -1) {
89 LOG(FATAL) << "Could not open uevent socket";
90 }
91
92 fcntl(device_fd_, F_SETFL, O_NONBLOCK);
93}
94
95bool UeventListener::ReadUevent(Uevent* uevent) const {
96 char msg[UEVENT_MSG_LEN + 2];
97 int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
98 if (n <= 0) {
99 if (errno != EAGAIN && errno != EWOULDBLOCK) {
100 LOG(ERROR) << "Error reading from Uevent Fd";
101 }
102 return false;
103 }
104 if (n >= UEVENT_MSG_LEN) {
105 LOG(ERROR) << "Uevent overflowed buffer, discarding";
106 // Return true here even if we discard as we may have more uevents pending and we
107 // want to keep processing them.
108 return true;
109 }
110
111 msg[n] = '\0';
112 msg[n + 1] = '\0';
113
114 ParseEvent(msg, uevent);
115
116 return true;
117}
118
119// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
120// to regenerate device add uevents that have already happened. This is particularly useful when
121// starting ueventd, to regenerate all of the uevents that it had previously missed.
122//
123// We drain any pending events from the netlink socket every time we poke another uevent file to
124// make sure we don't overrun the socket's buffer.
125//
126
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700127ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
128 const ListenerCallback& callback) const {
Tom Cherryed506f72017-05-25 15:58:59 -0700129 int dfd = dirfd(d);
130
131 int fd = openat(dfd, "uevent", O_WRONLY);
132 if (fd >= 0) {
133 write(fd, "add\n", 4);
134 close(fd);
135
136 Uevent uevent;
137 while (ReadUevent(&uevent)) {
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700138 if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
Tom Cherryed506f72017-05-25 15:58:59 -0700139 }
140 }
141
142 dirent* de;
143 while ((de = readdir(d)) != nullptr) {
144 if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
145
146 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
147 if (fd < 0) continue;
148
149 std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
150 if (d2 == 0) {
151 close(fd);
152 } else {
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700153 if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
154 return ListenerAction::kStop;
Tom Cherryed506f72017-05-25 15:58:59 -0700155 }
156 }
157 }
158
159 // default is always to continue looking for uevents
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700160 return ListenerAction::kContinue;
Tom Cherryed506f72017-05-25 15:58:59 -0700161}
162
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700163ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
164 const ListenerCallback& callback) const {
Tom Cherryed506f72017-05-25 15:58:59 -0700165 std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700166 if (!d) return ListenerAction::kContinue;
Tom Cherryed506f72017-05-25 15:58:59 -0700167
168 return RegenerateUeventsForDir(d.get(), callback);
169}
170
Tom Cherryd2fd54e2017-06-07 14:32:30 -0700171static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
Tom Cherryed506f72017-05-25 15:58:59 -0700172
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700173void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
Tom Cherryed506f72017-05-25 15:58:59 -0700174 for (const auto path : kRegenerationPaths) {
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700175 if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
Tom Cherryed506f72017-05-25 15:58:59 -0700176 }
177}
178
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700179void UeventListener::Poll(const ListenerCallback& callback,
180 const std::optional<std::chrono::milliseconds> relative_timeout) const {
181 using namespace std::chrono;
182
Tom Cherryed506f72017-05-25 15:58:59 -0700183 pollfd ufd;
184 ufd.events = POLLIN;
185 ufd.fd = device_fd_;
186
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700187 auto start_time = steady_clock::now();
188
Tom Cherryed506f72017-05-25 15:58:59 -0700189 while (true) {
190 ufd.revents = 0;
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700191
192 int timeout_ms = -1;
193 if (relative_timeout) {
194 auto now = steady_clock::now();
195 auto time_elapsed = duration_cast<milliseconds>(now - start_time);
196 if (time_elapsed > *relative_timeout) return;
197
198 auto remaining_timeout = *relative_timeout - time_elapsed;
199 timeout_ms = remaining_timeout.count();
200 }
201
202 int nr = poll(&ufd, 1, timeout_ms);
203 if (nr == 0) return;
204 if (nr < 0) {
205 PLOG(ERROR) << "poll() of uevent socket failed, continuing";
Tom Cherryed506f72017-05-25 15:58:59 -0700206 continue;
207 }
208 if (ufd.revents & POLLIN) {
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700209 // We're non-blocking, so if we receive a poll event keep processing until
Tom Cherryed506f72017-05-25 15:58:59 -0700210 // we have exhausted all uevent messages.
211 Uevent uevent;
212 while (ReadUevent(&uevent)) {
Sandeep Patil4cbedee2017-06-21 13:02:57 -0700213 if (callback(uevent) == ListenerAction::kStop) return;
Tom Cherryed506f72017-05-25 15:58:59 -0700214 }
215 }
216 }
217}
Tom Cherry81f5d3e2017-06-22 12:53:17 -0700218
219} // namespace init
220} // namespace android