1e426b589SAndrew Geissler #include "systemd_target_signal.hpp"
2e426b589SAndrew Geissler 
3234a3179SAndrew Geissler #include <phosphor-logging/elog-errors.hpp>
48ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp>
5e426b589SAndrew Geissler #include <sdbusplus/exception.hpp>
6234a3179SAndrew Geissler #include <sdbusplus/server/manager.hpp>
7234a3179SAndrew Geissler #include <sdeventplus/event.hpp>
8234a3179SAndrew Geissler #include <xyz/openbmc_project/Common/error.hpp>
9234a3179SAndrew Geissler 
10234a3179SAndrew Geissler namespace phosphor
11234a3179SAndrew Geissler {
12234a3179SAndrew Geissler namespace state
13234a3179SAndrew Geissler {
14234a3179SAndrew Geissler namespace manager
15234a3179SAndrew Geissler {
16234a3179SAndrew Geissler 
17234a3179SAndrew Geissler using phosphor::logging::elog;
188ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING;
198ffdb269SAndrew Geissler 
20234a3179SAndrew Geissler using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21234a3179SAndrew Geissler 
22*21d305e2SAndrew Geissler void SystemdTargetLogging::createBmcDump()
23*21d305e2SAndrew Geissler {
24*21d305e2SAndrew Geissler     auto method = this->bus.new_method_call(
25*21d305e2SAndrew Geissler         "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
26*21d305e2SAndrew Geissler         "xyz.openbmc_project.Dump.Create", "CreateDump");
27*21d305e2SAndrew Geissler     method.append(
28*21d305e2SAndrew Geissler         std::vector<
29*21d305e2SAndrew Geissler             std::pair<std::string, std::variant<std::string, uint64_t>>>());
30*21d305e2SAndrew Geissler     try
31*21d305e2SAndrew Geissler     {
32*21d305e2SAndrew Geissler         this->bus.call_noreply(method);
33*21d305e2SAndrew Geissler     }
34*21d305e2SAndrew Geissler     catch (const sdbusplus::exception::exception& e)
35*21d305e2SAndrew Geissler     {
36*21d305e2SAndrew Geissler         error("Failed to create BMC dump, exception:{ERROR}", "ERROR", e);
37*21d305e2SAndrew Geissler         // just continue, this is error path anyway so we're just collecting
38*21d305e2SAndrew Geissler         // what we can
39*21d305e2SAndrew Geissler     }
40*21d305e2SAndrew Geissler }
41*21d305e2SAndrew Geissler 
428ffdb269SAndrew Geissler void SystemdTargetLogging::logError(const std::string& errorLog,
43e6841034SAndrew Geissler                                     const std::string& result,
44e6841034SAndrew Geissler                                     const std::string& unit)
45234a3179SAndrew Geissler {
46234a3179SAndrew Geissler     auto method = this->bus.new_method_call(
47234a3179SAndrew Geissler         "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
48234a3179SAndrew Geissler         "xyz.openbmc_project.Logging.Create", "Create");
49234a3179SAndrew Geissler     // Signature is ssa{ss}
508ffdb269SAndrew Geissler     method.append(errorLog);
51234a3179SAndrew Geissler     method.append("xyz.openbmc_project.Logging.Entry.Level.Critical");
52e6841034SAndrew Geissler     method.append(std::array<std::pair<std::string, std::string>, 2>(
53e6841034SAndrew Geissler         {std::pair<std::string, std::string>({"SYSTEMD_RESULT", result}),
54e6841034SAndrew Geissler          std::pair<std::string, std::string>({"SYSTEMD_UNIT", unit})}));
55234a3179SAndrew Geissler     try
56234a3179SAndrew Geissler     {
57234a3179SAndrew Geissler         this->bus.call_noreply(method);
58234a3179SAndrew Geissler     }
590a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
60234a3179SAndrew Geissler     {
618ffdb269SAndrew Geissler         error("Failed to create systemd target error, error:{ERROR_MSG}, "
628ffdb269SAndrew Geissler               "result:{RESULT}, exception:{ERROR}",
638ffdb269SAndrew Geissler               "ERROR_MSG", errorLog, "RESULT", result, "ERROR", e);
64234a3179SAndrew Geissler     }
65234a3179SAndrew Geissler }
66234a3179SAndrew Geissler 
67f3870c62SAndrew Geissler const std::string SystemdTargetLogging::processError(const std::string& unit,
68234a3179SAndrew Geissler                                                      const std::string& result)
69234a3179SAndrew Geissler {
70234a3179SAndrew Geissler     auto targetEntry = this->targetData.find(unit);
71234a3179SAndrew Geissler     if (targetEntry != this->targetData.end())
72234a3179SAndrew Geissler     {
73234a3179SAndrew Geissler         // Check if its result matches any of our monitored errors
74234a3179SAndrew Geissler         if (std::find(targetEntry->second.errorsToMonitor.begin(),
75234a3179SAndrew Geissler                       targetEntry->second.errorsToMonitor.end(),
76234a3179SAndrew Geissler                       result) != targetEntry->second.errorsToMonitor.end())
77234a3179SAndrew Geissler         {
78ad65b2d6SAndrew Geissler             info(
79ad65b2d6SAndrew Geissler                 "Monitored systemd unit has hit an error, unit:{UNIT}, result:{RESULT}",
808ffdb269SAndrew Geissler                 "UNIT", unit, "RESULT", result);
81f3870c62SAndrew Geissler             return (targetEntry->second.errorToLog);
82234a3179SAndrew Geissler         }
83234a3179SAndrew Geissler     }
84f3870c62SAndrew Geissler 
85f3870c62SAndrew Geissler     // Check if it's in our list of services to monitor
86f3870c62SAndrew Geissler     if (std::find(this->serviceData.begin(), this->serviceData.end(), unit) !=
87f3870c62SAndrew Geissler         this->serviceData.end())
88f3870c62SAndrew Geissler     {
89f3870c62SAndrew Geissler         if (result == "failed")
90f3870c62SAndrew Geissler         {
91f3870c62SAndrew Geissler             info(
92f3870c62SAndrew Geissler                 "Monitored systemd service has hit an error, unit:{UNIT}, result:{RESULT}",
93f3870c62SAndrew Geissler                 "UNIT", unit, "RESULT", result);
94*21d305e2SAndrew Geissler 
95*21d305e2SAndrew Geissler             // Generate a BMC dump when a critical service fails
96*21d305e2SAndrew Geissler             createBmcDump();
97f3870c62SAndrew Geissler             return (std::string{
98f3870c62SAndrew Geissler                 "xyz.openbmc_project.State.Error.CriticalServiceFailure"});
99f3870c62SAndrew Geissler         }
100f3870c62SAndrew Geissler     }
101f3870c62SAndrew Geissler 
102f3870c62SAndrew Geissler     return (std::string{});
103234a3179SAndrew Geissler }
104234a3179SAndrew Geissler 
105234a3179SAndrew Geissler void SystemdTargetLogging::systemdUnitChange(sdbusplus::message::message& msg)
106234a3179SAndrew Geissler {
107234a3179SAndrew Geissler     uint32_t id;
108234a3179SAndrew Geissler     sdbusplus::message::object_path objPath;
109234a3179SAndrew Geissler     std::string unit{};
110234a3179SAndrew Geissler     std::string result{};
111234a3179SAndrew Geissler 
112234a3179SAndrew Geissler     msg.read(id, objPath, unit, result);
113234a3179SAndrew Geissler 
114234a3179SAndrew Geissler     // In most cases it will just be success, in which case just return
115234a3179SAndrew Geissler     if (result != "done")
116234a3179SAndrew Geissler     {
117f3870c62SAndrew Geissler         const std::string error = processError(unit, result);
118234a3179SAndrew Geissler 
119234a3179SAndrew Geissler         // If this is a monitored error then log it
120f3870c62SAndrew Geissler         if (!error.empty())
121234a3179SAndrew Geissler         {
122e6841034SAndrew Geissler             logError(error, result, unit);
123234a3179SAndrew Geissler         }
124234a3179SAndrew Geissler     }
125234a3179SAndrew Geissler     return;
126234a3179SAndrew Geissler }
127234a3179SAndrew Geissler 
12838605ee2SAndrew Geissler void SystemdTargetLogging::processNameChangeSignal(
12938605ee2SAndrew Geissler     sdbusplus::message::message& msg)
13038605ee2SAndrew Geissler {
13138605ee2SAndrew Geissler     std::string name;      // well-known
13238605ee2SAndrew Geissler     std::string old_owner; // unique-name
13338605ee2SAndrew Geissler     std::string new_owner; // unique-name
13438605ee2SAndrew Geissler 
13538605ee2SAndrew Geissler     msg.read(name, old_owner, new_owner);
13638605ee2SAndrew Geissler 
13738605ee2SAndrew Geissler     // Looking for systemd to be on dbus so we can call it
13838605ee2SAndrew Geissler     if (name == "org.freedesktop.systemd1")
13938605ee2SAndrew Geissler     {
1408ffdb269SAndrew Geissler         info("org.freedesktop.systemd1 is now on dbus");
14138605ee2SAndrew Geissler         subscribeToSystemdSignals();
14238605ee2SAndrew Geissler     }
14338605ee2SAndrew Geissler     return;
14438605ee2SAndrew Geissler }
14538605ee2SAndrew Geissler 
146234a3179SAndrew Geissler void SystemdTargetLogging::subscribeToSystemdSignals()
147234a3179SAndrew Geissler {
148234a3179SAndrew Geissler     auto method = this->bus.new_method_call(
149234a3179SAndrew Geissler         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
150234a3179SAndrew Geissler         "org.freedesktop.systemd1.Manager", "Subscribe");
151234a3179SAndrew Geissler 
152234a3179SAndrew Geissler     try
153234a3179SAndrew Geissler     {
154234a3179SAndrew Geissler         this->bus.call(method);
155234a3179SAndrew Geissler     }
1560a675215SPatrick Williams     catch (const sdbusplus::exception::exception& e)
157234a3179SAndrew Geissler     {
15838605ee2SAndrew Geissler         // If error indicates systemd is not on dbus yet then do nothing.
15938605ee2SAndrew Geissler         // The systemdNameChangeSignals callback will detect when it is on
16038605ee2SAndrew Geissler         // dbus and then call this function again
16138605ee2SAndrew Geissler         const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown");
16238605ee2SAndrew Geissler         if (noDbus == e.name())
16338605ee2SAndrew Geissler         {
1648ffdb269SAndrew Geissler             info("org.freedesktop.systemd1 not on dbus yet");
16538605ee2SAndrew Geissler         }
16638605ee2SAndrew Geissler         else
16738605ee2SAndrew Geissler         {
1688ffdb269SAndrew Geissler             error("Failed to subscribe to systemd signals: {ERROR}", "ERROR",
1698ffdb269SAndrew Geissler                   e);
170234a3179SAndrew Geissler             elog<InternalFailure>();
171234a3179SAndrew Geissler         }
17238605ee2SAndrew Geissler         return;
17338605ee2SAndrew Geissler     }
17438605ee2SAndrew Geissler 
17538605ee2SAndrew Geissler     // Call destructor on match callback since application is now subscribed to
17638605ee2SAndrew Geissler     // systemd signals
17738605ee2SAndrew Geissler     this->systemdNameOwnedChangedSignal.~match();
178234a3179SAndrew Geissler 
179234a3179SAndrew Geissler     return;
180234a3179SAndrew Geissler }
181234a3179SAndrew Geissler 
182234a3179SAndrew Geissler } // namespace manager
183234a3179SAndrew Geissler } // namespace state
184234a3179SAndrew Geissler } // namespace phosphor
185