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