1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Baptiste Daroussin <bapt@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/devctl.h> 31 #include <sys/errno.h> 32 #include <sys/module.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <net/vnet.h> 36 #include <netlink/netlink.h> 37 #include <netlink/netlink_ctl.h> 38 #include <netlink/netlink_generic.h> 39 #include <netlink/netlink_sysevent.h> 40 41 #define DEBUG_MOD_NAME nl_sysevent 42 #define DEBUG_MAX_LEVEL LOG_DEBUG3 43 #include <netlink/netlink_debug.h> 44 _DECLARE_DEBUG(LOG_INFO); 45 46 MALLOC_DEFINE(M_NLSE, "nlsysevent", "Memory used for Netlink sysevent"); 47 #define NLSE_FAMILY_NAME "nlsysevent" 48 static uint32_t ctrl_family_id; 49 50 #define MAX_SYSEVENT_GROUPS 64 51 static struct sysevent_group { 52 char *name; 53 uint32_t id; 54 } sysevent_groups[MAX_SYSEVENT_GROUPS] = {}; 55 56 static const char *devctl_systems[] = { 57 "ACPI", 58 "AEON", 59 "CAM", 60 "CARP", 61 "coretemp", 62 "DEVFS", 63 "device", 64 "ETHERNET", 65 "GEOM", 66 "HYPERV_NIC_VF", 67 "IFNET", 68 "INFINIBAND", 69 "KERNEL", 70 "nvme", 71 "PMU", 72 "RCTL", 73 "USB", 74 "VFS", 75 "VT", 76 "ZFS", 77 }; 78 79 static void 80 sysevent_write(struct sysevent_group *se, const char *subsystem, const char *type, 81 const char *data) 82 { 83 struct nl_writer nw = {}; 84 85 if (!nlmsg_get_group_writer(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id)) { 86 NL_LOG(LOG_DEBUG, "error allocating group writer"); 87 return; 88 } 89 struct nlmsghdr hdr = { .nlmsg_type = ctrl_family_id }; 90 if (!nlmsg_reply(&nw, &hdr, sizeof(struct genlmsghdr))) { 91 return; 92 } 93 94 struct genlmsghdr *ghdr = nlmsg_reserve_object(&nw, struct genlmsghdr); 95 if (ghdr == NULL) { 96 NL_LOG(LOG_DEBUG, "unable to allocate memory"); 97 return; 98 } 99 ghdr->version = 0; 100 ghdr->cmd = NLSE_CMD_NEWEVENT; 101 ghdr->reserved = 0; 102 nlattr_add_string(&nw, NLSE_ATTR_SYSTEM, se->name); 103 nlattr_add_string(&nw, NLSE_ATTR_SUBSYSTEM, subsystem); 104 nlattr_add_string(&nw, NLSE_ATTR_TYPE, type); 105 if (data != NULL) 106 nlattr_add_string(&nw, NLSE_ATTR_DATA, data); 107 nlmsg_end(&nw); 108 nlmsg_flush(&nw); 109 } 110 111 static void 112 sysevent_new_group(size_t index, const char *name) 113 { 114 if (index >= MAX_SYSEVENT_GROUPS) { 115 NL_LOG(LOG_WARNING, "impossible to add the event %s, " 116 "too many event groups\n", name); 117 return; 118 } 119 sysevent_groups[index].name = strdup(name, M_NLSE); 120 sysevent_groups[index].id = genl_register_group(NLSE_FAMILY_NAME, sysevent_groups[index].name); 121 } 122 123 static struct sysevent_group * 124 sysevent_get_group(const char *system) 125 { 126 for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) { 127 if (sysevent_groups[i].name == NULL) { 128 sysevent_new_group(i, system); 129 return (&sysevent_groups[i]); 130 } 131 if (strcmp(sysevent_groups[i].name, system) == 0) 132 return (&sysevent_groups[i]); 133 } 134 135 return (NULL); 136 } 137 138 static void 139 sysevent_send(const char *system, const char *subsystem, const char *type, 140 const char *data) 141 { 142 struct sysevent_group *se = sysevent_get_group(system); 143 144 if (se == NULL) { 145 NL_LOG(LOG_WARNING, "impossible to add the event %s, " 146 "too many event groups\n", system); 147 return; 148 } 149 150 CURVNET_SET(vnet0); 151 sysevent_write(se, subsystem, type, data); 152 CURVNET_RESTORE(); 153 } 154 155 static void 156 nlsysevent_load(void) 157 { 158 devctl_set_notify_hook(sysevent_send); 159 ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX); 160 for (size_t i = 0; i < nitems(devctl_systems); i++) { 161 if (i >= MAX_SYSEVENT_GROUPS) { 162 NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]); 163 continue; 164 } 165 sysevent_new_group(i, devctl_systems[i]); 166 } 167 } 168 169 static void 170 nlsysevent_unload(void) 171 { 172 devctl_unset_notify_hook(); 173 genl_unregister_family(NLSE_FAMILY_NAME); 174 for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) { 175 if (sysevent_groups[i].name == NULL) 176 break; 177 free(sysevent_groups[i].name, M_NLSE); 178 } 179 } 180 181 static int 182 nlsysevent_loader(module_t mod __unused, int what, void *priv __unused) 183 { 184 int err = 0; 185 186 switch (what) { 187 case MOD_LOAD: 188 nlsysevent_load(); 189 break; 190 case MOD_UNLOAD: 191 nlsysevent_unload(); 192 break; 193 default: 194 err = EOPNOTSUPP; 195 break; 196 } 197 return (err); 198 } 199 static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL}; 200 201 DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 202 MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1); 203 MODULE_VERSION(nlsysevent, 1); 204