netlink_sysevent.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. #include <sys/param.h>
  28. #include <sys/types.h>
  29. #include <sys/devctl.h>
  30. #include <sys/errno.h>
  31. #include <sys/module.h>
  32. #include <sys/kernel.h>
  33. #include <sys/malloc.h>
  34. #include <net/vnet.h>
  35. #include <netlink/netlink.h>
  36. #include <netlink/netlink_ctl.h>
  37. #include <netlink/netlink_generic.h>
  38. #include <netlink/netlink_sysevent.h>
  39. #define DEBUG_MOD_NAME nl_sysevent
  40. #define DEBUG_MAX_LEVEL LOG_DEBUG3
  41. #include <netlink/netlink_debug.h>
  42. _DECLARE_DEBUG(LOG_INFO);
  43. MALLOC_DEFINE(M_NLSE, "nlsysevent", "Memory used for Netlink sysevent");
  44. #define NLSE_FAMILY_NAME "nlsysevent"
  45. static uint32_t ctrl_family_id;
  46. #define MAX_SYSEVENT_GROUPS 64
  47. static struct sysevent_group {
  48. char *name;
  49. uint32_t id;
  50. } sysevent_groups[MAX_SYSEVENT_GROUPS] = {};
  51. static const char *devctl_systems[] = {
  52. "ACPI",
  53. "AEON",
  54. "CAM",
  55. "CARP",
  56. "coretemp",
  57. "DEVFS",
  58. "device",
  59. "ETHERNET",
  60. "GEOM",
  61. "HYPERV_NIC_VF",
  62. "IFNET",
  63. "INFINIBAND",
  64. "KERNEL",
  65. "nvme",
  66. "PMU",
  67. "RCTL",
  68. "USB",
  69. "VFS",
  70. "VT",
  71. "ZFS",
  72. };
  73. static void
  74. sysevent_write(struct sysevent_group *se, const char *subsystem, const char *type,
  75. const char *data)
  76. {
  77. struct nl_writer nw = {};
  78. if (!nlmsg_get_group_writer(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id)) {
  79. NL_LOG(LOG_DEBUG, "error allocating group writer");
  80. return;
  81. }
  82. struct nlmsghdr hdr = { .nlmsg_type = ctrl_family_id };
  83. if (!nlmsg_reply(&nw, &hdr, sizeof(struct genlmsghdr))) {
  84. return;
  85. }
  86. struct genlmsghdr *ghdr = nlmsg_reserve_object(&nw, struct genlmsghdr);
  87. if (ghdr == NULL) {
  88. NL_LOG(LOG_DEBUG, "unable to allocate memory");
  89. return;
  90. }
  91. ghdr->version = 0;
  92. ghdr->cmd = NLSE_CMD_NEWEVENT;
  93. ghdr->reserved = 0;
  94. nlattr_add_string(&nw, NLSE_ATTR_SYSTEM, se->name);
  95. nlattr_add_string(&nw, NLSE_ATTR_SUBSYSTEM, subsystem);
  96. nlattr_add_string(&nw, NLSE_ATTR_TYPE, type);
  97. if (data != NULL)
  98. nlattr_add_string(&nw, NLSE_ATTR_DATA, data);
  99. nlmsg_end(&nw);
  100. nlmsg_flush(&nw);
  101. }
  102. static void
  103. sysevent_new_group(size_t index, const char *name)
  104. {
  105. if (index >= MAX_SYSEVENT_GROUPS) {
  106. NL_LOG(LOG_WARNING, "impossible to add the event %s, "
  107. "too many event groups\n", name);
  108. return;
  109. }
  110. sysevent_groups[index].name = strdup(name, M_NLSE);
  111. sysevent_groups[index].id = genl_register_group(NLSE_FAMILY_NAME, sysevent_groups[index].name);
  112. }
  113. static struct sysevent_group *
  114. sysevent_get_group(const char *system)
  115. {
  116. for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
  117. if (sysevent_groups[i].name == NULL) {
  118. sysevent_new_group(i, system);
  119. return (&sysevent_groups[i]);
  120. }
  121. if (strcmp(sysevent_groups[i].name, system) == 0)
  122. return (&sysevent_groups[i]);
  123. }
  124. return (NULL);
  125. }
  126. static void
  127. sysevent_send(const char *system, const char *subsystem, const char *type,
  128. const char *data)
  129. {
  130. struct sysevent_group *se = sysevent_get_group(system);
  131. if (se == NULL) {
  132. NL_LOG(LOG_WARNING, "impossible to add the event %s, "
  133. "too many event groups\n", system);
  134. return;
  135. }
  136. CURVNET_SET(vnet0);
  137. sysevent_write(se, subsystem, type, data);
  138. CURVNET_RESTORE();
  139. }
  140. static void
  141. nlsysevent_load(void)
  142. {
  143. devctl_set_notify_hook(sysevent_send);
  144. ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX);
  145. for (size_t i = 0; i < nitems(devctl_systems); i++) {
  146. if (i >= MAX_SYSEVENT_GROUPS) {
  147. NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]);
  148. continue;
  149. }
  150. sysevent_new_group(i, devctl_systems[i]);
  151. }
  152. }
  153. static void
  154. nlsysevent_unload(void)
  155. {
  156. devctl_unset_notify_hook();
  157. genl_unregister_family(NLSE_FAMILY_NAME);
  158. for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) {
  159. if (sysevent_groups[i].name == NULL)
  160. break;
  161. free(sysevent_groups[i].name, M_NLSE);
  162. }
  163. }
  164. static int
  165. nlsysevent_loader(module_t mod __unused, int what, void *priv __unused)
  166. {
  167. int err = 0;
  168. switch (what) {
  169. case MOD_LOAD:
  170. nlsysevent_load();
  171. break;
  172. case MOD_UNLOAD:
  173. nlsysevent_unload();
  174. break;
  175. default:
  176. err = EOPNOTSUPP;
  177. break;
  178. }
  179. return (err);
  180. }
  181. static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL};
  182. DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
  183. MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1);
  184. MODULE_VERSION(nlsysevent, 1);