1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2019 Cyril Hrubis <chrubis@suse.cz>
4 */
5
6 #ifndef UEVENT_H__
7 #define UEVENT_H__
8
9 #include "tst_netlink.h"
10
11 /*
12 * There are two broadcast groups defined for the NETLINK_KOBJECT_UEVENT. The
13 * primary consument of the KERNEL group is udev which handles the hotplug
14 * events and then, once udev does it's magic the events are rebroadcasted to
15 * the UDEV group which is consumed by various daemons in the userspace.
16 */
17 enum monitor_netlink_group {
18 MONITOR_GROUP_NONE,
19 MONITOR_GROUP_KERNEL,
20 MONITOR_GROUP_UDEV,
21 };
22
23 /*
24 * The messages received from the NETLINK_KOBJECT_UEVENT socket are stored as a
25 * sequence of a null-terminated strings. First in the buffer is a summary of a
26 * action i.e. "$ACTION@$DEVPATH" which is then followed by a bunch of
27 * key-value pairs.
28 *
29 * For example attaching a file to loopback device generates event:
30 *
31 * "change@/devices/virtual/block/loop0\0
32 * ACTION=change\0
33 * DEVPATH=/devices/virtual/block/loop0\0
34 * SUBSYSTEM=block\0
35 * MAJOR=7\0
36 * MINOR=0\0
37 * DEVNAME=loop0\0
38 * DEVTYPE=disk\0
39 * SEQNUM=2677\0"
40 */
41
42 /*
43 * Prints uevent.
44 */
print_uevent(const char * event,int len)45 static inline void print_uevent(const char *event, int len)
46 {
47 int consumed = 0;
48
49 tst_res(TINFO, "Got uevent:");
50
51 while (consumed < len) {
52 tst_res(TINFO, "%s", event);
53 int l = strlen(event) + 1;
54 consumed += l;
55 event += l;
56 }
57 }
58
59 /*
60 * Uevents read from the socket are matched against this description.
61 *
62 * The msg is the overall action description e.g.
63 * "add@/class/input/input4/mouse1" which has to be matched exactly before we
64 * event attempt to check the key-value pairs stored in the values array. The
65 * event is considered to match if all key-value pairs in the values has been
66 * found in the received event.
67 */
68 struct uevent_desc {
69 const char *msg;
70 int value_cnt;
71 const char **values;
72 };
73
uevent_match(const char * event,int len,const struct uevent_desc * uevent)74 static inline int uevent_match(const char *event, int len,
75 const struct uevent_desc *uevent)
76 {
77 int consumed = 0;
78 int val_matches = 0;
79
80 if (memcmp(event, uevent->msg, strlen(uevent->msg)))
81 return 0;
82
83 int l = strlen(event) + 1;
84
85 consumed += l;
86 event += l;
87
88 while (consumed < len) {
89 int i;
90 for (i = 0; i < uevent->value_cnt; i++) {
91 if (!strcmp(event, uevent->values[i])) {
92 val_matches++;
93 break;
94 }
95 }
96
97 l = strlen(event) + 1;
98 consumed += l;
99 event += l;
100 }
101
102 return val_matches == uevent->value_cnt;
103 }
104
open_uevent_netlink(void)105 static inline int open_uevent_netlink(void)
106 {
107 int fd;
108 struct sockaddr_nl nl_addr = {
109 .nl_family = AF_NETLINK,
110 .nl_groups = MONITOR_GROUP_KERNEL,
111 };
112
113 fd = SAFE_SOCKET(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
114
115 SAFE_BIND(fd, (struct sockaddr *)&nl_addr, sizeof(nl_addr));
116
117 return fd;
118 }
119
120 /*
121 * Reads events from uevent netlink socket until all expected events passed in
122 * the uevent array are matched.
123 */
wait_for_uevents(int fd,const struct uevent_desc * const uevents[])124 static inline void wait_for_uevents(int fd, const struct uevent_desc *const uevents[])
125 {
126 int i = 0;
127
128 while (1) {
129 int len;
130 char buf[4096];
131
132 len = recv(fd, &buf, sizeof(buf), 0);
133
134 if (len == 0)
135 continue;
136
137 print_uevent(buf, len);
138
139 if (uevent_match(buf, len, uevents[i])) {
140 tst_res(TPASS, "Got expected UEVENT");
141 if (!uevents[++i]) {
142 close(fd);
143 return;
144 }
145 }
146 }
147 }
148
149 /*
150 * Waits 5 seconds for a child to exit, kills the child after a timeout.
151 */
wait_for_pid(int pid)152 static inline void wait_for_pid(int pid)
153 {
154 int status, ret;
155 int retries = 5000;
156
157 do {
158 ret = waitpid(pid, &status, WNOHANG);
159 usleep(1000);
160 } while (ret == 0 && retries--);
161
162 if (ret == pid) {
163 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
164 return;
165
166 tst_res(TFAIL, "Child exitted with %s", tst_strstatus(status));
167 }
168
169 SAFE_KILL(pid, SIGKILL);
170
171 SAFE_WAITPID(pid, NULL, 0);
172
173 tst_res(TFAIL, "Did not get all expected UEVENTS");
174 }
175
176 #endif /* UEVENT_H__ */
177