ssifbridge: Replace ssifbridge with C++ version

Rewrite the C-style ssifbridge in modern C++.

Test:
On linux host, run 'ipmitool mc info' or 'ipmitool fru print'

Change-Id: I5970853abbf4b2cfe893c76e819a22047baaa4a4
Signed-off-by: Dung Cao <dung@os.amperecomputing.com>
diff --git a/meson.build b/meson.build
index 08e5b72..bf8f01d 100644
--- a/meson.build
+++ b/meson.build
@@ -1,22 +1,23 @@
-project('ssifbridge', 'c',
-        default_options: [
-            'buildtype=debugoptimized',
-            'warning_level=3',
-            'werror=true',
-           ],
-        version: '1.0',
+project('ssifbridge', 'cpp',
+    default_options: [
+        'buildtype=debugoptimized',
+        'warning_level=3',
+        'werror=true',
+        'cpp_std=c++17'
+    ],
+    version: '1.0',
 )
 
-# Wno-psabi reduces the number of "Note:" messages when cross-compiling some STL
-# stuff for ARM. See https://stackoverflow.com/questions/48149323/strange-gcc-warning-when-compiling-qt-project
-# Basically, gcc 6 and gcc 7 are not ABI compatible, but since the whole OpenBMC
-# project uses the same compiler, we can safely ignmore these info notes.
-add_project_arguments('-Wno-psabi', language: 'c')
+add_project_arguments('-Wno-psabi', language: 'cpp')
 
 deps = [dependency('libsystemd', version : '>=221'),
         dependency('systemd'),
-       ]
+        dependency('phosphor-logging'),
+        dependency('sdbusplus'),
+        dependency('threads')
 
-executable('ssifbridged','ssifbridged.c',
+]
+
+executable('ssifbridged','ssifbridged.cpp',
            dependencies: deps,
            install: true)
diff --git a/ssifbridged.c b/ssifbridged.c
deleted file mode 100644
index 305eec7..0000000
--- a/ssifbridged.c
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (c) 2018-2020 Ampere Computing LLC
- *
- * 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.
- */
-
-/*
- * This is a daemon that forwards requests and receive responses from SSIF over
- * the D-Bus IPMI Interface.
- *
- * This daemon is derived from btbridged from https://github.com/openbmc/btbridge.
- * There is no need to queue messages for SSIF since they should not arrive out
- * of sequence.
- * The timer is used as we want to abort 'stuck' commands after an expiry time.
- * Messages that are received out of order are discarded.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <limits.h>
-#include <poll.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <sys/mman.h>
-#include <linux/ioctl.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/timerfd.h>
-#include <time.h>
-#include <unistd.h>
-#include <systemd/sd-bus.h>
-
-static const char *ssif_bmc_device = "/dev/ipmi-ssif-host";
-
-#define PREFIX			"SSIF_BRIDGED"
-
-#define SSIF_BMC_PATH		ssif_bmc_device
-#define SSIF_BMC_TIMEOUT_SEC	2
-#define SSIF_MAX_REQ_LEN	254
-#define SSIF_MAX_RESP_LEN	254
-
-/* Completion code specifies a command times out */
-#define IPMI_CC_CANNOT_PROVIDE_RESP	0xce
-
-
-#define DBUS_NAME	"org.openbmc.HostIpmi"
-#define OBJ_NAME	"/org/openbmc/HostIpmi/1"
-
-#define SD_BUS_FD	0
-#define SSIF_FD		1
-#define TIMER_FD	2
-#define TOTAL_FDS	3
-
-#define MSG_OUT(f_, ...) do { \
-	if (verbosity != SSIF_LOG_NONE) { \
-		ssif_log(LOG_INFO, f_, ##__VA_ARGS__); \
-	} \
-} while(0)
-#define MSG_ERR(f_, ...) do { \
-	if (verbosity != SSIF_LOG_NONE) { \
-		ssif_log(LOG_ERR, f_, ##__VA_ARGS__); \
-	} \
-} while(0)
-
-struct ipmi_msg {
-	uint8_t netfn;
-	uint8_t lun;
-	uint8_t seq;
-	uint8_t cmd;
-	uint8_t cc; /* Only used on responses */
-	uint8_t *data;
-	size_t data_len;
-};
-
-struct ssifbridged_context {
-	/* The file descriptors to poll */
-	struct pollfd fds[TOTAL_FDS];
-
-	/* Pointer to sdbus */
-	struct sd_bus *bus;
-
-	/* Tracking variable for a pending message so that if it times out,
-	 * we can send a response using the correct lun, netfn and command to
-	 * indicate to the host that we timed out waiting for a response
-	 */
-	struct ipmi_msg ssif_pending_msg;
-
-	/* Flag to indicate whether we are awaiting a response */
-	int awaiting_response;
-};
-
-static void (*ssif_vlog)(int p, const char *fmt, va_list args);
-static int running = 1;
-static enum {
-	SSIF_LOG_NONE = 0,
-	SSIF_LOG_VERBOSE,
-	SSIF_LOG_DEBUG
-} verbosity;
-
-static void ssif_log_console(int p, const char *fmt, va_list args)
-{
-	struct timespec time;
-	FILE *s = (p < LOG_WARNING) ? stdout : stderr;
-
-	clock_gettime(CLOCK_REALTIME, &time);
-
-	fprintf(s, "[%s %ld.%.9ld] ", PREFIX, time.tv_sec, time.tv_nsec);
-
-	vfprintf(s, fmt, args);
-}
-
-	__attribute__((format(printf, 2, 3)))
-static void ssif_log(int p, const char *fmt, ...)
-{
-	va_list args;
-
-	va_start(args, fmt);
-	ssif_vlog(p, fmt, args);
-	va_end(args);
-}
-
-static struct ipmi_msg *ssif_msg_create(struct ssifbridged_context *context,
-		uint8_t *ssif_data)
-{
-	int len;
-
-	assert(context && ssif_data);
-
-	/*
-	 * len here is the length of the array.
-	 * Helpfully SSIF doesn't count the length byte
-	 */
-	len = ssif_data[0] + 1;
-
-	if (len < 3) {
-		MSG_ERR("Trying to get SSIF message with a short length (%d)\n",
-				len);
-		return NULL;
-	}
-
-	MSG_OUT("Trying to get SSIF message with len (%d)\n", len);
-
-	/* Don't count the lenfn/ln, seq and command */
-	context->ssif_pending_msg.data_len = len - 3;
-	context->ssif_pending_msg.netfn = ssif_data[1] >> 2;
-	context->ssif_pending_msg.lun = ssif_data[1] & 0x3;
-	/* Force sequence field = 0 for SSIF */
-	context->ssif_pending_msg.seq = 0;
-	context->ssif_pending_msg.cmd = ssif_data[2];
-
-	return &context->ssif_pending_msg;
-}
-
-/*
- * Send request from the SSIF driver
- */
-static int send_received_message_signal(struct ssifbridged_context *context,
-		struct ipmi_msg *req,
-		uint8_t *data, uint8_t data_len)
-{
-	sd_bus_message *msg = NULL;
-	int r = 0;
-
-	/* Notify sdbus for incoming message */
-	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));
-		return r;
-	}
-
-	r = sd_bus_message_append(msg, "yyyy",
-			req->seq,
-			req->netfn,
-			req->lun,
-			req->cmd);
-	if (r < 0) {
-		MSG_ERR("Couldn't append to signal: %s\n", strerror(-r));
-		return r;
-	}
-
-	r = sd_bus_message_append_array(msg, 'y', data, data_len);
-	if (r < 0) {
-		MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
-		sd_bus_message_unref(msg);
-		return r;
-	}
-
-	MSG_OUT("Sending dbus signal with seq 0x%02x, netfn 0x%02x, "
-			"lun 0x%02x, cmd 0x%02x\n",
-			req->seq,
-			req->netfn,
-			req->lun,
-			req->cmd);
-
-	if (verbosity == SSIF_LOG_DEBUG) {
-		int i;
-		for (i = 0; i < req->data_len; i++) {
-			if (i % 8 == 0) {
-				if (i)
-					printf("\n");
-				MSG_OUT("\t");
-			}
-			printf("0x%02x ", data[i + 3]);
-		}
-		if (req->data_len)
-			printf("\n");
-	}
-
-	r = sd_bus_send(context->bus, msg, NULL);
-	if (r < 0) {
-		MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
-		return r;
-	}
-
-	sd_bus_message_unref(msg);
-
-	return r;
-}
-
-/*
- * Send a response on the SSIF driver
- */
-static int ssif_send_response(struct ssifbridged_context *context,
-		struct ipmi_msg *ssif_resp_msg)
-{
-	uint8_t data[SSIF_MAX_RESP_LEN] = { 0 };
-	int r = 0;
-	int len = 0;
-
-	assert(context);
-
-	if (!ssif_resp_msg)
-		return -EINVAL;
-
-	/* netfn/lun + cmd + cc = 3 */
-	data[0] = ssif_resp_msg->data_len + 3;
-
-	/* Copy response message to data buffer */
-	if (ssif_resp_msg->data_len)
-		memcpy(data + 4, ssif_resp_msg->data, ssif_resp_msg->data_len);
-
-	/* Prepare Header */
-	data[1] = (ssif_resp_msg->netfn << 2) |
-		(ssif_resp_msg->lun & 0x3);
-	data[2] = ssif_resp_msg->cmd;
-	data[3] = ssif_resp_msg->cc;
-	if (ssif_resp_msg->data_len > sizeof(data) - 4) {
-		MSG_ERR("Response message size (%zu) too big, truncating\n",
-				ssif_resp_msg->data_len);
-		ssif_resp_msg->data_len = sizeof(data) - 4;
-	}
-
-	/* Write data kernel space via system calls */
-	len = write(context->fds[SSIF_FD].fd, data, data[0] + 1);
-
-	if (len < 0) {
-		MSG_ERR("Failed to write to driver (ret: %d, errno: %d)\n",
-				len,
-				errno);
-		r = -errno;
-	} else if (len != data[0] + 1) {
-		MSG_ERR("Possible short write to %s, desired len: %d, "
-				"written len: %d\n",
-				SSIF_BMC_PATH,
-				data[0] + 1,
-				len);
-		r = -EINVAL;
-	} else {
-		MSG_OUT("Successfully wrote %d of %d bytes to %s\n",
-				len,
-				data[0] + 1,
-				SSIF_BMC_PATH);
-	}
-
-	return r;
-}
-
-static int method_send_message(sd_bus_message *msg,
-		void *userdata,
-		sd_bus_error *ret_error)
-{
-	struct ssifbridged_context *context;
-	sd_bus_message* resp_msg = NULL;
-	struct ipmi_msg ssif_resp_msg;
-	int r = 1;
-
-	context = (struct ssifbridged_context *)userdata;
-	if (!context) {
-		sd_bus_error_set_const(ret_error,
-				"org.openbmc.error",
-				"Internal error");
-		return -EINVAL;
-	}
-
-	r = sd_bus_message_new_method_return(msg, &resp_msg);
-	if (r < 0) {
-		MSG_ERR("Failed to create method response (ret: %d)\n", r);
-		return r;
-	}
-
-	if (!context->awaiting_response) {
-		/* We are not expecting a response at this time */
-		MSG_ERR("Response message received when in wrong state. "
-				"Discarding\n");
-		r = -EBUSY;
-	} else {
-		uint8_t *data;
-		size_t data_sz;
-		uint8_t netfn, lun, seq, cmd, cc;
-		struct itimerspec ts;
-
-		context->awaiting_response = 0;
-
-		r = sd_bus_message_read(msg, "yyyyy",
-				&seq,
-				&netfn,
-				&lun,
-				&cmd,
-				&cc);
-		if (r < 0) {
-			MSG_ERR("Couldn't parse leading bytes of message: %s\n",
-					strerror(-r));
-			sd_bus_error_set_const(ret_error,
-					"org.openbmc.error",
-					"Bad message");
-			r = -EINVAL;
-			goto done;
-		}
-		r = sd_bus_message_read_array(msg, 'y',
-				(const void **)&data,
-				&data_sz);
-		if (r < 0) {
-			MSG_ERR("Couldn't parse data bytes of message: %s\n",
-					strerror(-r));
-			sd_bus_error_set_const(ret_error,
-					"org.openbmc.error",
-					"Bad message data");
-			r = -EINVAL;
-			goto done;
-		}
-
-		MSG_OUT("Received a dbus response for msg with seq 0x%02x\n",
-				seq);
-
-		ssif_resp_msg.netfn = netfn;
-		ssif_resp_msg.lun = lun;
-		ssif_resp_msg.seq = seq;
-		ssif_resp_msg.cmd = cmd;
-		ssif_resp_msg.cc = cc;
-		ssif_resp_msg.data_len = data_sz;
-		/* Because we've ref'ed the msg, don't need to memcpy data */
-		ssif_resp_msg.data = data;
-
-		/* Clear the timer */
-		ts.it_interval.tv_sec = 0;
-		ts.it_interval.tv_nsec = 0;
-		ts.it_value.tv_sec = 0;
-		ts.it_value.tv_nsec = 0;
-		r = timerfd_settime(context->fds[TIMER_FD].fd,
-				TFD_TIMER_ABSTIME,
-				&ts,
-				NULL);
-
-		if (r < 0) {
-			MSG_ERR("Failed to clear timer\n");
-		}
-
-		r = ssif_send_response(context, &ssif_resp_msg);
-	}
-
-done:
-	r = sd_bus_message_append(resp_msg, "x", r);
-	if (r < 0) {
-		MSG_ERR("Failed to add result to method (ret: %d)\n", r);
-	}
-
-	r = sd_bus_send(context->bus, resp_msg, NULL);
-	if (r < 0) {
-		MSG_ERR("Failed to send response (ret: %d)\n", r);
-	}
-
-	return r;
-}
-
-static int dispatch_timer(struct ssifbridged_context *context)
-{
-	int r = 0;
-
-	if (context->fds[TIMER_FD].revents & POLLIN) {
-		if(!context->awaiting_response) {
-			/* Got a timeout but not expecting a response */
-			MSG_ERR("Timeout but no pending message\n");
-		} else {
-			struct itimerspec ts;
-
-			/* Clear the timer */
-			ts.it_interval.tv_sec = 0;
-			ts.it_interval.tv_nsec = 0;
-			ts.it_value.tv_sec = 0;
-			ts.it_value.tv_nsec = 0;
-			r = timerfd_settime(context->fds[TIMER_FD].fd,
-					TFD_TIMER_ABSTIME,
-					&ts,
-					NULL);
-			if (r < 0) {
-				MSG_ERR("Failed to clear timer\n");
-			}
-
-			MSG_ERR("Timing out message\n");
-
-			/* Add one to the netfn - response netfn is always
-			 * request netfn + 1
-			 */
-			context->ssif_pending_msg.netfn += 1;
-			context->ssif_pending_msg.cc =
-				IPMI_CC_CANNOT_PROVIDE_RESP;
-			context->ssif_pending_msg.data = NULL;
-			context->ssif_pending_msg.data_len = 0;
-
-			r = ssif_send_response(context,
-					&context->ssif_pending_msg);
-			if (r < 0) {
-				MSG_ERR("Failed to send timeout message "
-						"(ret: %d, errno: %d)\n",
-						r,
-						errno);
-			}
-
-			context->awaiting_response = 0;
-		}
-	}
-
-	return r;
-}
-
-static int dispatch_sd_bus(struct ssifbridged_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_ssif(struct ssifbridged_context *context)
-{
-	int r = 0;
-
-	assert(context);
-
-	if (context->fds[SSIF_FD].revents & POLLIN) {
-		/* We have received data on the driver */
-		struct itimerspec ts;
-		struct ipmi_msg *req;
-		uint8_t data[SSIF_MAX_REQ_LEN] = { 0 };
-
-		r = read(context->fds[SSIF_FD].fd, data, sizeof(data));
-		if (r < 0) {
-			MSG_ERR("Couldn't read from ssif: %s\n", strerror(-r));
-			return r;
-		}
-		if (r < data[0] + 1) {
-			MSG_ERR("Short read from ssif (%d vs %d)\n",
-					r,
-					data[1] + 2);
-			r = 0;
-			return r;
-		}
-
-		/* Check if response is still awaiting */
-		if (context->awaiting_response) {
-			MSG_ERR("Received SSIF message while awaiting response."
-					" Discarding\n");
-		} else {
-			/* Get SSIF request message that sent from
-			 * kernel space
-			 */
-			req = ssif_msg_create(context, data);
-			context->awaiting_response = 1;
-
-			if (!req) {
-				MSG_ERR("Can not create request\n");
-				r = -ENOMEM;
-				return r;
-			}
-
-			/* Set up the timer. We do this before sending
-			 * the signal to avoid a race condition with
-			 * the response
-			 */
-			ts.it_interval.tv_sec = 0;
-			ts.it_interval.tv_nsec = 0;
-			ts.it_value.tv_nsec = 0;
-			ts.it_value.tv_sec = SSIF_BMC_TIMEOUT_SEC;
-			r = timerfd_settime(context->fds[TIMER_FD].fd,
-					0,
-					&ts,
-					NULL);
-			if (r < 0)
-				MSG_ERR("Failed to set timer (ret: %d, "
-						"errno: %d)\n",
-						r,
-						errno);
-
-			r = send_received_message_signal(context,
-					req,
-					data + 3,
-					req->data_len);
-			if (r < 0) {
-				MSG_ERR("Failed to send Received Message "
-						"signal (ret: %d)\n",
-						r);
-			}
-		}
-	}
-
-	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. Default is '%s'\n\n",
- name, ssif_bmc_device);
-}
-
-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_SIGNAL("ReceivedMessage",
-			"yyyyay",
-			0),
-	SD_BUS_VTABLE_END
-};
-
-int main(int argc, char *argv[]) {
-	struct ssifbridged_context *context;
-	const char *name = argv[0];
-	int opt, polled, r;
-
-	static const struct option long_options[] = {
-		{ "device",  required_argument, NULL, 'd' },
-		{ "v",       no_argument, (int *)&verbosity, SSIF_LOG_VERBOSE },
-		{ "vv",      no_argument, (int *)&verbosity, SSIF_LOG_DEBUG   },
-		{ "syslog",  no_argument, 0,          's'         },
-		{ 0,         0,           0,          0           }
-	};
-
-	context = calloc(1, sizeof(*context));
-	if (context == NULL) {
-		MSG_ERR("Failed to allocate memory\n");
-		exit(EXIT_FAILURE);
-	}
-
-	ssif_vlog = &ssif_log_console;
-	while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
-		switch (opt) {
-			case 0:
-				break;
-			case 'd':
-				ssif_bmc_device = optarg;
-				break;
-			case 's':
-				/* Avoid a double openlog() */
-				if (ssif_vlog != &vsyslog) {
-					openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
-					ssif_vlog = &vsyslog;
-				}
-				break;
-			default:
-				usage(name);
-				exit(EXIT_FAILURE);
-		}
-	}
-
-	if (verbosity == SSIF_LOG_VERBOSE)
-		MSG_OUT("Verbose logging\n");
-
-	if (verbosity == SSIF_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 error;
-	}
-
-	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 deregister_sdbus;
-	}
-
-	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 deregister_sdbus;
-	}
-
-	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 deregister_sdbus;
-	}
-
-	MSG_OUT("Opening %s\n", SSIF_BMC_PATH);
-	context->fds[SSIF_FD].fd = open(SSIF_BMC_PATH, O_RDWR | O_NONBLOCK);
-	if (context->fds[SSIF_FD].fd < 0) {
-		r = -errno;
-		MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
-				SSIF_BMC_PATH,
-				strerror(errno));
-		goto free_sdbus_fd;
-	}
-
-	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 free_ssif_fd;
-	}
-	context->fds[SD_BUS_FD].events = POLLIN;
-	context->fds[SSIF_FD].events = POLLIN;
-	context->fds[TIMER_FD].events = POLLIN;
-
-	MSG_OUT("Entering polling loop\n");
-
-	while (running) {
-		polled = poll(context->fds, TOTAL_FDS, -1);
-		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_ssif(context);
-		if (r < 0) {
-			MSG_ERR("Error handling SSIF 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[TIMER_FD].fd);
-free_ssif_fd:
-	close(context->fds[SSIF_FD].fd);
-free_sdbus_fd:
-	close(context->fds[SD_BUS_FD].fd);
-deregister_sdbus:
-	sd_bus_unref(context->bus);
-error:
-	free(context);
-
-	return r;
-}
diff --git a/ssifbridged.cpp b/ssifbridged.cpp
new file mode 100644
index 0000000..f49d7c5
--- /dev/null
+++ b/ssifbridged.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2021 Ampere Computing LLC
+ *
+ * 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.
+ *
+ * This is a daemon that forwards requests and receive responses from SSIF over
+ * the D-Bus IPMI Interface.
+ */
+
+#include <getopt.h>
+#include <linux/ipmi_bmc.h>
+
+#include <CLI/CLI.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/asio.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <iostream>
+
+using namespace phosphor::logging;
+
+static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
+/* SSIF use IPMI SSIF channel */
+static constexpr const char* ssifBus =
+    "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
+static constexpr const char* ssifObj =
+    "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
+
+class SsifChannel
+{
+  public:
+    static constexpr size_t ssifMessageSize = 4096;
+    static constexpr uint8_t netFnShift = 2;
+    static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
+
+    SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
+                   std::shared_ptr<sdbusplus::asio::connection>& bus,
+                   const std::string& channel, bool verbose);
+    bool initOK() const
+    {
+        return !!dev;
+    }
+    void channelAbort(const char* msg, const boost::system::error_code& ec);
+    void async_read();
+    void processMessage(const boost::system::error_code& ecRd, size_t rlen);
+
+  protected:
+    std::array<uint8_t, ssifMessageSize> 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;
+};
+
+SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
+                         std::shared_ptr<sdbusplus::asio::connection>& bus,
+                         const std::string& device, bool verbose) :
+    io(io),
+    bus(bus), verbose(verbose)
+{
+    std::string devName = devBase;
+    if (!device.empty())
+    {
+        devName = device;
+    }
+
+    // open device
+    int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
+    if (fd < 0)
+    {
+        log<level::ERR>("Couldn't open SSIF driver with flags 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);
+    std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+        server->add_interface(ssifObj, ssifBus);
+    iface->initialize();
+}
+
+void SsifChannel::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 SsifChannel::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 SsifChannel::processMessage(const boost::system::error_code& ecRd,
+                                 size_t rlen)
+{
+    if (ecRd || rlen < 2)
+    {
+        channelAbort("Failed to read req msg", ecRd);
+        return;
+    }
+    async_read();
+
+    auto rawIter = xferBuffer.cbegin();
+    auto rawEnd = rawIter + rlen;
+    uint8_t len = rawIter[0] + 1;
+    uint8_t netfn = rawIter[1] >> netFnShift;
+    uint8_t lun = rawIter[1] & lunMask;
+    uint8_t cmd = rawIter[2];
+    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[3], rawEnd);
+    // non-session bridges still need to pass an empty options map
+    std::map<std::string, std::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>(
+                    "ssif<->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(uint8_t) + sizeof(netfn) + sizeof(cmd) +
+                           sizeof(cc));
+                rsp[0] = 3;
+                rsp[1] =
+                    ((netfnCap + 1) << netFnShift) | (lunCap & lunMask);
+                rsp[2] = cmdCap;
+                rsp[3] = ccResponseNotAvailable;
+            }
+            else
+            {
+                rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
+                           sizeof(cc) + payload.size());
+
+                // write the response
+                auto rspIter = rsp.begin();
+                rspIter[0] = payload.size() + 3;
+                rspIter[1] = (netfn << netFnShift) | (lun & lunMask);
+                rspIter[2] = cmd;
+                rspIter[3] = cc;
+                if (payload.size())
+                {
+                    std::copy(payload.cbegin(), payload.cend(),
+                              &rspIter[4]);
+                }
+            }
+            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);
+}
+
+
+int main(int argc, char* argv[])
+{
+    CLI::App app("SSIF IPMI bridge");
+    std::string device;
+    app.add_option("-d,--device", device,
+                   "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
+    bool verbose = false;
+    app.add_option("-v,--verbose", verbose, "print more verbose output");
+    CLI11_PARSE(app, argc, argv);
+
+    // 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);
+    bus->request_name(ssifBus);
+    // Create the SSIF channel, listening on D-Bus and on the SSIF device
+    SsifChannel ssifchannel(io, bus, device, verbose);
+    if (!ssifchannel.initOK())
+    {
+        return EXIT_FAILURE;
+    }
+    io->run();
+
+    return 0;
+}