redfish: Add a plugin that uses the Redfish API

Redfish is an open industry standard specification and schema that helps enable
simple and secure management of modern scalable platform hardware.

This has only ever been tested using an emulator and not on real hardware.
This commit is contained in:
Richard Hughes 2017-08-31 19:59:36 +01:00
parent 097b5ac996
commit ba103483fa
14 changed files with 942 additions and 0 deletions

View File

@ -53,6 +53,7 @@ Debian testing (cross compile s390x)
* Not packaged
* Tests for missing translation files
* No redfish support
* Compiled under gcc
* Tests with -Werror enabled
* Runs local test suite using qemu-user

View File

@ -12,6 +12,7 @@ meson .. \
--werror \
-Dplugin_uefi=false \
-Dplugin_dell=false \
-Dplugin_redfish=false \
-Dplugin_synaptics=false \
-Dintrospection=false \
-Dgtkdoc=false \

View File

@ -252,6 +252,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg
%{_libdir}/fwupd-plugins-3/libfu_plugin_dfu.so
%{_libdir}/fwupd-plugins-3/libfu_plugin_ebitdo.so
%{_libdir}/fwupd-plugins-3/libfu_plugin_nitrokey.so
%{_libdir}/fwupd-plugins-3/libfu_plugin_redfish.so
%{_libdir}/fwupd-plugins-3/libfu_plugin_steelseries.so
%if 0%{?have_dell}
%{_libdir}/fwupd-plugins-3/libfu_plugin_synapticsmst.so

View File

@ -201,6 +201,11 @@ if valgrind.found()
conf.set('HAVE_VALGRIND', '1')
endif
if get_option('plugin_redfish')
json_glib = dependency('json-glib-1.0')
efivar = dependency('efivar')
endif
if get_option('plugin_altos')
libelf = dependency('libelf')
endif

View File

@ -12,6 +12,7 @@ option('plugin_dell', type : 'boolean', value : true, description : 'enable Dell
option('plugin_dummy', type : 'boolean', value : false, description : 'enable the dummy device')
option('plugin_synaptics', type: 'boolean', value: true, description : 'enable Synaptics MST hub support')
option('plugin_thunderbolt', type : 'boolean', value : true, description : 'enable Thunderbolt support')
option('plugin_redfish', type : 'boolean', value : true, description : 'enable Redfish support')
option('plugin_uefi', type : 'boolean', value : true, description : 'enable UEFI support')
option('systemd', type : 'boolean', value : true, description : 'enable systemd support')
option('systemdunitdir', type: 'string', value: '', description: 'Directory for systemd units')

View File

@ -25,6 +25,10 @@ subdir('thunderbolt')
subdir('thunderbolt-power')
endif
if get_option('plugin_redfish')
subdir('redfish')
endif
if get_option('plugin_dell')
subdir('dell')
subdir('dell-esrt')

11
plugins/redfish/README.md Normal file
View File

@ -0,0 +1,11 @@
Redfish Support
===============
Introduction
------------
Redfish is an open industry standard specification and schema that helps enable
simple and secure management of modern scalable platform hardware.
By specifying a RESTful interface and utilizing JSON and OData, Redfish helps
customers integrate solutions within their existing tool chains.

View File

@ -0,0 +1,83 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-plugin.h"
#include "fu-plugin-vfuncs.h"
#include "fu-redfish-client.h"
#include "fu-redfish-common.h"
struct FuPluginData {
FuRedfishClient *client;
};
gboolean
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
{
FuPluginData *data = fu_plugin_get_data (plugin);
GPtrArray *devices;
/* get the list of devices */
if (!fu_redfish_client_coldplug (data->client, error))
return FALSE;
devices = fu_redfish_client_get_devices (data->client);
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index (devices, i);
fu_plugin_device_add (plugin, device);
}
return TRUE;
}
gboolean
fu_plugin_startup (FuPlugin *plugin, GError **error)
{
FuPluginData *data = fu_plugin_get_data (plugin);
GBytes *smbios_data = fu_plugin_get_smbios_data (plugin, REDFISH_SMBIOS_TABLE_TYPE);
const gchar *redfish_uri;
/* using the emulator */
redfish_uri = g_getenv ("FWUPD_REDFISH_URI");
if (redfish_uri != NULL) {
guint64 port;
g_auto(GStrv) split = g_strsplit (redfish_uri, ":", 2);
fu_redfish_client_set_hostname (data->client, split[0]);
port = g_ascii_strtoull (split[1], NULL, 10);
if (port == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no port specified");
return FALSE;
}
fu_redfish_client_set_port (data->client, port);
} else {
if (smbios_data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no SMBIOS table");
return FALSE;
}
}
return fu_redfish_client_setup (data->client, smbios_data, error);
}
void
fu_plugin_init (FuPlugin *plugin)
{
FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
data->client = fu_redfish_client_new ();
}
void
fu_plugin_destroy (FuPlugin *plugin)
{
FuPluginData *data = fu_plugin_get_data (plugin);
g_object_unref (data->client);
}

