Implement Managers interface for redfish

This commit implements the manager interface schema for redfish, as
well as the manager collection.

Change-Id: Ide6d6b500b8758dc693d65bad5f263baac9f529f
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp
index e106079..009ae61 100644
--- a/include/redfish_v1.hpp
+++ b/include/redfish_v1.hpp
@@ -1,7 +1,5 @@
 #pragma once
 
-#include <crow/app.h>
-
 #include <dbus/connection.hpp>
 #include <dbus/endpoint.hpp>
 #include <dbus/filter.hpp>
@@ -12,7 +10,8 @@
 #include <fstream>
 #include <streambuf>
 #include <string>
-
+#include <crow/app.h>
+#include <boost/algorithm/string.hpp>
 namespace crow {
 namespace redfish {
 
@@ -32,6 +31,18 @@
   }
 }
 
+std::string execute_process(const char* cmd) {
+  std::array<char, 128> buffer;
+  std::string result;
+  std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
+  if (!pipe) throw std::runtime_error("popen() failed!");
+  while (!feof(pipe.get())) {
+    if (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
+      result += buffer.data();
+  }
+  return result;
+}
+
 template <typename... Middlewares>
 void request_routes(Crow<Middlewares...>& app) {
   CROW_ROUTE(app, "/redfish/")
@@ -336,6 +347,118 @@
         }
         res.end();
       });
+
+  CROW_ROUTE(app, "/redfish/v1/Managers/")
+      .methods("GET"_method)(
+          [&](const crow::request& req, crow::response& res) {
+            res.json_value = {
+                {"@odata.context",
+                 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"},
+                {"@odata.id", "/redfish/v1/Managers"},
+                {"@odata.type", "#ManagerCollection.ManagerCollection"},
+                {"Name", "Manager Collection"},
+                {"Members@odata.count", 1},
+                {"Members", {{{"@odata.id", "/redfish/v1/Managers/openbmc"}}}}};
+            res.end();
+          });
+
+  CROW_ROUTE(app, "/redfish/v1/Managers/openbmc/")
+      .methods(
+          "GET"_method)([&](const crow::request& req, crow::response& res) {
+        time_t t = time(NULL);
+        tm* mytime = std::localtime(&t);
+        if (mytime == nullptr) {
+          res.code = 500;
+          res.end();
+          return;
+        }
+        std::array<char, 100> time_buffer;
+        std::size_t len = std::strftime(time_buffer.data(), time_buffer.size(),
+                                        "%FT%TZ", mytime);
+        if (len == 0) {
+          res.code = 500;
+          res.end();
+          return;
+        }
+        res.json_value = {
+            {"@odata.context", "/redfish/v1/$metadata#Manager.Manager"},
+            {"@odata.id", "/redfish/v1/Managers/openbmc"},
+            {"@odata.type", "#Manager.v1_3_0.Manager"},
+            {"Id", "openbmc"},
+            {"Name", "OpenBmc Manager"},
+            {"Description", "Baseboard Management Controller"},
+            {"UUID",
+             app.template get_middleware<PersistentData::Middleware>()
+                 .system_uuid},
+            {"Model", "OpenBmc"},  // TODO(ed), get model
+            {"DateTime", time_buffer.data()},
+            {"Status",
+             {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
+            {"FirmwareVersion", "1234456789"},  // TODO(ed) get fwversion
+            {"PowerState", "On"}};
+        get_redfish_sub_routes(app, "/redfish/v1/Managers/openbmc/",
+                               res.json_value);
+        res.end();
+      });
+
+  CROW_ROUTE(app, "/redfish/v1/Managers/NetworkProtocol/")
+      .methods(
+          "GET"_method)([&](const crow::request& req, crow::response& res) {
+        std::array<char, HOST_NAME_MAX> hostname;
+        if (gethostname(hostname.data(), hostname.size()) != 0) {
+          res.code = 500;
+          res.end();
+          return;
+        }
+        res.json_value = {
+            {"@odata.context",
+             "/redfish/v1/"
+             "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"},
+            {"@odata.id", "/redfish/v1/Managers/BMC/NetworkProtocol"},
+            {"@odata.type",
+             "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"},
+            {"Id", "NetworkProtocol"},
+            {"Name", "Manager Network Protocol"},
+            {"Description", "Manager Network Service"},
+            {"Status",
+             {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
+            {"HostName", hostname.data()}};  // TODO(ed) get hostname
+        std::string netstat_out = execute_process("netstat -tuln");
+
+        std::map<int, const char*> service_types{{22, "SSH"},
+                                                 {443, "HTTPS"},
+                                                 {1900, "SSDP"},
+                                                 {623, "IPMI"},
+                                                 {427, "SLP"}};
+
+        std::vector<std::string> lines;
+        boost::split(lines, netstat_out, boost::is_any_of("\n"));
+        auto lines_it = lines.begin();
+        lines_it++;  // skip the netstat header
+        lines_it++;
+        while (lines_it != lines.end()) {
+          std::vector<std::string> columns;
+          boost::split(columns, *lines_it, boost::is_any_of("\t "),
+                       boost::token_compress_on);
+          if (columns.size() >= 5) {
+            std::size_t found = columns[3].find_last_of(":");
+            if (found != std::string::npos) {
+              std::string port_str = columns[3].substr(found + 1);
+              int port = std::stoi(port_str.c_str());
+              auto type_it = service_types.find(port);
+              if (type_it != service_types.end()) {
+                type_it->second;
+                res.json_value[type_it->second] = {{"ProtocolEnabled", true},
+                                                   {"Port", port}};
+              }
+            }
+          }
+          lines_it++;
+        }
+
+        get_redfish_sub_routes(app, "/redfish/v1/", res.json_value);
+        res.end();
+      });
 }
 }  // namespace redfish
 }  // namespace crow