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