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     return nullptr;
63 }
64 
65 void SystemdTargetLogging::systemdUnitChange(sdbusplus::message::message& msg)
66 {
67     uint32_t id;
68     sdbusplus::message::object_path objPath;
69     std::string unit{};
70     std::string result{};
71 
72     msg.read(id, objPath, unit, result);
73 
74     // In most cases it will just be success, in which case just return
75     if (result != "done")
76     {
77         const std::string* error = processError(unit, result);
78 
79         // If this is a monitored error then log it
80         if (error)
81         {
82             logError(*error, result);
83         }
84     }
85     return;
86 }
87 
88 void SystemdTargetLogging::processNameChangeSignal(
89     sdbusplus::message::message& msg)
90 {
91     std::string name;      // well-known
92     std::string old_owner; // unique-name
93     std::string new_owner; // unique-name
94 
95     msg.read(name, old_owner, new_owner);
96 
97     // Looking for systemd to be on dbus so we can call it
98     if (name == "org.freedesktop.systemd1")
99     {
100         info("org.freedesktop.systemd1 is now on dbus");
101         subscribeToSystemdSignals();
102     }
103     return;
104 }
105 
106 void SystemdTargetLogging::subscribeToSystemdSignals()
107 {
108     auto method = this->bus.new_method_call(
109         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
110         "org.freedesktop.systemd1.Manager", "Subscribe");
111 
112     try
113     {
114         this->bus.call(method);
115     }
116     catch (const sdbusplus::exception::exception& e)
117     {
118         // If error indicates systemd is not on dbus yet then do nothing.
119         // The systemdNameChangeSignals callback will detect when it is on
120         // dbus and then call this function again
121         const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown");
122         if (noDbus == e.name())
123         {
124             info("org.freedesktop.systemd1 not on dbus yet");
125         }
126         else
127         {
128             error("Failed to subscribe to systemd signals: {ERROR}", "ERROR",
129                   e);
130             elog<InternalFailure>();
131         }
132         return;
133     }
134 
135     // Call destructor on match callback since application is now subscribed to
136     // systemd signals
137     this->systemdNameOwnedChangedSignal.~match();
138 
139     return;
140 }
141 
142 } // namespace manager
143 } // namespace state
144 } // namespace phosphor
145