1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2019 Cyril Hrubis <chrubis@suse.cz>
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci
6f08c3bdfSopenharmony_ci#ifndef UEVENT_H__
7f08c3bdfSopenharmony_ci#define UEVENT_H__
8f08c3bdfSopenharmony_ci
9f08c3bdfSopenharmony_ci#include "tst_netlink.h"
10f08c3bdfSopenharmony_ci
11f08c3bdfSopenharmony_ci/*
12f08c3bdfSopenharmony_ci * There are two broadcast groups defined for the NETLINK_KOBJECT_UEVENT. The
13f08c3bdfSopenharmony_ci * primary consument of the KERNEL group is udev which handles the hotplug
14f08c3bdfSopenharmony_ci * events and then, once udev does it's magic the events are rebroadcasted to
15f08c3bdfSopenharmony_ci * the UDEV group which is consumed by various daemons in the userspace.
16f08c3bdfSopenharmony_ci */
17f08c3bdfSopenharmony_cienum monitor_netlink_group {
18f08c3bdfSopenharmony_ci	MONITOR_GROUP_NONE,
19f08c3bdfSopenharmony_ci	MONITOR_GROUP_KERNEL,
20f08c3bdfSopenharmony_ci	MONITOR_GROUP_UDEV,
21f08c3bdfSopenharmony_ci};
22f08c3bdfSopenharmony_ci
23f08c3bdfSopenharmony_ci/*
24f08c3bdfSopenharmony_ci * The messages received from the NETLINK_KOBJECT_UEVENT socket are stored as a
25f08c3bdfSopenharmony_ci * sequence of a null-terminated strings. First in the buffer is a summary of a
26f08c3bdfSopenharmony_ci * action i.e. "$ACTION@$DEVPATH" which is then followed by a bunch of
27f08c3bdfSopenharmony_ci * key-value pairs.
28f08c3bdfSopenharmony_ci *
29f08c3bdfSopenharmony_ci * For example attaching a file to loopback device generates event:
30f08c3bdfSopenharmony_ci *
31f08c3bdfSopenharmony_ci * "change@/devices/virtual/block/loop0\0
32f08c3bdfSopenharmony_ci *  ACTION=change\0
33f08c3bdfSopenharmony_ci *  DEVPATH=/devices/virtual/block/loop0\0
34f08c3bdfSopenharmony_ci *  SUBSYSTEM=block\0
35f08c3bdfSopenharmony_ci *  MAJOR=7\0
36f08c3bdfSopenharmony_ci *  MINOR=0\0
37f08c3bdfSopenharmony_ci *  DEVNAME=loop0\0
38f08c3bdfSopenharmony_ci *  DEVTYPE=disk\0
39f08c3bdfSopenharmony_ci *  SEQNUM=2677\0"
40f08c3bdfSopenharmony_ci */
41f08c3bdfSopenharmony_ci
42f08c3bdfSopenharmony_ci/*
43f08c3bdfSopenharmony_ci * Prints uevent.
44f08c3bdfSopenharmony_ci */
45f08c3bdfSopenharmony_cistatic inline void print_uevent(const char *event, int len)
46f08c3bdfSopenharmony_ci{
47f08c3bdfSopenharmony_ci	int consumed = 0;
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_ci	tst_res(TINFO, "Got uevent:");
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_ci	while (consumed < len) {
52f08c3bdfSopenharmony_ci		tst_res(TINFO, "%s", event);
53f08c3bdfSopenharmony_ci		int l = strlen(event) + 1;
54f08c3bdfSopenharmony_ci		consumed += l;
55f08c3bdfSopenharmony_ci		event += l;
56f08c3bdfSopenharmony_ci	}
57f08c3bdfSopenharmony_ci}
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_ci/*
60f08c3bdfSopenharmony_ci * Uevents read from the socket are matched against this description.
61f08c3bdfSopenharmony_ci *
62f08c3bdfSopenharmony_ci * The msg is the overall action description e.g.
63f08c3bdfSopenharmony_ci * "add@/class/input/input4/mouse1" which has to be matched exactly before we
64f08c3bdfSopenharmony_ci * event attempt to check the key-value pairs stored in the values array. The
65f08c3bdfSopenharmony_ci * event is considered to match if all key-value pairs in the values has been
66f08c3bdfSopenharmony_ci * found in the received event.
67f08c3bdfSopenharmony_ci */
68f08c3bdfSopenharmony_cistruct uevent_desc {
69f08c3bdfSopenharmony_ci	const char *msg;
70f08c3bdfSopenharmony_ci	int value_cnt;
71f08c3bdfSopenharmony_ci	const char **values;
72f08c3bdfSopenharmony_ci};
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_cistatic inline int uevent_match(const char *event, int len,
75f08c3bdfSopenharmony_ci                               const struct uevent_desc *uevent)
76f08c3bdfSopenharmony_ci{
77f08c3bdfSopenharmony_ci	int consumed = 0;
78f08c3bdfSopenharmony_ci	int val_matches = 0;
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci	if (memcmp(event, uevent->msg, strlen(uevent->msg)))
81f08c3bdfSopenharmony_ci		return 0;
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_ci	int l = strlen(event) + 1;
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_ci	consumed += l;
86f08c3bdfSopenharmony_ci	event += l;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	while (consumed < len) {
89f08c3bdfSopenharmony_ci		int i;
90f08c3bdfSopenharmony_ci		for (i = 0; i < uevent->value_cnt; i++) {
91f08c3bdfSopenharmony_ci			if (!strcmp(event, uevent->values[i])) {
92f08c3bdfSopenharmony_ci				val_matches++;
93f08c3bdfSopenharmony_ci				break;
94f08c3bdfSopenharmony_ci			}
95f08c3bdfSopenharmony_ci		}
96f08c3bdfSopenharmony_ci
97f08c3bdfSopenharmony_ci		l = strlen(event) + 1;
98f08c3bdfSopenharmony_ci		consumed += l;
99f08c3bdfSopenharmony_ci		event += l;
100f08c3bdfSopenharmony_ci	}
101f08c3bdfSopenharmony_ci
102f08c3bdfSopenharmony_ci	return val_matches == uevent->value_cnt;
103f08c3bdfSopenharmony_ci}
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_cistatic inline int open_uevent_netlink(void)
106f08c3bdfSopenharmony_ci{
107f08c3bdfSopenharmony_ci	int fd;
108f08c3bdfSopenharmony_ci	struct sockaddr_nl nl_addr = {
109f08c3bdfSopenharmony_ci		.nl_family = AF_NETLINK,
110f08c3bdfSopenharmony_ci		.nl_groups = MONITOR_GROUP_KERNEL,
111f08c3bdfSopenharmony_ci	};
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_ci	fd = SAFE_SOCKET(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
114f08c3bdfSopenharmony_ci
115f08c3bdfSopenharmony_ci	SAFE_BIND(fd, (struct sockaddr *)&nl_addr, sizeof(nl_addr));
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	return fd;
118f08c3bdfSopenharmony_ci}
119f08c3bdfSopenharmony_ci
120f08c3bdfSopenharmony_ci/*
121f08c3bdfSopenharmony_ci * Reads events from uevent netlink socket until all expected events passed in
122f08c3bdfSopenharmony_ci * the uevent array are matched.
123f08c3bdfSopenharmony_ci */
124f08c3bdfSopenharmony_cistatic inline void wait_for_uevents(int fd, const struct uevent_desc *const uevents[])
125f08c3bdfSopenharmony_ci{
126f08c3bdfSopenharmony_ci	int i = 0;
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	while (1) {
129f08c3bdfSopenharmony_ci		int len;
130f08c3bdfSopenharmony_ci		char buf[4096];
131f08c3bdfSopenharmony_ci
132f08c3bdfSopenharmony_ci		len = recv(fd, &buf, sizeof(buf), 0);
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_ci		if (len == 0)
135f08c3bdfSopenharmony_ci			continue;
136f08c3bdfSopenharmony_ci
137f08c3bdfSopenharmony_ci		print_uevent(buf, len);
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci		if (uevent_match(buf, len, uevents[i])) {
140f08c3bdfSopenharmony_ci			tst_res(TPASS, "Got expected UEVENT");
141f08c3bdfSopenharmony_ci			if (!uevents[++i]) {
142f08c3bdfSopenharmony_ci				close(fd);
143f08c3bdfSopenharmony_ci				return;
144f08c3bdfSopenharmony_ci			}
145f08c3bdfSopenharmony_ci		}
146f08c3bdfSopenharmony_ci	}
147f08c3bdfSopenharmony_ci}
148f08c3bdfSopenharmony_ci
149f08c3bdfSopenharmony_ci/*
150f08c3bdfSopenharmony_ci * Waits 5 seconds for a child to exit, kills the child after a timeout.
151f08c3bdfSopenharmony_ci */
152f08c3bdfSopenharmony_cistatic inline void wait_for_pid(int pid)
153f08c3bdfSopenharmony_ci{
154f08c3bdfSopenharmony_ci	int status, ret;
155f08c3bdfSopenharmony_ci	int retries = 5000;
156f08c3bdfSopenharmony_ci
157f08c3bdfSopenharmony_ci	do {
158f08c3bdfSopenharmony_ci		ret = waitpid(pid, &status, WNOHANG);
159f08c3bdfSopenharmony_ci		usleep(1000);
160f08c3bdfSopenharmony_ci	} while (ret == 0 && retries--);
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_ci	if (ret == pid) {
163f08c3bdfSopenharmony_ci		if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
164f08c3bdfSopenharmony_ci			return;
165f08c3bdfSopenharmony_ci
166f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Child exited with %s", tst_strstatus(status));
167f08c3bdfSopenharmony_ci	}
168f08c3bdfSopenharmony_ci
169f08c3bdfSopenharmony_ci	SAFE_KILL(pid, SIGKILL);
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_ci	SAFE_WAITPID(pid, NULL, 0);
172f08c3bdfSopenharmony_ci
173f08c3bdfSopenharmony_ci	tst_res(TFAIL, "Did not get all expected UEVENTS");
174f08c3bdfSopenharmony_ci}
175f08c3bdfSopenharmony_ci
176f08c3bdfSopenharmony_ci#endif /* UEVENT_H__ */
177