xref: /openbmc/phosphor-state-manager/systemd_target_signal.cpp (revision 128ea8e3f1c56af1f7aa615a6f3b94088c9ba978)
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