bmc: provide disabled json support

Provide support such that a json configuration file can define a firmware
handler and its behavior.  This is not yet enabled.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: If21f4e97951b3bb91a8afc53bee493d8600a04b7
diff --git a/.gitignore b/.gitignore
index 2b70727..9cb1b4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@
 
 # Custom generated files
 *.target
+*.json
 
 # Output binaries
 burn_my_bmc
diff --git a/bmc/Makefile.am b/bmc/Makefile.am
index 8ada86b..ef1a455 100644
--- a/bmc/Makefile.am
+++ b/bmc/Makefile.am
@@ -1,5 +1,26 @@
 AM_DEFAULT_SOURCE_EXT = .cpp
 
+pkgdatadir = $(datadir)/phosphor-ipmi-flash
+dist_pkgdata_DATA =
+
+# reboot update is only effective for static layout
+# static layout is also possible with a tarball+bmc-code-mgmt
+#
+# the bmc-code-mgmt will reboot for us or if we need to support this
+# variation (via the ApplyTime to immediate) (only currently supported for
+# ubi).
+if ENABLE_STATIC_LAYOUT
+if ENABLE_REBOOT_UPDATE
+dist_pkgdata_DATA += config-static-bmc-reboot.json
+else
+dist_pkgdata_DATA += config-static-bmc.json
+endif
+endif
+
+if ENABLE_HOST_BIOS
+dist_pkgdata_DATA += config-bios.json
+endif
+
 if HAVE_SYSTEMD
 systemdsystemunit_DATA = \
 	phosphor-ipmi-flash-bmc-prepare.target \
@@ -15,6 +36,7 @@
 
 noinst_LTLIBRARIES = libfirmwareblob_common.la
 libfirmwareblob_common_la_SOURCES = \
+	buildjson.cpp \
 	firmware_handler.cpp \
 	file_handler.cpp \
 	prepare_systemd.cpp \
