bmc: implement reboot update mechanism

Implement an update mechanism that simply triggers a reboot of the BMC.
This implementation isn't tied into anything in the firmware handler
yet.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I116d4f631a44e7a4a999cf8ad403a00935f58710
diff --git a/Makefile.am b/Makefile.am
index cc7f110..6e03b0a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -43,7 +43,8 @@
 	firmware_handler.cpp \
 	file_handler.cpp \
 	internal/sys.cpp \
-	verify_systemd.cpp
+	verify_systemd.cpp \
+	update_systemd.cpp
 
 if ENABLE_LPC_BRIDGE
 libfirmwareblob_common_la_SOURCES += lpc_handler.cpp
diff --git a/README.md b/README.md
index 25686fe..5af6f97 100644
--- a/README.md
+++ b/README.md
@@ -104,3 +104,4 @@
 `HASH_FILENAME`              | `/tmp/bmc.sig`             | The file to use for the hash provided.
 `VERIFY_STATUS_FILENAME`     | `/tmp/bmc.verify`          | The file checked for the verification status.
 `VERIFY_DBUS_SERVICE`        | `verify_image.service`     | The systemd service started for verification.
+`UPDATE_DBUS_SERVICE`        | `update-bmc.service`       | The systemd service started for updating the BMC.
diff --git a/configure.ac b/configure.ac
index 02e6447..45380fd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,6 +81,14 @@
 )
 AM_CONDITIONAL([BUILD_HOST_TOOL], [test "x$enable_build_host_tool" != "xno"])
 
