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