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