ipmid: Add command filter mechanism

Every ipmi command will pass through a filter prior to execution by the
registered handler. The filter consists of all registered filter
handlers that all must either return an IPMI error code or allow the
command to be executed. If any of the filter handlers return an error
code, the remaining handlers will not get a chance to run.

Each handler, executed in registered priority order, can be passed the
full message, or just the context (metadata describing the command,
netfn, cmd, etc.)

Change-Id: I3c48f19ebae0d24344b15fbcd2b940a32f8511d7
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/include/ipmid/filter.hpp b/include/ipmid/filter.hpp
new file mode 100644
index 0000000..3cd5d21
--- /dev/null
+++ b/include/ipmid/filter.hpp
@@ -0,0 +1,94 @@
+/**
+ * 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.
+ * 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.
+ */
+#pragma once
+#include <algorithm>
+#include <boost/callable_traits.hpp>
+#include <cstdint>
+#include <ipmid/api.hpp>
+#include <ipmid/message.hpp>
+#include <memory>
+#include <tuple>
+#include <utility>
+
+namespace ipmi
+{
+
+using FilterFunction = ipmi::Cc(ipmi::message::Request::ptr);
+
+/**
+ * @brief Filter base class for dealing with IPMI request/response
+ *
+ * The subclasses are all templated so they can provide access to any type of
+ * command callback functions.
+ */
+class FilterBase
+{
+  public:
+    using ptr = std::shared_ptr<FilterBase>;
+
+    virtual ipmi::Cc call(message::Request::ptr request) = 0;
+};
+
+/**
+ * @brief filter concrete class
+ *
+ * This is the base template that ipmi filters will resolve into. This is
+ * essentially just a wrapper to hold the filter callback so it can be stored in
+ * the filter list.
+ *
+ * Filters are called with a ipmi::message::Request shared_ptr on all IPMI
+ * commands in priority order and each filter has the opportunity to reject the
+ * command (by returning an IPMI error competion code.) If all the filters
+ * return success, the actual IPMI command will be executed. Filters can reject
+ * the command for any reason, based on system state, the context, the command
+ * payload, etc.
+ */
+template <typename Filter>
+class IpmiFilter : public FilterBase
+{
+  public:
+    IpmiFilter(Filter&& filter) : filter_(std::move(filter))
+    {
+    }
+
+    ipmi::Cc call(message::Request::ptr request) override
+    {
+        return filter_(request);
+    }
+
+  private:
+    Filter filter_;
+};
+
+/**
+ * @brief helper function to construct a filter object
+ *
+ * This is called internally by the ipmi::registerFilter function.
+ */
+template <typename Filter>
+static inline auto makeFilter(Filter&& filter)
+{
+    FilterBase::ptr ptr(new IpmiFilter<Filter>(std::forward<Filter>(filter)));
+    return ptr;
+}
+template <typename Filter>
+static inline auto makeFilter(const Filter& filter)
+{
+    Filter lFilter = filter;
+    return makeFilter(std::forward<Filter>(lFilter));
+}
+
+} // namespace ipmi