Move bmcweb over to sdbusplus

This patchset moves bmcweb from using boost-dbus over entirely to
sdbusplus.  This has some nice improvements in performance (about 30%
of CPU cycles saved in dbus transactions), as well as makes this
project manuver closer to the upstream way of thinking.

Changes to bmcweb are largely ceremonial, and fall into a few
categories:
1. Moves async_method_call instances to the new format, and deletes any
use of the "endpoint" object in leiu of the sdbusplus style interface
2. sdbus object_path object doesn't allow access to the string
directly, so code that uses it moves to explicit casts.
3. The mapbox variant, while attempting to recreate boost::variant,
misses a T* get<T*>() method implementation, which allows using variant
without exceptions.  Currently, there is an overload for
mapbox::get_ptr implementation which replecates the functionality.

Tested by: Booting the bmcweb on a target, iterating through redfish
basic phosphor-webui usage, and websockets usage

Change-Id: I2d95882908d6eb6dba00b9219a221dd96449ca7b
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp
index 0306183..ab7ebef 100644
--- a/include/dbus_monitor.hpp
+++ b/include/dbus_monitor.hpp
@@ -1,7 +1,6 @@
 #pragma once
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
 #include <dbus_singleton.hpp>
+#include <sdbusplus/bus/match.hpp>
 #include <crow/app.h>
 #include <boost/container/flat_map.hpp>
 
@@ -9,34 +8,38 @@
 namespace dbus_monitor {
 
 struct DbusWebsocketSession {
-  std::vector<std::unique_ptr<dbus::match>> matches;
-  std::vector<dbus::filter> filters;
+  std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
 };
 
 static boost::container::flat_map<crow::websocket::connection*,
                                   DbusWebsocketSession>
     sessions;
 
-void on_property_update(dbus::filter& filter, boost::system::error_code ec,
-                        dbus::message s) {
-  if (!ec) {
-    std::string object_name;
-    std::vector<std::pair<std::string, dbus::dbus_variant>> values;
-    s.unpack(object_name, values);
-    nlohmann::json j;
-    for (auto& value : values) {
-      boost::apply_visitor([&](auto val) { j[s.get_path()] = val; },
-                           value.second);
-    }
-    auto data_to_send = j.dump();
-
-    for (auto& session : sessions) {
-      session.first->send_text(data_to_send);
-    }
+int on_property_update(sd_bus_message* m, void* userdata,
+                       sd_bus_error* ret_error) {
+  if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) {
+    CROW_LOG_ERROR << "Sdbus error in on_property_update";
+    return 0;
   }
-  filter.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
-    on_property_update(filter, ec, s);
-  });
+  sdbusplus::message::message message(m);
+  std::string object_name;
+  std::vector<
+      std::pair<std::string, sdbusplus::message::variant<
+                                 std::string, bool, int64_t, uint64_t, double>>>
+      values;
+  message.read(object_name, values);
+  nlohmann::json j;
+  const std::string& path = message.get_path();
+  for (auto& value : values) {
+    mapbox::util::apply_visitor([&](auto&& val) { j[path] = val; },
+                                value.second);
+  }
+  std::string data_to_send = j.dump();
+
+  for (const std::pair<crow::websocket::connection*, DbusWebsocketSession>&
+           session : sessions) {
+    session.first->send_text(data_to_send);
+  }
 };
 
 template <typename... Middlewares>
@@ -56,19 +59,10 @@
             "interface='org.freedesktop.DBus.Properties',"
             "path_namespace='" +
             path_namespace + "'");
-        sessions[&conn].matches.push_back(std::make_unique<dbus::match>(
-            crow::connections::system_bus, std::move(match_string)));
-
-        sessions[&conn].filters.emplace_back(
-            crow::connections::system_bus, [path_namespace](dbus::message m) {
-              return m.get_member() == "PropertiesChanged" &&
-                     boost::starts_with(m.get_path(), path_namespace);
-            });
-        auto& this_filter = sessions[&conn].filters.back();
-        this_filter.async_dispatch(
-            [&](boost::system::error_code ec, dbus::message s) {
-              on_property_update(this_filter, ec, s);
-            });
+        sessions[&conn].matches.emplace_back(
+            std::make_unique<sdbusplus::bus::match::match>(
+                *crow::connections::system_bus, match_string,
+                on_property_update));
 
       })
       .onclose([&](crow::websocket::connection& conn,
@@ -78,5 +72,5 @@
         CROW_LOG_ERROR << "Got unexpected message from client on sensorws";
       });
 }
