updater: add process-host-firmware subcommand

Add general support for subcommands.  Do this by bringing in the the
CLI11 library, and by skipping over dbus service initialization if a
subcommand is specified on the command line.

Implement the first subcommand: process-host-firmware.  If no subcommand
is specified on the command line, openpower-update-manager continues to
launch as a dbus service.

Introduce functions.cpp/functions.hpp.  functions.cpp is intended to be
a module in which process-hostfirmware and future openpower-item-updater
subcommands can be implemented.  functions.hpp should be used to export
declarations to unit tests or item_updater_main.cpp.

The IBM POWER host firmware runtime looks for data and/or additional
code while bootstraping in locations with well-known names.
Additionally, the host firmware runtime relies on an external party to
map platform specific data and/or code to the well known names.  The
process-host-firmware command maintains that mapping on behalf of the
host firmware and ensures the correct blob files exist in the well-known
locations prior to starting the host firmware runtime.

The process-host-firmware subcommand registers for callbacks from entity
manager when entity manager adds new DBus interfaces.  Interfaces other
than IBMCompatibleSystem are ignored.  Entity manager is started with
dbus activation if it is not already running.  If IBMCompatibleSystem is
found and a set of host firmware blob filename extensions are mapped,
they are used to write "well-known" names into the filesystem for use by
the host firmware runtime.

Once the well-known names are written to the filesystem the program will
exit.

If entity manager does not publish an IBMCompatibleSystem interface the
command will wait indefinitely for it to appear.  For this reason it is
not recommended to run the process-host-fimrware subcommand if
IBMCompatibleSystem is not implemented.

If IBMCompatibleSystem is implemented but no host firmware blob filename
extensions are mapped, the program will exit without doing anything.

Testcases are provided.

Change-Id: Icb066b0f3e164520cae312e3b03432a6ad67e0df
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/functions.hpp b/functions.hpp
new file mode 100644
index 0000000..f7511aa
--- /dev/null
+++ b/functions.hpp
@@ -0,0 +1,49 @@
+#include <filesystem>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace sdbusplus
+{
+namespace bus
+{
+class bus;
+} // namespace bus
+} // namespace sdbusplus
+
+namespace sdeventplus
+{
+class Event;
+} // namespace sdeventplus
+
+namespace functions
+{
+namespace process_hostfirmware
+{
+using ErrorCallbackType =
+    std::function<void(const std::filesystem::path&, std::error_code&)>;
+using LinkCallbackType =
+    std::function<void(const std::filesystem::path&,
+                       const std::filesystem::path&, const ErrorCallbackType&)>;
+using MaybeCallCallbackType =
+    std::function<void(const std::vector<std::string>&)>;
+bool getExtensionsForIbmCompatibleSystem(
+    const std::map<std::string, std::vector<std::string>>&,
+    const std::vector<std::string>&, std::vector<std::string>&);
+void writeLink(const std::filesystem::path&, const std::filesystem::path&,
+               const ErrorCallbackType&);
+void findLinks(const std::filesystem::path&, const std::vector<std::string>&,
+               const ErrorCallbackType&, const LinkCallbackType&);
+bool maybeCall(
+    const std::map<
+        std::string,
+        std::map<std::string, std::variant<std::vector<std::string>>>>&,
+    const MaybeCallCallbackType&);
+std::shared_ptr<void> processHostFirmware(
+    sdbusplus::bus::bus&, std::map<std::string, std::vector<std::string>>,
+    std::filesystem::path, ErrorCallbackType, sdeventplus::Event&);
+} // namespace process_hostfirmware
+} // namespace functions