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 Returns the system IM
206      *
207      * @return std::string The system IM
208      */
209     virtual std::vector<uint8_t> getSystemIMKeyword() const = 0;
210 
211     /**
212      * @brief Get the fields from the inventory necessary for doing
213      *        a callout on an inventory path.
214      *
215      * @param[in] inventoryPath - The item to get the data for
216      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
217      * @param[out] ccin - Filled in with the VINI/CC keyword
218      * @param[out] serialNumber - Filled in with the VINI/SN keyword
219      */
220     virtual void getHWCalloutFields(const std::string& inventoryPath,
221                                     std::string& fruPartNumber,
222                                     std::string& ccin,
223                                     std::string& serialNumber) const = 0;
224 
225     /**
226      * @brief Get the location code for an inventory item.
227      *
228      * @param[in] inventoryPath - The item to get the data for
229      *
230      * @return std::string - The location code
231      */
232     virtual std::string
233         getLocationCode(const std::string& inventoryPath) const = 0;
234 
235     /**
236      * @brief Get the list of system type names the system is called.
237      *
238      * @return std::vector<std::string> - The list of names
239      */
240     virtual std::vector<std::string> getSystemNames() const = 0;
241 
242     /**
243      * @brief Fills in the placeholder 'Ufcs' in the passed in location
244      *        code with the machine feature code and serial number, which
245      *        is needed to create a valid location code.
246      *
247      * @param[in] locationCode - Location code value starting with Ufcs-, and
248      *                           if that isn't present it will be added first.
249      *
250      * @param[in] node - The node number the location is on.
251      *
252      * @return std::string - The expanded location code
253      */
254     virtual std::string expandLocationCode(const std::string& locationCode,
255                                            uint16_t node) const = 0;
256 
257     /**
258      * @brief Returns the inventory path for the FRU that the location
259      *        code represents.
260      *
261      * @param[in] locationCode - If an expanded location code, then the
262      *                           full location code.
263      *                           If not expanded, a location code value
264      *                           starting with Ufcs-, and if that isn't
265      *                           present it will be added first.
266      *
267      * @param[in] node - The node number the location is on.  Ignored if the
268      *                   expanded location code is passed in.
269      *
270      * @param[in] expanded - If the location code already has the relevent
271      *                       VPD fields embedded in it.
272      *
273      * @return std::string - The inventory D-Bus object
274      */
275     virtual std::string getInventoryFromLocCode(const std::string& LocationCode,
276                                                 uint16_t node,
277                                                 bool expanded) const = 0;
278 
279     /**
280      * @brief Sets the Asserted property on the LED group passed in.
281      *
282      * @param[in] ledGroup - The LED group D-Bus path
283      * @param[in] value - The value to set it to
284      */
285     virtual void assertLEDGroup(const std::string& ledGroup,
286                                 bool value) const = 0;
287 
288     /**
289      * @brief Sets the Functional property on the OperationalStatus
290      *        interface on a D-Bus object.
291      *
292      * @param[in] objectPath - The D-Bus object path
293      * @param[in] functional - The value
294      */
295     virtual void setFunctional(const std::string& objectPath,
296                                bool functional) const = 0;
297 
298     /**
299      * @brief Sets the critical association on the D-Bus object.
300      *
301      * @param[in] objectPath - The D-Bus object path
302      */
303     virtual void
304         setCriticalAssociation(const std::string& objectPath) const = 0;
305 
306     /**
307      * @brief Returns the manufacturing QuiesceOnError property
308      *
309      * @return bool - Manufacturing QuiesceOnError property
310      */
311     virtual bool getQuiesceOnError() const = 0;
312 
313     /**
314      * @brief Split location code into base and connector segments
315      *
316      * A location code that ends in '-Tx', where 'x' is a number,
317      * represents a connector, such as a USB cable connector.
318      *
319      * This function splits the passed in location code into a
320      * base and connector segment.  e.g.:
321      *   P0-T1 -> ['P0', '-T1']
322      *   P0 -> ['P0', '']
323      *
324      * @param[in] locationCode - location code to split
325      * @return pair<string, string> - The base and connector segments
326      */
327     static std::pair<std::string, std::string>
328         extractConnectorFromLocCode(const std::string& locationCode);
329 
330   protected:
331     /**
332      * @brief Sets the host on/off state and runs any
333      *        callback functions (if there was a change).
334      */
335     void setHostUp(bool hostUp)
336     {
337         if (_hostUp != hostUp)
338         {
339             _hostUp = hostUp;
340 
341             for (auto& [name, func] : _hostChangeCallbacks)
342             {
343                 try
344                 {
345                     func(_hostUp);
346                 }
347                 catch (std::exception& e)
348                 {
349                     using namespace phosphor::logging;
350                     log<level::ERR>("A host state change callback threw "
351                                     "an exception");
352                 }
353             }
354         }
355     }
356 
357     /**
358      * @brief The hardware management console status.  Always kept
359      *        up to date.
360      */
361     bool _hmcManaged = false;
362 
363     /**
364      * @brief The host up status.  Always kept up to date.
365      */
366     bool _hostUp = false;
367 
368     /**
369      * @brief The map of host state change subscriber
370      *        names to callback functions.
371      */
372     std::map<std::string, HostStateChangeFunc> _hostChangeCallbacks;
373 
374     /**
375      * @brief The BMC firmware version string
376      */
377     std::string _bmcFWVersion;
378 
379     /**
380      * @brief The server firmware version string
381      */
382     std::string _serverFWVersion;
383 
384     /**
385      * @brief The BMC firmware version ID string
386      */
387     std::string _bmcFWVersionID;
388 
389     /**
390      * @brief If sending PELs is enabled.
391      *
392      * This is usually set to false in manufacturing test.
393      */
394     bool _sendPELsToHost = true;
395 
396     /**
397      * @brief The BMC state property
398      */
399     std::string _bmcState;
400 
401     /**
402      * @brief The Chassis current power state property
403      */
404     std::string _chassisState;
405 
406     /**
407      * @brief The Chassis requested power transition property
408      */
409     std::string _chassisTransition;
410 
411     /**
412      * @brief The host state property
413      */
414     std::string _hostState;
415 };
416 
417 /**
418  * @class DataInterface
419  *
420  * Concrete implementation of DataInterfaceBase.
421  */
422 class DataInterface : public DataInterfaceBase
423 {
424   public:
425     DataInterface() = delete;
426     ~DataInterface() = default;
427     DataInterface(const DataInterface&) = default;
428     DataInterface& operator=(const DataInterface&) = default;
429     DataInterface(DataInterface&&) = default;
430     DataInterface& operator=(DataInterface&&) = default;
431 
432     /**
433      * @brief Constructor
434      *
435      * @param[in] bus - The sdbusplus bus object
436      */
437     explicit DataInterface(sdbusplus::bus::bus& bus);
438 
439     /**
440      * @brief Finds the D-Bus service name that hosts the
441      *        passed in path and interface.
442      *
443      * @param[in] objectPath - The D-Bus object path
444      * @param[in] interface - The D-Bus interface
445      */
446     DBusService getService(const std::string& objectPath,
447                            const std::string& interface) const;
448 
449     /**
450      * @brief Wrapper for the 'GetAll' properties method call
451      *
452      * @param[in] service - The D-Bus service to call it on
453      * @param[in] objectPath - The D-Bus object path
454      * @param[in] interface - The interface to get the props on
455      *
456      * @return DBusPropertyMap - The property results
457      */
458     DBusPropertyMap getAllProperties(const std::string& service,
459                                      const std::string& objectPath,
460                                      const std::string& interface) const;
461     /**
462      * @brief Wrapper for the 'Get' properties method call
463      *
464      * @param[in] service - The D-Bus service to call it on
465      * @param[in] objectPath - The D-Bus object path
466      * @param[in] interface - The interface to get the property on
467      * @param[in] property - The property name
468      * @param[out] value - Filled in with the property value.
469      */
470     void getProperty(const std::string& service, const std::string& objectPath,
471                      const std::string& interface, const std::string& property,
472                      DBusValue& value) const;
473     /**
474      * @brief Returns the machine Type/Model
475      *
476      * @return string - The machine Type/Model string
477      */
478     std::string getMachineTypeModel() const override;
479 
480     /**
481      * @brief Returns the machine serial number
482      *
483      * @return string - The machine serial number
484      */
485     std::string getMachineSerialNumber() const override;
486 
487     /**
488      * @brief Returns the motherboard CCIN
489      *
490      * @return std::string The motherboard CCIN
491      */
492     std::string getMotherboardCCIN() const override;
493 
494     /**
495      * @brief Returns the system IM
496      *
497      * @return std::vector The system IM keyword in 4 byte vector
498      */
499     std::vector<uint8_t> getSystemIMKeyword() const override;
500 
501     /**
502      * @brief Get the fields from the inventory necessary for doing
503      *        a callout on an inventory path.
504      *
505      * @param[in] inventoryPath - The item to get the data for
506      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
507      * @param[out] ccin - Filled in with the VINI/CC keyword
508      * @param[out] serialNumber - Filled in with the VINI/SN keyword
509      */
510     void getHWCalloutFields(const std::string& inventoryPath,
511                             std::string& fruPartNumber, std::string& ccin,
512                             std::string& serialNumber) const override;
513 
514     /**
515      * @brief Get the location code for an inventory item.
516      *
517      * Throws an exception if the inventory item doesn't have the
518      * location code interface.
519      *
520      * @param[in] inventoryPath - The item to get the data for
521      *
522      * @return std::string - The location code
523      */
524     std::string
525         getLocationCode(const std::string& inventoryPath) const override;
526 
527     /**
528      * @brief Get the list of system type names the system is called.
529      *
530      * @return std::vector<std::string> - The list of names
531      */
532     std::vector<std::string> getSystemNames() const override;
533 
534     /**
535      * @brief Fills in the placeholder 'Ufcs' in the passed in location
536      *        code with the machine feature code and serial number, which
537      *        is needed to create a valid location code.
538      *
539      * @param[in] locationCode - Location code value starting with Ufcs-, and
540      *                           if that isn't present it will be added first.
541      *
542      * @param[in] node - The node number the location is one.
543      *
544      * @return std::string - The expanded location code
545      */
546     std::string expandLocationCode(const std::string& locationCode,
547                                    uint16_t node) const override;
548 
549     /**
550      * @brief Returns the inventory path for the FRU that the location
551      *        code represents.
552      *
553      * @param[in] locationCode - If an expanded location code, then the
554      *                           full location code.
555      *                           If not expanded, a location code value
556      *                           starting with Ufcs-, and if that isn't
557      *                           present it will be added first.
558      *
559      * @param[in] node - The node number the location is on.  Ignored if the
560      *                   expanded location code is passed in.
561      *
562      * @param[in] expanded - If the location code already has the relevent
563      *                       VPD fields embedded in it.
564      *
565      * @return std::string - The inventory D-Bus object
566      */
567     std::string getInventoryFromLocCode(const std::string& locationCode,
568                                         uint16_t node,
569                                         bool expanded) const override;
570 
571     /**
572      * @brief Sets the Asserted property on the LED group passed in.
573      *
574      * @param[in] ledGroup - The LED group D-Bus path
575      * @param[in] value - The value to set it to
576      */
577     void assertLEDGroup(const std::string& ledGroup, bool value) const override;
578 
579     /**
580      * @brief Sets the Functional property on the OperationalStatus
581      *        interface on a D-Bus object.
582      *
583      * @param[in] objectPath - The D-Bus object path
584      * @param[in] functional - The value
585      */
586     void setFunctional(const std::string& objectPath,
587                        bool functional) const override;
588 
589     /**
590      * @brief Sets the critical association on the D-Bus object.
591      *
592      * @param[in] objectPath - The D-Bus object path
593      */
594     void setCriticalAssociation(const std::string& objectPath) const override;
595 
596     /**
597      * @brief Returns the manufacturing QuiesceOnError property
598      *
599      * @return bool - Manufacturing QuiesceOnError property
600      */
601     bool getQuiesceOnError() const override;
602 
603   private:
604     /**
605      * @brief Reads the BMC firmware version string and puts it into
606      *        _bmcFWVersion.
607      */
608     void readBMCFWVersion();
609 
610     /**
611      * @brief Reads the server firmware version string and puts it into
612      *        _serverFWVersion.
613      */
614     void readServerFWVersion();
615 
616     /**
617      * @brief Reads the BMC firmware version ID and puts it into
618      *        _bmcFWVersionID.
619      */
620     void readBMCFWVersionID();
621 
622     /**
623      * @brief Finds all D-Bus paths that contain any of the interfaces
624      *        passed in, by using GetSubTreePaths.
625      *
626      * @param[in] interfaces - The desired interfaces
627      *
628      * @return The D-Bus paths.
629      */
630     DBusPathList getPaths(const DBusInterfaceList& interfaces) const;
631 
632     /**
633      * @brief The interfacesAdded callback used on the inventory to
634      *        find the D-Bus object that has the motherboard interface.
635      *        When the motherboard is found, it then adds a PropertyWatcher
636      *        for the motherboard CCIN.
637      */
638     void motherboardIfaceAdded(sdbusplus::message::message& msg);
639 
640     /**
641      * @brief Adds the Ufcs- prefix to the location code passed in
642      *        if necessary.
643      *
644      * Needed because the location codes that come back from the
645      * message registry and device callout JSON don't have it.
646      *
647      * @param[in] - The location code without a prefix, like P1-C1
648      *
649      * @return std::string - The location code with the prefix
650      */
651     static std::string addLocationCodePrefix(const std::string& locationCode);
652 
653     /**
654      * @brief The D-Bus property or interface watchers that have callbacks
655      *        registered that will set members in this class when
656      *        they change.
657      */
658     std::vector<std::unique_ptr<DBusWatcher>> _properties;
659 
660     /**
661      * @brief The sdbusplus bus object for making D-Bus calls.
662      */
663     sdbusplus::bus::bus& _bus;
664 
665     /**
666      * @brief The interfacesAdded match object used to wait for inventory
667      *        interfaces to show up, so that the object with the motherboard
668      *        interface can be found.  After it is found, this object is
669      *        deleted.
670      */
671     std::unique_ptr<sdbusplus::bus::match_t> _inventoryIfacesAddedMatch;
672 };
673 
674 } // namespace pels
675 } // namespace openpower
676