xref: /freebsd/sys/netlink/netlink_sysevent.c (revision ee507b70f150855cfff0ef1080c38a3c70c1e7cd)
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"
48*ee507b70SGleb Smirnoff static uint16_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
sysevent_write(struct sysevent_group * se,const char * subsystem,const char * type,const char * data)803f9c093dSBaptiste Daroussin sysevent_write(struct sysevent_group *se, const char *subsystem, const char *type,
818a2af0b4SBaptiste Daroussin     const char *data)
828a2af0b4SBaptiste Daroussin {
83a034c0aeSGleb Smirnoff 	struct nl_writer nw;
848a2af0b4SBaptiste Daroussin 
850fda4ffdSGleb Smirnoff 	if (!nl_writer_group(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id, 0,
86a034c0aeSGleb 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
sysevent_new_group(size_t index,const char * name)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);
121*ee507b70SGleb Smirnoff 	sysevent_groups[index].id = genl_register_group(ctrl_family_id,
122*ee507b70SGleb Smirnoff 	    sysevent_groups[index].name);
1230bcb3ebdSBaptiste Daroussin }
1240bcb3ebdSBaptiste Daroussin 
1250bcb3ebdSBaptiste Daroussin static struct sysevent_group *
sysevent_get_group(const char * system)1260bcb3ebdSBaptiste Daroussin sysevent_get_group(const char *system)
1270bcb3ebdSBaptiste Daroussin {
1280bcb3ebdSBaptiste Daroussin 	for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
1290bcb3ebdSBaptiste Daroussin 		if (sysevent_groups[i].name == NULL) {
1300bcb3ebdSBaptiste Daroussin 			sysevent_new_group(i, system);
1310bcb3ebdSBaptiste Daroussin 			return (&sysevent_groups[i]);
1320bcb3ebdSBaptiste Daroussin 		}
1330bcb3ebdSBaptiste Daroussin 		if (strcmp(sysevent_groups[i].name, system) == 0)
1340bcb3ebdSBaptiste Daroussin 			return (&sysevent_groups[i]);
1350bcb3ebdSBaptiste Daroussin 	}
1360bcb3ebdSBaptiste Daroussin 
1370bcb3ebdSBaptiste Daroussin 	return (NULL);
1380bcb3ebdSBaptiste Daroussin }
1390bcb3ebdSBaptiste Daroussin 
1400bcb3ebdSBaptiste Daroussin static void
sysevent_send(const char * system,const char * subsystem,const char * type,const char * data)1418a2af0b4SBaptiste Daroussin sysevent_send(const char *system, const char *subsystem, const char *type,
1428a2af0b4SBaptiste Daroussin     const char *data)
1438a2af0b4SBaptiste Daroussin {
1440bcb3ebdSBaptiste Daroussin 	struct sysevent_group *se = sysevent_get_group(system);
1458a2af0b4SBaptiste Daroussin 
1468a2af0b4SBaptiste Daroussin 	if (se == NULL) {
1478a2af0b4SBaptiste Daroussin 		NL_LOG(LOG_WARNING, "impossible to add the event %s, "
1480bcb3ebdSBaptiste Daroussin 		    "too many event groups\n", system);
1498a2af0b4SBaptiste Daroussin 		return;
1508a2af0b4SBaptiste Daroussin 	}
1518a2af0b4SBaptiste Daroussin 
1528a2af0b4SBaptiste Daroussin 	CURVNET_SET(vnet0);
1538a2af0b4SBaptiste Daroussin 	sysevent_write(se, subsystem, type, data);
1548a2af0b4SBaptiste Daroussin 	CURVNET_RESTORE();
1558a2af0b4SBaptiste Daroussin }
1568a2af0b4SBaptiste Daroussin 
1578a2af0b4SBaptiste Daroussin static void
nlsysevent_load(void)1588a2af0b4SBaptiste Daroussin nlsysevent_load(void)
1598a2af0b4SBaptiste Daroussin {
1608a2af0b4SBaptiste Daroussin 	devctl_set_notify_hook(sysevent_send);
1618a2af0b4SBaptiste Daroussin 	ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX);
1628a2af0b4SBaptiste Daroussin 	for (size_t i = 0; i < nitems(devctl_systems); i++) {
1633f9c093dSBaptiste Daroussin 		if (i >= MAX_SYSEVENT_GROUPS) {
1648a2af0b4SBaptiste Daroussin 			NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]);
1658a2af0b4SBaptiste Daroussin 			continue;
1668a2af0b4SBaptiste Daroussin 		}
1670bcb3ebdSBaptiste Daroussin 		sysevent_new_group(i, devctl_systems[i]);
1688a2af0b4SBaptiste Daroussin 	}
1698a2af0b4SBaptiste Daroussin }
1708a2af0b4SBaptiste Daroussin 
1718a2af0b4SBaptiste Daroussin static void
nlsysevent_unload(void)1728a2af0b4SBaptiste Daroussin nlsysevent_unload(void)
1738a2af0b4SBaptiste Daroussin {
1748a2af0b4SBaptiste Daroussin 	devctl_unset_notify_hook();
175*ee507b70SGleb Smirnoff 	genl_unregister_family(ctrl_family_id);
1763f9c093dSBaptiste Daroussin 	for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
1773f9c093dSBaptiste Daroussin 		if (sysevent_groups[i].name == NULL)
1788a2af0b4SBaptiste Daroussin 			break;
1793f9c093dSBaptiste Daroussin 		free(sysevent_groups[i].name, M_NLSE);
1808a2af0b4SBaptiste Daroussin 	}
1818a2af0b4SBaptiste Daroussin }
1828a2af0b4SBaptiste Daroussin 
1838a2af0b4SBaptiste Daroussin static int
nlsysevent_loader(module_t mod __unused,int what,void * priv __unused)1848a2af0b4SBaptiste Daroussin nlsysevent_loader(module_t mod __unused, int what, void *priv __unused)
1858a2af0b4SBaptiste Daroussin {
1868a2af0b4SBaptiste Daroussin 	int err = 0;
1878a2af0b4SBaptiste Daroussin 
1888a2af0b4SBaptiste Daroussin 	switch (what) {
1898a2af0b4SBaptiste Daroussin 	case MOD_LOAD:
1908a2af0b4SBaptiste Daroussin 		nlsysevent_load();
1918a2af0b4SBaptiste Daroussin 		break;
1928a2af0b4SBaptiste Daroussin 	case MOD_UNLOAD:
1938a2af0b4SBaptiste Daroussin 		nlsysevent_unload();
1948a2af0b4SBaptiste Daroussin 		break;
1958a2af0b4SBaptiste Daroussin 	default:
1968a2af0b4SBaptiste Daroussin 		err = EOPNOTSUPP;
1978a2af0b4SBaptiste Daroussin 		break;
1988a2af0b4SBaptiste Daroussin 	}
1998a2af0b4SBaptiste Daroussin 	return (err);
2008a2af0b4SBaptiste Daroussin }
2018a2af0b4SBaptiste Daroussin static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL};
2028a2af0b4SBaptiste Daroussin 
2038a2af0b4SBaptiste Daroussin DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
2048a2af0b4SBaptiste Daroussin MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1);
2058a2af0b4SBaptiste Daroussin MODULE_VERSION(nlsysevent, 1);
206