-}  // namespace redfish
+}  // namespace dbus_monitor
 }  // namespace crow
diff --git a/include/dbus_singleton.hpp b/include/dbus_singleton.hpp
index e2fd2d6..e6be81b 100644
--- a/include/dbus_singleton.hpp
+++ b/include/dbus_singleton.hpp
@@ -1,10 +1,21 @@
 #pragma once
-#include <dbus/connection.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <iostream>
+
+namespace mapbox {
+template <typename T, typename... Types>
+const T* get_ptr(const mapbox::util::variant<Types...>& v) {
+  if (v.template is<std::remove_const_t<T>>()) {
+    return &v.template get_unchecked<std::remove_const_t<T>>();
+  } else {
+    return nullptr;
+  }
+}
+}  // namespace mapbox
 
 namespace crow {
 namespace connections {
+static std::shared_ptr<sdbusplus::asio::connection> system_bus;
 
-static std::shared_ptr<dbus::connection> system_bus;
-
-}  // namespace dbus
-}  // namespace crow
\ No newline at end of file
+}  // namespace connections
+}  // namespace crow
diff --git a/include/intel_oem.hpp b/include/intel_oem.hpp
index e821807..f2dc163 100644
--- a/include/intel_oem.hpp
+++ b/include/intel_oem.hpp
@@ -10,24 +10,29 @@
 template <typename... Middlewares>
 void request_routes(Crow<Middlewares...>& app) {
   CROW_ROUTE(app, "/intel/firmwareupload")
-      .methods("POST"_method)([](const crow::request& req) {
+      .methods(
+          "POST"_method)([](const crow::request& req, crow::response& res) {
         std::string filepath("/tmp/fw_update_image");
         std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
                                         std::ofstream::trunc);
         out << req.body;
         out.close();
+        crow::connections::system_bus->async_method_call(
+            [&](boost::system::error_code ec) {
+              std::cout << "async_method_call callback\n";
+              if (ec) {
+                std::cerr << "error with async_method_call \n";
+                res.json_value["status"] = "Upload failed";
+              } else {
+                res.json_value["status"] = "Upload Successful";
+              }
+              res.end();
+            },
+            "xyz.openbmc_project.fwupdate1.server",
+            "/xyz/openbmc_project/fwupdate1", "xyz.openbmc_project.fwupdate1",
+            "start", "file://" + filepath);
 
-        auto m = dbus::message::new_call(
-            {"xyz.openbmc_project.fwupdate1.server",
-             "/xyz/openbmc_project/fwupdate1", "xyz.openbmc_project.fwupdate1"},
-            "start");
-
-        m.pack(std::string("file://") + filepath);
-        crow::connections::system_bus->send(m);
-        nlohmann::json j;
-        j["status"] = "Upload Successful";
-        return j;
       });
 }
-}  // namespace redfish
+}  // namespace intel_oem
 }  // namespace crow
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index d42c04b..4257127 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -1,11 +1,6 @@
 #include <crow/app.h>
 
 #include <tinyxml2.h>
-#include <dbus/connection.hpp>
-#include <dbus/endpoint.hpp>
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
-#include <dbus/message.hpp>
 #include <dbus_singleton.hpp>
 #include <boost/container/flat_set.hpp>
 