diff --git a/bmc/buildjson.cpp b/bmc/buildjson.cpp
new file mode 100644
index 0000000..5c14e61
--- /dev/null
+++ b/bmc/buildjson.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * 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 "buildjson.hpp"
+
+#include "file_handler.hpp"
+#include "prepare_systemd.hpp"
+#include "update_systemd.hpp"
+#include "verify_systemd.hpp"
+
+#include <cstdio>
+#include <exception>
+#include <nlohmann/json.hpp>
+#include <sdbusplus/bus.hpp>
+#include <string>
+#include <vector>
+
+namespace ipmi_flash
+{
+
+std::vector<HandlerConfig> buildHandlerFromJson(const nlohmann::json& data)
+{
+    std::vector<HandlerConfig> handlers;
+
+    for (const auto& item : data)
+    {
+        try
+        {
+            HandlerConfig output;
+
+            /* at() throws an exception when the key is not present. */
+            item.at("blob").get_to(output.blobId);
+
+            /* handler is required. */
+            const auto& h = item.at("handler");
+            const std::string handlerType = h.at("type");
+            if (handlerType == "file")
+            {
+                const auto& path = h.at("path");
+                output.handler = std::make_unique<FileHandler>(path);
+            }
+            else
+            {
+                throw std::runtime_error("Invalid handler type: " +
+                                         handlerType);
+            }
+
+            /* actions are required (presently). */
+            const auto& a = item.at("actions");
+            std::unique_ptr<ActionPack> pack = std::make_unique<ActionPack>();
+
+            /* It hasn't been fully determined if any action being optional is
+             * useful, so for now they will be required.
+             * TODO: Evaluate how the behaviors change if some actions are
+             * missing, does the code just assume it was successful?  I would
+             * think not.
+             */
+            const auto& prep = a.at("preparation");
+            const std::string prepareType = prep.at("type");
+            if (prepareType == "systemd")
+            {
+                const auto& unit = prep.at("unit");
+                pack->preparation = SystemdPreparation::CreatePreparation(
+                    sdbusplus::bus::new_default(), unit);
+            }
+            else
+            {
+                throw std::runtime_error("Invalid preparation type: " +
+                                         prepareType);
+            }
+
+            const auto& verify = a.at("verification");
+            const std::string verifyType = verify.at("type");
+            if (verifyType == "fileSystemdVerify")
+            {
+                const auto& path = verify.at("path");
+                const auto& unit = verify.at("unit");
+                pack->verification = SystemdVerification::CreateVerification(
+                    sdbusplus::bus::new_default(), path, unit);
+            }
+            else
+            {
+                throw std::runtime_error("Invalid verification type:" +
+                                         verifyType);
+            }
+
+            const auto& update = a.at("update");
+            const std::string updateType = update.at("type");
+            if (updateType == "reboot")
+            {
+                static constexpr auto rebootTarget = "reboot.target";
+                static constexpr auto rebootMode = "replace-irreversibly";
+                pack->update = SystemdUpdateMechanism::CreateSystemdUpdate(
+                    sdbusplus::bus::new_default(), rebootTarget, rebootMode);
+            }
+            else if (updateType == "systemd")
+            {
+                const auto& unit = update.at("unit");
+                pack->update = SystemdUpdateMechanism::CreateSystemdUpdate(
+                    sdbusplus::bus::new_default(), unit);
+            }
+            else
+            {
+                throw std::runtime_error("Invalid update type: " + updateType);
+            }
+
+            output.actions = std::move(pack);
+            handlers.push_back(std::move(output));
+        }
+        catch (const std::exception& e)
+        {
+            /* TODO: Once phosphor-logging supports unit-test injection, fix
+             * this to log.
+             */
+            std::fprintf(stderr,
+                         "Excepted building HandlerConfig from json: %s\n",
+                         e.what());
+        }
+    }
+
+    return handlers;
+}
+
+} // namespace ipmi_flash
diff --git a/bmc/buildjson.hpp b/bmc/buildjson.hpp
new file mode 100644
index 0000000..5f9affd
--- /dev/null
+++ b/bmc/buildjson.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "firmware_handler.hpp"
+#include "image_handler.hpp"
+
+#include <memory>
+#include <nlohmann/json.hpp>
+#include <string>
+#include <vector>
+
+namespace ipmi_flash
+{
+
+class HandlerConfig
+{
+  public:
+    HandlerConfig() = default;
+    ~HandlerConfig() = default;
+    HandlerConfig(const HandlerConfig&) = delete;
+    HandlerConfig& operator=(const HandlerConfig&) = delete;
+    HandlerConfig(HandlerConfig&&) = default;
+    HandlerConfig& operator=(HandlerConfig&&) = default;
+
+    /* A string in the form: /flash/{unique}, s.t. unique is something like,
+     * flash, ubitar, statictar, or bios
+     */
+    std::string blobId;
+
+    /* This owns a handler interface, this is typically going to be a file
+     * writer object.
+     */
+    std::unique_ptr<ImageHandlerInterface> handler;
+
+    /* Only the hashBlobId doesn't have an action pack, otherwise it's required.
+     */
+    std::unique_ptr<ActionPack> actions;
+};
+
+/**
+ * Given a list of handlers as json data, construct the appropriate
+ * HandlerConfig objects.  This method is meant to be called per json
+ * configuration file found.
+ *
+ * The list will only contain validly build HandlerConfig objects.  Any invalid
+ * configuration is skipped. The hope is that the BMC firmware update
+ * configuration will never be invalid, but if another aspect is invalid, it can
+ * be fixed with a BMC firmware update once the bug is identified.
+ *
+ * This code does not validate that the blob specified is unique, that should
+ * be handled at a higher layer.
+ *
+ * @param[in] data - json data from a json file.
+ * @return list of HandlerConfig objects.
+ */
+std::vector<HandlerConfig> buildHandlerFromJson(const nlohmann::json& data);
+
+} // namespace ipmi_flash
diff --git a/bmc/config-bios.json.in b/bmc/config-bios.json.in
new file mode 100644
index 0000000..90dbff9
--- /dev/null
+++ b/bmc/config-bios.json.in
@@ -0,0 +1,22 @@
+[{
+	"blob": "/flash/bios",
+	"handler": {
+		"type": "file",
+		"path": "@BIOS_STAGED_NAME@"
+	},
+	"actions": {
+		"preparation": {
+			"type": "systemd",
+			"unit": "@PREPARATION_BIOS_TARGET@"
+		},
+		"verification": {
+			"type": "fileSystemdVerify",
+			"unit": "@VERIFY_BIOS_TARGET@",
+			"path": "@BIOS_VERIFY_STATUS_FILENAME@"
+		},
+		"update": {
+			"type": "systemd",
+			"unit": "@UPDATE_BIOS_TARGET@"
+		}
+	}
+}]
diff --git a/bmc/config-static-bmc-reboot.json.in b/bmc/config-static-bmc-reboot.json.in
new file mode 100644
index 0000000..d5536e0
--- /dev/null
+++ b/bmc/config-static-bmc-reboot.json.in
@@ -0,0 +1,21 @@
+[{
+	"blob": "/flash/image",
+	"handler": {
+		"type": "file",
+		"path": "@STATIC_HANDLER_STAGED_NAME@"
+	},
+	"actions": {
+		"preparation": {
+			"type": "systemd",
+			"unit": "@PREPARATION_DBUS_SERVICE@"
+		},
+		"verification": {
+			"type": "fileSystemdVerify",
+			"unit": "@VERIFY_DBUS_SERVICE@",
+			"path": "@VERIFY_STATUS_FILENAME@"
+		},
+		"update": {
+			"type": "reboot"
+		}
+	}
+}]
diff --git a/bmc/config-static-bmc.json.in b/bmc/config-static-bmc.json.in
new file mode 100644
index 0000000..e3bc813
--- /dev/null
+++ b/bmc/config-static-bmc.json.in
@@ -0,0 +1,22 @@
+[{
+	"blob": "/flash/image",
+	"handler": {
+		"type": "file",
+		"path": "@STATIC_HANDLER_STAGED_NAME@"
+	},
+	"actions": {
+		"preparation": {
+			"type": "systemd",
+			"unit": "@PREPARATION_DBUS_SERVICE@"
+		},
+		"verification": {
+			"type": "fileSystemdVerify",
+			"unit": "@VERIFY_DBUS_SERVICE@",
+			"path": "@VERIFY_STATUS_FILENAME@"
+		},
+		"update": {
+			"type": "systemd",
+			"unit": "@UPDATE_DBUS_SERVICE@"
+		}
+	}
+}]
diff --git a/bmc/test/Makefile.am b/bmc/test/Makefile.am
index feaee78..a4d79f8 100644
--- a/bmc/test/Makefile.am
+++ b/bmc/test/Makefile.am
@@ -41,7 +41,8 @@
 	firmware_state_updatestarted_unittest \
 	firmware_state_updatecompleted_unittest \
 	firmware_state_notyetstarted_tarball_unittest \
