• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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