View File

@ -0,0 +1,592 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <json-glib/json-glib.h>
#include <libsoup/soup.h>
#include <string.h>
#include "fwupd-error.h"
#include "fwupd-enums.h"
#include "fu-device.h"
#include "fu-redfish-client.h"
#include "fu-redfish-common.h"
struct _FuRedfishClient
{
GObject parent_instance;
SoupSession *session;
gchar *hostname;
guint port;
gchar *username;
gchar *password;
gchar *update_uri_path;
GPtrArray *devices;
};
G_DEFINE_TYPE (FuRedfishClient, fu_redfish_client, G_TYPE_OBJECT)
static GBytes *
fu_redfish_client_fetch_data (FuRedfishClient *self, const gchar *uri_path, GError **error)
{
guint status_code;
g_autoptr(SoupMessage) msg = NULL;
g_autoptr(SoupURI) uri = NULL;
/* create URI */
uri = soup_uri_new (NULL);
soup_uri_set_scheme (uri, g_strcmp0 (self->hostname, "localhost") == 0 ?
"http" : "https");
soup_uri_set_path (uri, uri_path);
soup_uri_set_host (uri, self->hostname);
soup_uri_set_user (uri, self->username);
soup_uri_set_port (uri, self->port);
soup_uri_set_password (uri, self->password);
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
if (msg == NULL) {
g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to create message for URI %s", tmp);
return NULL;
}
status_code = soup_session_send_message (self->session, msg);
if (status_code != SOUP_STATUS_OK) {
g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to download %s: %s",
tmp, soup_status_get_phrase (status_code));
return NULL;
}
return g_bytes_new (msg->response_body->data, msg->response_body->length);
}
static gboolean
fu_redfish_client_coldplug_member (FuRedfishClient *self,
JsonObject *member,
GError **error)
{
g_autoptr(FuDevice) dev = fu_device_new ();
fu_device_set_name (dev, json_object_get_string_member (member, "Name"));
fu_device_set_summary (dev, "Redfish device");
fu_device_set_version (dev, json_object_get_string_member (member, "Version"));
if (json_object_has_member (member, "LowestSupportedVersion"))
fu_device_set_version_lowest (dev, json_object_get_string_member (member, "LowestSupportedVersion"));
if (json_object_has_member (member, "Description"))
fu_device_set_description (dev, json_object_get_string_member (member, "Description"));
if (json_object_get_boolean_member (member, "Updateable"))
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_guid (dev, json_object_get_string_member (member, "SoftwareId"));
/* success */
g_ptr_array_add (self->devices, g_steal_pointer (&dev));
return TRUE;
}
static gboolean
fu_redfish_client_coldplug_inventory (FuRedfishClient *self,
JsonObject *inventory,
GError **error)
{
JsonArray *members;
members = json_object_get_array_member (inventory, "Members");
for (guint i = 0; i < json_array_get_length (members); i++) {
JsonObject *member = json_array_get_object_element (members, i);
if (!fu_redfish_client_coldplug_member (self, member, error))
return FALSE;
}
return TRUE;
}
gboolean
fu_redfish_client_coldplug (FuRedfishClient *self, GError **error)
{
JsonNode *node_root;
JsonObject *obj_root = NULL;
g_autoptr(GBytes) blob = NULL;
g_autoptr(JsonParser) parser = json_parser_new ();
/* nothing set */
if (self->update_uri_path == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no update_uri_path");
return FALSE;
}
/* try to connect */
blob = fu_redfish_client_fetch_data (self, self->update_uri_path, error);
if (blob == NULL)
return FALSE;
/* get the update service */
if (!json_parser_load_from_data (parser,
g_bytes_get_data (blob, NULL),
(gssize) g_bytes_get_size (blob),
error)) {
g_prefix_error (error, "failed to parse node: ");
return FALSE;
}
node_root = json_parser_get_root (parser);
if (node_root == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no root node");
return FALSE;
}
obj_root = json_node_get_object (node_root);
if (obj_root == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no root object");
return FALSE;
}
if (!json_object_get_boolean_member (obj_root, "ServiceEnabled")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"service is not enabled");
return FALSE;
}
if (json_object_has_member (obj_root, "FirmwareInventory")) {
JsonObject *tmp = json_object_get_object_member (obj_root, "FirmwareInventory");
return fu_redfish_client_coldplug_inventory (self, tmp, error);
}
if (json_object_has_member (obj_root, "SoftwareInventory")) {
JsonObject *tmp = json_object_get_object_member (obj_root, "SoftwareInventory");
return fu_redfish_client_coldplug_inventory (self, tmp, error);
}
return TRUE;
}
static gboolean
fu_redfish_client_set_uefi_credentials (FuRedfishClient *self, GError **error)
{
guint32 indications_le;
g_autofree gchar *userpass_safe = NULL;
g_auto(GStrv) split = NULL;
g_autoptr(GBytes) indications = NULL;
g_autoptr(GBytes) userpass = NULL;
/* get the uint32 specifying if there are EFI variables set */
indications = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID,
REDFISH_EFI_INFORMATION_INDICATIONS,
error);
if (indications == NULL)
return FALSE;
if (g_bytes_get_size (indications) != 4) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid value for %s, got %" G_GSIZE_FORMAT " bytes",
REDFISH_EFI_INFORMATION_INDICATIONS,
g_bytes_get_size (indications));
return FALSE;
}
memcpy (&indications_le, g_bytes_get_data (indications, NULL), 4);
if ((indications_le & REDFISH_EFI_INDICATIONS_OS_CREDENTIALS) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no indications for OS credentials");
return FALSE;
}
/* read the correct EFI var for runtime */
userpass = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID,
REDFISH_EFI_INFORMATION_OS_CREDENTIALS,
error);
if (userpass == NULL)
return FALSE;
/* it might not be NUL terminated */
userpass_safe = g_strndup (g_bytes_get_data (userpass, NULL),
g_bytes_get_size (userpass));
split = g_strsplit (userpass_safe, ":", -1);
if (g_strv_length (split) != 2) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid format for username:password, got '%s'",
userpass_safe);
return FALSE;
}
fu_redfish_client_set_username (self, split[0]);
fu_redfish_client_set_password (self, split[1]);
return TRUE;
}
static void
fu_redfish_client_parse_interface_data (const guint8 *buf, guint8 sz)
{
switch (buf[0]) {
case REDFISH_INTERFACE_TYPE_USB_NEWORK:
g_debug ("USB Network Interface");
/*
* uint16 idVendor(2-bytes)
* uint16 idProduct(2-bytes)
* uint8 SerialNumberLen:
* uint8 DescriptorType:
* uint8* SerialNumber:
*/
break;
case REDFISH_INTERFACE_TYPE_PCI_NEWORK:
g_debug ("PCI Network Interface");
/*
* uint16 VendorID
* uint16 DeviceID
* uint16 Subsystem_Vendor_ID
* uint16 Subsystem_ID
*/
break;
default:
break;
}
}
typedef struct __attribute__((packed)) {
guint8 service_uuid[16];
guint8 host_ip_assignment_type;
guint8 host_ip_address_format;
guint8 host_ip_address[16];
guint8 host_ip_mask[16];
guint8 service_ip_assignment_type;
guint8 service_ip_address_format;
guint8 service_ip_address[16];
guint8 service_ip_mask[16];
guint16 service_ip_port;
guint32 service_ip_vlan_id;
guint8 service_hostname_len;
/* service_hostname; */
} RedfishProtocolDataOverIp;
static gboolean
fu_redfish_client_parse_protocol_data (FuRedfishClient *self,
const guint8 *buf,
guint8 sz,
GError **error)
{
RedfishProtocolDataOverIp *pr;
if (sz < sizeof(RedfishProtocolDataOverIp)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"protocol data too small");
return FALSE;
}
pr = (RedfishProtocolDataOverIp *) buf;
/* parse the hostname and port */
if (pr->service_ip_assignment_type == REDFISH_IP_ASSIGNMENT_TYPE_STATIC ||
pr->service_ip_assignment_type == REDFISH_IP_ASSIGNMENT_TYPE_AUTO_CONFIG) {
if (pr->service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V4) {
g_autofree gchar *tmp = NULL;
tmp = fu_redfish_common_buffer_to_ipv4 (pr->service_ip_address);
fu_redfish_client_set_hostname (self, tmp);
} else if (pr->service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V6) {
g_autofree gchar *tmp = NULL;
tmp = fu_redfish_common_buffer_to_ipv6 (pr->service_ip_address);
fu_redfish_client_set_hostname (self, tmp);
} else {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"address format is invalid");
return FALSE;
}
fu_redfish_client_set_port (self, pr->service_ip_port);
} else {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"DHCP address formats not supported (%0x2)",
pr->service_ip_assignment_type);
return FALSE;
}
return TRUE;
}
static gboolean
fu_redfish_client_set_smbios_interfaces (FuRedfishClient *self,
GBytes *smbios_table,
GError **error)
{
const guint8 *buf;
gsize sz = 0;
/* check size */
buf = g_bytes_get_data (smbios_table, &sz);
if (sz < 0x09) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"SMBIOS entry too small: %" G_GSIZE_FORMAT,
sz);
return FALSE;
}
/* check interface type */
if (buf[0x04] != REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"only Network Host Interface supported");
return FALSE;
}
/* check length */
if (buf[0x05] > sz - 0x08) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"interface specific data too large %u > %" G_GSIZE_FORMAT,
buf[0x05], sz - 0x08);
return FALSE;
}
/* parse data, for not just for debugging */
if (buf[0x05] > 0)
fu_redfish_client_parse_interface_data (&buf[0x06], buf[0x05]);
/* parse protocol records */
for (guint8 i = 0x07 + buf[0x05]; i < sz - 1; i++) {
guint8 protocol_id = buf[i];
guint8 protocol_sz = buf[i+1];
if (protocol_sz > sz - buf[0x05] + 0x07) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"protocol length too large");
return FALSE;
}
if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) {
if (!fu_redfish_client_parse_protocol_data (self,
&buf[i+2],
protocol_sz,
error))
return FALSE;
} else {
g_debug ("ignoring unsupported protocol ID %02x",
protocol_id);
}
i += protocol_sz - 1;
}
return TRUE;
}
gboolean
fu_redfish_client_setup (FuRedfishClient *self, GBytes *smbios_table, GError **error)
{
JsonNode *node_root;
JsonObject *obj_links = NULL;
JsonObject *obj_root = NULL;
JsonObject *obj_update_service = NULL;
const gchar *data_id;
g_autofree gchar *user_agent = NULL;
g_autoptr(GBytes) blob = NULL;
g_autoptr(JsonParser) parser = json_parser_new ();
/* sanity check */
if (self->port == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no port specified");
return FALSE;
}
if (self->port > 0xffff) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"invalid port specified: 0x%x",
self->port);
return FALSE;
}
/* create the soup session */
user_agent = g_strdup_printf ("%s/%s", PACKAGE_NAME, PACKAGE_VERSION);
self->session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent,
SOUP_SESSION_TIMEOUT, 60,
NULL);
if (self->session == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to setup networking");
return FALSE;
}
/* this is optional */
if (smbios_table != NULL) {
g_autoptr(GError) error_smbios = NULL;
g_autoptr(GError) error_uefi = NULL;
if (!fu_redfish_client_set_smbios_interfaces (self,
smbios_table,
&error_smbios)) {
g_debug ("failed to get connection URI automatically: %s",
error_smbios->message);
}
if (!fu_redfish_client_set_uefi_credentials (self, &error_uefi)) {
g_debug ("failed to get username and password automatically: %s",
error_uefi->message);
}
}
if (self->hostname != NULL)
g_debug ("Hostname: %s", self->hostname);
if (self->port != 0)
g_debug ("Port: %u", self->port);
if (self->username != NULL)
g_debug ("Username: %s", self->username);
if (self->password != NULL)
g_debug ("Password: %s", self->password);
/* try to connect */
blob = fu_redfish_client_fetch_data (self, "/redfish/v1", error);
if (blob == NULL)
return FALSE;
/* get the update service */
if (!json_parser_load_from_data (parser,
g_bytes_get_data (blob, NULL),
(gssize) g_bytes_get_size (blob),
error)) {
g_prefix_error (error, "failed to parse node: ");
return FALSE;
}
node_root = json_parser_get_root (parser);
if (node_root == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no root node");
return FALSE;
}
obj_root = json_node_get_object (node_root);
if (obj_root == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no root object");
return FALSE;
}
g_debug ("Version: %s",
json_object_get_string_member (obj_root, "ServiceVersion"));
g_debug ("UUID: %s",
json_object_get_string_member (obj_root, "UUID"));
/* look for UpdateService in Links */
if (json_object_has_member (obj_root, "Links"))
obj_links = json_object_get_object_member (obj_root, "Links");
if (obj_links == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no Links object");
return FALSE;
}
if (json_object_has_member (obj_links, "UpdateService"))
obj_update_service = json_object_get_object_member (obj_links, "UpdateService");
if (obj_update_service == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no UpdateService object");
return FALSE;
}
data_id = json_object_get_string_member (obj_update_service, "@odata.id");
if (data_id == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no @odata.id string");
return FALSE;
}
self->update_uri_path = g_strdup (data_id);
return TRUE;
}
GPtrArray *
fu_redfish_client_get_devices (FuRedfishClient *self)
{
return self->devices;
}
void
fu_redfish_client_set_hostname (FuRedfishClient *self, const gchar *hostname)
{
g_free (self->hostname);
self->hostname = g_strdup (hostname);
}
void
fu_redfish_client_set_port (FuRedfishClient *self, guint port)
{
self->port = port;
}
void
fu_redfish_client_set_username (FuRedfishClient *self, const gchar *username)
{
g_free (self->username);
self->username = g_strdup (username);
}
void
fu_redfish_client_set_password (FuRedfishClient *self, const gchar *password)
{
g_free (self->password);
self->password = g_strdup (password);
}
static void
fu_redfish_client_finalize (GObject *object)
{
FuRedfishClient *self = FU_REDFISH_CLIENT (object);
if (self->session != NULL)
g_object_unref (self->session);
g_free (self->update_uri_path);
g_free (self->hostname);
g_free (self->username);
g_free (self->password);
g_ptr_array_unref (self->devices);
G_OBJECT_CLASS (fu_redfish_client_parent_class)->finalize (object);
}
static void
fu_redfish_client_class_init (FuRedfishClientClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_redfish_client_finalize;
}
static void
fu_redfish_client_init (FuRedfishClient *self)
{
self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}
FuRedfishClient *
fu_redfish_client_new (void)
{
FuRedfishClient *self;
self = g_object_new (REDFISH_TYPE_CLIENT, NULL);
return FU_REDFISH_CLIENT (self);
}
/* vim: set noexpandtab: */

