1 #include "config.h" 2 3 #include "utils.hpp" 4 5 #include <gpiod.h> 6 7 #include <phosphor-logging/lg2.hpp> 8 #include <xyz/openbmc_project/Dump/Create/client.hpp> 9 #include <xyz/openbmc_project/Logging/Create/client.hpp> 10 #include <xyz/openbmc_project/ObjectMapper/client.hpp> 11 #include <xyz/openbmc_project/State/BMC/client.hpp> 12 13 #include <chrono> 14 #include <filesystem> 15 #include <format> 16 17 namespace phosphor 18 { 19 namespace state 20 { 21 namespace manager 22 { 23 namespace utils 24 { 25 26 using namespace std::literals::chrono_literals; 27 28 PHOSPHOR_LOG2_USING; 29 30 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 31 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 32 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 33 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 34 35 using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>; 36 37 void subscribeToSystemdSignals(sdbusplus::bus_t& bus) 38 { 39 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 40 SYSTEMD_INTERFACE, "Subscribe"); 41 42 try 43 { 44 // On OpenBMC based systems, systemd has had a few situations where it 45 // has been unable to respond to this call within the default d-bus 46 // timeout of 25 seconds. This is due to the large amount of work being 47 // done by systemd during OpenBMC startup. Set the timeout for this call 48 // to 60 seconds (worst case seen was around 30s so double it). 49 bus.call(method, 60s); 50 } 51 catch (const sdbusplus::exception_t& e) 52 { 53 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e); 54 throw std::runtime_error("Unable to subscribe to systemd signals"); 55 } 56 return; 57 } 58 59 std::string getService(sdbusplus::bus_t& bus, std::string path, 60 std::string interface) 61 { 62 auto mapper = bus.new_method_call(ObjectMapper::default_service, 63 ObjectMapper::instance_path, 64 ObjectMapper::interface, "GetObject"); 65 66 mapper.append(path, std::vector<std::string>({interface})); 67 68 std::vector<std::pair<std::string, std::vector<std::string>>> 69 mapperResponse; 70 71 try 72 { 73 auto mapperResponseMsg = bus.call(mapper); 74 75 mapperResponseMsg.read(mapperResponse); 76 if (mapperResponse.empty()) 77 { 78 error( 79 "Error no matching service with path {PATH} and interface {INTERFACE}", 80 "PATH", path, "INTERFACE", interface); 81 throw std::runtime_error("Error no matching service"); 82 } 83 } 84 catch (const sdbusplus::exception_t& e) 85 { 86 error("Error in mapper call with path {PATH}, interface " 87 "{INTERFACE}, and exception {ERROR}", 88 "PATH", path, "INTERFACE", interface, "ERROR", e); 89 throw; 90 } 91 92 return mapperResponse.begin()->first; 93 } 94 95 std::string getProperty(sdbusplus::bus_t& bus, const std::string& path, 96 const std::string& interface, 97 const std::string& propertyName) 98 { 99 std::variant<std::string> property; 100 std::string service = getService(bus, path, interface); 101 102 auto method = bus.new_method_call(service.c_str(), path.c_str(), 103 PROPERTY_INTERFACE, "Get"); 104 105 method.append(interface, propertyName); 106 107 try 108 { 109 auto reply = bus.call(method); 110 reply.read(property); 111 } 112 catch (const sdbusplus::exception_t& e) 113 { 114 error("Error in property Get, error {ERROR}, property {PROPERTY}", 115 "ERROR", e, "PROPERTY", propertyName); 116 throw; 117 } 118 119 if (std::get<std::string>(property).empty()) 120 { 121 error("Error reading property response for {PROPERTY}", "PROPERTY", 122 propertyName); 123 throw std::runtime_error("Error reading property response"); 124 } 125 126 return std::get<std::string>(property); 127 } 128 129 void setProperty(sdbusplus::bus_t& bus, const std::string& path, 130 const std::string& interface, const std::string& property, 131 const std::string& value) 132 { 133 std::variant<std::string> variantValue = value; 134 std::string service = getService(bus, path, interface); 135 136 auto method = bus.new_method_call(service.c_str(), path.c_str(), 137 PROPERTY_INTERFACE, "Set"); 138 139 method.append(interface, property, variantValue); 140 bus.call_noreply(method); 141 142 return; 143 } 144 145 int getGpioValue(const std::string& gpioName) 146 { 147 int gpioval = -1; 148 gpiod_line* line = gpiod_line_find(gpioName.c_str()); 149 150 if (nullptr != line) 151 { 152 // take ownership of gpio 153 if (0 != gpiod_line_request_input(line, "state-manager")) 154 { 155 error("Failed request for {GPIO_NAME} GPIO", "GPIO_NAME", gpioName); 156 } 157 else 158 { 159 // get gpio value 160 gpioval = gpiod_line_get_value(line); 161 162 // release ownership of gpio 163 gpiod_line_close_chip(line); 164 } 165 } 166 return gpioval; 167 } 168 169 void createError( 170 sdbusplus::bus_t& bus, const std::string& errorMsg, 171 sdbusplus::server::xyz::openbmc_project::logging::Entry::Level errLevel, 172 std::map<std::string, std::string> additionalData) 173 { 174 try 175 { 176 // Always add the _PID on for some extra logging debug 177 additionalData.emplace("_PID", std::to_string(getpid())); 178 179 using LoggingCreate = 180 sdbusplus::client::xyz::openbmc_project::logging::Create<>; 181 182 auto method = bus.new_method_call(LoggingCreate::default_service, 183 LoggingCreate::instance_path, 184 LoggingCreate::interface, "Create"); 185 186 method.append(errorMsg, errLevel, additionalData); 187 auto resp = bus.call(method); 188 } 189 catch (const sdbusplus::exception_t& e) 190 { 191 error("sdbusplus D-Bus call exception, error {ERROR} trying to create " 192 "an error with {ERROR_MSG}", 193 "ERROR", e, "ERROR_MSG", errorMsg); 194 195 throw std::runtime_error( 196 "Error in invoking D-Bus logging create interface"); 197 } 198 catch (const std::exception& e) 199 { 200 error("D-bus call exception: {ERROR}", "ERROR", e); 201 throw e; 202 } 203 } 204 205 void createBmcDump(sdbusplus::bus_t& bus) 206 { 207 using DumpCreate = sdbusplus::client::xyz::openbmc_project::dump::Create<>; 208 auto dumpPath = 209 sdbusplus::message::object_path(DumpCreate::namespace_path::value) / 210 DumpCreate::namespace_path::bmc; 211 212 auto method = bus.new_method_call(DumpCreate::default_service, 213 dumpPath.str.c_str(), 214 DumpCreate::interface, "CreateDump"); 215 method.append( 216 std::vector< 217 std::pair<std::string, std::variant<std::string, uint64_t>>>()); 218 try 219 { 220 bus.call_noreply(method); 221 } 222 catch (const sdbusplus::exception_t& e) 223 { 224 error("Failed to create BMC dump, exception:{ERROR}", "ERROR", e); 225 // just continue, this is error path anyway so we're just collecting 226 // what we can 227 } 228 } 229 230 bool checkACLoss(size_t& chassisId) 231 { 232 std::string chassisLostPowerFileFmt = std::format(CHASSIS_LOST_POWER_FILE, 233 chassisId); 234 235 std::filesystem::path chassisPowerLossFile{chassisLostPowerFileFmt}; 236 if (std::filesystem::exists(chassisPowerLossFile)) 237 { 238 return true; 239 } 240 241 return false; 242 } 243 244 bool isBmcReady(sdbusplus::bus_t& bus) 245 { 246 using BMC = sdbusplus::client::xyz::openbmc_project::state::BMC<>; 247 auto bmcPath = sdbusplus::message::object_path(BMC::namespace_path::value) / 248 BMC::namespace_path::bmc; 249 250 auto bmcState = getProperty(bus, bmcPath.str.c_str(), BMC::interface, 251 "CurrentBMCState"); 252 253 if (sdbusplus::message::convert_from_string<BMC::BMCState>(bmcState) != 254 BMC::BMCState::Ready) 255 { 256 debug("BMC State is {BMC_STATE}", "BMC_STATE", bmcState); 257 return false; 258 } 259 return true; 260 } 261 262 bool waitBmcReady(sdbusplus::bus_t& bus, std::chrono::seconds timeout) 263 { 264 while (timeout.count() != 0) 265 { 266 timeout--; 267 if (isBmcReady(bus)) 268 { 269 return true; 270 } 271 std::this_thread::sleep_for(std::chrono::seconds(1)); 272 } 273 return false; 274 } 275 276 } // namespace utils 277 } // namespace manager 278 } // namespace state 279 } // namespace phosphor 280