+# Enable the reboot update mechanism
+AC_ARG_ENABLE([reboot-update],
+    AS_HELP_STRING([--enable-reboot-update],
+                   [Enable use of reboot update mechanism.]))
+AS_IF([test "x$enable_reboot_update" = "xyes"], [
+    AX_APPEND_COMPILE_FLAGS([-DENABLE_REBOOT_UPDATE], [CXXFLAGS])
+])
+
 # Enable static layout for firmware image staging.
 AC_ARG_ENABLE([static-layout],
     AS_HELP_STRING([--enable-static-layout],
@@ -175,11 +183,15 @@
 AS_IF([test "xVERIFY_STATUS_FILENAME" == "x"], [VERIFY_STATUS_FILENAME="/tmp/bmc.verify"])
 AC_DEFINE_UNQUOTED([VERIFY_STATUS_FILENAME], ["$VERIFY_STATUS_FILENAME"], [The file checked for the verification status.])
 
-# VERIFY_DBUS_SERVICE
 AC_ARG_VAR(VERIFY_DBUS_SERVICE, [The systemd service started for verification.])
 AS_IF([test "xVERIFY_DBUS_SERVICE" == "x"], [VERIFY_DBUS_SERVICE="verify_image.service"])
 AC_DEFINE_UNQUOTED([VERIFY_DBUS_SERVICE], ["$VERIFY_DBUS_SERVICE"], [The systemd service started for verification.])
 
+AC_ARG_VAR(UPDATE_DBUS_SERVICE, [The systemd service started for updating the BMC.])
+AS_IF([test "xUPDATE_DBUS_SERVICE" == "x"], [UPDATE_DBUS_SERVICE="update-bmc.service"])
+AC_DEFINE_UNQUOTED([UPDATE_DBUS_SERVICE], ["$UPDATE_DBUS_SERVICE"], [The systemd service started for updating the BMC.])
+
+
 AC_CHECK_HEADER(linux/ipmi.h, [HAVE_LINUX_IPMI_H=""], [HAVE_LINUX_IPMI_H="-I linux/ipmi.h"])
 AS_IF([test "$HAVE_LINUX_IPMI_H" != ""],
     AC_MSG_WARN([Could not find linux/ipmi.h: Attempting to download locally for building from openbmc/linux/+/dev-4.18])
diff --git a/main.cpp b/main.cpp
index 00ced2a..7e9b5a3 100644
--- a/main.cpp
+++ b/main.cpp
@@ -23,6 +23,7 @@
 #include "lpc_handler.hpp"
 #include "lpc_nuvoton.hpp"
 #include "pci_handler.hpp"
+#include "update_systemd.hpp"
 #include "util.hpp"
 #include "verify.hpp"
 #include "verify_systemd.hpp"
@@ -99,6 +100,17 @@
 {
     using namespace phosphor::logging;
 
+#ifdef ENABLE_REBOOT_UPDATE
+    static constexpr auto rebootTarget = "reboot.target";
+    static constexpr auto rebootMode = "replace-irreversibly";
+
+    auto updater = ipmi_flash::SystemdUpdateMechanism::CreateSystemdUpdate(
+        sdbusplus::bus::new_default(), rebootTarget, rebootMode);
+#else
+    auto updater = ipmi_flash::SystemdUpdateMechanism::CreateSystemdUpdate(
+        sdbusplus::bus::new_default(), UPDATE_DBUS_SERVICE);
+#endif
+
     auto handler = ipmi_flash::FirmwareBlobHandler::CreateFirmwareBlobHandler(
         ipmi_flash::supportedFirmware, ipmi_flash::supportedTransports,
         ipmi_flash::SystemdVerification::CreateVerification(
diff --git a/update_systemd.cpp b/update_systemd.cpp
new file mode 100644
index 0000000..0802f43
--- /dev/null
+++ b/update_systemd.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "update_systemd.hpp"
+
+#include "status.hpp"
+#include "update.hpp"
+
+#include <memory>
+#include <sdbusplus/bus.hpp>
+#include <string>
+
+namespace ipmi_flash
+{
+
+std::unique_ptr<UpdateInterface>
+    SystemdUpdateMechanism::CreateSystemdUpdate(sdbusplus::bus::bus&& bus,
+                                                const std::string& target,
+                                                const std::string& mode)
+{
+    return std::make_unique<SystemdUpdateMechanism>(std::move(bus), target,
+                                                    mode);
+}
+
+bool SystemdUpdateMechanism::triggerUpdate()
+{
+    /* TODO: Add a util method for triggering a service with optional additional
+     * parameter. */
+    static constexpr auto systemdService = "org.freedesktop.systemd1";
+    static constexpr auto systemdRoot = "/org/freedesktop/systemd1";
+    static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
+
+    auto method = bus.new_method_call(systemdService, systemdRoot,
+                                      systemdInterface, "StartUnit");
+    method.append(target);
+
+    if (!mode.empty())
+    {
+        method.append(mode);
+    }
+
+    try
+    {
+        bus.call_noreply(method);
+        return true;
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        return false;
+    }
+}
+
+void SystemdUpdateMechanism::abortUpdate()
+{
+    return;
+}
+
+UpdateStatus SystemdUpdateMechanism::status()
+{
+    /* For now, don't check if the target failed. */
+    return UpdateStatus::running;
+}
+
+} // namespace ipmi_flash
diff --git a/update_systemd.hpp b/update_systemd.hpp
new file mode 100644
index 0000000..47c8d67
--- /dev/null
+++ b/update_systemd.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "update.hpp"
+
+#include <memory>
+#include <sdbusplus/bus.hpp>
+#include <string>
+
+namespace ipmi_flash
+{
+
+/**
+ * Implements the update interface by simply triggering a systemd unit.
+ */
+class SystemdUpdateMechanism : public UpdateInterface
+{
+  public:
+    static std::unique_ptr<UpdateInterface>
+        CreateSystemdUpdate(sdbusplus::bus::bus&& bus,
+                            const std::string& target,
+                            const std::string& mode = "");
+
+    SystemdUpdateMechanism(sdbusplus::bus::bus&& bus, const std::string& target,
+                           const std::string& mode) :
+        bus(std::move(bus)),
+        target(target), mode(mode)
+    {
+    }
+
+    ~SystemdUpdateMechanism() = default;
+    SystemdUpdateMechanism(const SystemdUpdateMechanism&) = delete;
+    SystemdUpdateMechanism& operator=(const SystemdUpdateMechanism&) = delete;
+    SystemdUpdateMechanism(SystemdUpdateMechanism&&) = default;
+    SystemdUpdateMechanism& operator=(SystemdUpdateMechanism&&) = default;
+
+    bool triggerUpdate() override;
+    void abortUpdate() override;
+    UpdateStatus status() override;
+
+  private:
+    sdbusplus::bus::bus bus;
+    const std::string target;
+    const std::string mode;
+};
+
+} // namespace ipmi_flash