| /** | 
 |  * Copyright © 2016 IBM Corporation | 
 |  * | 
 |  * 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 "config.h" | 
 |  | 
 | #include "softoff.hpp" | 
 |  | 
 | #include <ipmid/utils.hpp> | 
 | #include <phosphor-logging/lg2.hpp> | 
 | #include <xyz/openbmc_project/Control/Host/server.hpp> | 
 |  | 
 | #include <chrono> | 
 | namespace phosphor | 
 | { | 
 | namespace ipmi | 
 | { | 
 |  | 
 | using namespace sdbusplus::server::xyz::openbmc_project::control; | 
 |  | 
 | void SoftPowerOff::sendHostShutDownCmd() | 
 | { | 
 |     auto ctrlHostPath = | 
 |         std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0'; | 
 |     auto host = ::ipmi::getService(this->bus, CONTROL_HOST_BUSNAME, | 
 |                                    ctrlHostPath.c_str()); | 
 |  | 
 |     auto method = bus.new_method_call(host.c_str(), ctrlHostPath.c_str(), | 
 |                                       CONTROL_HOST_BUSNAME, "Execute"); | 
 |  | 
 |     method.append(convertForMessage(Host::Command::SoftOff).c_str()); | 
 |     try | 
 |     { | 
 |         auto reply = bus.call(method); | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         lg2::error("Error in call to control host Execute: {ERROR}", "ERROR", | 
 |                    e); | 
 |         // TODO openbmc/openbmc#851 - Once available, throw returned error | 
 |         throw std::runtime_error("Error in call to control host Execute"); | 
 |     } | 
 | } | 
 |  | 
 | // Function called on host control signals | 
 | void SoftPowerOff::hostControlEvent(sdbusplus::message_t& msg) | 
 | { | 
 |     std::string cmdCompleted{}; | 
 |     std::string cmdStatus{}; | 
 |  | 
 |     msg.read(cmdCompleted, cmdStatus); | 
 |  | 
 |     lg2::debug( | 
 |         "Host control signal values, command: {COMMAND}, status:{STATUS}", | 
 |         "COMMAND", cmdCompleted, "STATUS", cmdStatus); | 
 |  | 
 |     if (Host::convertResultFromString(cmdStatus) == Host::Result::Success) | 
 |     { | 
 |         // Set our internal property indicating we got host attention | 
 |         sdbusplus::server::xyz::openbmc_project::ipmi::internal::SoftPowerOff:: | 
 |             responseReceived(HostResponse::SoftOffReceived); | 
 |  | 
 |         // Start timer for host shutdown | 
 |         using namespace std::chrono; | 
 |         auto time = duration_cast<microseconds>( | 
 |             seconds(IPMI_HOST_SHUTDOWN_COMPLETE_TIMEOUT_SECS)); | 
 |         auto r = startTimer(time); | 
 |         if (r < 0) | 
 |         { | 
 |             lg2::error( | 
 |                 "Failure to start Host shutdown wait timer, ERRNO: {ERRNO}", | 
 |                 "ERRNO", lg2::hex, -r); | 
 |         } | 
 |         else | 
 |         { | 
 |             lg2::info("Timer started waiting for host to shutdown, " | 
 |                       "TIMEOUT_IN_MSEC: {TIMEOUT_IN_MSEC}", | 
 |                       "TIMEOUT_IN_MSEC", | 
 |                       (duration_cast<milliseconds>( | 
 |                            seconds(IPMI_HOST_SHUTDOWN_COMPLETE_TIMEOUT_SECS))) | 
 |                           .count()); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         // An error on the initial attention is not considered an error, just | 
 |         // exit normally and allow remaining shutdown targets to run | 
 |         lg2::info("Timeout on host attention, continue with power down"); | 
 |         completed = true; | 
 |     } | 
 |     return; | 
 | } | 
 |  | 
 | // Starts a timer | 
 | int SoftPowerOff::startTimer(const std::chrono::microseconds& usec) | 
 | { | 
 |     return timer.start(usec); | 
 | } | 
 |  | 
 | // Host Response handler | 
 | auto SoftPowerOff::responseReceived(HostResponse response) -> HostResponse | 
 | { | 
 |     using namespace std::chrono; | 
 |  | 
 |     if (response == HostResponse::HostShutdown) | 
 |     { | 
 |         // Disable the timer since Host has quiesced and we are | 
 |         // done with soft power off part | 
 |         auto r = timer.stop(); | 
 |         if (r < 0) | 
 |         { | 
 |             lg2::error("Failure to STOP the timer, ERRNO: {ERRNO}", "ERRNO", | 
 |                        lg2::hex, -r); | 
 |         } | 
 |  | 
 |         // This marks the completion of soft power off sequence. | 
 |         completed = true; | 
 |     } | 
 |  | 
 |     return sdbusplus::server::xyz::openbmc_project::ipmi::internal:: | 
 |         SoftPowerOff::responseReceived(response); | 
 | } | 
 |  | 
 | } // namespace ipmi | 
 | } // namespace phosphor |