ipmid/handler: Support exceptions with completion codes

Handlers don't always have a clear way to propagate error codes up to
the top level ipmi executor logic. This change defines an exception they
can throw that will provide a message + code for the executor.

Change-Id: I5491111e4471910669ddba1c3f4469dc004232eb
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/ipmid/handler.hpp b/include/ipmid/handler.hpp
index b6c6c0f..3b40762 100644
--- a/include/ipmid/handler.hpp
+++ b/include/ipmid/handler.hpp
@@ -24,6 +24,7 @@
 #include <memory>
 #include <optional>
 #include <phosphor-logging/log.hpp>
+#include <stdexcept>
 #include <tuple>
 #include <user_channel/channel_layer.hpp>
 #include <utility>
@@ -54,6 +55,38 @@
     return response;
 }
 
+/** @brief Exception extension that allows setting an IPMI return code */
+class HandlerCompletion
+{
+  public:
+    HandlerCompletion(Cc cc) noexcept : cc(cc)
+    {
+    }
+
+    Cc code() const noexcept
+    {
+        return cc;
+    }
+
+  private:
+    Cc cc;
+};
+
+/** @brief Exception extension that allows setting an IPMI return code and
+ * printing out a logged error */
+class HandlerException : public HandlerCompletion, public std::runtime_error
+{
+  public:
+    HandlerException(Cc cc, const char* what) :
+        HandlerCompletion(cc), std::runtime_error(what)
+    {
+    }
+    HandlerException(Cc cc, const std::string& what) :
+        HandlerException(cc, what.c_str())
+    {
+    }
+};
+
 /**
  * @brief Handler base class for dealing with IPMI request/response
  *
@@ -224,6 +257,16 @@
             // ipmi::RspType<>
             result = std::apply(handler_, *inputArgs);
         }
+        catch (const HandlerException& e)
+        {
+            phosphor::logging::log<phosphor::logging::level::INFO>(
+                "Handler produced exception",
+                phosphor::logging::entry("CC=%x", e.code()),
+                phosphor::logging::entry("EXCEPTION=%s", e.what()),
+                phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
+                phosphor::logging::entry("CMD=%x", request->ctx->cmd));
+            return errorResponse(request, e.code());
+        }
         catch (const std::exception& e)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
@@ -233,6 +276,10 @@
                 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
             return errorResponse(request, ccUnspecifiedError);
         }
+        catch (const HandlerCompletion& c)
+        {
+            return errorResponse(request, c.code());
+        }
         catch (...)
         {
             std::exception_ptr eptr;
@@ -323,6 +370,16 @@
                          request->payload.data() + request->payload.rawIndex,
                          response->payload.data(), &len, handlerCtx);
         }
+        catch (const HandlerException& e)
+        {
+            phosphor::logging::log<phosphor::logging::level::INFO>(
+                "Legacy Handler produced exception",
+                phosphor::logging::entry("CC=%x", e.code()),
+                phosphor::logging::entry("EXCEPTION=%s", e.what()),
+                phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
+                phosphor::logging::entry("CMD=%x", request->ctx->cmd));
+            return errorResponse(request, e.code());
+        }
         catch (const std::exception& e)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
@@ -332,6 +389,10 @@
                 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
             return errorResponse(request, ccUnspecifiedError);
         }
+        catch (const HandlerCompletion& c)
+        {
+            return errorResponse(request, c.code());
+        }
         catch (...)
         {
             std::exception_ptr eptr;
@@ -412,6 +473,16 @@
                          request->payload.data() + request->payload.rawIndex,
                          response->payload.data(), &len);
         }
+        catch (const HandlerException& e)
+        {
+            phosphor::logging::log<phosphor::logging::level::INFO>(
+                "Legacy OEM Handler produced exception",
+                phosphor::logging::entry("CC=%x", e.code()),
+                phosphor::logging::entry("EXCEPTION=%s", e.what()),
+                phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
+                phosphor::logging::entry("CMD=%x", request->ctx->cmd));
+            return errorResponse(request, e.code());
+        }
         catch (const std::exception& e)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
@@ -421,6 +492,10 @@
                 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
             return errorResponse(request, ccUnspecifiedError);
         }
+        catch (const HandlerCompletion& c)
+        {
+            return errorResponse(request, c.code());
+        }
         catch (...)
         {
             std::exception_ptr eptr;