@@ -15,12 +10,11 @@
 void introspect_objects(crow::response &res, std::string process_name,
                         std::string path,
                         std::shared_ptr<nlohmann::json> transaction) {
-  dbus::endpoint introspect_endpoint(
-      process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
   crow::connections::system_bus->async_method_call(
-      [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
-          const boost::system::error_code ec,
-          const std::string &introspect_xml) {
+      [
+        &res, transaction, process_name{std::move(process_name)},
+        object_path{std::move(path)}
+      ](const boost::system::error_code ec, const std::string &introspect_xml) {
         if (ec) {
           CROW_LOG_ERROR << "Introspect call failed with error: "
                          << ec.message() << " on process: " << process_name
@@ -35,7 +29,7 @@
           tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
           if (pRoot == nullptr) {
             CROW_LOG_ERROR << "XML document failed to parse " << process_name
-                           << " " << path << "\n";
+                           << " " << object_path << "\n";
 
           } else {
             tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
@@ -61,14 +55,23 @@
           res.end();
         }
       },
-      introspect_endpoint);
+      process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
 }
-using ManagedObjectType = std::vector<std::pair<
-    dbus::object_path, boost::container::flat_map<
-                           std::string, boost::container::flat_map<
-                                            std::string, dbus::dbus_variant>>>>;
 
-void get_manged_objects_for_enumerate(
+// A smattering of common types to unpack.  TODO(ed) this should really iterate
+// the sdbusplus object directly and build the json response
+using DbusRestVariantType = sdbusplus::message::variant<
+    std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
+    int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
+    bool>;
+
+using ManagedObjectType = std::vector<std::pair<
+    sdbusplus::message::object_path,
+    boost::container::flat_map<
+        std::string,
+        boost::container::flat_map<std::string, DbusRestVariantType>>>>;
+
+void get_managed_objects_for_enumerate(
     const std::string &object_name, const std::string &connection_name,
     crow::response &res, std::shared_ptr<nlohmann::json> transaction) {
   crow::connections::system_bus->async_method_call(
@@ -78,13 +81,29 @@
           CROW_LOG_ERROR << ec;
         } else {
           nlohmann::json &data_json = *transaction;
+
           for (auto &object_path : objects) {
-            nlohmann::json &object_json = data_json[object_path.first.value];
+            CROW_LOG_DEBUG << "Reading object "
+                           << static_cast<const std::string&>(object_path.first);
+            nlohmann::json &object_json =
+                data_json[static_cast<const std::string&>(object_path.first)];
+            if (object_json.is_null()) {
+              object_json = nlohmann::json::object();
+            }
             for (const auto &interface : object_path.second) {
               for (const auto &property : interface.second) {
-                boost::apply_visitor(
-                    [&](auto &&val) { object_json[property.first] = val; },
+                nlohmann::json &property_json = object_json[property.first];
+                mapbox::util::apply_visitor(
+                    [&property_json](auto &&val) { property_json = val; },
                     property.second);
+
+                // dbus-rest represents booleans as 1 or 0, implement to match
+                // TODO(ed) see if dbus-rest should be changed
+                const bool *property_bool =
+                    property_json.get_ptr<const bool *>();
+                if (property_bool != nullptr) {
+                  property_json = *property_bool ? 1 : 0;
+                }
               }
             }
           }
@@ -97,9 +116,9 @@
           res.end();
         }
       },
-      {connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
-       "GetManagedObjects"});
-}  // namespace openbmc_mapper
+      connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
+      "GetManagedObjects");
+}
 
 using GetSubTreeType = std::vector<
     std::pair<std::string,
@@ -132,14 +151,14 @@
         auto transaction =
             std::make_shared<nlohmann::json>(nlohmann::json::object());
         for (const std::string &connection : connections) {
-          get_manged_objects_for_enumerate(object_path, connection, res,
-                                           transaction);
+          get_managed_objects_for_enumerate(object_path, connection, res,
+                                            transaction);
         }
 
       },
-      {"xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
-       "xyz.openbmc_project.ObjectMapper", "GetSubTree"},
-      object_path, (int32_t)0, std::array<std::string, 0>());
+      "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
+      "xyz.openbmc_project.ObjectMapper", "GetSubTree", object_path, (int32_t)0,
+      std::array<std::string, 0>());
 }
 
 template <typename... Middlewares>
