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