/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
2.1.2
plugins/devlink/fu-devlink-plugin.c
377 строк
10 KB
Richard Hughes
trivial: Chain up the `GObjectClass->constructed()` vfunc
22 мар 2026, 00:18
22 мар 2026, 00:18
2c681e4
Код
Авторство
О чём код?
/* * Copyright 2025 NVIDIA Corporation & Affiliates * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "fu-devlink-backend.h" #include "fu-devlink-device.h" #include "fu-devlink-netlink.h" #include "fu-devlink-plugin.h" struct _FuDevlinkPlugin { FuPlugin parent_instance; FuDevlinkGenSocket *nlg; GSource *netlink_source; FuDevlinkBackend *backend; }; G_DEFINE_TYPE(FuDevlinkPlugin, fu_devlink_plugin, FU_TYPE_PLUGIN) static gint fu_devlink_plugin_get_serial_number_cb(const struct nlmsghdr *nlh, gpointer data) { struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; gchar **serial_number = data; if (genl->cmd != DEVLINK_CMD_INFO_GET) return MNL_CB_OK; mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_attr_cb, tb); if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER] != NULL) *serial_number = fu_strsafe(mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]), G_MAXSIZE); return MNL_CB_OK; } static gboolean fu_devlink_plugin_get_serial_number(const gchar *bus_name, const gchar *dev_name, gchar **serial_number, GError **error) { struct nlmsghdr *nlh; g_autoptr(FuDevlinkGenSocket) nlg = NULL; nlg = fu_devlink_netlink_gen_socket_open(NULL, error); if (nlg == NULL) return FALSE; /* prepare dev info command */ nlh = fu_devlink_netlink_cmd_prepare(nlg, DEVLINK_CMD_INFO_GET, FALSE); mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, bus_name); mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, dev_name); /* send command and process response */ if (!fu_devlink_netlink_msg_send_recv(nlg, nlh, fu_devlink_plugin_get_serial_number_cb, serial_number, error)) { g_prefix_error_literal(error, "failed to get device info: "); return FALSE; } /* success */ return TRUE; } static void fu_devlink_plugin_device_added_from_netlink(FuDevlinkPlugin *self, const struct nlmsghdr *nlh) { struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; const gchar *bus_name = NULL; const gchar *dev_name = NULL; g_autoptr(FuDevice) devlink_device = NULL; g_autoptr(GError) error_local = NULL; g_autofree gchar *serial_number = NULL; g_autofree gchar *cache_id = NULL; /* parse netlink attributes using libmnl */ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), fu_devlink_netlink_attr_cb, tb); /* extract bus and device names */ if (tb[DEVLINK_ATTR_BUS_NAME] != NULL) bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); if (tb[DEVLINK_ATTR_DEV_NAME] != NULL) dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); if (bus_name == NULL || dev_name == NULL) { g_debug("devlink device notification missing bus_name or dev_name"); return; } g_debug("devlink device added: %s/%s", bus_name, dev_name); if (!fu_devlink_plugin_get_serial_number(bus_name, dev_name, &serial_number, &error_local)) { g_warning("failed to get serial number for devlink device %s/%s: %s", bus_name, dev_name, error_local->message); return; } /* use backend to create device with proper hierarchy */ devlink_device = fu_devlink_backend_device_added(self->backend, bus_name, dev_name, serial_number, &error_local); if (devlink_device == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_debug("failed to add devlink device %s/%s: %s", bus_name, dev_name, error_local->message); } else { g_warning("failed to add devlink device %s/%s: %s", bus_name, dev_name, error_local->message); } return; } cache_id = g_strdup_printf("%s/%s", bus_name, dev_name); fu_plugin_cache_add(FU_PLUGIN(self), cache_id, devlink_device); } static void fu_devlink_plugin_device_removed_from_netlink(FuDevlinkPlugin *self, const struct nlmsghdr *nlh) { struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; const gchar *bus_name = NULL; const gchar *dev_name = NULL; g_autofree gchar *cache_id = NULL; FuDevice *devlink_device = NULL; /* parse netlink attributes using libmnl */ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), fu_devlink_netlink_attr_cb, tb); /* extract bus and device names */ if (tb[DEVLINK_ATTR_BUS_NAME] != NULL) bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); if (tb[DEVLINK_ATTR_DEV_NAME] != NULL) dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); if (bus_name == NULL || dev_name == NULL) { g_debug("devlink device removal notification missing bus_name or dev_name"); return; } g_debug("devlink device removed: %s/%s", bus_name, dev_name); cache_id = g_strdup_printf("%s/%s", bus_name, dev_name); devlink_device = fu_plugin_cache_lookup(FU_PLUGIN(self), cache_id); if (devlink_device == NULL) return; fu_devlink_backend_device_removed(self->backend, devlink_device); fu_plugin_cache_remove(FU_PLUGIN(self), cache_id); } /* callback for processing individual netlink messages */ static gint fu_devlink_plugin_process_message_cb(const struct nlmsghdr *nlh, gpointer data) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(data); struct genlmsghdr *genl; genl = mnl_nlmsg_get_payload(nlh); switch (genl->cmd) { case DEVLINK_CMD_NEW: fu_devlink_plugin_device_added_from_netlink(self, nlh); break; case DEVLINK_CMD_DEL: fu_devlink_plugin_device_removed_from_netlink(self, nlh); break; default: break; } return MNL_CB_OK; } static gboolean fu_devlink_plugin_netlink_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(user_data); gsize len; GIOStatus status; g_autoptr(GError) error_local = NULL; if (condition & (G_IO_ERR | G_IO_HUP)) { g_debug("devlink netlink socket error"); return FALSE; } /* read netlink message via GIOChannel */ status = g_io_channel_read_chars(channel, fu_devlink_netlink_gen_socket_get_buf(self->nlg), FU_DEVLINK_NETLINK_BUF_SIZE, &len, &error_local); if (status != G_IO_STATUS_NORMAL) { if (error_local != NULL) g_debug("failed to read devlink netlink message: %s", error_local->message); return TRUE; } /* process netlink messages */ if (!fu_devlink_netlink_msg_run(self->nlg, len, 0, fu_devlink_plugin_process_message_cb, self, &error_local)) g_warning("failed to process netlink message: %s", error_local->message); return TRUE; } static gboolean fu_devlink_plugin_setup_netlink(FuDevlinkPlugin *self, GError **error) { gint fd; guint watch_id; g_autoptr(GIOChannel) channel = NULL; /* open devlink netlink socket */ self->nlg = fu_devlink_netlink_gen_socket_open(NULL, error); if (self->nlg == NULL) return FALSE; /* subscribe to devlink multicast notifications */ if (!fu_devlink_netlink_mcast_group_subscribe(self->nlg, error)) return FALSE; /* create GIOChannel for the netlink socket */ fd = fu_devlink_netlink_gen_socket_get_fd(self->nlg); channel = g_io_channel_unix_new(fd); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); /* setup monitoring source using proper GIO pattern */ watch_id = g_io_add_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP, fu_devlink_plugin_netlink_cb, self); self->netlink_source = g_main_context_find_source_by_id(NULL, watch_id); if (self->netlink_source != NULL) g_source_ref(self->netlink_source); return TRUE; } /* device enumeration callback */ static gint fu_devlink_plugin_enumerate_cb(const struct nlmsghdr *nlh, gpointer data) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(data); struct genlmsghdr *genl; genl = mnl_nlmsg_get_payload(nlh); if (genl->cmd == DEVLINK_CMD_NEW) fu_devlink_plugin_device_added_from_netlink(self, nlh); return MNL_CB_OK; } static gboolean fu_devlink_plugin_enumerate_devices(FuDevlinkPlugin *self, GError **error) { struct nlmsghdr *nlh; /* prepare device enumeration command */ nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_GET, TRUE); /* send enumeration request and receive responses */ if (!fu_devlink_netlink_msg_send_recv(self->nlg, nlh, fu_devlink_plugin_enumerate_cb, self, error)) { g_prefix_error_literal(error, "failed to enumerate devlink devices: "); return FALSE; } return TRUE; } static gboolean fu_devlink_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(plugin); /* setup devlink netlink monitoring */ if (!fu_devlink_plugin_setup_netlink(self, error)) { g_prefix_error_literal(error, "failed to setup devlink netlink: "); return FALSE; } return TRUE; } static gboolean fu_devlink_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(plugin); /* enumerate existing devlink devices */ if (!fu_devlink_plugin_enumerate_devices(self, error)) { g_prefix_error_literal(error, "failed to enumerate devlink devices: "); return FALSE; } return TRUE; } static void fu_devlink_plugin_finalize(GObject *object) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(object); /* clean up netlink resources */ if (self->netlink_source != NULL) { g_source_destroy(self->netlink_source); g_source_unref(self->netlink_source); } fu_devlink_netlink_gen_socket_close(self->nlg); /* clean up backend */ if (self->backend != NULL) g_object_unref(self->backend); G_OBJECT_CLASS(fu_devlink_plugin_parent_class)->finalize(object); } static void fu_devlink_plugin_constructed(GObject *obj) { FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(obj); FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); /* create and add devlink backend */ self->backend = FU_DEVLINK_BACKEND(fu_devlink_backend_new(ctx)); fu_context_add_backend(ctx, FU_BACKEND(self->backend)); fu_context_add_quirk_key(ctx, "DevlinkFixedVersions"); fu_plugin_add_device_gtype(plugin, FU_TYPE_DEVLINK_DEVICE); /* chain up to parent */ G_OBJECT_CLASS(fu_devlink_plugin_parent_class)->constructed(obj); } static void fu_devlink_plugin_init(FuDevlinkPlugin *self) { } static void fu_devlink_plugin_class_init(FuDevlinkPluginClass *klass) { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = fu_devlink_plugin_finalize; object_class->constructed = fu_devlink_plugin_constructed; plugin_class->startup = fu_devlink_plugin_startup; plugin_class->coldplug = fu_devlink_plugin_coldplug; }