@@ -151,33 +170,32 @@
 
   CROW_ROUTE(app, "/bus/system/")
       .methods("GET"_method)([](const crow::request &req, crow::response &res) {
+
+        auto myCallback = [&res](const boost::system::error_code ec,
+                                 std::vector<std::string> &names) {
+          if (ec) {
+            res.code = 500;
+          } else {
+            std::sort(names.begin(), names.end());
+            nlohmann::json j{{"status", "ok"}};
+            auto &objects_sub = j["objects"];
+            for (auto &name : names) {
+              objects_sub.push_back({{"name", name}});
+            }
+            res.json_value = std::move(j);
+          }
+          res.end();
+        };
         crow::connections::system_bus->async_method_call(
-            [&](const boost::system::error_code ec,
-                std::vector<std::string> &names) {
-
-              if (ec) {
-                res.code = 500;
-              } else {
-                std::sort(names.begin(), names.end());
-                nlohmann::json j{{"status", "ok"}};
-                auto &objects_sub = j["objects"];
-                for (auto &name : names) {
-                  objects_sub.push_back({{"name", name}});
-                }
-                res.json_value = std::move(j);
-              }
-              res.end();
-            },
-            {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
-
+            std::move(myCallback), "org.freedesktop.DBus", "/",
+            "org.freedesktop.DBus", "ListNames");
       });
 
   CROW_ROUTE(app, "/list/")
       .methods("GET"_method)([](const crow::request &req, crow::response &res) {
         crow::connections::system_bus->async_method_call(
-            [&](const boost::system::error_code ec,
-                const std::vector<std::string> &object_paths) {
-
+            [&res](const boost::system::error_code ec,
+                   const std::vector<std::string> &object_paths) {
               if (ec) {
                 res.code = 500;
               } else {
@@ -187,10 +205,10 @@
               }
               res.end();
             },
-            {"xyz.openbmc_project.ObjectMapper",
-             "/xyz/openbmc_project/object_mapper",
-             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"},
-            "", static_cast<int32_t>(99), std::array<std::string, 0>());
+            "xyz.openbmc_project.ObjectMapper",
+            "/xyz/openbmc_project/object_mapper",
+            "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "",
+            static_cast<int32_t>(99), std::array<std::string, 0>());
       });
 
   CROW_ROUTE(app, "/xyz/<path>")
@@ -242,9 +260,9 @@
 
         crow::connections::system_bus->async_method_call(
             [
-                  &, object_path{std::move(object_path)},
-                  dest_property{std::move(dest_property)},
-                  property_set_value{std::move(property_set_value)}, transaction
+              &res, &req, object_path{std::move(object_path)},
+              dest_property{std::move(dest_property)},
+              property_set_value{std::move(property_set_value)}, transaction
             ](const boost::system::error_code ec,
               const GetObjectType &object_names) {
               if (ec) {
@@ -260,17 +278,17 @@
               if (req.method == "GET"_method) {
                 for (auto &interface : object_names[0].second) {
                   crow::connections::system_bus->async_method_call(
-                      [&](const boost::system::error_code ec,
+                      [&res, transaction](
+                          const boost::system::error_code ec,
                           const std::vector<std::pair<
-                              std::string, dbus::dbus_variant>> &properties) {
+                              std::string, DbusRestVariantType>> &properties) {
                         if (ec) {
                           CROW_LOG_ERROR << "Bad dbus request error: " << ec;
                         } else {
                           for (auto &property : properties) {
-                            boost::apply_visitor(
-                                [&](auto val) {
-                                  (*transaction)[property.first] = val;
-                                },
+                            nlohmann::json &it = (*transaction)[property.first];
+                            mapbox::util::apply_visitor(
+                                [&it](auto &&val) { it = val; },
                                 property.second);
                           }
                         }
@@ -282,9 +300,8 @@
                           res.end();
                         }
                       },
-                      {object_names[0].first, object_path,
-                       "org.freedesktop.DBus.Properties", "GetAll"},
-                      interface);
+                      object_names[0].first, object_path,
+                      "org.freedesktop.DBus.Properties", "GetAll", interface);
                 }
               } else if (req.method == "PUT"_method) {
                 for (auto &interface : object_names[0].second) {
@@ -298,18 +315,19 @@
                             transaction
                       ](const boost::system::error_code ec,
                         const boost::container::flat_map<
-                            std::string, dbus::dbus_variant> &properties) {
+                            std::string, DbusRestVariantType> &properties) {
                         if (ec) {
                           CROW_LOG_ERROR << "Bad dbus request error: " << ec;
                         } else {
                           auto it = properties.find(dest_property);
                           if (it != properties.end()) {
                             // find the matched property in the interface
-                            dbus::dbus_variant property_value(
-                                property_set_value);  // create the dbus
-                                                      // variant for dbus call
+                            DbusRestVariantType property_value(
+                                property_set_value);
+                            // create the dbus variant for dbus call
                             crow::connections::system_bus->async_method_call(
-                                [&](const boost::system::error_code ec) {
+                                [transaction](
+                                    const boost::system::error_code ec) {
                                   // use the method "Set" to set the property
                                   // value
                                   if (ec) {
@@ -323,8 +341,8 @@
                                                   {"data", nullptr}};
 
                                 },
-                                {object_names[0].first, object_path,
-                                 "org.freedesktop.DBus.Properties", "Set"},
+                                object_names[0].first, object_path,
+                                "org.freedesktop.DBus.Properties", "Set",
                                 interface, dest_property, property_value);
                           }
                         }
@@ -350,16 +368,15 @@
                           return;
                         }
                       },
-                      {object_names[0].first, object_path,
-                       "org.freedesktop.DBus.Properties", "GetAll"},
-                      interface);
+                      object_names[0].first, object_path,
+                      "org.freedesktop.DBus.Properties", "GetAll", interface);
                 }
               }
             },
-            {"xyz.openbmc_project.ObjectMapper",
-             "/xyz/openbmc_project/object_mapper",
-             "xyz.openbmc_project.ObjectMapper", "GetObject"},
-            object_path, std::array<std::string, 0>());
+            "xyz.openbmc_project.ObjectMapper",
+            "/xyz/openbmc_project/object_mapper",
+            "xyz.openbmc_project.ObjectMapper", "GetObject", object_path,
+            std::array<std::string, 0>());
       });
 
   CROW_ROUTE(app, "/bus/system/<str>/")