-	firmware_multiplebundle_unittest
+	firmware_multiplebundle_unittest \
+	firmware_json_unittest
 
 TESTS = $(check_PROGRAMS)
 
@@ -104,3 +105,6 @@
 
 firmware_multiplebundle_unittest_SOURCES = firmware_multiplebundle_unittest.cpp
 firmware_multiplebundle_unittest_LDADD = $(top_builddir)/bmc/libfirmwareblob_common.la
+
+firmware_json_unittest_SOURCES = firmware_json_unittest.cpp
+firmware_json_unittest_LDADD = $(top_builddir)/bmc/libfirmwareblob_common.la
diff --git a/bmc/test/firmware_json_unittest.cpp b/bmc/test/firmware_json_unittest.cpp
new file mode 100644
index 0000000..704243b
--- /dev/null
+++ b/bmc/test/firmware_json_unittest.cpp
@@ -0,0 +1,429 @@
+#include "buildjson.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace ipmi_flash
+{
+namespace
+{
+using ::testing::IsEmpty;
+
+using json = nlohmann::json;
+
+TEST(FirmwareJsonTest, InvalidHandlerType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "unsupported",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidPreparationType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "superfun",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidVerificationType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "funtimes",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidUpdateType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingHandler)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActions)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionPreparation)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionVerification)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionUpdate)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, TwoConfigsOneInvalidReturnsValid)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         },
+         {
+            "blob" : "/flash/image2",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image2");
+    EXPECT_EQ(h.size(), 1);
+}
+
+/*
+ * TODO: It may be worth individually using builders per type, and testing
+ * those.
+ *
+ * TODO: Only allow unique handler blob paths (tested at a higher level).
+ */
+
+TEST(FirmwareJsonTest, VerifySystemdWithReboot)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_FALSE(h[0].handler == nullptr);
+    EXPECT_FALSE(h[0].actions == nullptr);
+    EXPECT_FALSE(h[0].actions->preparation == nullptr);
+    EXPECT_FALSE(h[0].actions->verification == nullptr);
+    EXPECT_FALSE(h[0].actions->update == nullptr);
+}
+
+TEST(FirmwareJsonTest, VerifyMultipleHandlersReturned)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+        },
+        {
+            "blob" : "/flash/bios",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+        }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h.size(), 2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_EQ(h[1].blobId, "/flash/bios");
+}
+
+TEST(FirmwareJsonTest, VerifyValidSingleNonReboot)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+         }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_FALSE(h[0].handler == nullptr);
+    EXPECT_FALSE(h[0].actions == nullptr);
+    EXPECT_FALSE(h[0].actions->preparation == nullptr);
+    EXPECT_FALSE(h[0].actions->verification == nullptr);
+    EXPECT_FALSE(h[0].actions->update == nullptr);
+}
+
+} // namespace
+} // namespace ipmi_flash
diff --git a/configure.ac b/configure.ac
index aea5d42..8354cdd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -83,6 +83,12 @@
         [AC_MSG_ERROR(["phosphor-ipmi-blobs required and not found."])]
     )
 
