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