1e426b589SAndrew Geissler #include "systemd_target_signal.hpp"
2e426b589SAndrew Geissler 
355e96ac5SAndrew Geissler #include "utils.hpp"
455e96ac5SAndrew Geissler 
5234a3179SAndrew Geissler #include <phosphor-logging/elog-errors.hpp>
68ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp>
7e426b589SAndrew Geissler #include <sdbusplus/exception.hpp>
8234a3179SAndrew Geissler #include <sdbusplus/server/manager.hpp>
9234a3179SAndrew Geissler #include <xyz/openbmc_project/Common/error.hpp>
10*9a286db2SPatrick Williams #include <xyz/openbmc_project/Logging/Create/client.hpp>
11*9a286db2SPatrick Williams #include <xyz/openbmc_project/Logging/Entry/client.hpp>
12234a3179SAndrew Geissler 
13234a3179SAndrew Geissler namespace phosphor
14234a3179SAndrew Geissler {
15234a3179SAndrew Geissler namespace state
16234a3179SAndrew Geissler {
17234a3179SAndrew Geissler namespace manager
18234a3179SAndrew Geissler {
19234a3179SAndrew Geissler 
20234a3179SAndrew Geissler using phosphor::logging::elog;
218ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
228ffdb269SAndrew Geissler 
23234a3179SAndrew Geissler using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
24234a3179SAndrew Geissler 
25*9a286db2SPatrick Williams using LoggingCreate =
26*9a286db2SPatrick Williams     sdbusplus::client::xyz::openbmc_project::logging::Create<>;
27*9a286db2SPatrick Williams using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
28*9a286db2SPatrick Williams 
startBmcQuiesceTarget()2973d2ac96SAndrew Geissler void SystemdTargetLogging::startBmcQuiesceTarget()
3073d2ac96SAndrew Geissler {
3173d2ac96SAndrew Geissler     auto method = this->bus.new_method_call(
3273d2ac96SAndrew Geissler         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
3373d2ac96SAndrew Geissler         "org.freedesktop.systemd1.Manager", "StartUnit");
3473d2ac96SAndrew Geissler 
3573d2ac96SAndrew Geissler     // TODO: Enhance when needed to support multiple-bmc instance systems
3673d2ac96SAndrew Geissler     method.append("obmc-bmc-service-quiesce@0.target");
3773d2ac96SAndrew Geissler     method.append("replace");
3873d2ac96SAndrew Geissler     try
3973d2ac96SAndrew Geissler     {
4073d2ac96SAndrew Geissler         this->bus.call_noreply(method);
4173d2ac96SAndrew Geissler     }
42f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
4373d2ac96SAndrew Geissler     {
4473d2ac96SAndrew Geissler         error("Failed to start BMC quiesce target, exception:{ERROR}", "ERROR",
4573d2ac96SAndrew Geissler               e);
4673d2ac96SAndrew Geissler         // just continue, this is error path anyway so we're just doing what
4773d2ac96SAndrew Geissler         // we can
4873d2ac96SAndrew Geissler     }
4973d2ac96SAndrew Geissler 
5073d2ac96SAndrew Geissler     return;
5173d2ac96SAndrew Geissler }
5273d2ac96SAndrew Geissler 
logError(const std::string & errorLog,const std::string & result,const std::string & unit)538ffdb269SAndrew Geissler void SystemdTargetLogging::logError(const std::string& errorLog,
54e6841034SAndrew Geissler                                     const std::string& result,
55e6841034SAndrew Geissler                                     const std::string& unit)
56234a3179SAndrew Geissler {
57*9a286db2SPatrick Williams     auto method = this->bus.new_method_call(LoggingCreate::default_service,
58*9a286db2SPatrick Williams                                             LoggingCreate::instance_path,
59*9a286db2SPatrick Williams                                             LoggingCreate::interface, "Create");
60234a3179SAndrew Geissler     // Signature is ssa{ss}
61*9a286db2SPatrick Williams     method.append(
62*9a286db2SPatrick Williams         errorLog, LoggingEntry::Level::Critical,
63*9a286db2SPatrick Williams         std::array<std::pair<std::string, std::string>, 2>(
64e6841034SAndrew Geissler             {std::pair<std::string, std::string>({"SYSTEMD_RESULT", result}),
65e6841034SAndrew Geissler              std::pair<std::string, std::string>({"SYSTEMD_UNIT", unit})}));
66234a3179SAndrew Geissler     try
67234a3179SAndrew Geissler     {
68234a3179SAndrew Geissler         this->bus.call_noreply(method);
69234a3179SAndrew Geissler     }
70f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
71234a3179SAndrew Geissler     {
728ffdb269SAndrew Geissler         error("Failed to create systemd target error, error:{ERROR_MSG}, "
738ffdb269SAndrew Geissler               "result:{RESULT}, exception:{ERROR}",
748ffdb269SAndrew Geissler               "ERROR_MSG", errorLog, "RESULT", result, "ERROR", e);
75234a3179SAndrew Geissler     }
76234a3179SAndrew Geissler }
77234a3179SAndrew Geissler 
processError(const std::string & unit,const std::string & result)78f3870c62SAndrew Geissler const std::string SystemdTargetLogging::processError(const std::string& unit,
79234a3179SAndrew Geissler                                                      const std::string& result)
80234a3179SAndrew Geissler {
81234a3179SAndrew Geissler     auto targetEntry = this->targetData.find(unit);
82234a3179SAndrew Geissler     if (targetEntry != this->targetData.end())
83234a3179SAndrew Geissler     {
84234a3179SAndrew Geissler         // Check if its result matches any of our monitored errors
85234a3179SAndrew Geissler         if (std::find(targetEntry->second.errorsToMonitor.begin(),
86234a3179SAndrew Geissler                       targetEntry->second.errorsToMonitor.end(),
87234a3179SAndrew Geissler                       result) != targetEntry->second.errorsToMonitor.end())
88234a3179SAndrew Geissler         {
89ad65b2d6SAndrew Geissler             info(
90ad65b2d6SAndrew Geissler                 "Monitored systemd unit has hit an error, unit:{UNIT}, result:{RESULT}",
918ffdb269SAndrew Geissler                 "UNIT", unit, "RESULT", result);
92d59006aeSAndrew Geissler 
93d59006aeSAndrew Geissler             // Generate a BMC dump when a monitored target fails
9455e96ac5SAndrew Geissler             utils::createBmcDump(this->bus);
95f3870c62SAndrew Geissler             return (targetEntry->second.errorToLog);
96234a3179SAndrew Geissler         }
97234a3179SAndrew Geissler     }
98f3870c62SAndrew Geissler 
99f3870c62SAndrew Geissler     // Check if it's in our list of services to monitor
100f3870c62SAndrew Geissler     if (std::find(this->serviceData.begin(), this->serviceData.end(), unit) !=
101f3870c62SAndrew Geissler         this->serviceData.end())
102f3870c62SAndrew Geissler     {
103f3870c62SAndrew Geissler         if (result == "failed")
104f3870c62SAndrew Geissler         {
105f3870c62SAndrew Geissler             info(
106f3870c62SAndrew Geissler                 "Monitored systemd service has hit an error, unit:{UNIT}, result:{RESULT}",
107f3870c62SAndrew Geissler                 "UNIT", unit, "RESULT", result);
10821d305e2SAndrew Geissler 
10921d305e2SAndrew Geissler             // Generate a BMC dump when a critical service fails
11055e96ac5SAndrew Geissler             utils::createBmcDump(this->bus);
11173d2ac96SAndrew Geissler             // Enter BMC Quiesce when a critical service fails
11273d2ac96SAndrew Geissler             startBmcQuiesceTarget();
113f3870c62SAndrew Geissler             return (std::string{
114f3870c62SAndrew Geissler                 "xyz.openbmc_project.State.Error.CriticalServiceFailure"});
115f3870c62SAndrew Geissler         }
116f3870c62SAndrew Geissler     }
117f3870c62SAndrew Geissler 
118f3870c62SAndrew Geissler     return (std::string{});
119234a3179SAndrew Geissler }
120234a3179SAndrew Geissler 
systemdUnitChange(sdbusplus::message_t & msg)121f053e6feSPatrick Williams void SystemdTargetLogging::systemdUnitChange(sdbusplus::message_t& msg)
122234a3179SAndrew Geissler {
123234a3179SAndrew Geissler     uint32_t id;
124234a3179SAndrew Geissler     sdbusplus::message::object_path objPath;
125234a3179SAndrew Geissler     std::string unit{};
126234a3179SAndrew Geissler     std::string result{};
127234a3179SAndrew Geissler 
128234a3179SAndrew Geissler     msg.read(id, objPath, unit, result);
129234a3179SAndrew Geissler 
130234a3179SAndrew Geissler     // In most cases it will just be success, in which case just return
131234a3179SAndrew Geissler     if (result != "done")
132234a3179SAndrew Geissler     {
133f3870c62SAndrew Geissler         const std::string error = processError(unit, result);
134234a3179SAndrew Geissler 
135234a3179SAndrew Geissler         // If this is a monitored error then log it
136f3870c62SAndrew Geissler         if (!error.empty())
137234a3179SAndrew Geissler         {
138e6841034SAndrew Geissler             logError(error, result, unit);
139234a3179SAndrew Geissler         }
140234a3179SAndrew Geissler     }
141234a3179SAndrew Geissler     return;
142234a3179SAndrew Geissler }
143234a3179SAndrew Geissler 
processNameChangeSignal(sdbusplus::message_t & msg)144f053e6feSPatrick Williams void SystemdTargetLogging::processNameChangeSignal(sdbusplus::message_t& msg)
14538605ee2SAndrew Geissler {
14638605ee2SAndrew Geissler     std::string name;      // well-known
14738605ee2SAndrew Geissler     std::string old_owner; // unique-name
14838605ee2SAndrew Geissler     std::string new_owner; // unique-name
14938605ee2SAndrew Geissler 
15038605ee2SAndrew Geissler     msg.read(name, old_owner, new_owner);
15138605ee2SAndrew Geissler 
15238605ee2SAndrew Geissler     // Looking for systemd to be on dbus so we can call it
15338605ee2SAndrew Geissler     if (name == "org.freedesktop.systemd1")
15438605ee2SAndrew Geissler     {
1558ffdb269SAndrew Geissler         info("org.freedesktop.systemd1 is now on dbus");
15638605ee2SAndrew Geissler         subscribeToSystemdSignals();
15738605ee2SAndrew Geissler     }
15838605ee2SAndrew Geissler     return;
15938605ee2SAndrew Geissler }
16038605ee2SAndrew Geissler 
subscribeToSystemdSignals()161234a3179SAndrew Geissler void SystemdTargetLogging::subscribeToSystemdSignals()
162234a3179SAndrew Geissler {
163234a3179SAndrew Geissler     auto method = this->bus.new_method_call(
164234a3179SAndrew Geissler         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
165234a3179SAndrew Geissler         "org.freedesktop.systemd1.Manager", "Subscribe");
166234a3179SAndrew Geissler 
167234a3179SAndrew Geissler     try
168234a3179SAndrew Geissler     {
169234a3179SAndrew Geissler         this->bus.call(method);
170234a3179SAndrew Geissler     }
171f053e6feSPatrick Williams     catch (const sdbusplus::exception_t& e)
172234a3179SAndrew Geissler     {
17338605ee2SAndrew Geissler         // If error indicates systemd is not on dbus yet then do nothing.
17438605ee2SAndrew Geissler         // The systemdNameChangeSignals callback will detect when it is on
17538605ee2SAndrew Geissler         // dbus and then call this function again
17638605ee2SAndrew Geissler         const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown");
17738605ee2SAndrew Geissler         if (noDbus == e.name())
17838605ee2SAndrew Geissler         {
1798ffdb269SAndrew Geissler             info("org.freedesktop.systemd1 not on dbus yet");
18038605ee2SAndrew Geissler         }
18138605ee2SAndrew Geissler         else
18238605ee2SAndrew Geissler         {
1838ffdb269SAndrew Geissler             error("Failed to subscribe to systemd signals: {ERROR}", "ERROR",
1848ffdb269SAndrew Geissler                   e);
185234a3179SAndrew Geissler             elog<InternalFailure>();
186234a3179SAndrew Geissler         }
18738605ee2SAndrew Geissler         return;
18838605ee2SAndrew Geissler     }
18938605ee2SAndrew Geissler 
19038605ee2SAndrew Geissler     // Call destructor on match callback since application is now subscribed to
19138605ee2SAndrew Geissler     // systemd signals
19238605ee2SAndrew Geissler     this->systemdNameOwnedChangedSignal.~match();
193234a3179SAndrew Geissler 
194234a3179SAndrew Geissler     return;
195234a3179SAndrew Geissler }
196234a3179SAndrew Geissler 
197234a3179SAndrew Geissler } // namespace manager
198234a3179SAndrew Geissler } // namespace state
199234a3179SAndrew Geissler } // namespace phosphor
200