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