1 #include "systemd_target_signal.hpp" 2 3 #include <phosphor-logging/elog-errors.hpp> 4 #include <phosphor-logging/lg2.hpp> 5 #include <sdbusplus/exception.hpp> 6 #include <sdbusplus/server/manager.hpp> 7 #include <sdeventplus/event.hpp> 8 #include <xyz/openbmc_project/Common/error.hpp> 9 10 namespace phosphor 11 { 12 namespace state 13 { 14 namespace manager 15 { 16 17 using phosphor::logging::elog; 18 PHOSPHOR_LOG2_USING; 19 20 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 21 22 void SystemdTargetLogging::createBmcDump() 23 { 24 auto method = this->bus.new_method_call( 25 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc", 26 "xyz.openbmc_project.Dump.Create", "CreateDump"); 27 method.append( 28 std::vector< 29 std::pair<std::string, std::variant<std::string, uint64_t>>>()); 30 try 31 { 32 this->bus.call_noreply(method); 33 } 34 catch (const sdbusplus::exception::exception& e) 35 { 36 error("Failed to create BMC dump, exception:{ERROR}", "ERROR", e); 37 // just continue, this is error path anyway so we're just collecting 38 // what we can 39 } 40 } 41 42 void SystemdTargetLogging::startBmcQuiesceTarget() 43 { 44 auto method = this->bus.new_method_call( 45 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 46 "org.freedesktop.systemd1.Manager", "StartUnit"); 47 48 // TODO: Enhance when needed to support multiple-bmc instance systems 49 method.append("obmc-bmc-service-quiesce@0.target"); 50 method.append("replace"); 51 try 52 { 53 this->bus.call_noreply(method); 54 } 55 catch (const sdbusplus::exception::exception& e) 56 { 57 error("Failed to start BMC quiesce target, exception:{ERROR}", "ERROR", 58 e); 59 // just continue, this is error path anyway so we're just doing what 60 // we can 61 } 62 63 return; 64 } 65 66 void SystemdTargetLogging::logError(const std::string& errorLog, 67 const std::string& result, 68 const std::string& unit) 69 { 70 auto method = this->bus.new_method_call( 71 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 72 "xyz.openbmc_project.Logging.Create", "Create"); 73 // Signature is ssa{ss} 74 method.append(errorLog); 75 method.append("xyz.openbmc_project.Logging.Entry.Level.Critical"); 76 method.append(std::array<std::pair<std::string, std::string>, 2>( 77 {std::pair<std::string, std::string>({"SYSTEMD_RESULT", result}), 78 std::pair<std::string, std::string>({"SYSTEMD_UNIT", unit})})); 79 try 80 { 81 this->bus.call_noreply(method); 82 } 83 catch (const sdbusplus::exception::exception& e) 84 { 85 error("Failed to create systemd target error, error:{ERROR_MSG}, " 86 "result:{RESULT}, exception:{ERROR}", 87 "ERROR_MSG", errorLog, "RESULT", result, "ERROR", e); 88 } 89 } 90 91 const std::string SystemdTargetLogging::processError(const std::string& unit, 92 const std::string& result) 93 { 94 auto targetEntry = this->targetData.find(unit); 95 if (targetEntry != this->targetData.end()) 96 { 97 // Check if its result matches any of our monitored errors 98 if (std::find(targetEntry->second.errorsToMonitor.begin(), 99 targetEntry->second.errorsToMonitor.end(), 100 result) != targetEntry->second.errorsToMonitor.end()) 101 { 102 info( 103 "Monitored systemd unit has hit an error, unit:{UNIT}, result:{RESULT}", 104 "UNIT", unit, "RESULT", result); 105 106 // Generate a BMC dump when a monitored target fails 107 createBmcDump(); 108 return (targetEntry->second.errorToLog); 109 } 110 } 111 112 // Check if it's in our list of services to monitor 113 if (std::find(this->serviceData.begin(), this->serviceData.end(), unit) != 114 this->serviceData.end()) 115 { 116 if (result == "failed") 117 { 118 info( 119 "Monitored systemd service has hit an error, unit:{UNIT}, result:{RESULT}", 120 "UNIT", unit, "RESULT", result); 121 122 // Generate a BMC dump when a critical service fails 123 createBmcDump(); 124 // Enter BMC Quiesce when a critical service fails 125 startBmcQuiesceTarget(); 126 return (std::string{ 127 "xyz.openbmc_project.State.Error.CriticalServiceFailure"}); 128 } 129 } 130 131 return (std::string{}); 132 } 133 134 void SystemdTargetLogging::systemdUnitChange(sdbusplus::message::message& msg) 135 { 136 uint32_t id; 137 sdbusplus::message::object_path objPath; 138 std::string unit{}; 139 std::string result{}; 140 141 msg.read(id, objPath, unit, result); 142 143 // In most cases it will just be success, in which case just return 144 if (result != "done") 145 { 146 const std::string error = processError(unit, result); 147 148 // If this is a monitored error then log it 149 if (!error.empty()) 150 { 151 logError(error, result, unit); 152 } 153 } 154 return; 155 } 156 157 void SystemdTargetLogging::processNameChangeSignal( 158 sdbusplus::message::message& msg) 159 { 160 std::string name; // well-known 161 std::string old_owner; // unique-name 162 std::string new_owner; // unique-name 163 164 msg.read(name, old_owner, new_owner); 165 166 // Looking for systemd to be on dbus so we can call it 167 if (name == "org.freedesktop.systemd1") 168 { 169 info("org.freedesktop.systemd1 is now on dbus"); 170 subscribeToSystemdSignals(); 171 } 172 return; 173 } 174 175 void SystemdTargetLogging::subscribeToSystemdSignals() 176 { 177 auto method = this->bus.new_method_call( 178 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 179 "org.freedesktop.systemd1.Manager", "Subscribe"); 180 181 try 182 { 183 this->bus.call(method); 184 } 185 catch (const sdbusplus::exception::exception& e) 186 { 187 // If error indicates systemd is not on dbus yet then do nothing. 188 // The systemdNameChangeSignals callback will detect when it is on 189 // dbus and then call this function again 190 const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown"); 191 if (noDbus == e.name()) 192 { 193 info("org.freedesktop.systemd1 not on dbus yet"); 194 } 195 else 196 { 197 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", 198 e); 199 elog<InternalFailure>(); 200 } 201 return; 202 } 203 204 // Call destructor on match callback since application is now subscribed to 205 // systemd signals 206 this->systemdNameOwnedChangedSignal.~match(); 207 208 return; 209 } 210 211 } // namespace manager 212 } // namespace state 213 } // namespace phosphor 214