View File

@ -0,0 +1,40 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef __FU_REDFISH_CLIENT_H
#define __FU_REDFISH_CLIENT_H
#include <glib-object.h>
#include <gio/gio.h>
G_BEGIN_DECLS
#define REDFISH_TYPE_CLIENT (fu_redfish_client_get_type ())
G_DECLARE_FINAL_TYPE (FuRedfishClient, fu_redfish_client, FU, REDFISH_CLIENT, GObject)
FuRedfishClient *fu_redfish_client_new (void);
void fu_redfish_client_set_hostname (FuRedfishClient *self,
const gchar *hostname);
void fu_redfish_client_set_username (FuRedfishClient *self,
const gchar *username);
void fu_redfish_client_set_password (FuRedfishClient *self,
const gchar *password);
void fu_redfish_client_set_port (FuRedfishClient *self,
guint port);
gboolean fu_redfish_client_setup (FuRedfishClient *self,
GBytes *smbios_table,
GError **error);
gboolean fu_redfish_client_coldplug (FuRedfishClient *self,
GError **error);
GPtrArray *fu_redfish_client_get_devices (FuRedfishClient *self);
G_END_DECLS
#endif /* __FU_REDFISH_CLIENT_H */
/* vim: set noexpandtab: */

View File

@ -0,0 +1,60 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fwupd-error.h"
#include "fu-redfish-common.h"
GBytes *
fu_redfish_common_get_evivar_raw (efi_guid_t guid, const gchar *name, GError **error)
{
gsize sz = 0;
guint32 attribs = 0;
guint8 *data = NULL;
if (efi_get_variable (guid, name, &data, &sz, &attribs) < 0) {
g_autofree gchar *guid_str = NULL;
efi_guid_to_str (&guid, &guid_str);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to get efivar for %s %s",
guid_str, name);
return NULL;
}
return g_bytes_new_take (data, sz);
}
gchar *
fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer)
{
GString *str = g_string_new (NULL);
for (guint i = 0; i < 4; i++) {
g_string_append_printf (str, "%u", buffer[i]);
if (i != 3)
g_string_append (str, ".");
}
return g_string_free (str, FALSE);
}
gchar *
fu_redfish_common_buffer_to_ipv6 (const guint8 *buffer)
{
GString *str = g_string_new (NULL);
for (guint i = 0; i < 16; i += 4) {
g_string_append_printf (str, "%02x%02x%02x%02x",
buffer[i+0], buffer[i+1],
buffer[i+2], buffer[i+3]);
if (i != 12)
g_string_append (str, ":");
}
return g_string_free (str, FALSE);
}
/* vim: set noexpandtab: */

