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