@@ -413,9 +430,6 @@
           res.end();
           return;
         }
-        dbus::endpoint introspect_endpoint(
-            process_name, object_path, "org.freedesktop.DBus.Introspectable",
-            "Introspect");
         if (interface_name.empty()) {
           crow::connections::system_bus->async_method_call(
               [
@@ -459,7 +473,8 @@
                 }
                 res.end();
               },
-              introspect_endpoint);
+              process_name, object_path, "org.freedesktop.DBus.Introspectable",
+              "Introspect");
         } else {
           crow::connections::system_bus->async_method_call(
               [
@@ -566,10 +581,11 @@
                 }
                 res.end();
               },
-              introspect_endpoint);
+              process_name, object_path, "org.freedesktop.DBus.Introspectable",
+              "Introspect");
         }
 
       });
-}
+}  // namespace openbmc_mapper
 }  // namespace openbmc_mapper
 }  // namespace crow
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp
index c28208d..03b9051 100644
--- a/include/redfish_v1.hpp
+++ b/include/redfish_v1.hpp
@@ -1,10 +1,6 @@
 #pragma once
 
-#include <dbus/connection.hpp>
-#include <dbus/endpoint.hpp>
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
-#include <dbus/message.hpp>
+#include <dbus_singleton.hpp>
 #include <persistent_data_middleware.hpp>
 #include <token_authorization_middleware.hpp>
 #include <fstream>
@@ -43,10 +39,13 @@
   return result;
 }
 
+// GetManagedObjects unpack type.  Observe that variant has only one bool type,
+// because we don't actually use the values it provides
 using ManagedObjectType = std::vector<std::pair<
-    dbus::object_path, boost::container::flat_map<
-                           std::string, boost::container::flat_map<
-                                            std::string, dbus::dbus_variant>>>>;
+    sdbusplus::message::object_path,
+    boost::container::flat_map<
+        std::string, boost::container::flat_map<
+                         std::string, sdbusplus::message::variant<bool>>>>>;
 
 template <typename... Middlewares>
 void request_routes(Crow<Middlewares...>& app) {
@@ -79,7 +78,8 @@
                 nlohmann::json member_array = nlohmann::json::array();
                 int user_index = 0;
                 for (auto& user : users) {
-                  const std::string& path = user.first.value;
+                  const std::string& path =
+                      static_cast<std::string>(user.first);
                   std::size_t last_index = path.rfind("/");
                   if (last_index == std::string::npos) {
                     last_index = 0;
@@ -94,8 +94,8 @@
               }
               res.end();
             },
-            {"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
+            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
       });
 
   CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
@@ -110,7 +110,8 @@
                 res.code = 500;
               } else {
                 for (auto& user : users) {
-                  const std::string& path = user.first.value;
+                  const std::string& path =
+                      static_cast<std::string>(user.first);
                   std::size_t last_index = path.rfind("/");
                   if (last_index == std::string::npos) {
                     last_index = 0;
@@ -145,8 +146,8 @@
               }
               res.end();
             },
-            {"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
-             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
+            "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
       });
 }
 }  // namespace redfish
diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp
index 59e9cca..07540d4 100644
--- a/include/token_authorization_middleware.hpp
+++ b/include/token_authorization_middleware.hpp
@@ -173,7 +173,8 @@
   bool is_on_whitelist(const crow::request& req) const {
     // it's allowed to GET root node without authentication
     if ("GET"_method == req.method) {
-      if (req.url == "/redfish/v1") {
+      CROW_LOG_DEBUG << "TESTING ROUTE " << req.url;
+      if (req.url == "/redfish/v1" || req.url == "/redfish/v1/") {
         return true;
       } else if (crow::webassets::routes.find(req.url) !=
                  crow::webassets::routes.end()) {
@@ -183,7 +184,8 @@
 
     // it's allowed to POST on session collection & login without authentication
     if ("POST"_method == req.method) {
-      if ((req.url == "/redfish/v1/SessionService/Sessions") ||
+      if ((req.url == "/redfish/v1/SessionService/Sessions" ||
+           req.url == "/redfish/v1/SessionService/Sessions/") ||
           (req.url == "/login") || (req.url == "/logout")) {
         return true;
       }