Add initial kcsbridge package
kcsbridge implements a KCM ipmi bridge for openbmc in a similar way to
btbridged.
Change-Id: I7f89e4ef3ccaef5b08ecef150a9672f5bd1ea85d
Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..e479d3f
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,22 @@
+---
+BasedOnStyle: LLVM
+Language: Cpp
+IndentWidth: 8
+UseTab: Always
+BreakBeforeBraces: Linux
+AlwaysBreakBeforeMultilineStrings: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+IndentCaseLabels: false
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AlignAfterOpenBracket: true
+SpaceAfterCStyleCast: false
+MaxEmptyLinesToKeep: 2
+BreakBeforeBinaryOperators: NonAssignment
+BreakStringLiterals: false
+SortIncludes: false
+ContinuationIndentWidth: 8
+
diff --git a/LICENSE b/LICENSE
index 261eeb9..9955994 100644
--- a/LICENSE
+++ b/LICENSE
@@ -178,7 +178,7 @@
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
+ boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..19bcf20
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,46 @@
+How to use this list:
+ Find the most specific section entry (described below) that matches where
+ your change lives and add the reviewers (R) and maintainers (M) as
+ reviewers. You can use the same method to track down who knows a particular
+ code base best.
+
+ Your change/query may span multiple entries; that is okay.
+
+ If you do not find an entry that describes your request at all, someone
+ forgot to update this list; please at least file an issue or send an email
+ to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+ Section entries are structured according to the following scheme:
+
+ X: NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+ X: ...
+ .
+ .
+ .
+
+ Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+ organization; FILE_PATH is a file path within the repository, possibly with
+ wildcards; X is a tag of one of the following types:
+
+ M: Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ if omitted from an entry, assume one of the maintainers from the
+ MAINTAINERS entry.
+ R: Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ these people are to be added as reviewers for a change matching the repo
+ path.
+ F: Denotes forked from an external repository; has fields URL.
+
+ Line comments are to be denoted "# SOME COMMENT" (typical shell style
+ comment); it is important to follow the correct syntax and semantics as we
+ may want to use automated tools with this file in the future.
+
+ A change cannot be added to an OpenBMC repository without a MAINTAINER's
+ approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M: Ed Tanous <ed.tanous@intel.com> <edtanous!>
+M: Vernon Mauery <veron.mauery@linux.intel.com> <vmauery!>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5ae0dcd
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,5 @@
+sbin_PROGRAMS = kcsbridged
+
+kcsbridged_SOURCES = kcsbridged.c
+kcsbridged_LDFLAGS = $(SYSTEMD_LIBS)
+kcsbridged_CFLAGS = $(SYSTEMD_CFLAGS)
diff --git a/README.md b/README.md
index 3617144..eb16f23 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,9 @@
-# kcsbridge
\ No newline at end of file
+# kcsbridge
+## To Build
+To build this package, do the following steps:
+
+ 1. ./bootstrap.sh
+ 2. ./configure ${CONFIGURE_FLAGS}
+ 3. make
+
+To full clean the repository again run `./bootstrap.sh clean`.
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..7747fb6
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+ config.guess config.h.in config.sub configure depcomp install-sh \
+ ltmain.sh missing *libtool test-driver"
+
+case $1 in
+ clean)
+ test -f Makefile && make maintainer-clean
+ test -f linux/ipmi_bmc.h && rm -rf linux/ipmi_bmc.h
+ test -d linux && find linux -type d -empty | xargs -r rm -rf
+ for file in ${AUTOCONF_FILES}; do
+ find -name "$file" | xargs -r rm -rf
+ done
+ exit 0
+ ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f0eb279
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,55 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([kcsbridge], [1.0], [https://github.com/openbmc/kcsbridge/issues])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs.
+AC_PROG_CC
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for libraries.
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# Checks for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
+AC_CHECK_HEADER(linux/ipmi_bmc.h,[HAVE_LINUX_IPMI_BMC_H=""],[HAVE_LINUX_IPMI_BMC_H="-I linux/ipmi_bmc.h"])
+AS_IF([test "$HAVE_LINUX_IPMI_BMC_H" != ""],
+ AC_MSG_WARN([Could not find linux/ipmi-bmc.h: Attempting to download locally for building from https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/ipmi_bmc.h])
+ AC_SUBST([IPMI_BMC_DL],[`mkdir -p linux;wget https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/ipmi_bmc.h -O linux/ipmi_bmc.h`])
+)
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_APPEND_COMPILE_FLAGS([-fpic -Wall -Werror], [CFLAGS])
+
+# Checks for library functions.
+LT_INIT # Removes 'unrecognized options: --with-libtool-sysroot'
+
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+AC_ARG_ENABLE([oe-sdk],
+ AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.])
+)
+AC_ARG_VAR(OECORE_TARGET_SYSROOT,
+ [Path to the OE SDK SYSROOT])
+AS_IF([test "x$enable_oe_sdk" == "xyes"],
+ AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"],
+ AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk])
+ )
+ AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT])
+ [
+ testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib"
+ testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib"
+ testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`"
+ ]
+ AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
+)
+
+# Create configured output
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/kcsbridged.c b/kcsbridged.c
new file mode 100644
index 0000000..2234501
--- /dev/null
+++ b/kcsbridged.c
@@ -0,0 +1,538 @@
+/* Copyright 2017 - 2018 Intel
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/ipmi_bmc.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <systemd/sd-bus.h>
+#include <time.h>
+#include <unistd.h>
+
+#define DBUS_ERR "org.openbmc.error"
+#define DBUS_NAME "org.openbmc.HostIpmi"
+#define OBJ_NAME "/org/openbmc/HostIpmi/1"
+
+#define LOG_PREFIX "KCSBRIDGED"
+
+#define KCS_TIMEOUT_IN_SEC 5
+#define KCS_MESSAGE_SIZE 256
+
+#define SD_BUS_FD 0
+#define KCS_FD 1
+#define TIMER_FD 2
+#define TOTAL_FDS 3
+
+struct kcs_msg_req {
+ uint8_t netfn;
+ uint8_t lun;
+ uint8_t cmd;
+ uint8_t *data;
+ size_t data_len;
+};
+
+struct kcsbridged_context {
+ struct pollfd fds[TOTAL_FDS];
+ struct sd_bus *bus;
+
+ /*
+ * Request and Response Messages are paired together as a Write Transfer
+ * to the BMC to send the request followed by a Read Transfer from the
+ * BMC to get the response.
+ */
+ int expired;
+ uint8_t seqnum;
+ struct kcs_msg_req req;
+};
+
+enum { KCS_LOG_NONE = 0, KCS_LOG_VERBOSE, KCS_LOG_DEBUG };
+
+static void (*kcs_vlog)(int p, const char *fmt, va_list args);
+static int verbosity = KCS_LOG_NONE;
+
+#define MSG_OUT(f_, ...) \
+ do { \
+ if (verbosity != KCS_LOG_NONE) \
+ kcs_log(LOG_INFO, f_, ##__VA_ARGS__); \
+ } while (0)
+
+#define MSG_ERR(f_, ...) \
+ do { \
+ if (verbosity != KCS_LOG_NONE) \
+ kcs_log(LOG_ERR, f_, ##__VA_ARGS__); \
+ } while (0)
+
+static void kcs_log_console(int p, const char *fmt, va_list args)
+{
+ vfprintf(stderr, fmt, args);
+}
+
+__attribute__((format(printf, 2, 3))) static void kcs_log(int p,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ kcs_vlog(p, fmt, args);
+ va_end(args);
+}
+
+static void kcs_dump_data(uint8_t *data, size_t data_len)
+{
+ size_t i;
+ int str_len;
+ char str[64];
+
+ str_len = 0;
+ for (i = 0; i < data_len; i++) {
+ if (i % 8 == 0) {
+ if (i != 0) {
+ kcs_log(LOG_INFO, "%s\n", str);
+ str_len = 0;
+ }
+ str_len += sprintf(&str[str_len], "\t");
+ }
+
+ str_len += sprintf(&str[str_len], "0x%02x ", data[i]);
+ }
+
+ if (str_len != 0)
+ kcs_log(LOG_INFO, "%s\n", str);
+}
+
+static void kcs_set_timer(struct kcsbridged_context *context, int seconds)
+{
+ struct itimerspec ts;
+ int r;
+
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+ ts.it_value.tv_nsec = 0;
+ ts.it_value.tv_sec = seconds;
+
+ r = timerfd_settime(context->fds[TIMER_FD].fd, 0, &ts, NULL);
+ if (r == -1)
+ MSG_ERR("Couldn't set timerfd: %s\n", strerror(errno));
+}
+
+static int handle_kcs_request(struct kcsbridged_context *context, uint8_t *msg,
+ size_t msglen)
+{
+ struct kcs_msg_req *req;
+
+ if (msglen < 2) {
+ MSG_ERR("KCS message with a short length (%zd)\n", msglen);
+ return -1;
+ }
+
+ context->expired = 0;
+ context->seqnum++;
+
+ req = &context->req;
+ req->netfn = msg[0] >> 2;
+ req->lun = msg[0] & 0x3;
+ req->cmd = msg[1];
+ req->data = msg + 2;
+ req->data_len = msglen - 2;
+
+ return 0;
+}
+
+static int method_send_message(sd_bus_message *msg, void *userdata,
+ sd_bus_error *err)
+{
+ struct kcsbridged_context *context = userdata;
+ uint8_t netfn, lun, seqnum, cmd, cc;
+ struct kcs_msg_req *req;
+ uint8_t *data;
+ size_t data_sz;
+ int r;
+ uint8_t rsp[KCS_MESSAGE_SIZE];
+
+ if (!context || context->expired) {
+ sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
+ r = 0;
+ goto out;
+ }
+
+ r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
+ if (r < 0) {
+ sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
+ r = -EINVAL;
+ goto out;
+ }
+
+ req = &context->req;
+ if (context->seqnum != seqnum || (req->netfn | 1) != netfn
+ || req->lun != lun || req->cmd != cmd) {
+ sd_bus_error_set_const(err, DBUS_ERR, "No matching request");
+ r = -EINVAL;
+ goto out;
+ }
+
+ kcs_set_timer(context, 0); /* Stop the timer. */
+
+ r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
+ if (r < 0 || data_sz > sizeof(rsp) - 3) {
+ sd_bus_error_set_const(err, DBUS_ERR, "Bad message data");
+ r = -EINVAL;
+ goto out;
+ }
+
+ rsp[0] = (netfn << 2) | (lun & 0x3);
+ rsp[1] = cmd;
+ rsp[2] = cc;
+ if (data_sz)
+ memcpy(rsp + 3, data, data_sz);
+
+ r = write(context->fds[KCS_FD].fd, rsp, 3 + data_sz);
+ if (r > 0)
+ r = 0;
+
+ MSG_OUT("Send rsp msg <- seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x cc=0x%02x\n",
+ seqnum, netfn, lun, cmd, cc);
+
+ if (verbosity == KCS_LOG_DEBUG && data_sz != 0)
+ kcs_dump_data(data, data_sz);
+
+out:
+ return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_set_sms_atn(sd_bus_message *msg, void *userdata,
+ sd_bus_error *err)
+{
+ struct kcsbridged_context *context = userdata;
+ int r;
+
+ MSG_OUT("Sending SET_SMS_ATN\n");
+
+ r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_SET_SMS_ATN);
+ if (r == -1) {
+ r = errno;
+ MSG_ERR("Couldn't SET_SMS_ATN: %s\n", strerror(r));
+ return sd_bus_reply_method_errno(msg, errno, err);
+ }
+
+ r = 0;
+ return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_clear_sms_atn(sd_bus_message *msg, void *userdata,
+ sd_bus_error *err)
+{
+ struct kcsbridged_context *context = userdata;
+ int r;
+
+ MSG_OUT("Sending CLEAR_SMS_ATN\n");
+
+ r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
+ if (r == -1) {
+ r = errno;
+ MSG_ERR("Couldn't CLEAR_SMS_ATN: %s\n", strerror(r));
+ return sd_bus_reply_method_errno(msg, errno, err);
+ }
+
+ r = 0;
+ return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_force_abort(sd_bus_message *msg, void *userdata,
+ sd_bus_error *err)
+{
+ struct kcsbridged_context *context = userdata;
+ int r;
+
+ MSG_OUT("Sending FORCE_ABORT\n");
+
+ r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_FORCE_ABORT);
+ if (r == -1) {
+ r = errno;
+ MSG_ERR("Couldn't FORCE_ABORT: %s\n", strerror(r));
+ return sd_bus_reply_method_errno(msg, errno, err);
+ }
+
+ r = 0;
+ return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int dispatch_sd_bus(struct kcsbridged_context *context)
+{
+ int r = 0;
+
+ if (context->fds[SD_BUS_FD].revents) {
+ r = sd_bus_process(context->bus, NULL);
+ if (r > 0)
+ MSG_OUT("Processed %d dbus events\n", r);
+ }
+
+ return r;
+}
+
+static int dispatch_timer(struct kcsbridged_context *context)
+{
+ if (context->fds[TIMER_FD].revents & POLLIN) {
+ struct kcs_msg_req *req;
+ uint8_t rsp[3];
+
+ MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
+
+ context->expired = 1;
+
+ req = &context->req;
+ rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
+ rsp[1] = req->cmd;
+ rsp[2] = 0xce; /* Command response could not be provided */
+ if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
+ MSG_ERR("Failed to send the timeout response!\n");
+ }
+
+ return 0;
+}
+
+static int dispatch_kcs(struct kcsbridged_context *context)
+{
+ struct kcs_msg_req *req = &context->req;
+ sd_bus_message *msg;
+ int r = 0, len;
+ uint8_t data[KCS_MESSAGE_SIZE];
+
+ if (!(context->fds[KCS_FD].revents & POLLIN))
+ goto out;
+
+ len = read(context->fds[KCS_FD].fd, data, sizeof(data));
+ if (len < 0 || handle_kcs_request(context, data, len))
+ goto out;
+
+ r = sd_bus_message_new_signal(context->bus, &msg, OBJ_NAME, DBUS_NAME,
+ "ReceivedMessage");
+ if (r < 0) {
+ MSG_ERR("Failed to create signal: %s\n", strerror(-r));
+ goto out;
+ }
+
+ r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
+ req->lun, req->cmd);
+ if (r < 0) {
+ MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
+ goto bail;
+ }
+
+ r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
+ if (r < 0) {
+ MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
+ goto bail;
+ }
+
+ r = sd_bus_send(context->bus, msg, NULL);
+ if (r < 0) {
+ MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
+ goto bail;
+ }
+
+ kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
+
+ MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
+ context->seqnum, req->netfn, req->lun, req->cmd);
+
+ if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
+ kcs_dump_data(req->data, req->data_len);
+
+bail:
+ sd_bus_message_unref(msg);
+out:
+ return r;
+}
+
+static void usage(const char *name)
+{
+ fprintf(stderr,
+ "Usage %s [--v[v] | --syslog] --d <DEVICE>\n"
+ "--v Be verbose\n"
+ "--vv Be verbose and dump entire messages\n"
+ "--s, --syslog Log output to syslog (pointless without --verbose)\n"
+ "--d, --device <DEVICE> use <DEVICE> file.\n\n",
+ name);
+}
+
+static const sd_bus_vtable ipmid_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
+ SD_BUS_VTABLE_END};
+
+int main(int argc, char *argv[])
+{
+ struct kcsbridged_context *context;
+ const char *kcs_bmc_device = NULL;
+ const char *name = argv[0];
+ int opt, polled, r;
+ static const struct option long_options[] = {
+ {"device", required_argument, 0, 'd'},
+ {"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
+ {"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
+ {"syslog", no_argument, 0, 's'},
+ {0, 0, 0, 0}};
+
+ context = calloc(1, sizeof(*context));
+ if (!context) {
+ fprintf(stderr, "OOM!\n");
+ return -1;
+ }
+
+ kcs_vlog = &kcs_log_console;
+ while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+ switch (opt) {
+ case 0:
+ break;
+
+ case 'd':
+ kcs_bmc_device = optarg;
+ break;
+
+ case 's':
+ if (kcs_vlog != &vsyslog) {
+ openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
+ kcs_vlog = &vsyslog;
+ }
+ break;
+
+ default:
+ usage(name);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!kcs_bmc_device) {
+ usage(name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbosity == KCS_LOG_VERBOSE)
+ MSG_OUT("Verbose logging\n");
+ else if (verbosity == KCS_LOG_DEBUG)
+ MSG_OUT("Debug logging\n");
+
+ MSG_OUT("Starting\n");
+ r = sd_bus_default_system(&context->bus);
+ if (r < 0) {
+ MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
+ goto finish;
+ }
+
+ MSG_OUT("Registering dbus methods/signals\n");
+ r = sd_bus_add_object_vtable(context->bus, NULL, OBJ_NAME, DBUS_NAME,
+ ipmid_vtable, context);
+ if (r < 0) {
+ MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
+ goto finish;
+ }
+
+ MSG_OUT("Requesting dbus name: %s\n", DBUS_NAME);
+ r = sd_bus_request_name(context->bus, DBUS_NAME,
+ SD_BUS_NAME_ALLOW_REPLACEMENT
+ | SD_BUS_NAME_REPLACE_EXISTING);
+ if (r < 0) {
+ MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
+ goto finish;
+ }
+
+ MSG_OUT("Getting dbus file descriptors\n");
+ context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
+ if (context->fds[SD_BUS_FD].fd < 0) {
+ r = -errno;
+ MSG_OUT("Couldn't get the bus file descriptor: %s\n",
+ strerror(errno));
+ goto finish;
+ }
+
+ MSG_OUT("Opening %s\n", kcs_bmc_device);
+ context->fds[KCS_FD].fd = open(kcs_bmc_device, O_RDWR | O_NONBLOCK);
+ if (context->fds[KCS_FD].fd < 0) {
+ r = -errno;
+ MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
+ kcs_bmc_device, strerror(errno));
+ goto finish;
+ }
+
+ MSG_OUT("Creating timer fd\n");
+ context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (context->fds[TIMER_FD].fd < 0) {
+ r = -errno;
+ MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
+ goto finish;
+ }
+ context->fds[SD_BUS_FD].events = POLLIN;
+ context->fds[KCS_FD].events = POLLIN;
+ context->fds[TIMER_FD].events = POLLIN;
+
+ MSG_OUT("Entering polling loop\n");
+
+ while (1) {
+ polled = poll(context->fds, TOTAL_FDS, 5000);
+ if (polled == 0)
+ continue;
+ if (polled < 0) {
+ r = -errno;
+ MSG_ERR("Error from poll(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ r = dispatch_sd_bus(context);
+ if (r < 0) {
+ MSG_ERR("Error handling dbus event: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+ r = dispatch_kcs(context);
+ if (r < 0) {
+ MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
+ goto finish;
+ }
+ r = dispatch_timer(context);
+ if (r < 0) {
+ MSG_ERR("Error handling timer event: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+ }
+
+finish:
+ close(context->fds[KCS_FD].fd);
+ close(context->fds[TIMER_FD].fd);
+ sd_bus_unref(context->bus);
+ free(context);
+
+ return r;
+}
diff --git a/kcsbridged.service b/kcsbridged.service
new file mode 100644
index 0000000..cac223f
--- /dev/null
+++ b/kcsbridged.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=KCS Bridge Daemon
+
+[Service]
+Type=dbus
+BusName=org.openbmc.HostIpmi
+ExecStart=/bin/kcsbridged
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/org.openbmc.HostIpmi.conf b/org.openbmc.HostIpmi.conf
new file mode 100644
index 0000000..b1d30ed
--- /dev/null
+++ b/org.openbmc.HostIpmi.conf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration
+1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+ This file is need to run openbmc kcs bridge daemon.
+ Place this file in /etc/dbus-1/system.d/
+-->
+
+<busconfig>
+
+ <policy user="root">
+ <allow own="org.openbmc.HostIpmi"/>
+ <allow send_destination="org.openbmc.HostIpmi"/>
+ <allow receive_sender="org.openbmc.HostIpmi"/>
+ </policy>
+
+ <policy context="default">
+ <allow send_destination="org.openbmc.HostIpmi"/>
+ <allow receive_sender="org.openbmc.HostIpmi"/>
+ </policy>
+
+</busconfig>
+