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);
+}