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