+    AC_CHECK_HEADER(
+        [nlohmann/json.hpp],
+        [],
+        [AC_MSG_ERROR([Could not find nlohmann/json.hpp])]
+    )
+
     PKG_PROG_PKG_CONFIG
     AC_ARG_WITH([systemdsystemunitdir],
         [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],
@@ -136,6 +142,7 @@
 AC_ARG_ENABLE([reboot-update],
     AS_HELP_STRING([--enable-reboot-update],
                    [Enable use of reboot update mechanism.]))
+AM_CONDITIONAL([ENABLE_REBOOT_UPDATE], [test "x$enable_reboot_update" = "xyes"])
 AS_IF([test "x$enable_reboot_update" = "xyes"], [
     AX_APPEND_COMPILE_FLAGS([-DENABLE_REBOOT_UPDATE], [CXXFLAGS])
 ])
@@ -150,6 +157,7 @@
 AC_ARG_ENABLE([static-layout],
     AS_HELP_STRING([--enable-static-layout],
                    [Enable static layout firmware update via Blobs]))
+AM_CONDITIONAL([ENABLE_STATIC_LAYOUT], [test "x$enable_static_layout" = "xyes"])
 AS_IF([test "x$enable_static_layout" = "xyes"], [
     AX_APPEND_COMPILE_FLAGS([-DENABLE_STATIC_LAYOUT], [CXXFLAGS])
 ])
@@ -539,4 +547,7 @@
 AC_CONFIG_FILES([bmc/phosphor-ipmi-flash-bios-prepare.target])
 AC_CONFIG_FILES([bmc/phosphor-ipmi-flash-bios-verify.target])
 AC_CONFIG_FILES([bmc/phosphor-ipmi-flash-bios-update.target])
+AC_CONFIG_FILES([bmc/config-static-bmc-reboot.json])
+AC_CONFIG_FILES([bmc/config-static-bmc.json])
+AC_CONFIG_FILES([bmc/config-bios.json])
 AC_OUTPUT