View File

@ -0,0 +1,56 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef __FU_REDFISH_COMMON_H
#define __FU_REDFISH_COMMON_H
#include <gio/gio.h>
#include <efivar.h>
G_BEGIN_DECLS
/* SMBIOS */
#define REDFISH_SMBIOS_TABLE_TYPE 0x42
#define REDFISH_PROTOCOL_REDFISH_OVER_IP 0x04
#define REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST 0x40
#define REDFISH_INTERFACE_TYPE_USB_NEWORK 0x02
#define REDFISH_INTERFACE_TYPE_PCI_NEWORK 0x03
#define REDFISH_IP_ASSIGNMENT_TYPE_STATIC 0x00
#define REDFISH_IP_ASSIGNMENT_TYPE_DHCP 0x02
#define REDFISH_IP_ASSIGNMENT_TYPE_AUTO_CONFIG 0x03
#define REDFISH_IP_ASSIGNMENT_TYPE_HOST_SELECT 0x04
#define REDFISH_IP_ADDRESS_FORMAT_UNKNOWN 0x00
#define REDFISH_IP_ADDRESS_FORMAT_V4 0x01
#define REDFISH_IP_ADDRESS_FORMAT_V6 0x02
/* EFI */
#define REDFISH_EFI_INFORMATION_GUID EFI_GUID(0x16faa37e,0x4b6a,0x4891,0x9028,0x24,0x2d,0xe6,0x5a,0x3b,0x70)
#define REDFISH_EFI_INFORMATION_INDICATIONS "RedfishIndications"
#define REDFISH_EFI_INFORMATION_FW_CREDENTIALS "RedfishFWCredentials"
#define REDFISH_EFI_INFORMATION_OS_CREDENTIALS "RedfishOSCredentials"
#define REDFISH_EFI_INDICATIONS_FW_CREDENTIALS 0x00000001
#define REDFISH_EFI_INDICATIONS_OS_CREDENTIALS 0x00000002
/* shared */
GBytes *fu_redfish_common_get_evivar_raw (efi_guid_t guid,
const gchar *name,
GError **error);
gchar *fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer);
gchar *fu_redfish_common_buffer_to_ipv6 (const guint8 *buffer);
G_END_DECLS
#endif /* __FU_REDFISH_COMMON_H */
/* vim: set noexpandtab: */

