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 return (targetEntry->second.errorToLog); 106 } 107 } 108 109 // Check if it's in our list of services to monitor 110 if (std::find(this->serviceData.begin(), this->serviceData.end(), unit) != 111 this->serviceData.end()) 112 { 113 if (result == "failed") 114 { 115 info( 116 "Monitored systemd service has hit an error, unit:{UNIT}, result:{RESULT}", 117 "UNIT", unit, "RESULT", result); 118 119 // Generate a BMC dump when a critical service fails 120 createBmcDump(); 121 // Enter BMC Quiesce when a critical service fails 122 startBmcQuiesceTarget(); 123 return (std::string{ 124 "xyz.openbmc_project.State.Error.CriticalServiceFailure"}); 125 } 126 } 127 128 return (std::string{}); 129 } 130 131 void SystemdTargetLogging::systemdUnitChange(sdbusplus::message::message& msg) 132 { 133 uint32_t id; 134 sdbusplus::message::object_path objPath; 135 std::string unit{}; 136 std::string result{}; 137 138 msg.read(id, objPath, unit, result); 139 140 // In most cases it will just be success, in which case just return 141 if (result != "done") 142 { 143 const std::string error = processError(unit, result); 144 145 // If this is a monitored error then log it 146 if (!error.empty()) 147 { 148 logError(error, result, unit); 149 } 150 } 151 return; 152 } 153 154 void SystemdTargetLogging::processNameChangeSignal( 155 sdbusplus::message::message& msg) 156 { 157 std::string name; // well-known 158 std::string old_owner; // unique-name 159 std::string new_owner; // unique-name 160 161 msg.read(name, old_owner, new_owner); 162 163 // Looking for systemd to be on dbus so we can call it 164 if (name == "org.freedesktop.systemd1") 165 { 166 info("org.freedesktop.systemd1 is now on dbus"); 167 subscribeToSystemdSignals(); 168 } 169 return; 170 } 171 172 void SystemdTargetLogging::subscribeToSystemdSignals() 173 { 174 auto method = this->bus.new_method_call( 175 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 176 "org.freedesktop.systemd1.Manager", "Subscribe"); 177 178 try 179 { 180 this->bus.call(method); 181 } 182 catch (const sdbusplus::exception::exception& e) 183 { 184 // If error indicates systemd is not on dbus yet then do nothing. 185 // The systemdNameChangeSignals callback will detect when it is on 186 // dbus and then call this function again 187 const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown"); 188 if (noDbus == e.name()) 189 { 190 info("org.freedesktop.systemd1 not on dbus yet"); 191 } 192 else 193 { 194 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", 195 e); 196 elog<InternalFailure>(); 197 } 198 return; 199 } 200 201 // Call destructor on match callback since application is now subscribed to 202 // systemd signals 203 this->systemdNameOwnedChangedSignal.~match(); 204 205 return; 206 } 207 208 } // namespace manager 209 } // namespace state 210 } // namespace phosphor 211