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 56*f40cd16bSEd Maste static const char *devctl_systems[] = { 57*f40cd16bSEd Maste "ACPI", 58*f40cd16bSEd Maste "AEON", 59*f40cd16bSEd Maste "CAM", 60*f40cd16bSEd Maste "CARP", 61*f40cd16bSEd Maste "coretemp", 62*f40cd16bSEd Maste "DEVFS", 63*f40cd16bSEd Maste "device", 64*f40cd16bSEd Maste "ETHERNET", 65*f40cd16bSEd Maste "GEOM", 66*f40cd16bSEd Maste "HYPERV_NIC_VF", 67*f40cd16bSEd Maste "IFNET", 68*f40cd16bSEd Maste "INFINIBAND", 69*f40cd16bSEd Maste "KERNEL", 70*f40cd16bSEd Maste "nvme", 71*f40cd16bSEd Maste "PMU", 72*f40cd16bSEd Maste "RCTL", 73*f40cd16bSEd Maste "USB", 74*f40cd16bSEd Maste "VFS", 75*f40cd16bSEd Maste "VT", 76*f40cd16bSEd Maste "ZFS", 77*f40cd16bSEd Maste }; 78*f40cd16bSEd 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 { 838a2af0b4SBaptiste Daroussin struct nl_writer nw = {}; 848a2af0b4SBaptiste Daroussin 858a2af0b4SBaptiste Daroussin if (!nlmsg_get_group_writer(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id)) { 868a2af0b4SBaptiste Daroussin NL_LOG(LOG_DEBUG, "error allocating group writer"); 878a2af0b4SBaptiste Daroussin return; 888a2af0b4SBaptiste Daroussin } 898a2af0b4SBaptiste Daroussin struct nlmsghdr hdr = { .nlmsg_type = ctrl_family_id }; 908a2af0b4SBaptiste Daroussin if (!nlmsg_reply(&nw, &hdr, sizeof(struct genlmsghdr))) { 918a2af0b4SBaptiste Daroussin return; 928a2af0b4SBaptiste Daroussin } 938a2af0b4SBaptiste Daroussin 948a2af0b4SBaptiste Daroussin struct genlmsghdr *ghdr = nlmsg_reserve_object(&nw, struct genlmsghdr); 958a2af0b4SBaptiste Daroussin if (ghdr == NULL) { 968a2af0b4SBaptiste Daroussin NL_LOG(LOG_DEBUG, "unable to allocate memory"); 978a2af0b4SBaptiste Daroussin return; 988a2af0b4SBaptiste Daroussin } 998a2af0b4SBaptiste Daroussin ghdr->version = 0; 10099084611SBaptiste Daroussin ghdr->cmd = NLSE_CMD_NEWEVENT; 1018a2af0b4SBaptiste Daroussin ghdr->reserved = 0; 1028a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_SYSTEM, se->name); 1038a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_SUBSYSTEM, subsystem); 1048a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_TYPE, type); 1058a2af0b4SBaptiste Daroussin if (data != NULL) 1068a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_DATA, data); 1078a2af0b4SBaptiste Daroussin nlmsg_end(&nw); 1088a2af0b4SBaptiste Daroussin nlmsg_flush(&nw); 1098a2af0b4SBaptiste Daroussin } 1108a2af0b4SBaptiste Daroussin 1118a2af0b4SBaptiste Daroussin static void 1120bcb3ebdSBaptiste Daroussin sysevent_new_group(size_t index, const char *name) 1130bcb3ebdSBaptiste Daroussin { 1140bcb3ebdSBaptiste Daroussin if (index >= MAX_SYSEVENT_GROUPS) { 1150bcb3ebdSBaptiste Daroussin NL_LOG(LOG_WARNING, "impossible to add the event %s, " 1160bcb3ebdSBaptiste Daroussin "too many event groups\n", name); 1170bcb3ebdSBaptiste Daroussin return; 1180bcb3ebdSBaptiste Daroussin } 1190bcb3ebdSBaptiste Daroussin sysevent_groups[index].name = strdup(name, M_NLSE); 1200bcb3ebdSBaptiste Daroussin sysevent_groups[index].id = genl_register_group(NLSE_FAMILY_NAME, sysevent_groups[index].name); 1210bcb3ebdSBaptiste Daroussin } 1220bcb3ebdSBaptiste Daroussin 1230bcb3ebdSBaptiste Daroussin static struct sysevent_group * 1240bcb3ebdSBaptiste Daroussin sysevent_get_group(const char *system) 1250bcb3ebdSBaptiste Daroussin { 1260bcb3ebdSBaptiste Daroussin for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) { 1270bcb3ebdSBaptiste Daroussin if (sysevent_groups[i].name == NULL) { 1280bcb3ebdSBaptiste Daroussin sysevent_new_group(i, system); 1290bcb3ebdSBaptiste Daroussin return (&sysevent_groups[i]); 1300bcb3ebdSBaptiste Daroussin } 1310bcb3ebdSBaptiste Daroussin if (strcmp(sysevent_groups[i].name, system) == 0) 1320bcb3ebdSBaptiste Daroussin return (&sysevent_groups[i]); 1330bcb3ebdSBaptiste Daroussin } 1340bcb3ebdSBaptiste Daroussin 1350bcb3ebdSBaptiste Daroussin return (NULL); 1360bcb3ebdSBaptiste Daroussin } 1370bcb3ebdSBaptiste Daroussin 1380bcb3ebdSBaptiste Daroussin static void 1398a2af0b4SBaptiste Daroussin sysevent_send(const char *system, const char *subsystem, const char *type, 1408a2af0b4SBaptiste Daroussin const char *data) 1418a2af0b4SBaptiste Daroussin { 1420bcb3ebdSBaptiste Daroussin struct sysevent_group *se = sysevent_get_group(system); 1438a2af0b4SBaptiste Daroussin 1448a2af0b4SBaptiste Daroussin if (se == NULL) { 1458a2af0b4SBaptiste Daroussin NL_LOG(LOG_WARNING, "impossible to add the event %s, " 1460bcb3ebdSBaptiste Daroussin "too many event groups\n", system); 1478a2af0b4SBaptiste Daroussin return; 1488a2af0b4SBaptiste Daroussin } 1498a2af0b4SBaptiste Daroussin 1508a2af0b4SBaptiste Daroussin CURVNET_SET(vnet0); 1518a2af0b4SBaptiste Daroussin sysevent_write(se, subsystem, type, data); 1528a2af0b4SBaptiste Daroussin CURVNET_RESTORE(); 1538a2af0b4SBaptiste Daroussin } 1548a2af0b4SBaptiste Daroussin 1558a2af0b4SBaptiste Daroussin static void 1568a2af0b4SBaptiste Daroussin nlsysevent_load(void) 1578a2af0b4SBaptiste Daroussin { 1588a2af0b4SBaptiste Daroussin devctl_set_notify_hook(sysevent_send); 1598a2af0b4SBaptiste Daroussin ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX); 1608a2af0b4SBaptiste Daroussin for (size_t i = 0; i < nitems(devctl_systems); i++) { 1613f9c093dSBaptiste Daroussin if (i >= MAX_SYSEVENT_GROUPS) { 1628a2af0b4SBaptiste Daroussin NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]); 1638a2af0b4SBaptiste Daroussin continue; 1648a2af0b4SBaptiste Daroussin } 1650bcb3ebdSBaptiste Daroussin sysevent_new_group(i, devctl_systems[i]); 1668a2af0b4SBaptiste Daroussin } 1678a2af0b4SBaptiste Daroussin } 1688a2af0b4SBaptiste Daroussin 1698a2af0b4SBaptiste Daroussin static void 1708a2af0b4SBaptiste Daroussin nlsysevent_unload(void) 1718a2af0b4SBaptiste Daroussin { 1728a2af0b4SBaptiste Daroussin devctl_unset_notify_hook(); 1738a2af0b4SBaptiste Daroussin genl_unregister_family(NLSE_FAMILY_NAME); 1743f9c093dSBaptiste Daroussin for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) { 1753f9c093dSBaptiste Daroussin if (sysevent_groups[i].name == NULL) 1768a2af0b4SBaptiste Daroussin break; 1773f9c093dSBaptiste Daroussin free(sysevent_groups[i].name, M_NLSE); 1788a2af0b4SBaptiste Daroussin } 1798a2af0b4SBaptiste Daroussin } 1808a2af0b4SBaptiste Daroussin 1818a2af0b4SBaptiste Daroussin static int 1828a2af0b4SBaptiste Daroussin nlsysevent_loader(module_t mod __unused, int what, void *priv __unused) 1838a2af0b4SBaptiste Daroussin { 1848a2af0b4SBaptiste Daroussin int err = 0; 1858a2af0b4SBaptiste Daroussin 1868a2af0b4SBaptiste Daroussin switch (what) { 1878a2af0b4SBaptiste Daroussin case MOD_LOAD: 1888a2af0b4SBaptiste Daroussin nlsysevent_load(); 1898a2af0b4SBaptiste Daroussin break; 1908a2af0b4SBaptiste Daroussin case MOD_UNLOAD: 1918a2af0b4SBaptiste Daroussin nlsysevent_unload(); 1928a2af0b4SBaptiste Daroussin break; 1938a2af0b4SBaptiste Daroussin default: 1948a2af0b4SBaptiste Daroussin err = EOPNOTSUPP; 1958a2af0b4SBaptiste Daroussin break; 1968a2af0b4SBaptiste Daroussin } 1978a2af0b4SBaptiste Daroussin return (err); 1988a2af0b4SBaptiste Daroussin } 1998a2af0b4SBaptiste Daroussin static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL}; 2008a2af0b4SBaptiste Daroussin 2018a2af0b4SBaptiste Daroussin DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2028a2af0b4SBaptiste Daroussin MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1); 2038a2af0b4SBaptiste Daroussin MODULE_VERSION(nlsysevent, 1); 204