1 #include <attn_dbus.hpp>
2 #include <attn_handler.hpp>
3 #include <attn_logging.hpp>
4 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
5 
6 #include <string>
7 #include <vector>
8 
9 namespace attn
10 {
11 
12 /** @brief Create a dbus method */
13 int dbusMethod(const std::string& i_path, const std::string& i_interface,
14                const std::string& i_function,
15                sdbusplus::message::message& o_method,
16                const std::string& i_extended)
17 {
18     int rc = RC_DBUS_ERROR; // assume error
19 
20     try
21     {
22         constexpr auto serviceFind   = "xyz.openbmc_project.ObjectMapper";
23         constexpr auto pathFind      = "/xyz/openbmc_project/object_mapper";
24         constexpr auto interfaceFind = "xyz.openbmc_project.ObjectMapper";
25         constexpr auto functionFind  = "GetObject";
26 
27         auto bus = sdbusplus::bus::new_system(); // using system dbus
28 
29         // method to find service from object path and object interface
30         auto method = bus.new_method_call(serviceFind, pathFind, interfaceFind,
31                                           functionFind);
32 
33         // find the service for specified object path and interface
34         method.append(i_path.c_str());
35         method.append(std::vector<std::string>({i_interface}));
36         auto reply = bus.call(method);
37 
38         // dbus call results
39         std::map<std::string, std::vector<std::string>> responseFindService;
40         reply.read(responseFindService);
41 
42         // If we successfully found the service associated with the dbus object
43         // path and interface then create a method for the specified interface
44         // and function.
45         if (!responseFindService.empty())
46         {
47             auto service = responseFindService.begin()->first;
48 
49             // Some methods (e.g. get attribute) take an extended parameter
50             if (i_extended == "")
51             {
52                 // return the method
53                 o_method = bus.new_method_call(service.c_str(), i_path.c_str(),
54                                                i_interface.c_str(),
55                                                i_function.c_str());
56             }
57             else
58             {
59                 // return extended method
60                 o_method =
61                     bus.new_method_call(service.c_str(), i_path.c_str(),
62                                         i_extended.c_str(), i_function.c_str());
63             }
64 
65             rc = RC_SUCCESS;
66         }
67         else
68         {
69             // This trace will be picked up in event log
70             trace<level::INFO>("dbusMethod service not found");
71             std::string traceMsgPath = std::string(i_path, maxTraceLen);
72             trace<level::INFO>(traceMsgPath.c_str());
73             std::string traceMsgIface = std::string(i_interface, maxTraceLen);
74             trace<level::INFO>(traceMsgIface.c_str());
75         }
76     }
77     catch (const sdbusplus::exception::SdBusError& e)
78     {
79         trace<level::ERROR>("dbusMethod exception");
80         std::string traceMsg = std::string(e.what(), maxTraceLen);
81         trace<level::ERROR>(traceMsg.c_str());
82     }
83 
84     return rc;
85 }
86 
87 /** @brief Create a PEL for the specified event type */
88 uint32_t createPel(const std::string& i_event,
89                    std::map<std::string, std::string>& i_additional,
90                    const std::vector<util::FFDCTuple>& i_ffdc)
91 {
92     // CreatePELWithFFDCFiles returns plid
93     int plid = 0;
94 
95     // Need to provide pid when using create or create-with-ffdc methods
96     i_additional.emplace("_PID", std::to_string(getpid()));
97 
98     // Sdbus call specifics
99     constexpr auto interface = "org.open_power.Logging.PEL";
100     constexpr auto function  = "CreatePELWithFFDCFiles";
101 
102     sdbusplus::message::message method;
103 
104     if (0 == dbusMethod(pathLogging, interface, function, method))
105     {
106         try
107         {
108             // append additional dbus call paramaters
109             method.append(i_event, levelPelError, i_additional, i_ffdc);
110 
111             // using system dbus
112             auto bus      = sdbusplus::bus::new_system();
113             auto response = bus.call(method);
114 
115             // reply will be tuple containing bmc log id, platform log id
116             std::tuple<uint32_t, uint32_t> reply = {0, 0};
117 
118             // parse dbus response into reply
119             response.read(reply);
120             plid = std::get<1>(reply); // platform log id is tuple "second"
121         }
122         catch (const sdbusplus::exception::SdBusError& e)
123         {
124             trace<level::ERROR>("createPel exception");
125             std::string traceMsg = std::string(e.what(), maxTraceLen);
126             trace<level::ERROR>(traceMsg.c_str());
127         }
128     }
129 
130     return plid; // platform log id or 0
131 }
132 
133 /** @brief Create a PEL from raw PEL data */
134 void createPelRaw(const std::vector<uint8_t>& i_buffer)
135 {
136     // Create FFDC file from buffer data
137     util::FFDCFile pelFile{util::FFDCFormat::Text};
138     auto fd = pelFile.getFileDescriptor();
139 
140     auto filePath = pelFile.getPath(); // path to ffdc file
141 
142     size_t numBytes = write(fd, i_buffer.data(), i_buffer.size());
143     if (i_buffer.size() != numBytes)
144     {
145         std::stringstream traceMsg;
146         traceMsg << filePath.c_str() << " only " << (int)numBytes << " of "
147                  << (int)i_buffer.size() << " bytes written";
148         auto strobj = traceMsg.str();
149         trace<level::ERROR>(strobj.c_str());
150     }
151 
152     lseek(fd, 0, SEEK_SET);
153 
154     // Additional data for log
155     std::map<std::string, std::string> additional;
156     additional.emplace("RAWPEL", filePath.string());
157     additional.emplace("_PID", std::to_string(getpid()));
158 
159     // dbus specifics
160     constexpr auto interface = "xyz.openbmc_project.Logging.Create";
161     constexpr auto function  = "Create";
162 
163     sdbusplus::message::message method;
164 
165     if (0 == dbusMethod(pathLogging, interface, function, method))
166     {
167         try
168         {
169             // append additional dbus call parameters
170             method.append(eventPelTerminate, levelPelError, additional);
171 
172             // using system dbus, no reply
173             auto bus = sdbusplus::bus::new_system();
174             bus.call_noreply(method);
175         }
176         catch (const sdbusplus::exception::SdBusError& e)
177         {
178             trace<level::ERROR>("createPelRaw exception");
179             std::string traceMsg = std::string(e.what(), maxTraceLen);
180             trace<level::ERROR>(traceMsg.c_str());
181         }
182     }
183 }
184 
185 /** @brief Get file descriptor of exisitng PEL */
186 int getPel(const uint32_t i_pelId)
187 {
188     // GetPEL returns file descriptor (int)
189     int fd = -1;
190 
191     // dbus specifics
192     constexpr auto interface = "org.open_power.Logging.PEL";
193     constexpr auto function  = "GetPEL";
194 
195     sdbusplus::message::message method;
196 
197     if (0 == dbusMethod(pathLogging, interface, function, method))
198     {
199         try
200         {
201             // additional dbus call parameters
202             method.append(i_pelId);
203 
204             // using system dbus
205             auto bus      = sdbusplus::bus::new_system();
206             auto response = bus.call(method);
207 
208             // reply will be a unix file descriptor
209             sdbusplus::message::unix_fd reply;
210 
211             // parse dbus response into reply
212             response.read(reply);
213 
214             fd = dup(reply); // need to copy (dup) the file descriptor
215         }
216         catch (const sdbusplus::exception::SdBusError& e)
217         {
218             trace<level::ERROR>("getPel exception");
219             std::string traceMsg = std::string(e.what(), maxTraceLen);
220             trace<level::ERROR>(traceMsg.c_str());
221         }
222     }
223 
224     return fd; // file descriptor or -1
225 }
226 
227 /** @brief Get the running state of the host */
228 HostRunningState hostRunningState()
229 {
230     HostRunningState host = HostRunningState::Unknown;
231 
232     // dbus specifics
233     constexpr auto path      = "/xyz/openbmc_project/state/host0";
234     constexpr auto interface = "xyz.openbmc_project.State.Boot.Progress";
235     constexpr auto extended  = "org.freedesktop.DBus.Properties";
236     constexpr auto function  = "Get";
237 
238     sdbusplus::message::message method;
239 
240     if (0 == dbusMethod(path, interface, function, method, extended))
241     {
242         try
243         {
244             // additional dbus call parameters
245             method.append(interface, "BootProgress");
246 
247             // using system dbus
248             auto bus      = sdbusplus::bus::new_system();
249             auto response = bus.call(method);
250 
251             // reply will be a variant
252             std::variant<std::string, bool, std::vector<uint8_t>,
253                          std::vector<std::string>>
254                 reply;
255 
256             // parse dbus response into reply
257             response.read(reply);
258 
259             // get boot progress (string) and convert to boot stage
260             std::string bootProgress(std::get<std::string>(reply));
261 
262             using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::
263                 server::Progress::ProgressStages;
264 
265             BootProgress stage = sdbusplus::xyz::openbmc_project::State::Boot::
266                 server::Progress::convertProgressStagesFromString(bootProgress);
267 
268             if ((stage == BootProgress::SystemInitComplete) ||
269                 (stage == BootProgress::OSStart) ||
270                 (stage == BootProgress::OSRunning))
271             {
272                 host = HostRunningState::Started;
273             }
274             else
275             {
276                 host = HostRunningState::NotStarted;
277             }
278         }
279         catch (const sdbusplus::exception::SdBusError& e)
280         {
281             trace<level::ERROR>("hostRunningState exception");
282             std::string traceMsg = std::string(e.what(), maxTraceLen);
283             trace<level::ERROR>(traceMsg.c_str());
284         }
285     }
286 
287     return host;
288 }
289 } // namespace attn
290