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