Convert kcsbridged to C++17, sdbusplus::asio, and new IPMI D-Bus API

This rewrites the C-style kcsbridged in modern C++, taking advantage of
the sdbusplus::asio bindings. This also converts the bridge to use the
new IPMI D-Bus API, which is method based instead of signal/method
based.

Tested-by: Using a parameterized unit file, launching via
           sytemctl start phosphor-ipmi-kcs@ipmi-kcs3
    verify from host:
           modprobe ipmi_si
           ipmitool mc info

Change-Id: I3a3e85a23fa7b76814848fcf270d25e4510bdd77
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/.clang-format b/.clang-format
index e479d3f..ea71ad6 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,22 +1,99 @@
 ---
-BasedOnStyle: LLVM
-Language: Cpp
-IndentWidth: 8
-UseTab: Always
-BreakBeforeBraces: Linux
-AlwaysBreakBeforeMultilineStrings: true
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
 AllowShortIfStatementsOnASingleLine: false
 AllowShortLoopsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: false
-IndentCaseLabels: false
-AlignEscapedNewlinesLeft: false
-AlignTrailingComments: true
-AllowAllParametersOfDeclarationOnNextLine: false
-AlignAfterOpenBracket: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      true
+  AfterControlStatement: true
+  AfterEnum:       true
+  AfterFunction:   true
+  AfterNamespace:  true
+  AfterObjCDeclaration: true
+  AfterStruct:     true
+  AfterUnion:      true
+  BeforeCatch:     true
+  BeforeElse:      true
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+  - Regex:           '^[<"](gtest|gmock)'
+    Priority:        5
+  - Regex:           '^"config.h"'
+    Priority:        -1
+  - Regex:           '^".*\.hpp"'
+    Priority:        1
+  - Regex:           '^<.*\.h>'
+    Priority:        2
+  - Regex:           '^<.*'
+    Priority:        3
+  - Regex:           '.*'
+    Priority:        4
+IndentCaseLabels: true
+IndentWidth:     4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+ReflowComments:  true
+SortIncludes:    true
+SortUsingDeclarations: true
 SpaceAfterCStyleCast: false
-MaxEmptyLinesToKeep: 2
-BreakBeforeBinaryOperators: NonAssignment
-BreakStringLiterals: false
-SortIncludes:    false
-ContinuationIndentWidth: 8
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        4
+UseTab:          Never
+...
 
diff --git a/Makefile.am b/Makefile.am
index 751bb7e..4fa8796 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,17 @@
 bin_PROGRAMS = kcsbridged
 
-kcsbridged_SOURCES = kcsbridged.c
-kcsbridged_LDFLAGS = $(SYSTEMD_LIBS)
-kcsbridged_CFLAGS = $(SYSTEMD_CFLAGS)
+kcsbridged_SOURCES = kcsbridged.cpp
+kcsbridged_LDFLAGS = \
+	$(SYSTEMD_LIBS) \
+	$(PHOSPHOR_LOGGING_LIBS)
+kcsbridged_CXXFLAGS = \
+	-flto \
+	-Wno-psabi \
+	$(SYSTEMD_CFLAGS) \
+	$(SDBUSPLUS_CFLAGS) \
+	$(PHOSPHOR_LOGGING_CFLAGS) \
+	-DBOOST_ERROR_CODE_HEADER_ONLY \
+	-DBOOST_SYSTEM_NO_DEPRECATED \
+	-DBOOST_COROUTINES_NO_DEPRECATION_WARNING \
+	-DBOOST_ASIO_DISABLE_THREADS \
+	-DBOOST_ALL_NO_LIB
diff --git a/configure.ac b/configure.ac
index a2e5836..08994cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,15 +4,18 @@
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
 AM_SILENT_RULES([yes])
+AC_LANG([C++])
 
 # Checks for programs.
-AC_PROG_CC
+AC_PROG_CXX
 AM_PROG_AR
 AC_PROG_INSTALL
 AC_PROG_MAKE_SET
 
 # Checks for libraries.
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
 
 # Checks for header files.
 AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd development package required])])
@@ -23,6 +26,7 @@
 )
 
 # Checks for typedefs, structures, and compiler characteristics.
+AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
 AX_APPEND_COMPILE_FLAGS([-fpic -Wall -Werror], [CFLAGS])
 
 # Checks for library functions.
