1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2019 Cyril Hrubis <[email protected]>
4 * Copyright (c) Linux Test Project, 2019-2023
5 */
6
7 /*\
8 * [Description]
9 * Very simple uevent netlink socket test.
10 *
11 * We fork a child that listens for a kernel events while parents creates and
12 * removes a virtual mouse which produces add and remove event for the device
13 * itself and for two event handlers called eventX and mouseY.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <sys/wait.h>
19 #include <sys/sysmacros.h>
20 #include <linux/uinput.h>
21 #include "tst_test.h"
22 #include "tst_uinput.h"
23 #include "uevent.h"
24
25 static int mouse_fd;
26
create_uinput_mouse(void)27 static void create_uinput_mouse(void)
28 {
29 mouse_fd = open_uinput();
30 setup_mouse_events(mouse_fd);
31 create_input_device(mouse_fd);
32 }
33
destroy_uinput_mouse(void)34 static void destroy_uinput_mouse(void)
35 {
36 destroy_input_device(mouse_fd);
37 }
38
get_minor_major(char * device,char * minor,char * major,size_t buf_sizes)39 static void get_minor_major(char *device, char *minor, char *major, size_t buf_sizes)
40 {
41 char path[1024];
42 struct stat stbuf;
43
44 snprintf(path, sizeof(path), "/dev/input/%s", device);
45
46 SAFE_STAT(path, &stbuf);
47
48 snprintf(major, buf_sizes, "MAJOR=%i", major(stbuf.st_rdev));
49 snprintf(minor, buf_sizes, "MINOR=%i", minor(stbuf.st_rdev));
50 }
51
52 #define MINOR_MAJOR_SIZE 32
53
verify_uevent(void)54 static void verify_uevent(void)
55 {
56 int pid, fd;
57 char add_msg[1024];
58 char rem_msg[1024];
59 char dev_path[1024];
60 char add_msg_event1[1024];
61 char rem_msg_event1[1024];
62 char dev_path_event1[1024];
63 char add_msg_event2[1024];
64 char rem_msg_event2[1024];
65 char dev_path_event2[1024];
66 char dev_name1[1024];
67 char dev_name2[1024];
68
69 char minor_event1[MINOR_MAJOR_SIZE];
70 char minor_event2[MINOR_MAJOR_SIZE];
71 char major_event1[MINOR_MAJOR_SIZE];
72 char major_event2[MINOR_MAJOR_SIZE];
73
74 char *handlers, *handler1, *handler2, *sysname;
75 struct uevent_desc add = {
76 .msg = add_msg,
77 .value_cnt = 7,
78 .values = (const char*[]) {
79 "ACTION=add",
80 dev_path,
81 "SUBSYSTEM=input",
82 "NAME=\"virtual-device-ltp\"",
83 "PROP=0",
84 "EV=7",
85 "REL=3",
86 }
87 };
88
89 struct uevent_desc add_event1 = {
90 .msg = add_msg_event1,
91 .value_cnt = 6,
92 .values = (const char*[]) {
93 "ACTION=add",
94 "SUBSYSTEM=input",
95 dev_name1,
96 dev_path_event1,
97 minor_event1,
98 major_event1,
99 }
100 };
101
102 struct uevent_desc add_event2 = {
103 .msg = add_msg_event2,
104 .value_cnt = 6,
105 .values = (const char*[]) {
106 "ACTION=add",
107 "SUBSYSTEM=input",
108 dev_name2,
109 dev_path_event2,
110 minor_event2,
111 major_event2,
112 }
113 };
114
115 struct uevent_desc rem_event1 = {
116 .msg = rem_msg_event1,
117 .value_cnt = 6,
118 .values = (const char*[]) {
119 "ACTION=remove",
120 "SUBSYSTEM=input",
121 dev_name1,
122 dev_path_event1,
123 minor_event1,
124 major_event1,
125 }
126 };
127
128 struct uevent_desc rem_event2 = {
129 .msg = rem_msg_event2,
130 .value_cnt = 6,
131 .values = (const char*[]) {
132 "ACTION=remove",
133 "SUBSYSTEM=input",
134 dev_name2,
135 dev_path_event2,
136 minor_event2,
137 major_event2,
138 }
139 };
140
141 struct uevent_desc rem = {
142 .msg = rem_msg,
143 .value_cnt = 7,
144 .values = (const char*[]) {
145 "ACTION=remove",
146 dev_path,
147 "SUBSYSTEM=input",
148 "NAME=\"virtual-device-ltp\"",
149 "PROP=0",
150 "EV=7",
151 "REL=3",
152 }
153 };
154
155 const struct uevent_desc *const uevents[] = {
156 &add,
157 &add_event1,
158 &add_event2,
159 &rem_event1,
160 &rem_event2,
161 &rem,
162 NULL
163 };
164
165 fd = open_uevent_netlink();
166
167 create_uinput_mouse();
168
169 sysname = get_input_field_value('S');
170 handlers = get_input_field_value('H');
171
172 if (!sysname)
173 tst_brk(TBROK, "Expected /devices/virtual/input/inputN sysname!");
174
175 tst_res(TINFO, "Sysname: %s", sysname);
176 tst_res(TINFO, "Handlers: %s", handlers);
177
178 handler1 = strtok(handlers, " ");
179 if (!handler1)
180 tst_brk(TBROK, "Expected mouseX and eventY handlers!");
181
182 get_minor_major(handler1, minor_event1, major_event1, MINOR_MAJOR_SIZE);
183
184 handler2 = strtok(NULL, " ");
185 if (!handler2)
186 tst_brk(TBROK, "Expected mouseX and eventY handlers!");
187
188 get_minor_major(handler2, minor_event2, major_event2, MINOR_MAJOR_SIZE);
189
190 destroy_uinput_mouse();
191
192 snprintf(add_msg, sizeof(add_msg), "add@%s", sysname);
193
194 snprintf(rem_msg, sizeof(rem_msg), "remove@%s", sysname);
195
196 snprintf(dev_path, sizeof(dev_path), "DEVPATH=%s", sysname);
197
198 snprintf(add_msg_event1, sizeof(add_msg_event1),
199 "add@%s/%s", sysname, handler1);
200
201 snprintf(rem_msg_event1, sizeof(rem_msg_event1),
202 "remove@%s/%s", sysname, handler1);
203
204 snprintf(dev_path_event1, sizeof(dev_path_event1),
205 "DEVPATH=%s/%s", sysname, handler1);
206
207 snprintf(dev_name1, sizeof(dev_name1),
208 "DEVNAME=input/%s", handler1);
209
210
211 snprintf(add_msg_event2, sizeof(add_msg_event2),
212 "add@%s/%s", sysname, handler2);
213
214 snprintf(rem_msg_event2, sizeof(rem_msg_event2),
215 "remove@%s/%s", sysname, handler2);
216
217 snprintf(dev_path_event2, sizeof(dev_path_event2),
218 "DEVPATH=%s/%s", sysname, handler2);
219
220 snprintf(dev_name2, sizeof(dev_name2),
221 "DEVNAME=input/%s", handler2);
222
223 free(sysname);
224 free(handlers);
225
226 pid = SAFE_FORK();
227 if (!pid) {
228 wait_for_uevents(fd, uevents);
229 exit(0);
230 }
231
232 SAFE_CLOSE(fd);
233 wait_for_pid(pid);
234 }
235
236 static struct tst_test test = {
237 .test_all = verify_uevent,
238 .forks_child = 1,
239 .needs_checkpoints = 1,
240 .needs_drivers = (const char *const[]) {
241 "uinput",
242 NULL
243 },
244 .needs_root = 1,
245 };
246