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 = 0;
38 
39     /**
40      * @brief Returns the machine serial number
41      *
42      * @return string - The machine serial number
43      */
44     virtual std::string getMachineSerialNumber() const = 0;
45 
46     /**
47      * @brief Says if the system is managed by a hardware
48      *        management console.
49      * @return bool - If the system is HMC managed
50      */
51     virtual bool isHMCManaged() const
52     {
53         return _hmcManaged;
54     }
55 
56     /**
57      * @brief Says if the host is up and running
58      *
59      * @return bool - If the host is running
60      */
61     virtual bool isHostUp() const
62     {
63         return _hostUp;
64     }
65 
66     using HostStateChangeFunc = std::function<void(bool)>;
67 
68     /**
69      * @brief Register a callback function that will get
70      *        called on all host on/off transitions.
71      *
72      * The void(bool) function will get passed the new
73      * value of the host state.
74      *
75      * @param[in] name - The subscription name
76      * @param[in] func - The function to run
77      */
78     void subscribeToHostStateChange(const std::string& name,
79                                     HostStateChangeFunc func)
80     {
81         _hostChangeCallbacks[name] = func;
82     }
83 
84     /**
85      * @brief Unsubscribe from host state changes.
86      *
87      * @param[in] name - The subscription name
88      */
89     void unsubscribeFromHostStateChange(const std::string& name)
90     {
91         _hostChangeCallbacks.erase(name);
92     }
93 
94     /**
95      * @brief Returns the BMC firmware version
96      *
97      * @return std::string - The BMC version
98      */
99     virtual std::string getBMCFWVersion() const
100     {
101         return _bmcFWVersion;
102     }
103 
104     /**
105      * @brief Returns the server firmware version
106      *
107      * @return std::string - The server firmware version
108      */
109     virtual std::string getServerFWVersion() const
110     {
111         return _serverFWVersion;
112     }
113 
114     /**
115      * @brief Returns the BMC FW version ID
116      *
117      * @return std::string - The BMC FW version ID
118      */
119     virtual std::string getBMCFWVersionID() const
120     {
121         return _bmcFWVersionID;
122     }
123 
124     /**
125      * @brief Returns the process name given its PID.
126      *
127      * @param[in] pid - The PID value as a string
128      *
129      * @return std::optional<std::string> - The name, or std::nullopt
130      */
131     std::optional<std::string> getProcessName(const std::string& pid) const
132     {
133         namespace fs = std::filesystem;
134 
135         fs::path path{"/proc"};
136         path /= fs::path{pid} / "exe";
137 
138         if (fs::exists(path))
139         {
140             return fs::read_symlink(path);
141         }
142 
143         return std::nullopt;
144     }
145 
146     /**
147      * @brief Returns the 'send event logs to host' setting.
148      *
149      * @return bool - If sending PELs to the host is enabled.
150      */
151     virtual bool getHostPELEnablement() const
152     {
153         return _sendPELsToHost;
154     }
155 
156     /**
157      * @brief Returns the BMC state
158      *
159      * @return std::string - The BMC state property value
160      */
161     virtual std::string getBMCState() const
162     {
163         return _bmcState;
164     }
165 
166     /**
167      * @brief Returns the Chassis state
168      *
169      * @return std::string - The chassis state property value
170      */
171     virtual std::string getChassisState() const
172     {
173         return _chassisState;
174     }
175 
176     /**
177      * @brief Returns the chassis requested power
178      *        transition value.
179      *
180      * @return std::string - The chassis transition property
181      */
182     virtual std::string getChassisTransition() const
183     {
184         return _chassisTransition;
185     }
186 
187     /**
188      * @brief Returns the Host state
189      *
190      * @return std::string - The Host state property value
191      */
192     virtual std::string getHostState() const
193     {
194         return _hostState;
195     }
196 
197     /**
198      * @brief Returns the motherboard CCIN
199      *
200      * @return std::string The motherboard CCIN
201      */
202     virtual std::string getMotherboardCCIN() const = 0;
203 
204     /**
205      * @brief Get the fields from the inventory necessary for doing
206      *        a callout on an inventory path.
207      *
208      * @param[in] inventoryPath - The item to get the data for
209      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
210      * @param[out] ccin - Filled in with the VINI/CC keyword
211      * @param[out] serialNumber - Filled in with the VINI/SN keyword
212      */
213     virtual void getHWCalloutFields(const std::string& inventoryPath,
214                                     std::string& fruPartNumber,
215                                     std::string& ccin,
216                                     std::string& serialNumber) const = 0;
217 
218     /**
219      * @brief Get the location code for an inventory item.
220      *
221      * @param[in] inventoryPath - The item to get the data for
222      *
223      * @return std::string - The location code
224      */
225     virtual std::string
226         getLocationCode(const std::string& inventoryPath) const = 0;
227 
228     /**
229      * @brief Get the list of system type names the system is called.
230      *
231      * @return std::vector<std::string> - The list of names
232      */
233     virtual std::vector<std::string> getSystemNames() const = 0;
234 
235     /**
236      * @brief Fills in the placeholder 'Ufcs' in the passed in location
237      *        code with the machine feature code and serial number, which
238      *        is needed to create a valid location code.
239      *
240      * @param[in] locationCode - Location code value starting with Ufcs-, and
241      *                           if that isn't present it will be added first.
242      *
243      * @param[in] node - The node number the location is on.
244      *
245      * @return std::string - The expanded location code
246      */
247     virtual std::string expandLocationCode(const std::string& locationCode,
248                                            uint16_t node) const = 0;
249 
250     /**
251      * @brief Returns the inventory path for the FRU that the location
252      *        code represents.
253      *
254      * @param[in] locationCode - If an expanded location code, then the
255      *                           full location code.
256      *                           If not expanded, a location code value
257      *                           starting with Ufcs-, and if that isn't
258      *                           present it will be added first.
259      *
260      * @param[in] node - The node number the location is on.  Ignored if the
261      *                   expanded location code is passed in.
262      *
263      * @param[in] expanded - If the location code already has the relevent
264      *                       VPD fields embedded in it.
265      *
266      * @return std::string - The inventory D-Bus object
267      */
268     virtual std::string getInventoryFromLocCode(const std::string& LocationCode,
269                                                 uint16_t node,
270                                                 bool expanded) const = 0;
271 
272     /**
273      * @brief Sets the Asserted property on the LED group passed in.
274      *
275      * @param[in] ledGroup - The LED group D-Bus path
276      * @param[in] value - The value to set it to
277      */
278     virtual void assertLEDGroup(const std::string& ledGroup,
279                                 bool value) const = 0;
280 
281     /**
282      * @brief Sets the Functional property on the OperationalStatus
283      *        interface on a D-Bus object.
284      *
285      * @param[in] objectPath - The D-Bus object path
286      * @param[in] functional - The value
287      */
288     virtual void setFunctional(const std::string& objectPath,
289                                bool functional) const = 0;
290 
291   protected:
292     /**
293      * @brief Sets the host on/off state and runs any
294      *        callback functions (if there was a change).
295      */
296     void setHostUp(bool hostUp)
297     {
298         if (_hostUp != hostUp)
299         {
300             _hostUp = hostUp;
301 
302             for (auto& [name, func] : _hostChangeCallbacks)
303             {
304                 try
305                 {
306                     func(_hostUp);
307                 }
308                 catch (std::exception& e)
309                 {
310                     using namespace phosphor::logging;
311                     log<level::ERR>("A host state change callback threw "
312                                     "an exception");
313                 }
314             }
315         }
316     }
317 
318     /**
319      * @brief The hardware management console status.  Always kept
320      *        up to date.
321      */
322     bool _hmcManaged = false;
323 
324     /**
325      * @brief The host up status.  Always kept up to date.
326      */
327     bool _hostUp = false;
328 
329     /**
330      * @brief The map of host state change subscriber
331      *        names to callback functions.
332      */
333     std::map<std::string, HostStateChangeFunc> _hostChangeCallbacks;
334 
335     /**
336      * @brief The BMC firmware version string
337      */
338     std::string _bmcFWVersion;
339 
340     /**
341      * @brief The server firmware version string
342      */
343     std::string _serverFWVersion;
344 
345     /**
346      * @brief The BMC firmware version ID string
347      */
348     std::string _bmcFWVersionID;
349 
350     /**
351      * @brief If sending PELs is enabled.
352      *
353      * This is usually set to false in manufacturing test.
354      */
355     bool _sendPELsToHost = true;
356 
357     /**
358      * @brief The BMC state property
359      */
360     std::string _bmcState;
361 
362     /**
363      * @brief The Chassis current power state property
364      */
365     std::string _chassisState;
366 
367     /**
368      * @brief The Chassis requested power transition property
369      */
370     std::string _chassisTransition;
371 
372     /**
373      * @brief The host state property
374      */
375     std::string _hostState;
376 };
377 
378 /**
379  * @class DataInterface
380  *
381  * Concrete implementation of DataInterfaceBase.
382  */
383 class DataInterface : public DataInterfaceBase
384 {
385   public:
386     DataInterface() = delete;
387     ~DataInterface() = default;
388     DataInterface(const DataInterface&) = default;
389     DataInterface& operator=(const DataInterface&) = default;
390     DataInterface(DataInterface&&) = default;
391     DataInterface& operator=(DataInterface&&) = default;
392 
393     /**
394      * @brief Constructor
395      *
396      * @param[in] bus - The sdbusplus bus object
397      */
398     explicit DataInterface(sdbusplus::bus::bus& bus);
399 
400     /**
401      * @brief Finds the D-Bus service name that hosts the
402      *        passed in path and interface.
403      *
404      * @param[in] objectPath - The D-Bus object path
405      * @param[in] interface - The D-Bus interface
406      */
407     DBusService getService(const std::string& objectPath,
408                            const std::string& interface) const;
409 
410     /**
411      * @brief Wrapper for the 'GetAll' properties method call
412      *
413      * @param[in] service - The D-Bus service to call it on
414      * @param[in] objectPath - The D-Bus object path
415      * @param[in] interface - The interface to get the props on
416      *
417      * @return DBusPropertyMap - The property results
418      */
419     DBusPropertyMap getAllProperties(const std::string& service,
420                                      const std::string& objectPath,
421                                      const std::string& interface) const;
422     /**
423      * @brief Wrapper for the 'Get' properties method call
424      *
425      * @param[in] service - The D-Bus service to call it on
426      * @param[in] objectPath - The D-Bus object path
427      * @param[in] interface - The interface to get the property on
428      * @param[in] property - The property name
429      * @param[out] value - Filled in with the property value.
430      */
431     void getProperty(const std::string& service, const std::string& objectPath,
432                      const std::string& interface, const std::string& property,
433                      DBusValue& value) const;
434     /**
435      * @brief Returns the machine Type/Model
436      *
437      * @return string - The machine Type/Model string
438      */
439     std::string getMachineTypeModel() const override;
440 
441     /**
442      * @brief Returns the machine serial number
443      *
444      * @return string - The machine serial number
445      */
446     std::string getMachineSerialNumber() const override;
447 
448     /**
449      * @brief Returns the motherboard CCIN
450      *
451      * @return std::string The motherboard CCIN
452      */
453     std::string getMotherboardCCIN() const override;
454 
455     /**
456      * @brief Get the fields from the inventory necessary for doing
457      *        a callout on an inventory path.
458      *
459      * @param[in] inventoryPath - The item to get the data for
460      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
461      * @param[out] ccin - Filled in with the VINI/CC keyword
462      * @param[out] serialNumber - Filled in with the VINI/SN keyword
463      */
464     void getHWCalloutFields(const std::string& inventoryPath,
465                             std::string& fruPartNumber, std::string& ccin,
466                             std::string& serialNumber) const override;
467 
468     /**
469      * @brief Get the location code for an inventory item.
470      *
471      * Throws an exception if the inventory item doesn't have the
472      * location code interface.
473      *
474      * @param[in] inventoryPath - The item to get the data for
475      *
476      * @return std::string - The location code
477      */
478     std::string
479         getLocationCode(const std::string& inventoryPath) const override;
480 
481     /**
482      * @brief Get the list of system type names the system is called.
483      *
484      * @return std::vector<std::string> - The list of names
485      */
486     std::vector<std::string> getSystemNames() const override;
487 
488     /**
489      * @brief Fills in the placeholder 'Ufcs' in the passed in location
490      *        code with the machine feature code and serial number, which
491      *        is needed to create a valid location code.
492      *
493      * @param[in] locationCode - Location code value starting with Ufcs-, and
494      *                           if that isn't present it will be added first.
495      *
496      * @param[in] node - The node number the location is one.
497      *
498      * @return std::string - The expanded location code
499      */
500     std::string expandLocationCode(const std::string& locationCode,
501                                    uint16_t node) const override;
502 
503     /**
504      * @brief Returns the inventory path for the FRU that the location
505      *        code represents.
506      *
507      * @param[in] locationCode - If an expanded location code, then the
508      *                           full location code.
509      *                           If not expanded, a location code value
510      *                           starting with Ufcs-, and if that isn't
511      *                           present it will be added first.
512      *
513      * @param[in] node - The node number the location is on.  Ignored if the
514      *                   expanded location code is passed in.
515      *
516      * @param[in] expanded - If the location code already has the relevent
517      *                       VPD fields embedded in it.
518      *
519      * @return std::string - The inventory D-Bus object
520      */
521     std::string getInventoryFromLocCode(const std::string& locationCode,
522                                         uint16_t node,
523                                         bool expanded) const override;
524 
525     /**
526      * @brief Sets the Asserted property on the LED group passed in.
527      *
528      * @param[in] ledGroup - The LED group D-Bus path
529      * @param[in] value - The value to set it to
530      */
531     void assertLEDGroup(const std::string& ledGroup, bool value) const override;
532 
533     /**
534      * @brief Sets the Functional property on the OperationalStatus
535      *        interface on a D-Bus object.
536      *
537      * @param[in] objectPath - The D-Bus object path
538      * @param[in] functional - The value
539      */
540     void setFunctional(const std::string& objectPath,
541                        bool functional) const override;
542 
543   private:
544     /**
545      * @brief Reads the BMC firmware version string and puts it into
546      *        _bmcFWVersion.
547      */
548     void readBMCFWVersion();
549 
550     /**
551      * @brief Reads the server firmware version string and puts it into
552      *        _serverFWVersion.
553      */
554     void readServerFWVersion();
555 
556     /**
557      * @brief Reads the BMC firmware version ID and puts it into
558      *        _bmcFWVersionID.
559      */
560     void readBMCFWVersionID();
561 
562     /**
563      * @brief Finds all D-Bus paths that contain any of the interfaces
564      *        passed in, by using GetSubTreePaths.
565      *
566      * @param[in] interfaces - The desired interfaces
567      *
568      * @return The D-Bus paths.
569      */
570     DBusPathList getPaths(const DBusInterfaceList& interfaces) const;
571 
572     /**
573      * @brief The interfacesAdded callback used on the inventory to
574      *        find the D-Bus object that has the motherboard interface.
575      *        When the motherboard is found, it then adds a PropertyWatcher
576      *        for the motherboard CCIN.
577      */
578     void motherboardIfaceAdded(sdbusplus::message::message& msg);
579 
580     /**
581      * @brief Adds the Ufcs- prefix to the location code passed in
582      *        if necessary.
583      *
584      * Needed because the location codes that come back from the
585      * message registry and device callout JSON don't have it.
586      *
587      * @param[in] - The location code without a prefix, like P1-C1
588      *
589      * @return std::string - The location code with the prefix
590      */
591     static std::string addLocationCodePrefix(const std::string& locationCode);
592 
593     /**
594      * @brief The D-Bus property or interface watchers that have callbacks
595      *        registered that will set members in this class when
596      *        they change.
597      */
598     std::vector<std::unique_ptr<DBusWatcher>> _properties;
599 
600     /**
601      * @brief The sdbusplus bus object for making D-Bus calls.
602      */
603     sdbusplus::bus::bus& _bus;
604 
605     /**
606      * @brief The interfacesAdded match object used to wait for inventory
607      *        interfaces to show up, so that the object with the motherboard
608      *        interface can be found.  After it is found, this object is
609      *        deleted.
610      */
611     std::unique_ptr<sdbusplus::bus::match_t> _inventoryIfacesAddedMatch;
612 };
613 
614 } // namespace pels
615 } // namespace openpower
616