Implement Manager/ServiceRootUptime

This property was added in Redfish 2022.2 to denote how long this
service has been up and available.

This implementation opts to go to systemd to get the bmcweb service
uptime rather than track it through internal process state, given that
systemd already has an API that tracks the bmcweb uptime, and bmcweb
attempts to keep as little state as possible.  Given that we already
have helper functions that give durations in milliseconds precision,
this patchset opts to keep the millisecond granularity, rather than
dropping to microsecond precision of the systemd API.  There are no use
cases that would require microsecond precision, so this patchset opts
for lower complexity.

Tested:
Redfish service validator passes.

GET /redfish/v1/Managers/bmc
Returns a ServiceRootUptime property.  Value matches systemctl status
bmcweb.
systemctl restart bmcweb, causes counter to reset.

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Iae7e805f3f7f5f26745476eaeaecb63bda16a957
diff --git a/Redfish.md b/Redfish.md
index ba58c55..3739581 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -404,6 +404,7 @@
 - Links/ManagerForServers
 - Links/ManagerForServers@odata.count
 - Links/ManagerInChassis
+- Links/ServiceRootUptime
 - Links/SoftwareImages
 - Links/SoftwareImages@odata.count
 - LogServices
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 376f70c..0be1354 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -33,6 +33,7 @@
 
 #include <algorithm>
 #include <array>
+#include <chrono>
 #include <cstdint>
 #include <memory>
 #include <sstream>
@@ -1769,6 +1770,47 @@
         });
 }
 
+inline void
+    afterGetManagerStartTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+                             const boost::system::error_code& ec,
+                             uint64_t bmcwebResetTime)
+{
+    if (ec)
+    {
+        return;
+    }
+    using std::chrono::steady_clock;
+    std::chrono::microseconds usReset{bmcwebResetTime};
+    steady_clock::time_point resetTime{usReset};
+
+    steady_clock::time_point now = steady_clock::now();
+
+    steady_clock::duration run = now - resetTime;
+
+    if (run < steady_clock::duration::zero())
+    {
+        BMCWEB_LOG_CRITICAL << "Uptime was negative????";
+        messages::internalError(aResp->res);
+        return;
+    }
+
+    using Milli = std::chrono::milliseconds;
+    Milli uptimeMs = std::chrono::duration_cast<Milli>(run);
+
+    using redfish::time_utils::toDurationString;
+    aResp->res.jsonValue["ServiceRootUptime"] = toDurationString(uptimeMs);
+}
+
+inline void
+    managerGetServiceRootUptime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
+{
+    sdbusplus::asio::getProperty<uint64_t>(
+        *crow::connections::systemBus, "org.freedesktop.systemd1",
+        "/org/freedesktop/systemd1/unit/bmcweb_2eservice",
+        "org.freedesktop.systemd1.Unit", "ActiveEnterTimestampMonotonic",
+        std::bind_front(afterGetManagerStartTime, aResp));
+}
+
 /**
  * @brief Set the running firmware image
  *
@@ -2026,6 +2068,8 @@
 
         managerGetLastResetTime(asyncResp);
 
+        managerGetServiceRootUptime(asyncResp);
+
         // ManagerDiagnosticData is added for all BMCs.
         nlohmann::json& managerDiagnosticData =
             asyncResp->res.jsonValue["ManagerDiagnosticData"];