1 #pragma once
2 
3 #include "config.h"
4 
5 #include "activation.hpp"
6 #include "association_interface.hpp"
7 #include "types.hpp"
8 #include "utils.hpp"
9 #include "version.hpp"
10 
11 #include <phosphor-logging/log.hpp>
12 #include <sdbusplus/server.hpp>
13 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
14 #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
15 
16 #include <filesystem>
17 #include <map>
18 #include <string>
19 #include <variant>
20 #include <vector>
21 
22 class TestItemUpdater;
23 
24 namespace phosphor
25 {
26 namespace software
27 {
28 namespace updater
29 {
30 
31 class Version;
32 
33 using ItemUpdaterInherit = sdbusplus::server::object_t<
34     sdbusplus::xyz::openbmc_project::Association::server::Definitions>;
35 
36 namespace MatchRules = sdbusplus::bus::match::rules;
37 
38 namespace fs = std::filesystem;
39 
40 /** @class ItemUpdater
41  *  @brief Manages the activation of the PSU version items.
42  */
43 class ItemUpdater :
44     public ItemUpdaterInherit,
45     public AssociationInterface,
46     public ActivationListener
47 {
48     friend class ::TestItemUpdater;
49 
50   public:
51     /** @brief Constructs ItemUpdater
52      *
53      * @param[in] bus    - The D-Bus bus object
54      * @param[in] path   - The D-Bus path
55      */
ItemUpdater(sdbusplus::bus_t & bus,const std::string & path)56     ItemUpdater(sdbusplus::bus_t& bus, const std::string& path) :
57         ItemUpdaterInherit(bus, path.c_str()), bus(bus),
58         versionMatch(
59             bus,
60             MatchRules::interfacesAdded() + MatchRules::path(SOFTWARE_OBJPATH),
61             std::bind(std::mem_fn(&ItemUpdater::onVersionInterfacesAddedMsg),
62                       this, std::placeholders::_1)),
63         psuInterfaceMatch(
64             bus,
65             MatchRules::interfacesAdded() +
66                 MatchRules::path("/xyz/openbmc_project/inventory") +
67                 MatchRules::sender("xyz.openbmc_project.Inventory.Manager"),
68             std::bind(std::mem_fn(&ItemUpdater::onPSUInterfacesAdded), this,
69                       std::placeholders::_1))
70     {
71         processPSUImageAndSyncToLatest();
72     }
73 
74     /** @brief Deletes version
75      *
76      *  @param[in] versionId - Id of the version to delete
77      */
78     void erase(const std::string& versionId);
79 
80     /** @brief Creates an active association to the
81      *  newly active software image
82      *
83      * @param[in]  path - The path to create the association to.
84      */
85     void createActiveAssociation(const std::string& path) override;
86 
87     /** @brief Add the functional association to the
88      *  new "running" PSU images
89      *
90      * @param[in]  path - The path to add the association to.
91      */
92     void addFunctionalAssociation(const std::string& path) override;
93 
94     /** @brief Add the updateable association to the
95      *  "running" PSU software image
96      *
97      * @param[in]  path - The path to create the association.
98      */
99     void addUpdateableAssociation(const std::string& path) override;
100 
101     /** @brief Removes the associations from the provided software image path
102      *
103      * @param[in]  path - The path to remove the association from.
104      */
105     void removeAssociation(const std::string& path) override;
106 
107     /** @brief Notify a PSU is updated
108      *
109      * @param[in]  versionId - The versionId of the activation
110      * @param[in]  psuInventoryPath - The PSU inventory path that is updated
111      */
112     void onUpdateDone(const std::string& versionId,
113                       const std::string& psuInventoryPath) override;
114 
115   private:
116     using Properties =
117         std::map<std::string, utils::UtilsInterface::PropertyType>;
118     using InterfacesAddedMap =
119         std::map<std::string,
120                  std::map<std::string, std::variant<bool, std::string>>>;
121 
122     /** @brief Callback function for Software.Version match.
123      *
124      * @param[in]  msg       - Data associated with subscribed signal
125      */
126     void onVersionInterfacesAddedMsg(sdbusplus::message_t& msg);
127 
128     /** @brief Called when new Software.Version interfaces are found
129      *  @details Creates an Activation D-Bus object if appropriate
130      *           Throws an exception if an error occurs.
131      *
132      * @param[in]  path       - D-Bus object path
133      * @param[in]  interfaces - D-Bus interfaces that were added
134      */
135     void onVersionInterfacesAdded(const std::string& path,
136                                   const InterfacesAddedMap& interfaces);
137 
138     /** @brief Callback function for PSU inventory match.
139      *
140      * @param[in]  msg       - Data associated with subscribed signal
141      */
142     void onPsuInventoryChangedMsg(sdbusplus::message_t& msg);
143 
144     /** @brief Called when a PSU inventory object has changed
145      *  @details Update an Activation D-Bus object for PSU inventory.
146      *           Throws an exception if an error occurs.
147      *
148      * @param[in]  psuPath - The PSU inventory path
149      * @param[in]  properties - The updated properties
150      */
151     void onPsuInventoryChanged(const std::string& psuPath,
152                                const Properties& properties);
153 
154     /** @brief Create Activation object */
155     std::unique_ptr<Activation> createActivationObject(
156         const std::string& path, const std::string& versionId,
157         const std::string& extVersion, Activation::Status activationStatus,
158         const AssociationList& assocs, const std::string& filePath);
159 
160     /** @brief Create Version object */
161     std::unique_ptr<Version> createVersionObject(
162         const std::string& objPath, const std::string& versionId,
163         const std::string& versionString,
164         sdbusplus::xyz::openbmc_project::Software::server::Version::
165             VersionPurpose versionPurpose);
166 
167     /** @brief Create Activation and Version object for PSU inventory
168      *  @details If the same version exists for multiple PSUs, just add
169      *           related association, instead of creating new objects.
170      * */
171     void createPsuObject(const std::string& psuInventoryPath,
172                          const std::string& psuVersion);
173 
174     /** @brief Remove Activation and Version object for PSU inventory
175      *  @details If the same version exists for multiple PSUs, just remove
176      *           related association.
177      *           If the version has no association, the Activation and
178      *           Version object will be removed
179      */
180     void removePsuObject(const std::string& psuInventoryPath);
181 
182     /** @brief Add PSU inventory path to the PSU status map
183      *  @details Also adds a PropertiesChanged listener for the inventory path
184      *           so we are notified when the Present property changes.
185      *           Does nothing if the inventory path already exists in the map.
186      *
187      * @param[in]  psuPath - The PSU inventory path
188      */
189     void addPsuToStatusMap(const std::string& psuPath);
190 
191     /** @brief Handle a change in presence for a PSU.
192      *
193      * @param[in]  psuPath - The PSU inventory path
194      */
195     void handlePSUPresenceChanged(const std::string& psuPath);
196 
197     /**
198      * @brief Create and populate the active PSU Version.
199      */
200     void processPSUImage();
201 
202     /** @brief Create PSU Version from stored images */
203     void processStoredImage();
204 
205     /** @brief Scan a directory and create PSU Version from stored images
206      *  @details Throws an exception if an error occurs
207      *
208      * @param[in] dir Directory path to scan
209      */
210     void scanDirectory(const fs::path& dir);
211 
212     /** @brief Find the PSU model subdirectory within the specified directory
213      *  @details Throws an exception if an error occurs
214      *
215      * @param[in] dir Directory path to search
216      *
217      * @return Subdirectory path, or an empty path if none found
218      */
219     fs::path findModelDirectory(const fs::path& dir);
220 
221     /** @brief Get the versionId of the latest PSU version */
222     std::optional<std::string> getLatestVersionId();
223 
224     /** @brief Update PSUs to the latest version */
225     void syncToLatestImage();
226 
227     /** @brief Invoke the activation via DBus */
228     static void invokeActivation(const std::unique_ptr<Activation>& activation);
229 
230     /** @brief Callback function for interfaces added signal.
231      *
232      * This method is called when new interfaces are added. It updates the
233      * internal status map and processes the new PSU if it's present.
234      *
235      *  @param[in] msg - Data associated with subscribed signal
236      */
237     void onPSUInterfacesAdded(sdbusplus::message_t& msg);
238 
239     /**
240      * @brief Handles the processing of PSU images.
241      *
242      * This function responsible for invoking the sequence of processing PSU
243      * images, processing stored images, and syncing to the latest firmware
244      * image.
245      */
246     void processPSUImageAndSyncToLatest();
247 
248     /** @brief Retrieve FW version from IMG_DIR_BUILTIN
249      *
250      * This function retrieves the firmware version from the PSU model directory
251      * that is in the IMG_DIR_BUILTIN. It loops through the activations map to
252      * find matching path starts with IMG_DIR_BUILTIN, then gets the
253      * corresponding version ID, and then looks it up in the versions map to
254      * retrieve the associated version string.
255      */
256     std::string getFWVersionFromBuiltinDir();
257 
258     /** @brief Persistent sdbusplus D-Bus bus connection. */
259     sdbusplus::bus_t& bus;
260 
261     /** @brief Persistent map of Activation D-Bus objects and their
262      * version id */
263     std::map<std::string, std::unique_ptr<Activation>> activations;
264 
265     /** @brief Persistent map of Version D-Bus objects and their
266      * version id */
267     std::map<std::string, std::unique_ptr<Version>> versions;
268 
269     /** @brief The reference map of PSU Inventory objects and the
270      * Activation*/
271     std::map<std::string, const std::unique_ptr<Activation>&>
272         psuPathActivationMap;
273 
274     /** @brief sdbusplus signal match for PSU Software*/
275     sdbusplus::bus::match_t versionMatch;
276 
277     /** @brief sdbusplus signal matches for PSU Inventory */
278     std::vector<sdbusplus::bus::match_t> psuMatches;
279 
280     /** @brief This entry's associations */
281     AssociationList assocs;
282 
283     /** @brief A collection of the version strings */
284     std::set<std::string> versionStrings;
285 
286     /** @brief A struct to hold the PSU present status and model */
287     struct psuStatus
288     {
289         bool present;
290         std::string model;
291     };
292 
293     /** @brief The map of PSU inventory path and the psuStatus
294      *
295      * It is used to handle psu inventory changed event, that only create psu
296      * software object when a PSU is present and the model is retrieved */
297     std::map<std::string, psuStatus> psuStatusMap;
298 
299     /** @brief Signal match for PSU interfaces added.
300      *
301      * This match listens for D-Bus signals indicating new interface has been
302      * added. When such a signal received, it triggers the
303      * `onInterfacesAdded` method to handle the new PSU.
304      */
305     sdbusplus::bus::match_t psuInterfaceMatch;
306 };
307 
308 } // namespace updater
309 } // namespace software
310 } // namespace phosphor
311