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