View File

@ -0,0 +1,38 @@
/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupd.h>
#include "fu-plugin-private.h"
#include "fu-test.h"
#include "fu-redfish-common.h"
static void
fu_test_redfish_common_func (void)
{
const guint8 buf[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
g_autofree gchar *ipv4 = NULL;
g_autofree gchar *ipv6 = NULL;
ipv4 = fu_redfish_common_buffer_to_ipv4 (buf);
g_assert_cmpstr (ipv4, ==, "0.1.2.3");
ipv6 = fu_redfish_common_buffer_to_ipv6 (buf);
g_assert_cmpstr (ipv6, ==, "00010203:04050607:08090a0b:0c0d0e0f");
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_test_add_func ("/redfish/common", fu_test_redfish_common_func);
return g_test_run ();
}

View File

@ -0,0 +1,49 @@
cargs = ['-DG_LOG_DOMAIN="FuPluginRedfish"']
shared_module('fu_plugin_redfish',
sources : [
'fu-plugin-redfish.c',
'fu-redfish-client.c',
'fu-redfish-common.c',
],
include_directories : [
include_directories('../..'),
include_directories('../../src'),
include_directories('../../libfwupd'),
],
install : true,
install_dir: plugin_dir,
c_args : cargs,
dependencies : [
plugin_deps,
efivar,
json_glib,
],
)
if get_option('tests')
e = executable(
'redfish-self-test',
sources : [
'fu-self-test.c',
'fu-redfish-client.c',
'fu-redfish-common.c',
],
include_directories : [
include_directories('../..'),
include_directories('../../src'),
include_directories('../../libfwupd'),
],
dependencies : [
plugin_deps,
efivar,
json_glib,
],
link_with : [
fwupd,
libfwupdprivate,
],
c_args : cargs
)
test('redfish-self-test', e)
endif