From b7b455a38cbd5f46fb6231168ac9b3acf5dd392a Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Wed, 6 Feb 2019 12:00:54 -0800 Subject: [PATCH] add OpenBMC D-Bus interface OpenBMC runs a D-Bus interface internally and has the option of compiling ipmitool so it can run natively on the BMC. This adds the D-Bus interface to ipmitool so it can be used with the OpenBMC project. Signed-off-by: Vernon Mauery --- configure.ac | 30 +++++ src/plugins/Makefile.am | 4 +- src/plugins/dbus/Makefile.am | 41 ++++++ src/plugins/dbus/dbus.c | 241 +++++++++++++++++++++++++++++++++++ src/plugins/ipmi_intf.c | 6 + 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 src/plugins/dbus/Makefile.am create mode 100644 src/plugins/dbus/dbus.c diff --git a/configure.ac b/configure.ac index 7bb6cbc..34c2081 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ dnl xenable_all_options=yes xenable_intf_bmc=no +xenable_intf_dbus=no xenable_intf_dummy=no xenable_intf_imb=yes xenable_intf_lipmi=yes @@ -191,6 +192,14 @@ AC_CHECK_LIB([crypto], [MD2_Init], fi], [], [-lcrypto]) +dnl check for libsystemd in case dbus-intf is requested +AC_CHECK_LIB([systemd], [sd_bus_default], + [ + LIBS="$LIBS -lsystemd" + have_systemd=yes + ], + [ have_systemd=no],[]) + dnl enable IPMIv1.5 LAN interface AC_ARG_ENABLE([intf-lan], [AC_HELP_STRING([--enable-intf-lan], @@ -554,6 +563,25 @@ if test "x$xenable_intf_bmc" = "xyes"; then IPMITOOL_INTF_LIB="$IPMITOOL_INTF_LIB bmc/libintf_bmc.la" fi +dnl enable IPMI dbus interface +AC_ARG_ENABLE([intf-dbus], + [AC_HELP_STRING([--enable-intf-dbus], + [enable IPMI dbus interface [default=no]])], + [xenable_intf_dbus=$enableval], + [xenable_intf_dbus=no]) +if test "x$xenable_intf_dbus" != "xno"; then + if test "x$have_systemd" != "xyes"; then + AC_MSG_ERROR([** Unable to find libsystemd required by dbus-intf.]) + xenable_intf_dbus=no + fi +fi +if test "x$xenable_intf_dbus" = "xyes"; then + AC_DEFINE(IPMI_INTF_DBUS, [1], [Define to 1 to enable dbus interface.]) + AC_SUBST(INTF_DBUS, [dbus]) + AC_SUBST(INTF_DBUS_LIB, [libintf_dbus.la]) + IPMITOOL_INTF_LIB="$IPMITOOL_INTF_LIB dbus/libintf_dbus.la" +fi + dnl enable Dummy interface for testing AC_ARG_ENABLE([intf-dummy], [AC_HELP_STRING([--enable-intf-dummy], @@ -664,6 +692,7 @@ AC_CONFIG_FILES([Makefile src/plugins/free/Makefile src/plugins/imb/Makefile src/plugins/bmc/Makefile + src/plugins/dbus/Makefile src/plugins/usb/Makefile src/plugins/lipmi/Makefile src/plugins/serial/Makefile @@ -681,6 +710,7 @@ AC_MSG_RESULT([ open : $xenable_intf_open]) AC_MSG_RESULT([ free : $xenable_intf_free]) AC_MSG_RESULT([ imb : $xenable_intf_imb]) AC_MSG_RESULT([ bmc : $xenable_intf_bmc]) +AC_MSG_RESULT([ dbus : $xenable_intf_dbus]) AC_MSG_RESULT([ usb : $xenable_intf_usb]) AC_MSG_RESULT([ lipmi : $xenable_intf_lipmi]) AC_MSG_RESULT([ serial : $xenable_intf_serial]) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 7ad8173..c0b0b29 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -32,8 +32,8 @@ MAINTAINERCLEANFILES = Makefile.in AM_CPPFLAGS = -I$(top_srcdir)/include -SUBDIRS = @INTF_LAN@ @INTF_LANPLUS@ @INTF_OPEN@ @INTF_LIPMI@ @INTF_IMB@ @INTF_BMC@ @INTF_FREE@ @INTF_SERIAL@ @INTF_DUMMY@ @INTF_USB@ -DIST_SUBDIRS = lan lanplus open lipmi imb bmc free serial dummy usb +SUBDIRS = @INTF_LAN@ @INTF_LANPLUS@ @INTF_OPEN@ @INTF_LIPMI@ @INTF_IMB@ @INTF_BMC@ @INTF_FREE@ @INTF_SERIAL@ @INTF_DUMMY@ @INTF_USB@ @INTF_DBUS@ +DIST_SUBDIRS = lan lanplus open lipmi imb bmc free serial dummy usb dbus noinst_LTLIBRARIES = libintf.la libintf_la_SOURCES = ipmi_intf.c diff --git a/src/plugins/dbus/Makefile.am b/src/plugins/dbus/Makefile.am new file mode 100644 index 0000000..3e92113 --- /dev/null +++ b/src/plugins/dbus/Makefile.am @@ -0,0 +1,41 @@ + # + # Copyright (c) 2015 IBM Corporation + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification,are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # 2. Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # 3. Neither the name of the copyright holder nor the names of its contributors + # may be used to endorse or promote products derived from this software + # without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + # AND ANY EXPRESS OR IMPLIED WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + # POSSIBILITY OF SUCH DAMAGE. + # + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/include + +EXTRA_LTLIBRARIES = libintf_dbus.la +noinst_LTLIBRARIES = @INTF_DBUS_LIB@ +libintf_dbus_la_LDFLAGS = -lsystemd +libintf_dbus_la_LIBADD = $(top_builddir)/lib/libipmitool.la +libintf_dbus_la_SOURCES = dbus.c + diff --git a/src/plugins/dbus/dbus.c b/src/plugins/dbus/dbus.c new file mode 100644 index 0000000..be72dcb --- /dev/null +++ b/src/plugins/dbus/dbus.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2015 IBM Corporation + * Copyright (c) 2019 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +static sd_bus *bus; + +static +struct ipmi_rs * +ipmi_dbus_sendrecv(struct ipmi_intf *intf, + struct ipmi_rq *req) +{ + static const char *destination = "xyz.openbmc_project.Ipmi.Host"; + static const char *object_path = "/xyz/openbmc_project/Ipmi"; + static const char *interface = "xyz.openbmc_project.Ipmi.Server"; + static const char *method_name = "execute"; + static const char SD_BUS_TYPE_3_BYTES[] = { + SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, 0 + }; + static const char SD_BUS_TYPE_4_BYTES[] = { + SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, 0 + }; + static const char SD_BUS_TYPE_DICT_OF_VARIANTS[] = { + SD_BUS_TYPE_ARRAY, + SD_BUS_TYPE_DICT_ENTRY_BEGIN, + SD_BUS_TYPE_STRING, SD_BUS_TYPE_VARIANT, + SD_BUS_TYPE_DICT_ENTRY_END, 0 + }; + static const char SD_BUS_TYPE_IPMI_RESPONSE[] = { + SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BYTE, SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_BYTE, 0 + }; + + sd_bus_message *request = NULL; + int rc; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message* reply = NULL; + uint8_t recv_netfn; + uint8_t recv_lun; + uint8_t recv_cmd; + uint8_t recv_cc; + const void *data; + size_t data_len; + static struct ipmi_rs rsp; + struct ipmi_rs *ipmi_response = NULL; + + if (!intf->opened || !bus) + { + goto out_no_free; + } + + rsp.ccode = IPMI_CC_UNSPECIFIED_ERROR; + rsp.data_len = 0; + memset(rsp.data, 0, sizeof(rsp.data)); + + /* The D-Bus xyz.openbmc_project.Ipmi.Server.execute interface + * looks like this: + * + * Request: + * byte: net function + * byte: lun + * byte: command + * byte array: data (possibly zero length) + * array of (string,variant): options + * Response: + * byte: net function + * byte: lun + * byte: command + * byte: completion code + * byte array: response data (possibly zero length) + */ + rc = sd_bus_message_new_method_call(bus, &request, destination, + object_path, interface, + method_name); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to create message: %s\n", + __func__, strerror(-rc)); + goto out_no_free; + } + /* pack the header: netfn, lun, cmd */ + rc = sd_bus_message_append(request, SD_BUS_TYPE_3_BYTES, req->msg.netfn, + req->msg.lun, req->msg.cmd); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to append parameters\n", __func__); + goto out_free_request; + } + /* pack the variable length data */ + rc = sd_bus_message_append_array(request, SD_BUS_TYPE_BYTE, + req->msg.data, req->msg.data_len); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to append body\n", __func__); + goto out_free_request; + } + + /* Options are only needed for session-based channels, but + * in order to fulfill the correct signature, an empty array + * must be packed */ + rc = sd_bus_message_append(request, SD_BUS_TYPE_DICT_OF_VARIANTS, + NULL, 0); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to append options\n", __func__); + goto out_free_request; + } + + rc = sd_bus_call(bus, request, 0, &error, &reply); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to send dbus message (%s)\n", + __func__, error.message); + goto out_free_request; + } + + /* unpack the response; check that it has the expected types */ + rc = sd_bus_message_enter_container(reply, SD_BUS_TYPE_STRUCT, + SD_BUS_TYPE_IPMI_RESPONSE); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to parse reply\n", __func__); + goto out_free_reply; + } + /* read the header: CC netfn lun cmd */ + rc = sd_bus_message_read(reply, SD_BUS_TYPE_4_BYTES, &recv_netfn, + &recv_lun, &recv_cmd, &recv_cc); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to read reply\n", __func__); + goto out_free_reply; + } + /* read the variable length data */ + rc = sd_bus_message_read_array(reply, SD_BUS_TYPE_BYTE, + &data, &data_len); + if (rc < 0) { + lprintf(LOG_ERR, "%s: failed to read reply data\n", __func__); + goto out_free_reply; + } + rc = sd_bus_message_exit_container(reply); + if (rc < 0) { + lprintf(LOG_ERR, "%s: final unpack of message failed\n", + __func__); + goto out_free_reply; + } + + if (data_len > sizeof(rsp.data)) { + lprintf(LOG_ERR, "%s: data too long!\n", __func__); + goto out_free_reply; + } + + /* At this point, all the parts are available for a response + * other than unspecified error. */ + rsp.ccode = recv_cc; + rsp.data_len = data_len; + memcpy(rsp.data, data, data_len); + ipmi_response = &rsp; + +out_free_reply: + /* message unref will free resources owned by the message */ + sd_bus_message_unref(reply); +out_free_request: + sd_bus_message_unref(request); +out_no_free: + return ipmi_response; +} + +static +int +ipmi_dbus_setup(struct ipmi_intf *intf) +{ + int rc; + + rc = sd_bus_default_system(&bus); + if (rc < 0) { + lprintf(LOG_ERR, "Can't connect to session bus: %s\n", + strerror(-rc)); + return -1; + } + intf->opened = 1; + + return 0; +} + +static +void +ipmi_dbus_close(struct ipmi_intf *intf) +{ + if (intf->opened) + { + sd_bus_close(bus); + } + intf->opened = 0; +} + +struct ipmi_intf ipmi_dbus_intf = { + .name = "dbus", + .desc = "OpenBMC D-Bus interface", + .setup = ipmi_dbus_setup, + .close = ipmi_dbus_close, + .sendrecv = ipmi_dbus_sendrecv, +}; + + diff --git a/src/plugins/ipmi_intf.c b/src/plugins/ipmi_intf.c index f6bd2f6..d815e2b 100644 --- a/src/plugins/ipmi_intf.c +++ b/src/plugins/ipmi_intf.c @@ -86,6 +86,9 @@ extern struct ipmi_intf ipmi_dummy_intf; #ifdef IPMI_INTF_USB extern struct ipmi_intf ipmi_usb_intf; #endif +#ifdef IPMI_INTF_DBUS +extern struct ipmi_intf ipmi_dbus_intf; +#endif struct ipmi_intf * ipmi_intf_table[] = { #ifdef IPMI_INTF_OPEN @@ -118,6 +121,9 @@ struct ipmi_intf * ipmi_intf_table[] = { #endif #ifdef IPMI_INTF_USB &ipmi_usb_intf, +#endif +#ifdef IPMI_INTF_DBUS + &ipmi_dbus_intf, #endif NULL };