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