blob: ed15fea9397ced328ec3ef5f8b229dc5593f87ed [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 Cyril Hrubis <chrubis@suse.cz>
*/
/*
* Very simple uevent netlink socket test.
*
* We fork a child that listens for a kernel events while parents creates and
* removes a virtual mouse which produces add and remove event for the device
* itself and for two event handlers called eventX and mouseY.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/sysmacros.h>
#include <linux/uinput.h>
#include "tst_test.h"
#include "tst_uinput.h"
#include "uevent.h"
static int mouse_fd;
static void create_uinput_mouse(void)
{
mouse_fd = open_uinput();
setup_mouse_events(mouse_fd);
create_input_device(mouse_fd);
}
static void destroy_uinput_mouse(void)
{
destroy_input_device(mouse_fd);
}
static void get_minor_major(char *device, char *minor, char *major, size_t buf_sizes)
{
char path[1024];
struct stat stbuf;
snprintf(path, sizeof(path), "/dev/input/%s", device);
SAFE_STAT(path, &stbuf);
snprintf(major, buf_sizes, "MAJOR=%i", major(stbuf.st_rdev));
snprintf(minor, buf_sizes, "MINOR=%i", minor(stbuf.st_rdev));
}
#define MINOR_MAJOR_SIZE 32
static void verify_uevent(void)
{
int pid, fd;
char add_msg[1024];
char rem_msg[1024];
char dev_path[1024];
char add_msg_event1[1024];
char rem_msg_event1[1024];
char dev_path_event1[1024];
char add_msg_event2[1024];
char rem_msg_event2[1024];
char dev_path_event2[1024];
char dev_name1[1024];
char dev_name2[1024];
char minor_event1[MINOR_MAJOR_SIZE];
char minor_event2[MINOR_MAJOR_SIZE];
char major_event1[MINOR_MAJOR_SIZE];
char major_event2[MINOR_MAJOR_SIZE];
char *handlers, *handler1, *handler2, *sysname;
struct uevent_desc add = {
.msg = add_msg,
.value_cnt = 7,
.values = (const char*[]) {
"ACTION=add",
dev_path,
"SUBSYSTEM=input",
"NAME=\"virtual-device-ltp\"",
"PROP=0",
"EV=7",
"REL=3",
}
};
struct uevent_desc add_event1 = {
.msg = add_msg_event1,
.value_cnt = 6,
.values = (const char*[]) {
"ACTION=add",
"SUBSYSTEM=input",
dev_name1,
dev_path_event1,
minor_event1,
major_event1,
}
};
struct uevent_desc add_event2 = {
.msg = add_msg_event2,
.value_cnt = 6,
.values = (const char*[]) {
"ACTION=add",
"SUBSYSTEM=input",
dev_name2,
dev_path_event2,
minor_event2,
major_event2,
}
};
struct uevent_desc rem_event1 = {
.msg = rem_msg_event1,
.value_cnt = 6,
.values = (const char*[]) {
"ACTION=remove",
"SUBSYSTEM=input",
dev_name1,
dev_path_event1,
minor_event1,
major_event1,
}
};
struct uevent_desc rem_event2 = {
.msg = rem_msg_event2,
.value_cnt = 6,
.values = (const char*[]) {
"ACTION=remove",
"SUBSYSTEM=input",
dev_name2,
dev_path_event2,
minor_event2,
major_event2,
}
};
struct uevent_desc rem = {
.msg = rem_msg,
.value_cnt = 7,
.values = (const char*[]) {
"ACTION=remove",
dev_path,
"SUBSYSTEM=input",
"NAME=\"virtual-device-ltp\"",
"PROP=0",
"EV=7",
"REL=3",
}
};
const struct uevent_desc *const uevents[] = {
&add,
&add_event1,
&add_event2,
&rem_event1,
&rem_event2,
&rem,
NULL
};
fd = open_uevent_netlink();
create_uinput_mouse();
sysname = get_input_field_value('S');
handlers = get_input_field_value('H');
if (!sysname)
tst_brk(TBROK, "Expected /devices/virtual/input/inputN sysname!");
tst_res(TINFO, "Sysname: %s", sysname);
tst_res(TINFO, "Handlers: %s", handlers);
handler1 = strtok(handlers, " ");
if (!handler1)
tst_brk(TBROK, "Expected mouseX and eventY handlers!");
get_minor_major(handler1, minor_event1, major_event1, MINOR_MAJOR_SIZE);
handler2 = strtok(NULL, " ");
if (!handler2)
tst_brk(TBROK, "Expected mouseX and eventY handlers!");
get_minor_major(handler2, minor_event2, major_event2, MINOR_MAJOR_SIZE);
destroy_uinput_mouse();
snprintf(add_msg, sizeof(add_msg), "add@%s", sysname);
snprintf(rem_msg, sizeof(rem_msg), "remove@%s", sysname);
snprintf(dev_path, sizeof(dev_path), "DEVPATH=%s", sysname);
snprintf(add_msg_event1, sizeof(add_msg_event1),
"add@%s/%s", sysname, handler1);
snprintf(rem_msg_event1, sizeof(rem_msg_event1),
"remove@%s/%s", sysname, handler1);
snprintf(dev_path_event1, sizeof(dev_path_event1),
"DEVPATH=%s/%s", sysname, handler1);
snprintf(dev_name1, sizeof(dev_name1),
"DEVNAME=input/%s", handler1);
snprintf(add_msg_event2, sizeof(add_msg_event2),
"add@%s/%s", sysname, handler2);
snprintf(rem_msg_event2, sizeof(rem_msg_event2),
"remove@%s/%s", sysname, handler2);
snprintf(dev_path_event2, sizeof(dev_path_event2),
"DEVPATH=%s/%s", sysname, handler2);
snprintf(dev_name2, sizeof(dev_name2),
"DEVNAME=input/%s", handler2);
free(sysname);
free(handlers);
pid = SAFE_FORK();
if (!pid) {
wait_for_uevents(fd, uevents);
exit(0);
}
SAFE_CLOSE(fd);
wait_for_pid(pid);
}
static struct tst_test test = {
.test_all = verify_uevent,
.forks_child = 1,
.needs_checkpoints = 1,
.needs_drivers = (const char *const[]) {
"uinput",
NULL
},
.needs_root = 1,
};