Support IPMI Sync RTC time to BMC command

Implement the IPMI command to sync RTC time to BMC as follow:
- In the NTP mode, does not set the system time
- In the NTP mode is disabled, set the system time using hwclock

Tested:
1. Check the NTP mode is active
   # timedatectl | grep "NTP service"
     NTP service: active
2. Change time on Host
   # date --set="20190601 17:30"
3. Sync the system time to RTC on Host
   # hwclock --systohc
4. Run ipmi command to sync RTC to BMC
   # ipmitool raw 0x3c 0xf9
   Unable to send RAW command (channel=0x0 netfn=0x3c lun=0x0 cmd =
   0xf9 rsp=0x1): Unknown (0x01)
5. Check the system time via IPMI/Webui or #date command in bmc console.
   # date
   # ipmitool sel time get
   The system time does not change.
6. Disable NTP service and repeat test cases #4, #5
   # systemctl stop systemd-timesyncd.service
   # ipmitool raw 0x3c 0xf9
   Make sure the system time can be change.

Signed-off-by: Hieu Huynh <hieuh@os.amperecomputing.com>
Change-Id: I3c6fbc3314d4a75814d5e05022ba8d3a720e7c1e
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
new file mode 100644
index 0000000..f55c16e
--- /dev/null
+++ b/src/oemcommands.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018-2021 Ampere Computing LLC
+ *
+ * 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 <ipmid/api.hpp>
+#include <ipmid/types.hpp>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include "oemcommands.hpp"
+#include <cstdlib>
+
+using namespace phosphor::logging;
+
+static inline auto response(uint8_t cc)
+{
+    return std::make_tuple(cc, std::nullopt);
+}
+
+static inline auto responseFailure()
+{
+    return response(responseFail);
+}
+
+/** @brief execute a command and get the output of the command
+ *  @param[in] the command
+ *  @returns output of the command
+ */
+std::string exec(const char* cmd) {
+    char buffer[128];
+    std::string result = "";
+    /* Pipe stream from a command */
+    FILE* pipe = popen(cmd, "r");
+    if (!pipe) throw std::runtime_error("popen() failed!");
+    try {
+        /* Reads a line from the specified stream and stores it */
+        while (fgets(buffer, sizeof buffer, pipe) != NULL) {
+            result += buffer;
+        }
+    } catch (...) {
+        pclose(pipe);
+        throw;
+    }
+    /* Close a stream that was opened by popen() */
+    pclose(pipe);
+    return result;
+}
+
+/** @brief implements sync RTC time to BMC commands
+ *  @param - None
+ *  @returns IPMI completion code.
+ */
+ipmi::RspType<> ipmiSyncRTCTimeToBMC()
+{
+    std::string cmd;
+    std::string cmdOutput;
+    try
+    {
+        /* Check the mode of NTP in the system, set the system time in case the
+         * NTP mode is disabled.
+         */
+        cmd = "systemctl status systemd-timesyncd.service | grep inactive";
+        cmdOutput = exec(cmd.c_str());
+        if (cmdOutput.empty())
+        {
+            log<level::INFO>("Can not set system time while the mode is NTP");
+            return responseFailure();
+        }
+        else
+        {
+            /* Sync time from RTC to BMC using hwclock */
+            system("hwclock --hctosys");
+        }
+    }
+    catch(const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        return responseFailure();
+    }
+
+    return ipmi::responseSuccess();
+}
+
+void registerOEMFunctions() __attribute__((constructor));
+void registerOEMFunctions()
+{
+    ipmi::registerHandler(ipmi::prioOemBase, ipmi::ampere::netFnAmpere,
+                          ipmi::general::cmdSyncRtcTime,
+                          ipmi::Privilege::User, ipmiSyncRTCTimeToBMC);
+}