xref: /freebsd/sys/netlink/netlink_sysevent.c (revision a034c0aeccd899a5872333625771df278e0a8ba7)
18a2af0b4SBaptiste Daroussin /*-
28a2af0b4SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
38a2af0b4SBaptiste Daroussin  *
48a2af0b4SBaptiste Daroussin  * Copyright (c) 2023 Baptiste Daroussin <bapt@FreeBSD.org>
58a2af0b4SBaptiste Daroussin  *
68a2af0b4SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
78a2af0b4SBaptiste Daroussin  * modification, are permitted provided that the following conditions
88a2af0b4SBaptiste Daroussin  * are met:
98a2af0b4SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
108a2af0b4SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
118a2af0b4SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
128a2af0b4SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
138a2af0b4SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
148a2af0b4SBaptiste Daroussin  *
158a2af0b4SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168a2af0b4SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178a2af0b4SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
188a2af0b4SBaptiste Daroussin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
198a2af0b4SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208a2af0b4SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
218a2af0b4SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
228a2af0b4SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
238a2af0b4SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248a2af0b4SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258a2af0b4SBaptiste Daroussin  * SUCH DAMAGE.
268a2af0b4SBaptiste Daroussin  */
278a2af0b4SBaptiste Daroussin 
288a2af0b4SBaptiste Daroussin #include <sys/param.h>
298a2af0b4SBaptiste Daroussin #include <sys/types.h>
308a2af0b4SBaptiste Daroussin #include <sys/devctl.h>
318a2af0b4SBaptiste Daroussin #include <sys/errno.h>
328a2af0b4SBaptiste Daroussin #include <sys/module.h>
338a2af0b4SBaptiste Daroussin #include <sys/kernel.h>
348a2af0b4SBaptiste Daroussin #include <sys/malloc.h>
358a2af0b4SBaptiste Daroussin #include <net/vnet.h>
368a2af0b4SBaptiste Daroussin #include <netlink/netlink.h>
378a2af0b4SBaptiste Daroussin #include <netlink/netlink_ctl.h>
388a2af0b4SBaptiste Daroussin #include <netlink/netlink_generic.h>
39cef0bbaeSBaptiste Daroussin #include <netlink/netlink_sysevent.h>
408a2af0b4SBaptiste Daroussin 
418a2af0b4SBaptiste Daroussin #define DEBUG_MOD_NAME  nl_sysevent
428a2af0b4SBaptiste Daroussin #define DEBUG_MAX_LEVEL LOG_DEBUG3
438a2af0b4SBaptiste Daroussin #include <netlink/netlink_debug.h>
448a2af0b4SBaptiste Daroussin _DECLARE_DEBUG(LOG_INFO);
458a2af0b4SBaptiste Daroussin 
468a2af0b4SBaptiste Daroussin MALLOC_DEFINE(M_NLSE, "nlsysevent", "Memory used for Netlink sysevent");
478a2af0b4SBaptiste Daroussin #define	NLSE_FAMILY_NAME	"nlsysevent"
488a2af0b4SBaptiste Daroussin static uint32_t ctrl_family_id;
498a2af0b4SBaptiste Daroussin 
503f9c093dSBaptiste Daroussin #define MAX_SYSEVENT_GROUPS	64
513f9c093dSBaptiste Daroussin static struct sysevent_group {
528a2af0b4SBaptiste Daroussin 	char *name;
538a2af0b4SBaptiste Daroussin 	uint32_t id;
543f9c093dSBaptiste Daroussin } sysevent_groups[MAX_SYSEVENT_GROUPS] = {};
558a2af0b4SBaptiste Daroussin 
56f40cd16bSEd Maste static const char *devctl_systems[] = {
57f40cd16bSEd Maste 	"ACPI",
58f40cd16bSEd Maste 	"AEON",
59f40cd16bSEd Maste 	"CAM",
60f40cd16bSEd Maste 	"CARP",
61f40cd16bSEd Maste 	"coretemp",
62f40cd16bSEd Maste 	"DEVFS",
63f40cd16bSEd Maste 	"device",
64f40cd16bSEd Maste 	"ETHERNET",
65f40cd16bSEd Maste 	"GEOM",
66f40cd16bSEd Maste 	"HYPERV_NIC_VF",
67f40cd16bSEd Maste 	"IFNET",
68f40cd16bSEd Maste 	"INFINIBAND",
69f40cd16bSEd Maste 	"KERNEL",
70f40cd16bSEd Maste 	"nvme",
71f40cd16bSEd Maste 	"PMU",
72f40cd16bSEd Maste 	"RCTL",
73f40cd16bSEd Maste 	"USB",
74f40cd16bSEd Maste 	"VFS",
75f40cd16bSEd Maste 	"VT",
76f40cd16bSEd Maste 	"ZFS",
77f40cd16bSEd Maste };
78f40cd16bSEd Maste 
798a2af0b4SBaptiste Daroussin static void
803f9c093dSBaptiste Daroussin sysevent_write(struct sysevent_group *se, const char *subsystem, const char *type,
818a2af0b4SBaptiste Daroussin     const char *data)
828a2af0b4SBaptiste Daroussin {
83*a034c0aeSGleb Smirnoff 	struct nl_writer nw;
848a2af0b4SBaptiste Daroussin 
85*a034c0aeSGleb Smirnoff 	if (!nl_writer_group(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id,
86*a034c0aeSGleb Smirnoff 	    false)) {
878a2af0b4SBaptiste Daroussin 		NL_LOG(LOG_DEBUG, "error allocating group writer");
888a2af0b4SBaptiste Daroussin 		return;
898a2af0b4SBaptiste Daroussin 	}
908a2af0b4SBaptiste Daroussin 	struct nlmsghdr hdr = { .nlmsg_type = ctrl_family_id };
918a2af0b4SBaptiste Daroussin 	if (!nlmsg_reply(&nw, &hdr, sizeof(struct genlmsghdr))) {
928a2af0b4SBaptiste Daroussin 		return;
938a2af0b4SBaptiste Daroussin 	}
948a2af0b4SBaptiste Daroussin 
958a2af0b4SBaptiste Daroussin 	struct genlmsghdr *ghdr = nlmsg_reserve_object(&nw, struct genlmsghdr);
968a2af0b4SBaptiste Daroussin 	if (ghdr == NULL) {
978a2af0b4SBaptiste Daroussin 		NL_LOG(LOG_DEBUG, "unable to allocate memory");
988a2af0b4SBaptiste Daroussin 		return;
998a2af0b4SBaptiste Daroussin 	}
1008a2af0b4SBaptiste Daroussin 	ghdr->version = 0;
10199084611SBaptiste Daroussin 	ghdr->cmd = NLSE_CMD_NEWEVENT;
1028a2af0b4SBaptiste Daroussin 	ghdr->reserved = 0;
1038a2af0b4SBaptiste Daroussin 	nlattr_add_string(&nw, NLSE_ATTR_SYSTEM, se->name);
1048a2af0b4SBaptiste Daroussin 	nlattr_add_string(&nw, NLSE_ATTR_SUBSYSTEM, subsystem);
1058a2af0b4SBaptiste Daroussin 	nlattr_add_string(&nw, NLSE_ATTR_TYPE, type);
1068a2af0b4SBaptiste Daroussin 	if (data != NULL)
1078a2af0b4SBaptiste Daroussin 		nlattr_add_string(&nw, NLSE_ATTR_DATA, data);
1088a2af0b4SBaptiste Daroussin 	nlmsg_end(&nw);
1098a2af0b4SBaptiste Daroussin 	nlmsg_flush(&nw);
1108a2af0b4SBaptiste Daroussin }
1118a2af0b4SBaptiste Daroussin 
1128a2af0b4SBaptiste Daroussin static void
1130bcb3ebdSBaptiste Daroussin sysevent_new_group(size_t index, const char *name)
1140bcb3ebdSBaptiste Daroussin {
1150bcb3ebdSBaptiste Daroussin 	if (index >= MAX_SYSEVENT_GROUPS) {
1160bcb3ebdSBaptiste Daroussin 		NL_LOG(LOG_WARNING, "impossible to add the event %s, "
1170bcb3ebdSBaptiste Daroussin 		    "too many event groups\n", name);
1180bcb3ebdSBaptiste Daroussin 		return;
1190bcb3ebdSBaptiste Daroussin 	}
1200bcb3ebdSBaptiste Daroussin 	sysevent_groups[index].name = strdup(name, M_NLSE);
1210bcb3ebdSBaptiste Daroussin 	sysevent_groups[index].id = genl_register_group(NLSE_FAMILY_NAME, sysevent_groups[index].name);
1220bcb3ebdSBaptiste Daroussin }
1230bcb3ebdSBaptiste Daroussin 
1240bcb3ebdSBaptiste Daroussin static struct sysevent_group *
1250bcb3ebdSBaptiste Daroussin sysevent_get_group(const char *system)
1260bcb3ebdSBaptiste Daroussin {
1270bcb3ebdSBaptiste Daroussin 	for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
1280bcb3ebdSBaptiste Daroussin 		if (sysevent_groups[i].name == NULL) {
1290bcb3ebdSBaptiste Daroussin 			sysevent_new_group(i, system);
1300bcb3ebdSBaptiste Daroussin 			return (&sysevent_groups[i]);
1310bcb3ebdSBaptiste Daroussin 		}
1320bcb3ebdSBaptiste Daroussin 		if (strcmp(sysevent_groups[i].name, system) == 0)
1330bcb3ebdSBaptiste Daroussin 			return (&sysevent_groups[i]);
1340bcb3ebdSBaptiste Daroussin 	}
1350bcb3ebdSBaptiste Daroussin 
1360bcb3ebdSBaptiste Daroussin 	return (NULL);
1370bcb3ebdSBaptiste Daroussin }
1380bcb3ebdSBaptiste Daroussin 
1390bcb3ebdSBaptiste Daroussin static void
1408a2af0b4SBaptiste Daroussin sysevent_send(const char *system, const char *subsystem, const char *type,
1418a2af0b4SBaptiste Daroussin     const char *data)
1428a2af0b4SBaptiste Daroussin {
1430bcb3ebdSBaptiste Daroussin 	struct sysevent_group *se = sysevent_get_group(system);
1448a2af0b4SBaptiste Daroussin 
1458a2af0b4SBaptiste Daroussin 	if (se == NULL) {
1468a2af0b4SBaptiste Daroussin 		NL_LOG(LOG_WARNING, "impossible to add the event %s, "
1470bcb3ebdSBaptiste Daroussin 		    "too many event groups\n", system);
1488a2af0b4SBaptiste Daroussin 		return;
1498a2af0b4SBaptiste Daroussin 	}
1508a2af0b4SBaptiste Daroussin 
1518a2af0b4SBaptiste Daroussin 	CURVNET_SET(vnet0);
1528a2af0b4SBaptiste Daroussin 	sysevent_write(se, subsystem, type, data);
1538a2af0b4SBaptiste Daroussin 	CURVNET_RESTORE();
1548a2af0b4SBaptiste Daroussin }
1558a2af0b4SBaptiste Daroussin 
1568a2af0b4SBaptiste Daroussin static void
1578a2af0b4SBaptiste Daroussin nlsysevent_load(void)
1588a2af0b4SBaptiste Daroussin {
1598a2af0b4SBaptiste Daroussin 	devctl_set_notify_hook(sysevent_send);
1608a2af0b4SBaptiste Daroussin 	ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX);
1618a2af0b4SBaptiste Daroussin 	for (size_t i = 0; i < nitems(devctl_systems); i++) {
1623f9c093dSBaptiste Daroussin 		if (i >= MAX_SYSEVENT_GROUPS) {
1638a2af0b4SBaptiste Daroussin 			NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]);
1648a2af0b4SBaptiste Daroussin 			continue;
1658a2af0b4SBaptiste Daroussin 		}
1660bcb3ebdSBaptiste Daroussin 		sysevent_new_group(i, devctl_systems[i]);
1678a2af0b4SBaptiste Daroussin 	}
1688a2af0b4SBaptiste Daroussin }
1698a2af0b4SBaptiste Daroussin 
1708a2af0b4SBaptiste Daroussin static void
1718a2af0b4SBaptiste Daroussin nlsysevent_unload(void)
1728a2af0b4SBaptiste Daroussin {
1738a2af0b4SBaptiste Daroussin 	devctl_unset_notify_hook();
1748a2af0b4SBaptiste Daroussin 	genl_unregister_family(NLSE_FAMILY_NAME);
1753f9c093dSBaptiste Daroussin 	for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
1763f9c093dSBaptiste Daroussin 		if (sysevent_groups[i].name == NULL)
1778a2af0b4SBaptiste Daroussin 			break;
1783f9c093dSBaptiste Daroussin 		free(sysevent_groups[i].name, M_NLSE);
1798a2af0b4SBaptiste Daroussin 	}
1808a2af0b4SBaptiste Daroussin }
1818a2af0b4SBaptiste Daroussin 
1828a2af0b4SBaptiste Daroussin static int
1838a2af0b4SBaptiste Daroussin nlsysevent_loader(module_t mod __unused, int what, void *priv __unused)
1848a2af0b4SBaptiste Daroussin {
1858a2af0b4SBaptiste Daroussin 	int err = 0;
1868a2af0b4SBaptiste Daroussin 
1878a2af0b4SBaptiste Daroussin 	switch (what) {
1888a2af0b4SBaptiste Daroussin 	case MOD_LOAD:
1898a2af0b4SBaptiste Daroussin 		nlsysevent_load();
1908a2af0b4SBaptiste Daroussin 		break;
1918a2af0b4SBaptiste Daroussin 	case MOD_UNLOAD:
1928a2af0b4SBaptiste Daroussin 		nlsysevent_unload();
1938a2af0b4SBaptiste Daroussin 		break;
1948a2af0b4SBaptiste Daroussin 	default:
1958a2af0b4SBaptiste Daroussin 		err = EOPNOTSUPP;
1968a2af0b4SBaptiste Daroussin 		break;
1978a2af0b4SBaptiste Daroussin 	}
1988a2af0b4SBaptiste Daroussin 	return (err);
1998a2af0b4SBaptiste Daroussin }
2008a2af0b4SBaptiste Daroussin static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL};
2018a2af0b4SBaptiste Daroussin 
2028a2af0b4SBaptiste Daroussin DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
2038a2af0b4SBaptiste Daroussin MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1);
2048a2af0b4SBaptiste Daroussin MODULE_VERSION(nlsysevent, 1);
205