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