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