diff --git a/kcsbridged.c b/kcsbridged.c
deleted file mode 100644
index 5f50d02..0000000
--- a/kcsbridged.c
+++ /dev/null
@@ -1,569 +0,0 @@
-/* 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>
-#include <stdbool.h>
-
-#define DBUS_ERR "org.openbmc.error"
-
-#define LOG_PREFIX "KCSBRIDGED"
-#define DBUS_NAME "org.openbmc.HostIpmi."
-#define OBJ_NAME "/org/openbmc/HostIpmi/"
-
-#define DEFAULT_DBUS "org.openbmc.HostIpmi"
-#define DEFAULT_OBJ "/org/openbmc/HostIpmi/1"
-#define DBUS_INTF "org.openbmc.HostIpmi"
-
-#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
-
-#define NAMEBUFFERLEN 50
-#define OPTMAXLEN (NAMEBUFFERLEN - sizeof(OBJ_NAME) - 1)
-
-char kcsDevice[NAMEBUFFERLEN];
-char busName[NAMEBUFFERLEN];
-char objPath[NAMEBUFFERLEN];
-
-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)
-		return sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
-
-	r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
-	if (r < 0) {
-		return sd_bus_error_set_errnof(
-			err, -r, "Bad message from seq %u cmd %u", seqnum, cmd);
-	}
-
-	req = &context->req;
-
-	if (context->seqnum != seqnum || (req->netfn | 1) != netfn
-	    || req->lun != lun || req->cmd != cmd) {
-		MSG_ERR("Mismatch: context seqnum %u netfn %u lun %u cmd %u,"
-			"received seqnum %u netfn %u lun %u cmd %u",
-			context->seqnum, req->netfn, req->lun, req->cmd, seqnum,
-			netfn, lun, cmd);
-		return sd_bus_error_set_errnof(err, EINVAL,
-					       "No matching request");
-	}
-
-	kcs_set_timer(context, 0); /* Stop the timer. */
-
-	r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
-	if (r < 0)
-		return sd_bus_error_set_errnof(
-			err, -r, "Message data read failure seq %u cmd %u",
-			seqnum, cmd);
-
-	if (data_sz > sizeof(rsp) - 3)
-		return sd_bus_error_set_errnof(err, EINVAL, "Bad message data");
-
-	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)
-		return sd_bus_error_set_errnof(err, errno,
-					       "Device file write error");
-
-	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);
-
-	return sd_bus_reply_method_return(msg, "x", 0);
-}
-
-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 < 0)
-		return sd_bus_error_set_errnof(err, errno,
-					       "Couldn't SET_SMS_ATN");
-
-	return sd_bus_reply_method_return(msg, "x", 0);
-}
-
-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 < 0)
-		return sd_bus_error_set_errnof(err, errno,
-					       "Couldn't CLEAR_SMS_ATN");
-
-	return sd_bus_reply_method_return(msg, "x", 0);
-}
-
-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 < 0)
-		return sd_bus_error_set_errnof(err, errno,
-					       "Couldn't FORCE_ABORT");
-
-	return sd_bus_reply_method_return(msg, "x", 0);
-}
-
-static int dispatch_sd_bus(struct kcsbridged_context *context)
-{
-	int r = 0;
-
-	if (context->fds[SD_BUS_FD].revents) {
-		// docs say to call this in a loop until no events are left
-		// to be processed
-		do {
-			r = sd_bus_process(context->bus, NULL);
-		} while (r > 0);
-	}
-
-	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: %s\n",
-				strerror(errno));
-	}
-
-	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, objPath, DBUS_INTF,
-				      "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] --i <ID> --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"
-		"--i, --instanceid <ID>   instance id (string type) optional\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 *name = argv[0];
-	bool deviceOptFlag = false;
-	int opt, polled, r;
-	static const struct option long_options[] = {
-		{"device", required_argument, 0, 'd'},
-		{"instanceid", required_argument, 0, 'i'},
-		{"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;
-	}
-
-	snprintf(busName, NAMEBUFFERLEN, "%s", DEFAULT_DBUS);
-	snprintf(objPath, NAMEBUFFERLEN, "%s", DEFAULT_OBJ);
-
-	kcs_vlog = &kcs_log_console;
-	while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
-		switch (opt) {
-		case 0:
-			break;
-
-		case 'd':
-			snprintf(kcsDevice, NAMEBUFFERLEN, "%s", optarg);
-			deviceOptFlag = true;
-			break;
-
-		case 'i':
-			if (sizeof(*optarg) > OPTMAXLEN) {
-				fprintf(stderr, "ID is too long!\n");
-				exit(EXIT_FAILURE);
-			}
-			if ((NULL != strstr(optarg, "."))
-			    || (NULL != strstr(optarg, "/"))) {
-				fprintf(stderr, "invalid ID!\n");
-				exit(EXIT_FAILURE);
-			}
-			snprintf(busName, NAMEBUFFERLEN, "%s%s", DBUS_NAME,
-				 optarg);
-			snprintf(objPath, NAMEBUFFERLEN, "%s%s", OBJ_NAME,
-				 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 (false == deviceOptFlag) {
-		usage(name);
-		MSG_OUT("Flag: device %d \n", deviceOptFlag);
-		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, objPath, DBUS_INTF,
-				     ipmid_vtable, context);
-	if (r < 0) {
-		MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
-		goto finish;
-	}
-
-	MSG_OUT("Requesting dbus : %s objpath:%s \n", busName, objPath);
-	r = sd_bus_request_name(context->bus, busName,
-				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", kcsDevice);
-	context->fds[KCS_FD].fd = open(kcsDevice, 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", kcsDevice,
-			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.cpp b/kcsbridged.cpp
new file mode 100644
index 0000000..8367512
--- /dev/null
+++ b/kcsbridged.cpp
@@ -0,0 +1,361 @@
+/* Copyright 2017 - 2019 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 <getopt.h>
+#include <linux/ipmi_bmc.h>
+
+#include <CLI/CLI.hpp>
+#include <boost/asio.hpp>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+using namespace phosphor::logging;
+
+namespace
+{
+namespace io_control
+{
+struct ClearSmsAttention
+{
+    // Get the name of the IO control command.
+    int name() const
+    {
+        return static_cast<int>(IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
+    }
+
+    // Get the address of the command data.
+    boost::asio::detail::ioctl_arg_type* data()
+    {
+        return nullptr;
+    }
+};
+
+struct SetSmsAttention
+{
+    // Get the name of the IO control command.
+    int name() const
+    {
+        return static_cast<int>(IPMI_BMC_IOCTL_SET_SMS_ATN);
+    }
+
+    // Get the address of the command data.
+    boost::asio::detail::ioctl_arg_type* data()
+    {
+        return nullptr;
+    }
+};
+
+struct ForceAbort
+{
+    // Get the name of the IO control command.
+    int name() const
+    {
+        return static_cast<int>(IPMI_BMC_IOCTL_FORCE_ABORT);
+    }
+
+    // Get the address of the command data.
+    boost::asio::detail::ioctl_arg_type* data()
+    {
+        return nullptr;
+    }
+};
+} // namespace io_control
+
+class SmsChannel
+{
+  public:
+    static constexpr size_t kcsMessageSize = 256;
+    static constexpr uint8_t netFnShift = 2;
+    static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
+
+    SmsChannel(std::shared_ptr<boost::asio::io_context>& io,
+               std::shared_ptr<sdbusplus::asio::connection>& bus,
+               const std::string& channel, bool verbose) :
+        io(io),
+        bus(bus), verbose(verbose)
+    {
+        static constexpr const char devBase[] = "/dev/";
+        std::string devName = devBase + channel;
+        // open device
+        int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
+        if (fd < 0)
+        {
+            log<level::ERR>("Couldn't open SMS channel O_RDWR",
+                            entry("FILENAME=%s", devName.c_str()),
+                            entry("ERROR=%s", strerror(errno)));
+            return;
+        }
+        else
+        {
+            dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
+                                                                          fd);
+        }
+
+        async_read();
+
+        // register interfaces...
+        server = std::make_shared<sdbusplus::asio::object_server>(bus);
+
+        static constexpr const char pathBase[] =
+            "/xyz/openbmc_project/Ipmi/Channel/";
+        std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+            server->add_interface(pathBase + channel,
+                                  "xyz.openbmc_project.Ipmi.Channel.SMS");
+        iface->register_method("setAttention",
+                               [this]() { return setAttention(); });
+        iface->register_method("clearAttention",
+                               [this]() { return clearAttention(); });
+        iface->register_method("forceAbort", [this]() { return forceAbort(); });
+        iface->initialize();
+    }
+
+    bool initOK() const
+    {
+        return !!dev;
+    }
+
+    void channelAbort(const char* msg, const boost::system::error_code& ec)
+    {
+        log<level::ERR>(msg, entry("ERROR=%s", ec.message().c_str()));
+        // bail; maybe a restart from systemd can clear the error
+        io->stop();
+    }
+
+    void async_read()
+    {
+        boost::asio::async_read(
+            *dev, boost::asio::buffer(xferBuffer, xferBuffer.size()),
+            boost::asio::transfer_at_least(2),
+            [this](const boost::system::error_code& ec, size_t rlen) {
+                processMessage(ec, rlen);
+            });
+    }
+
+    void processMessage(const boost::system::error_code& ecRd, size_t rlen)
+    {
+        if (ecRd || rlen < 2)
+        {
+            channelAbort("Failed to read req msg", ecRd);
+            return;
+        }
+
+        async_read();
+
+        // trim raw to be only bytes returned from read
+        // separate netfn/lun/cmd from payload
+        auto rawIter = xferBuffer.cbegin();
+        auto rawEnd = rawIter + rlen;
+        uint8_t netfn = *rawIter >> netFnShift;
+        uint8_t lun = *rawIter++ & lunMask;
+        uint8_t cmd = *rawIter++;
+        if (verbose)
+        {
+            log<level::INFO>("Read req msg", entry("NETFN=0x%02x", netfn),
+                             entry("LUN=0x%02x", lun),
+                             entry("CMD=0x%02x", cmd));
+        }
+        // copy out payload
+        std::vector<uint8_t> data(rawIter, rawEnd);
+        // non-session bridges still need to pass an empty options map
+        std::map<std::string, sdbusplus::message::variant<int>> options;
+        // the response is a tuple because dbus can only return a single value
+        using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
+                                           std::vector<uint8_t>>;
+        static constexpr const char ipmiQueueService[] =
+            "xyz.openbmc_project.Ipmi.Host";
+        static constexpr const char ipmiQueuePath[] =
+            "/xyz/openbmc_project/Ipmi";
+        static constexpr const char ipmiQueueIntf[] =
+            "xyz.openbmc_project.Ipmi.Server";
+        static constexpr const char ipmiQueueMethod[] = "execute";
+        bus->async_method_call(
+            [this, netfnCap{netfn}, lunCap{lun},
+             cmdCap{cmd}](const boost::system::error_code& ec,
+                          const IpmiDbusRspType& response) {
+                std::vector<uint8_t> rsp;
+                const auto& [netfn, lun, cmd, cc, payload] = response;
+                if (ec)
+                {
+                    log<level::ERR>(
+                        "kcs<->ipmid bus error:", entry("NETFN=0x%02x", netfn),
+                        entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
+                        entry("ERROR=%s", ec.message().c_str()));
+                    // send unspecified error for a D-Bus error
+                    constexpr uint8_t ccResponseNotAvailable = 0xce;
+                    rsp.resize(sizeof(netfn) + sizeof(cmd) + sizeof(cc));
+                    rsp[0] =
+                        ((netfnCap + 1) << netFnShift) | (lunCap & lunMask);
+                    rsp[1] = cmdCap;
+                    rsp[2] = ccResponseNotAvailable;
+                }
+                else
+                {
+                    rsp.resize(sizeof(netfn) + sizeof(cmd) + sizeof(cc) +
+                               payload.size());
+
+                    // write the response
+                    auto rspIter = rsp.begin();
+                    *rspIter++ = (netfn << netFnShift) | (lun & lunMask);
+                    *rspIter++ = cmd;
+                    *rspIter++ = cc;
+                    if (payload.size())
+                    {
+                        std::copy(payload.cbegin(), payload.cend(), rspIter);
+                    }
+                }
+                if (verbose)
+                {
+                    log<level::INFO>(
+                        "Send rsp msg", entry("NETFN=0x%02x", netfn),
+                        entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
+                        entry("CC=0x%02x", cc));
+                }
+                boost::system::error_code ecWr;
+                size_t wlen =
+                    boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
+                if (ecWr || wlen != rsp.size())
+                {
+                    log<level::ERR>(
+                        "Failed to send rsp msg", entry("SIZE=%d", wlen),
+                        entry("EXPECT=%d", rsp.size()),
+                        entry("ERROR=%s", ecWr.message().c_str()),
+                        entry("NETFN=0x%02x", netfn), entry("LUN=0x%02x", lun),
+                        entry("CMD=0x%02x", cmd), entry("CC=0x%02x", cc));
+                }
+            },
+            ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
+            netfn, lun, cmd, data, options);
+    }
+
+    int64_t setAttention()
+    {
+        if (verbose)
+        {
+            log<level::INFO>("Sending SET_SMS_ATTENTION");
+        }
+        io_control::SetSmsAttention command;
+        boost::system::error_code ec;
+        dev->io_control(command, ec);
+        if (ec)
+        {
+            log<level::ERR>("Couldn't SET_SMS_ATTENTION",
+                            entry("ERROR=%s", ec.message().c_str()));
+            return ec.value();
+        }
+        return 0;
+    }
+
+    int64_t clearAttention()
+    {
+        if (verbose)
+        {
+            log<level::INFO>("Sending CLEAR_SMS_ATTENTION");
+        }
+        io_control::ClearSmsAttention command;
+        boost::system::error_code ec;
+        dev->io_control(command, ec);
+        if (ec)
+        {
+            log<level::ERR>("Couldn't CLEAR_SMS_ATTENTION",
+                            entry("ERROR=%s", ec.message().c_str()));
+            return ec.value();
+        }
+        return 0;
+    }
+
+    int64_t forceAbort()
+    {
+        if (verbose)
+        {
+            log<level::INFO>("Sending FORCE_ABORT");
+        }
+        io_control::ForceAbort command;
+        boost::system::error_code ec;
+        dev->io_control(command, ec);
+        if (ec)
+        {
+            log<level::ERR>("Couldn't FORCE_ABORT",
+                            entry("ERROR=%s", ec.message().c_str()));
+            return ec.value();
+        }
+        return 0;
+    }
+
+  protected:
+    std::array<uint8_t, kcsMessageSize> xferBuffer;
+    std::shared_ptr<boost::asio::io_context> io;
+    std::shared_ptr<sdbusplus::asio::connection> bus;
+    std::shared_ptr<sdbusplus::asio::object_server> server;
+    std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
+    bool verbose;
+};
+
+} // namespace
+
+// this is a hack to allow the new thing run with the old service file
+// it will be removed once the bb file is updated to use the new service file
+#define ALLOW_OLD_ARGS 1
+
+int main(int argc, char* argv[])
+{
+    CLI::App app("KCS IPMI bridge");
+    std::string channel;
+    app.add_option("-c,--channel", channel, "channel name. e.g., ipmi-kcs3");
+    bool verbose = false;
+    app.add_option("-v,--verbose", verbose, "print more verbose output");
+#ifdef ALLOW_OLD_ARGS
+    std::string device;
+    app.add_option("--d,--device", device, "device name. e.g., /dev/ipmi-kcs3");
+#endif
+    CLI11_PARSE(app, argc, argv);
+
+#ifdef ALLOW_OLD_ARGS
+    if (channel.size() == 0)
+    {
+        size_t start = device.rfind('/');
+        if (start == std::string::npos)
+        {
+            log<level::ERR>("bad device option",
+                            entry("DEVICE=%s", device.c_str()));
+            return EXIT_FAILURE;
+        }
+        channel = device.substr(start + 1);
+    }
+#endif
+
+    // Connect to system bus
+    auto io = std::make_shared<boost::asio::io_context>();
+    sd_bus* dbus;
+    sd_bus_default_system(&dbus);
+    auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
+
+    // Create the channel, listening on D-Bus and on the SMS device
+    SmsChannel smsChannel(io, bus, channel, verbose);
+
+    if (!smsChannel.initOK())
+    {
+        return EXIT_FAILURE;
+    }
+
+    static constexpr const char busBase[] = "xyz.openbmc_project.Ipmi.Channel.";
+    std::string busName(busBase + channel);
+    bus->request_name(busName.c_str());
+
+    io->run();
+
+    return 0;
+}
diff --git a/phosphor-ipmi-kcs@.service b/phosphor-ipmi-kcs@.service
new file mode 100644
index 0000000..43cce3b
--- /dev/null
+++ b/phosphor-ipmi-kcs@.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Phosphor IPMI KCS DBus Bridge
+Requires=phosphor-ipmi-host
+After=phosphor-ipmi-host
+
+[Service]
+Restart=always
+ExecStart={bindir}/kcsbridged -c "%i"
+SyslogIdentifier=kcsbridged-%i
+
+[Install]
+WantedBy=multi-user.target
+RequiredBy=