1 #pragma once
2 
3 #include "dbus_types.hpp"
4 #include "dbus_watcher.hpp"
5 
6 #include <filesystem>
7 #include <phosphor-logging/log.hpp>
8 #include <sdbusplus/bus.hpp>
9 #include <sdbusplus/bus/match.hpp>
10 
11 namespace openpower
12 {
13 namespace pels
14 {
15 
16 /**
17  * @class DataInterface
18  *
19  * A base class for gathering data about the system for use
20  * in PELs. Implemented this way to facilitate mocking.
21  */
22 class DataInterfaceBase
23 {
24   public:
25     DataInterfaceBase() = default;
26     virtual ~DataInterfaceBase() = default;
27     DataInterfaceBase(const DataInterfaceBase&) = default;
28     DataInterfaceBase& operator=(const DataInterfaceBase&) = default;
29     DataInterfaceBase(DataInterfaceBase&&) = default;
30     DataInterfaceBase& operator=(DataInterfaceBase&&) = default;
31 
32     /**
33      * @brief Returns the machine Type/Model
34      *
35      * @return string - The machine Type/Model string
36      */
37     virtual std::string getMachineTypeModel() const
38     {
39         return _machineTypeModel;
40     }
41 
42     /**
43      * @brief Returns the machine serial number
44      *
45      * @return string - The machine serial number
46      */
47     virtual std::string getMachineSerialNumber() const
48     {
49         return _machineSerialNumber;
50     }
51 
52     /**
53      * @brief Says if the system is managed by a hardware
54      *        management console.
55      * @return bool - If the system is HMC managed
56      */
57     virtual bool isHMCManaged() const
58     {
59         return _hmcManaged;
60     }
61 
62     /**
63      * @brief Says if the host is up and running
64      *
65      * @return bool - If the host is running
66      */
67     virtual bool isHostUp() const
68     {
69         return _hostUp;
70     }
71 
72     using HostStateChangeFunc = std::function<void(bool)>;
73 
74     /**
75      * @brief Register a callback function that will get
76      *        called on all host on/off transitions.
77      *
78      * The void(bool) function will get passed the new
79      * value of the host state.
80      *
81      * @param[in] name - The subscription name
82      * @param[in] func - The function to run
83      */
84     void subscribeToHostStateChange(const std::string& name,
85                                     HostStateChangeFunc func)
86     {
87         _hostChangeCallbacks[name] = func;
88     }
89 
90     /**
91      * @brief Unsubscribe from host state changes.
92      *
93      * @param[in] name - The subscription name
94      */
95     void unsubscribeFromHostStateChange(const std::string& name)
96     {
97         _hostChangeCallbacks.erase(name);
98     }
99 
100     /**
101      * @brief Returns the BMC firmware version
102      *
103      * @return std::string - The BMC version
104      */
105     virtual std::string getBMCFWVersion() const
106     {
107         return _bmcFWVersion;
108     }
109 
110     /**
111      * @brief Returns the server firmware version
112      *
113      * @return std::string - The server firmware version
114      */
115     virtual std::string getServerFWVersion() const
116     {
117         return _serverFWVersion;
118     }
119 
120     /**
121      * @brief Returns the BMC FW version ID
122      *
123      * @return std::string - The BMC FW version ID
124      */
125     virtual std::string getBMCFWVersionID() const
126     {
127         return _bmcFWVersionID;
128     }
129 
130     /**
131      * @brief Returns the process name given its PID.
132      *
133      * @param[in] pid - The PID value as a string
134      *
135      * @return std::optional<std::string> - The name, or std::nullopt
136      */
137     std::optional<std::string> getProcessName(const std::string& pid) const
138     {
139         namespace fs = std::filesystem;
140 
141         fs::path path{"/proc"};
142         path /= fs::path{pid} / "exe";
143 
144         if (fs::exists(path))
145         {
146             return fs::read_symlink(path);
147         }
148 
149         return std::nullopt;
150     }
151 
152     /**
153      * @brief Returns the 'send event logs to host' setting.
154      *
155      * @return bool - If sending PELs to the host is enabled.
156      */
157     virtual bool getHostPELEnablement() const
158     {
159         return _sendPELsToHost;
160     }
161 
162     /**
163      * @brief Returns the BMC state
164      *
165      * @return std::string - The BMC state property value
166      */
167     virtual std::string getBMCState() const
168     {
169         return _bmcState;
170     }
171 
172     /**
173      * @brief Returns the Chassis state
174      *
175      * @return std::string - The chassis state property value
176      */
177     virtual std::string getChassisState() const
178     {
179         return _chassisState;
180     }
181 
182     /**
183      * @brief Returns the chassis requested power
184      *        transition value.
185      *
186      * @return std::string - The chassis transition property
187      */
188     virtual std::string getChassisTransition() const
189     {
190         return _chassisTransition;
191     }
192 
193     /**
194      * @brief Returns the Host state
195      *
196      * @return std::string - The Host state property value
197      */
198     virtual std::string getHostState() const
199     {
200         return _hostState;
201     }
202 
203     /**
204      * @brief Returns the motherboard CCIN
205      *
206      * @return std::string The motherboard CCIN
207      */
208     virtual std::string getMotherboardCCIN() const
209     {
210         return _motherboardCCIN;
211     }
212 
213   protected:
214     /**
215      * @brief Sets the host on/off state and runs any
216      *        callback functions (if there was a change).
217      */
218     void setHostUp(bool hostUp)
219     {
220         if (_hostUp != hostUp)
221         {
222             _hostUp = hostUp;
223 
224             for (auto& [name, func] : _hostChangeCallbacks)
225             {
226                 try
227                 {
228                     func(_hostUp);
229                 }
230                 catch (std::exception& e)
231                 {
232                     using namespace phosphor::logging;
233                     log<level::ERR>("A host state change callback threw "
234                                     "an exception");
235                 }
236             }
237         }
238     }
239 
240     /**
241      * @brief The machine type-model.  Always kept up to date
242      */
243     std::string _machineTypeModel;
244 
245     /**
246      * @brief The machine serial number.  Always kept up to date
247      */
248     std::string _machineSerialNumber;
249 
250     /**
251      * @brief The hardware management console status.  Always kept
252      *        up to date.
253      */
254     bool _hmcManaged = false;
255 
256     /**
257      * @brief The host up status.  Always kept up to date.
258      */
259     bool _hostUp = false;
260 
261     /**
262      * @brief The map of host state change subscriber
263      *        names to callback functions.
264      */
265     std::map<std::string, HostStateChangeFunc> _hostChangeCallbacks;
266 
267     /**
268      * @brief The BMC firmware version string
269      */
270     std::string _bmcFWVersion;
271 
272     /**
273      * @brief The server firmware version string
274      */
275     std::string _serverFWVersion;
276 
277     /**
278      * @brief The BMC firmware version ID string
279      */
280     std::string _bmcFWVersionID;
281 
282     /**
283      * @brief If sending PELs is enabled.
284      *
285      * This is usually set to false in manufacturing test.
286      */
287     bool _sendPELsToHost = true;
288 
289     /**
290      * @brief The BMC state property
291      */
292     std::string _bmcState;
293 
294     /**
295      * @brief The Chassis current power state property
296      */
297     std::string _chassisState;
298 
299     /**
300      * @brief The Chassis requested power transition property
301      */
302     std::string _chassisTransition;
303 
304     /**
305      * @brief The host state property
306      */
307     std::string _hostState;
308 
309     /**
310      * @brief The motherboard CCIN
311      */
312     std::string _motherboardCCIN;
313 };
314 
315 /**
316  * @class DataInterface
317  *
318  * Concrete implementation of DataInterfaceBase.
319  */
320 class DataInterface : public DataInterfaceBase
321 {
322   public:
323     DataInterface() = delete;
324     ~DataInterface() = default;
325     DataInterface(const DataInterface&) = default;
326     DataInterface& operator=(const DataInterface&) = default;
327     DataInterface(DataInterface&&) = default;
328     DataInterface& operator=(DataInterface&&) = default;
329 
330     /**
331      * @brief Constructor
332      *
333      * @param[in] bus - The sdbusplus bus object
334      */
335     explicit DataInterface(sdbusplus::bus::bus& bus);
336 
337     /**
338      * @brief Finds the D-Bus service name that hosts the
339      *        passed in path and interface.
340      *
341      * @param[in] objectPath - The D-Bus object path
342      * @param[in] interface - The D-Bus interface
343      */
344     DBusService getService(const std::string& objectPath,
345                            const std::string& interface) const;
346 
347     /**
348      * @brief Wrapper for the 'GetAll' properties method call
349      *
350      * @param[in] service - The D-Bus service to call it on
351      * @param[in] objectPath - The D-Bus object path
352      * @param[in] interface - The interface to get the props on
353      *
354      * @return DBusPropertyMap - The property results
355      */
356     DBusPropertyMap getAllProperties(const std::string& service,
357                                      const std::string& objectPath,
358                                      const std::string& interface) const;
359     /**
360      * @brief Wrapper for the 'Get' properties method call
361      *
362      * @param[in] service - The D-Bus service to call it on
363      * @param[in] objectPath - The D-Bus object path
364      * @param[in] interface - The interface to get the property on
365      * @param[in] property - The property name
366      * @param[out] value - Filled in with the property value.
367      */
368     void getProperty(const std::string& service, const std::string& objectPath,
369                      const std::string& interface, const std::string& property,
370                      DBusValue& value) const;
371 
372   private:
373     /**
374      * @brief Reads the BMC firmware version string and puts it into
375      *        _bmcFWVersion.
376      */
377     void readBMCFWVersion();
378 
379     /**
380      * @brief Reads the server firmware version string and puts it into
381      *        _serverFWVersion.
382      */
383     void readServerFWVersion();
384 
385     /**
386      * @brief Reads the BMC firmware version ID and puts it into
387      *        _bmcFWVersionID.
388      */
389     void readBMCFWVersionID();
390 
391     /**
392      * @brief Reads the motherboard CCIN and puts it into _motherboardCCIN.
393      *
394      * It finds the motherboard first, possibly having to wait for it to
395      * show up.
396      */
397     void readMotherboardCCIN();
398 
399     /**
400      * @brief Finds all D-Bus paths that contain any of the interfaces
401      *        passed in, by using GetSubTreePaths.
402      *
403      * @param[in] interfaces - The desired interfaces
404      *
405      * @return The D-Bus paths.
406      */
407     DBusPathList getPaths(const DBusInterfaceList& interfaces) const;
408 
409     /**
410      * @brief The interfacesAdded callback used on the inventory to
411      *        find the D-Bus object that has the motherboard interface.
412      *        When the motherboard is found, it then adds a PropertyWatcher
413      *        for the motherboard CCIN.
414      */
415     void motherboardIfaceAdded(sdbusplus::message::message& msg);
416 
417     /**
418      * @brief Set the motherboard CCIN from the DBus variant that
419      *        contains it.
420      *
421      * @param[in] ccin - The CCIN variant, a vector<uint8_t>.
422      */
423     void setMotherboardCCIN(const DBusValue& ccin)
424     {
425         const auto& c = std::get<std::vector<uint8_t>>(ccin);
426         _motherboardCCIN = std::string{c.begin(), c.end()};
427     }
428 
429     /**
430      * @brief The D-Bus property or interface watchers that have callbacks
431      *        registered that will set members in this class when
432      *        they change.
433      */
434     std::vector<std::unique_ptr<DBusWatcher>> _properties;
435 
436     /**
437      * @brief The sdbusplus bus object for making D-Bus calls.
438      */
439     sdbusplus::bus::bus& _bus;
440 
441     /**
442      * @brief The interfacesAdded match object used to wait for inventory
443      *        interfaces to show up, so that the object with the motherboard
444      *        interface can be found.  After it is found, this object is
445      *        deleted.
446      */
447     std::unique_ptr<sdbusplus::bus::match_t> _inventoryIfacesAddedMatch;
448 };
449 
450 } // namespace pels
451 } // namespace openpower
452