1 #pragma once
2 
3 #include "dbus_types.hpp"
4 #include "dbus_watcher.hpp"
5 
6 #include <filesystem>
7 #include <fstream>
8 #include <phosphor-logging/log.hpp>
9 #include <sdbusplus/bus.hpp>
10 #include <sdbusplus/bus/match.hpp>
11 
12 namespace openpower
13 {
14 namespace pels
15 {
16 
17 /**
18  * @class DataInterface
19  *
20  * A base class for gathering data about the system for use
21  * in PELs. Implemented this way to facilitate mocking.
22  */
23 class DataInterfaceBase
24 {
25   public:
26     DataInterfaceBase() = default;
27     virtual ~DataInterfaceBase() = default;
28     DataInterfaceBase(const DataInterfaceBase&) = default;
29     DataInterfaceBase& operator=(const DataInterfaceBase&) = default;
30     DataInterfaceBase(DataInterfaceBase&&) = default;
31     DataInterfaceBase& operator=(DataInterfaceBase&&) = default;
32 
33     /**
34      * @brief Returns the machine Type/Model
35      *
36      * @return string - The machine Type/Model string
37      */
38     virtual std::string getMachineTypeModel() const = 0;
39 
40     /**
41      * @brief Returns the machine serial number
42      *
43      * @return string - The machine serial number
44      */
45     virtual std::string getMachineSerialNumber() const = 0;
46 
47     /**
48      * @brief Says if the system is managed by a hardware
49      *        management console.
50      * @return bool - If the system is HMC managed
51      */
52     virtual bool isHMCManaged() const
53     {
54         return _hmcManaged;
55     }
56 
57     /**
58      * @brief Says if the host is up and running
59      *
60      * @return bool - If the host is running
61      */
62     virtual bool isHostUp() const
63     {
64         return _hostUp;
65     }
66 
67     using HostStateChangeFunc = std::function<void(bool)>;
68 
69     /**
70      * @brief Register a callback function that will get
71      *        called on all host on/off transitions.
72      *
73      * The void(bool) function will get passed the new
74      * value of the host state.
75      *
76      * @param[in] name - The subscription name
77      * @param[in] func - The function to run
78      */
79     void subscribeToHostStateChange(const std::string& name,
80                                     HostStateChangeFunc func)
81     {
82         _hostChangeCallbacks[name] = func;
83     }
84 
85     /**
86      * @brief Unsubscribe from host state changes.
87      *
88      * @param[in] name - The subscription name
89      */
90     void unsubscribeFromHostStateChange(const std::string& name)
91     {
92         _hostChangeCallbacks.erase(name);
93     }
94 
95     /**
96      * @brief Returns the BMC firmware version
97      *
98      * @return std::string - The BMC version
99      */
100     virtual std::string getBMCFWVersion() const
101     {
102         return _bmcFWVersion;
103     }
104 
105     /**
106      * @brief Returns the server firmware version
107      *
108      * @return std::string - The server firmware version
109      */
110     virtual std::string getServerFWVersion() const
111     {
112         return _serverFWVersion;
113     }
114 
115     /**
116      * @brief Returns the BMC FW version ID
117      *
118      * @return std::string - The BMC FW version ID
119      */
120     virtual std::string getBMCFWVersionID() const
121     {
122         return _bmcFWVersionID;
123     }
124 
125     /**
126      * @brief Returns the process name given its PID.
127      *
128      * @param[in] pid - The PID value as a string
129      *
130      * @return std::optional<std::string> - The name, or std::nullopt
131      */
132     std::optional<std::string> getProcessName(const std::string& pid) const
133     {
134         namespace fs = std::filesystem;
135 
136         fs::path path{"/proc"};
137         path /= fs::path{pid} / "exe";
138 
139         if (fs::exists(path))
140         {
141             return fs::read_symlink(path);
142         }
143 
144         return std::nullopt;
145     }
146 
147     /**
148      * @brief Returns the time the system was running.
149      *
150      * @return std::optional<uint64_t> - The System uptime or std::nullopt
151      */
152     std::optional<uint64_t> getUptimeInSeconds() const
153     {
154         std::ifstream versionFile{"/proc/uptime"};
155         std::string line{};
156 
157         std::getline(versionFile, line);
158         auto pos = line.find(" ");
159         if (pos == std::string::npos)
160         {
161             return std::nullopt;
162         }
163 
164         uint64_t seconds = atol(line.substr(0, pos).c_str());
165         if (seconds == 0)
166         {
167             return std::nullopt;
168         }
169 
170         return seconds;
171     }
172 
173     /**
174      * @brief Returns the time the system was running.
175      *
176      * @param[in] seconds - The number of seconds the system has been running
177      *
178      * @return std::string - days/hours/minutes/seconds
179      */
180     std::string getBMCUptime(uint64_t seconds) const
181     {
182         time_t t(seconds);
183         tm* p = gmtime(&t);
184 
185         std::string uptime = std::to_string(p->tm_year - 70) + "y " +
186                              std::to_string(p->tm_yday) + "d " +
187                              std::to_string(p->tm_hour) + "h " +
188                              std::to_string(p->tm_min) + "m " +
189                              std::to_string(p->tm_sec) + "s";
190 
191         return uptime;
192     }
193 
194     /**
195      * @brief Returns the system load average over the past 1 minute, 5 minutes
196      *        and 15 minutes.
197      *
198      * @return std::string - The system load average
199      */
200     std::string getBMCLoadAvg() const
201     {
202         std::string loadavg{};
203 
204         std::ifstream loadavgFile{"/proc/loadavg"};
205         std::string line;
206         std::getline(loadavgFile, line);
207 
208         size_t count = 3;
209         for (size_t i = 0; i < count; i++)
210         {
211             auto pos = line.find(" ");
212             if (pos == std::string::npos)
213             {
214                 return {};
215             }
216 
217             if (i != count - 1)
218             {
219                 loadavg.append(line.substr(0, pos + 1));
220             }
221             else
222             {
223                 loadavg.append(line.substr(0, pos));
224             }
225 
226             line = line.substr(pos + 1);
227         }
228 
229         return loadavg;
230     }
231 
232     /**
233      * @brief Returns the 'send event logs to host' setting.
234      *
235      * @return bool - If sending PELs to the host is enabled.
236      */
237     virtual bool getHostPELEnablement() const
238     {
239         return _sendPELsToHost;
240     }
241 
242     /**
243      * @brief Returns the BMC state
244      *
245      * @return std::string - The BMC state property value
246      */
247     virtual std::string getBMCState() const
248     {
249         return _bmcState;
250     }
251 
252     /**
253      * @brief Returns the Chassis state
254      *
255      * @return std::string - The chassis state property value
256      */
257     virtual std::string getChassisState() const
258     {
259         return _chassisState;
260     }
261 
262     /**
263      * @brief Returns the chassis requested power
264      *        transition value.
265      *
266      * @return std::string - The chassis transition property
267      */
268     virtual std::string getChassisTransition() const
269     {
270         return _chassisTransition;
271     }
272 
273     /**
274      * @brief Returns the Host state
275      *
276      * @return std::string - The Host state property value
277      */
278     virtual std::string getHostState() const
279     {
280         return _hostState;
281     }
282 
283     /**
284      * @brief Returns the Boot state
285      *
286      * @return std::string - The Boot state property value
287      */
288     virtual std::string getBootState() const
289     {
290         return _bootState;
291     }
292 
293     /**
294      * @brief Returns the motherboard CCIN
295      *
296      * @return std::string The motherboard CCIN
297      */
298     virtual std::string getMotherboardCCIN() const = 0;
299 
300     /**
301      * @brief Returns the system IM
302      *
303      * @return std::string The system IM
304      */
305     virtual std::vector<uint8_t> getSystemIMKeyword() const = 0;
306 
307     /**
308      * @brief Get the fields from the inventory necessary for doing
309      *        a callout on an inventory path.
310      *
311      * @param[in] inventoryPath - The item to get the data for
312      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
313      * @param[out] ccin - Filled in with the VINI/CC keyword
314      * @param[out] serialNumber - Filled in with the VINI/SN keyword
315      */
316     virtual void getHWCalloutFields(const std::string& inventoryPath,
317                                     std::string& fruPartNumber,
318                                     std::string& ccin,
319                                     std::string& serialNumber) const = 0;
320 
321     /**
322      * @brief Get the location code for an inventory item.
323      *
324      * @param[in] inventoryPath - The item to get the data for
325      *
326      * @return std::string - The location code
327      */
328     virtual std::string
329         getLocationCode(const std::string& inventoryPath) const = 0;
330 
331     /**
332      * @brief Get the list of system type names the system is called.
333      *
334      * @return std::vector<std::string> - The list of names
335      */
336     virtual std::vector<std::string> getSystemNames() const = 0;
337 
338     /**
339      * @brief Fills in the placeholder 'Ufcs' in the passed in location
340      *        code with the machine feature code and serial number, which
341      *        is needed to create a valid location code.
342      *
343      * @param[in] locationCode - Location code value starting with Ufcs-, and
344      *                           if that isn't present it will be added first.
345      *
346      * @param[in] node - The node number the location is on.
347      *
348      * @return std::string - The expanded location code
349      */
350     virtual std::string expandLocationCode(const std::string& locationCode,
351                                            uint16_t node) const = 0;
352 
353     /**
354      * @brief Returns the inventory path for the FRU that the location
355      *        code represents.
356      *
357      * @param[in] locationCode - If an expanded location code, then the
358      *                           full location code.
359      *                           If not expanded, a location code value
360      *                           starting with Ufcs-, and if that isn't
361      *                           present it will be added first.
362      *
363      * @param[in] node - The node number the location is on.  Ignored if the
364      *                   expanded location code is passed in.
365      *
366      * @param[in] expanded - If the location code already has the relevent
367      *                       VPD fields embedded in it.
368      *
369      * @return std::string - The inventory D-Bus object
370      */
371     virtual std::string getInventoryFromLocCode(const std::string& LocationCode,
372                                                 uint16_t node,
373                                                 bool expanded) const = 0;
374 
375     /**
376      * @brief Sets the Asserted property on the LED group passed in.
377      *
378      * @param[in] ledGroup - The LED group D-Bus path
379      * @param[in] value - The value to set it to
380      */
381     virtual void assertLEDGroup(const std::string& ledGroup,
382                                 bool value) const = 0;
383 
384     /**
385      * @brief Sets the Functional property on the OperationalStatus
386      *        interface on a D-Bus object.
387      *
388      * @param[in] objectPath - The D-Bus object path
389      * @param[in] functional - The value
390      */
391     virtual void setFunctional(const std::string& objectPath,
392                                bool functional) const = 0;
393 
394     /**
395      * @brief Sets the critical association on the D-Bus object.
396      *
397      * @param[in] objectPath - The D-Bus object path
398      */
399     virtual void
400         setCriticalAssociation(const std::string& objectPath) const = 0;
401 
402     /**
403      * @brief Returns the manufacturing QuiesceOnError property
404      *
405      * @return bool - Manufacturing QuiesceOnError property
406      */
407     virtual bool getQuiesceOnError() const = 0;
408 
409     /**
410      * @brief Split location code into base and connector segments
411      *
412      * A location code that ends in '-Tx', where 'x' is a number,
413      * represents a connector, such as a USB cable connector.
414      *
415      * This function splits the passed in location code into a
416      * base and connector segment.  e.g.:
417      *   P0-T1 -> ['P0', '-T1']
418      *   P0 -> ['P0', '']
419      *
420      * @param[in] locationCode - location code to split
421      * @return pair<string, string> - The base and connector segments
422      */
423     static std::pair<std::string, std::string>
424         extractConnectorFromLocCode(const std::string& locationCode);
425 
426     /**
427      * @brief Returns the dump status
428      *
429      * @return bool dump status
430      */
431     virtual std::vector<bool>
432         checkDumpStatus(const std::vector<std::string>& type) const = 0;
433 
434     /**
435      * @brief Create guard record
436      *
437      *  @param[in] binPath: phal devtree binary path used as key
438      *  @param[in] type: Guard type
439      *  @param[in] logPath: error log entry object path
440      */
441     virtual void createGuardRecord(const std::vector<uint8_t>& binPath,
442                                    const std::string& type,
443                                    const std::string& logPath) const = 0;
444 
445     /**
446      * @brief Create Progress SRC property on the boot progress
447      *        interface on a D-Bus object.
448      *
449      * @param[in] priSRC - Primary SRC value (e.g. BD8D1001)
450      * @param[in] srcStruct - Full SRC base structure
451      */
452     virtual void
453         createProgressSRC(const uint64_t& priSRC,
454                           const std::vector<uint8_t>& srcStruct) const = 0;
455 
456     /**
457      * @brief Get the list of unresolved OpenBMC event log ids that have an
458      * associated hardware isolation entry.
459      *
460      * @return std::vector<uint32_t> - The list of log ids
461      */
462     virtual std::vector<uint32_t> getLogIDWithHwIsolation() const = 0;
463 
464   protected:
465     /**
466      * @brief Sets the host on/off state and runs any
467      *        callback functions (if there was a change).
468      */
469     void setHostUp(bool hostUp)
470     {
471         if (_hostUp != hostUp)
472         {
473             _hostUp = hostUp;
474 
475             for (auto& [name, func] : _hostChangeCallbacks)
476             {
477                 try
478                 {
479                     func(_hostUp);
480                 }
481                 catch (const std::exception& e)
482                 {
483                     using namespace phosphor::logging;
484                     log<level::ERR>("A host state change callback threw "
485                                     "an exception");
486                 }
487             }
488         }
489     }
490 
491     /**
492      * @brief The hardware management console status.  Always kept
493      *        up to date.
494      */
495     bool _hmcManaged = false;
496 
497     /**
498      * @brief The host up status.  Always kept up to date.
499      */
500     bool _hostUp = false;
501 
502     /**
503      * @brief The map of host state change subscriber
504      *        names to callback functions.
505      */
506     std::map<std::string, HostStateChangeFunc> _hostChangeCallbacks;
507 
508     /**
509      * @brief The BMC firmware version string
510      */
511     std::string _bmcFWVersion;
512 
513     /**
514      * @brief The server firmware version string
515      */
516     std::string _serverFWVersion;
517 
518     /**
519      * @brief The BMC firmware version ID string
520      */
521     std::string _bmcFWVersionID;
522 
523     /**
524      * @brief If sending PELs is enabled.
525      *
526      * This is usually set to false in manufacturing test.
527      */
528     bool _sendPELsToHost = true;
529 
530     /**
531      * @brief The BMC state property
532      */
533     std::string _bmcState;
534 
535     /**
536      * @brief The Chassis current power state property
537      */
538     std::string _chassisState;
539 
540     /**
541      * @brief The Chassis requested power transition property
542      */
543     std::string _chassisTransition;
544 
545     /**
546      * @brief The host state property
547      */
548     std::string _hostState;
549 
550     /**
551      * @brief The boot state property
552      */
553     std::string _bootState;
554 };
555 
556 /**
557  * @class DataInterface
558  *
559  * Concrete implementation of DataInterfaceBase.
560  */
561 class DataInterface : public DataInterfaceBase
562 {
563   public:
564     DataInterface() = delete;
565     ~DataInterface() = default;
566     DataInterface(const DataInterface&) = default;
567     DataInterface& operator=(const DataInterface&) = default;
568     DataInterface(DataInterface&&) = default;
569     DataInterface& operator=(DataInterface&&) = default;
570 
571     /**
572      * @brief Constructor
573      *
574      * @param[in] bus - The sdbusplus bus object
575      */
576     explicit DataInterface(sdbusplus::bus_t& bus);
577 
578     /**
579      * @brief Finds the D-Bus service name that hosts the
580      *        passed in path and interface.
581      *
582      * @param[in] objectPath - The D-Bus object path
583      * @param[in] interface - The D-Bus interface
584      */
585     DBusService getService(const std::string& objectPath,
586                            const std::string& interface) const;
587 
588     /**
589      * @brief Wrapper for the 'GetAll' properties method call
590      *
591      * @param[in] service - The D-Bus service to call it on
592      * @param[in] objectPath - The D-Bus object path
593      * @param[in] interface - The interface to get the props on
594      *
595      * @return DBusPropertyMap - The property results
596      */
597     DBusPropertyMap getAllProperties(const std::string& service,
598                                      const std::string& objectPath,
599                                      const std::string& interface) const;
600     /**
601      * @brief Wrapper for the 'Get' properties method call
602      *
603      * @param[in] service - The D-Bus service to call it on
604      * @param[in] objectPath - The D-Bus object path
605      * @param[in] interface - The interface to get the property on
606      * @param[in] property - The property name
607      * @param[out] value - Filled in with the property value.
608      */
609     void getProperty(const std::string& service, const std::string& objectPath,
610                      const std::string& interface, const std::string& property,
611                      DBusValue& value) const;
612     /**
613      * @brief Returns the machine Type/Model
614      *
615      * @return string - The machine Type/Model string
616      */
617     std::string getMachineTypeModel() const override;
618 
619     /**
620      * @brief Returns the machine serial number
621      *
622      * @return string - The machine serial number
623      */
624     std::string getMachineSerialNumber() const override;
625 
626     /**
627      * @brief Returns the motherboard CCIN
628      *
629      * @return std::string The motherboard CCIN
630      */
631     std::string getMotherboardCCIN() const override;
632 
633     /**
634      * @brief Returns the system IM
635      *
636      * @return std::vector The system IM keyword in 4 byte vector
637      */
638     std::vector<uint8_t> getSystemIMKeyword() const override;
639 
640     /**
641      * @brief Get the fields from the inventory necessary for doing
642      *        a callout on an inventory path.
643      *
644      * @param[in] inventoryPath - The item to get the data for
645      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
646      * @param[out] ccin - Filled in with the VINI/CC keyword
647      * @param[out] serialNumber - Filled in with the VINI/SN keyword
648      */
649     void getHWCalloutFields(const std::string& inventoryPath,
650                             std::string& fruPartNumber, std::string& ccin,
651                             std::string& serialNumber) const override;
652 
653     /**
654      * @brief Get the location code for an inventory item.
655      *
656      * Throws an exception if the inventory item doesn't have the
657      * location code interface.
658      *
659      * @param[in] inventoryPath - The item to get the data for
660      *
661      * @return std::string - The location code
662      */
663     std::string
664         getLocationCode(const std::string& inventoryPath) const override;
665 
666     /**
667      * @brief Get the list of system type names the system is called.
668      *
669      * @return std::vector<std::string> - The list of names
670      */
671     std::vector<std::string> getSystemNames() const override;
672 
673     /**
674      * @brief Fills in the placeholder 'Ufcs' in the passed in location
675      *        code with the machine feature code and serial number, which
676      *        is needed to create a valid location code.
677      *
678      * @param[in] locationCode - Location code value starting with Ufcs-, and
679      *                           if that isn't present it will be added first.
680      *
681      * @param[in] node - The node number the location is one.
682      *
683      * @return std::string - The expanded location code
684      */
685     std::string expandLocationCode(const std::string& locationCode,
686                                    uint16_t node) const override;
687 
688     /**
689      * @brief Returns the inventory path for the FRU that the location
690      *        code represents.
691      *
692      * @param[in] locationCode - If an expanded location code, then the
693      *                           full location code.
694      *                           If not expanded, a location code value
695      *                           starting with Ufcs-, and if that isn't
696      *                           present it will be added first.
697      *
698      * @param[in] node - The node number the location is on.  Ignored if the
699      *                   expanded location code is passed in.
700      *
701      * @param[in] expanded - If the location code already has the relevent
702      *                       VPD fields embedded in it.
703      *
704      * @return std::string - The inventory D-Bus object
705      */
706     std::string getInventoryFromLocCode(const std::string& locationCode,
707                                         uint16_t node,
708                                         bool expanded) const override;
709 
710     /**
711      * @brief Sets the Asserted property on the LED group passed in.
712      *
713      * @param[in] ledGroup - The LED group D-Bus path
714      * @param[in] value - The value to set it to
715      */
716     void assertLEDGroup(const std::string& ledGroup, bool value) const override;
717 
718     /**
719      * @brief Sets the Functional property on the OperationalStatus
720      *        interface on a D-Bus object.
721      *
722      * @param[in] objectPath - The D-Bus object path
723      * @param[in] functional - The value
724      */
725     void setFunctional(const std::string& objectPath,
726                        bool functional) const override;
727 
728     /**
729      * @brief Sets the critical association on the D-Bus object.
730      *
731      * @param[in] objectPath - The D-Bus object path
732      */
733     void setCriticalAssociation(const std::string& objectPath) const override;
734 
735     /**
736      * @brief Returns the manufacturing QuiesceOnError property
737      *
738      * @return bool - Manufacturing QuiesceOnError property
739      */
740     bool getQuiesceOnError() const override;
741 
742     /**
743      * @brief Returns the dump status
744      *
745      * @param[in] type - The dump type to check for
746      *
747      * @return bool dump status
748      */
749     std::vector<bool>
750         checkDumpStatus(const std::vector<std::string>& type) const override;
751 
752     /**
753      * @brief Create guard record
754      *
755      *  @param[in] binPath: phal devtree binary path used as key
756      *  @param[in] type: Guard type
757      *  @param[in] logPath: error log entry object path
758      */
759     void createGuardRecord(const std::vector<uint8_t>& binPath,
760                            const std::string& type,
761                            const std::string& logPath) const override;
762 
763     /**
764      * @brief Create Progress SRC property on the boot progress
765      *        interface on a D-Bus object.
766      *
767      * @param[in] priSRC - Primary SRC value
768      * @param[in] srcStruct - Full SRC base structure
769      */
770     void
771         createProgressSRC(const uint64_t& priSRC,
772                           const std::vector<uint8_t>& srcStruct) const override;
773 
774     /**
775      * @brief Get the list of unresolved OpenBMC event log ids that have an
776      * associated hardware isolation entry.
777      *
778      * @return std::vector<uint32_t> - The list of log ids
779      */
780     std::vector<uint32_t> getLogIDWithHwIsolation() const override;
781 
782   private:
783     /**
784      * @brief Reads the BMC firmware version string and puts it into
785      *        _bmcFWVersion.
786      */
787     void readBMCFWVersion();
788 
789     /**
790      * @brief Reads the server firmware version string and puts it into
791      *        _serverFWVersion.
792      */
793     void readServerFWVersion();
794 
795     /**
796      * @brief Reads the BMC firmware version ID and puts it into
797      *        _bmcFWVersionID.
798      */
799     void readBMCFWVersionID();
800 
801     /**
802      * @brief Finds all D-Bus paths that contain any of the interfaces
803      *        passed in, by using GetSubTreePaths.
804      *
805      * @param[in] interfaces - The desired interfaces
806      *
807      * @return The D-Bus paths.
808      */
809     DBusPathList getPaths(const DBusInterfaceList& interfaces) const;
810 
811     /**
812      * @brief The interfacesAdded callback used on the inventory to
813      *        find the D-Bus object that has the motherboard interface.
814      *        When the motherboard is found, it then adds a PropertyWatcher
815      *        for the motherboard CCIN.
816      */
817     void motherboardIfaceAdded(sdbusplus::message_t& msg);
818 
819     /**
820      * @brief Adds the Ufcs- prefix to the location code passed in
821      *        if necessary.
822      *
823      * Needed because the location codes that come back from the
824      * message registry and device callout JSON don't have it.
825      *
826      * @param[in] - The location code without a prefix, like P1-C1
827      *
828      * @return std::string - The location code with the prefix
829      */
830     static std::string addLocationCodePrefix(const std::string& locationCode);
831 
832     /**
833      * @brief The D-Bus property or interface watchers that have callbacks
834      *        registered that will set members in this class when
835      *        they change.
836      */
837     std::vector<std::unique_ptr<DBusWatcher>> _properties;
838 
839     /**
840      * @brief The sdbusplus bus object for making D-Bus calls.
841      */
842     sdbusplus::bus_t& _bus;
843 
844     /**
845      * @brief The interfacesAdded match object used to wait for inventory
846      *        interfaces to show up, so that the object with the motherboard
847      *        interface can be found.  After it is found, this object is
848      *        deleted.
849      */
850     std::unique_ptr<sdbusplus::bus::match_t> _inventoryIfacesAddedMatch;
851 };
852 
853 } // namespace pels
854 } // namespace openpower
855