1 #pragma once
2 
3 #include "config.h"
4 
5 #include "types.hpp"
6 
7 #include <nlohmann/json.hpp>
8 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
9 
10 #include <any>
11 #include <filesystem>
12 
13 namespace phosphor
14 {
15 namespace inventory
16 {
17 namespace manager
18 {
19 namespace associations
20 {
21 
22 static constexpr auto forwardTypePos = 0;
23 static constexpr auto reverseTypePos = 1;
24 using Types = std::tuple<std::string, std::string>;
25 using Paths = std::vector<std::string>;
26 
27 static constexpr auto typesPos = 0;
28 static constexpr auto pathsPos = 1;
29 using EndpointsEntry = std::vector<std::tuple<Types, Paths>>;
30 
31 using AssociationMap = std::map<std::string, EndpointsEntry>;
32 
33 using AssociationObject = sdbusplus::server::object::object<
34     sdbusplus::xyz::openbmc_project::Association::server::Definitions>;
35 
36 using AssociationIfaceMap =
37     std::map<std::string, std::unique_ptr<AssociationObject>>;
38 
39 /**
40  * @class Manager
41  *
42  * @brief This class provides the ability to add org.openbmc.Associations
43  *        interfaces on inventory D-Bus objects, based on a definition in a
44  *        JSON file.
45  *
46  *        The purpose for this is to be able to associate other D-Bus paths
47  *        with the inventory items they relate to.
48  *
49  *        For example, a card temperature sensor D-Bus object can be associated
50  *        with the D-Bus object for that card's inventory entry so that some
51  *        code can tie them together.
52  */
53 class Manager
54 {
55   public:
56     struct Condition
57     {
58         std::string path;
59         std::string interface;
60         std::string property;
61         std::vector<InterfaceVariantType> values;
62         std::filesystem::path file;
63         InterfaceVariantType actualValue;
64     };
65 
66     Manager() = delete;
67     ~Manager() = default;
68     Manager(const Manager&) = delete;
69     Manager& operator=(const Manager&) = delete;
70     Manager(Manager&&) = delete;
71     Manager& operator=(Manager&&) = delete;
72 
73     /**
74      * @brief Constructor
75      *
76      * @param[in] bus - sdbusplus object
77      * @param[in] jsonPath - path to the JSON File that contains associations
78      */
79     Manager(sdbusplus::bus::bus& bus, const std::string& jsonPath);
80 
81     /**
82      * @brief Constructor
83      *
84      * @param[in] bus - sdbusplus object
85      */
86     explicit Manager(sdbusplus::bus::bus& bus) :
87         Manager(bus, ASSOCIATIONS_FILE_PATH)
88     {}
89 
90     /**
91      * @brief Creates any association D-Bus interfaces required based on
92      *        the JSON associations definition for the object path passed
93      *        in.
94      *
95      * Called after PIM creates a new inventory D-Bus interface on objectPath.
96      *
97      * @param[in] objectPath - the D-Bus object path to check for associations
98      * @param[in] deferSignal - whether or not to send a Properties or
99      *                          ObjectManager signal
100      */
101     void createAssociations(const std::string& objectPath, bool deferSignal);
102 
103     /**
104      * @brief Returned the association configuration.
105      *        Used for testing.
106      *
107      * @return AssociationMap& - the association config
108      */
109     const AssociationMap& getAssociationsConfig()
110     {
111         return _associations;
112     }
113 
114     /**
115      * @brief Returns the list of conditions
116      *
117      * @return vector<Condition>& - The conditions
118      */
119     std::vector<Condition>& getConditions()
120     {
121         return _conditions;
122     }
123 
124     /**
125      * @brief Says if there are conditions that need to be met
126      *        before an associations file is valid.
127      *
128      * @return bool - If there are pending conditions
129      */
130     bool pendingCondition() const
131     {
132         return !_conditions.empty();
133     }
134 
135     /**
136      * @brief Checks if a pending condition is satisfied based on the
137      *        path, interface, and property value of the object passed
138      *        in.
139      *
140      *        If it is valid, it will load the associations pointed to
141      *        by that condition and erase the _conditions vector as
142      *        there are no longer any pending conditions.
143      *
144      * @param[in] objectPath - The D-Bus path of the object to check
145      * @param[in] in object - The interface and properties of the object
146      *
147      * @return bool - If the object matched a condition
148      */
149     bool conditionMatch(const sdbusplus::message::object_path& objectPath,
150                         const Object& object);
151 
152     /**
153      * @brief Checks if a pending condition is satisfied based on if the
154      *        actualValue field in the condition matches one of the values
155      *        in the values field.
156      *
157      *        The actualValue field was previously set by code based on the
158      *        real property value of the specified interface on the specified
159      *        path.
160      *
161      *        If it is valid, it will load the associations pointed to
162      *        by that condition and erase the _conditions vector as
163      *        there are no longer any pending conditions.
164      *
165      * @return bool - If a condition was met
166      */
167     bool conditionMatch();
168 
169   private:
170     /**
171      *  @brief Loads the association JSON into the _associations data
172      *         structure.
173      *
174      *  @param[in] json - The associations JSON
175      */
176     void load(const nlohmann::json& json);
177 
178     /**
179      * @brief Creates an instance of an org.openbmc.Associations
180      *        interface using the passed in properties.
181      *
182      * @param[in] forwardPath - the path of the forward association
183      * @param[in] forwardType - the type of the forward association
184      * @param[in] reversePath - the path of the reverse association
185      * @param[in] reverseType - the type of the reverse association
186      * @param[in] deferSignal - whether or not to send a Properties or
187      *                          ObjectManager signal
188      */
189     void createAssociation(const std::string& forwardPath,
190                            const std::string& forwardType,
191                            const std::string& reversePath,
192                            const std::string& reverseType, bool deferSignal);
193 
194     /**
195      * @brief Looks for all JSON files in the associations directory that
196      *        contain a valid association condition, and loads the
197      *        conditions into the _conditions vector.
198      */
199     bool loadConditions();
200 
201     /**
202      * @brief The map of association data that is loaded from its
203      *        JSON definition.  Association D-Bus objects will be
204      *        created from this data.
205      */
206     AssociationMap _associations;
207 
208     /**
209      * @brief The map of org.openbmc_project.Associations D-Bus
210      *        interfaces objects based on their object path.
211      */
212     AssociationIfaceMap _associationIfaces;
213 
214     /**
215      * @brief The sdbusplus bus object.
216      */
217     sdbusplus::bus::bus& _bus;
218 
219     /**
220      * @brief The path to the associations JSON File.
221      */
222     const std::filesystem::path _jsonFile;
223 
224     /**
225      * A list of the inventory association paths that have already been handled.
226      */
227     std::vector<std::string> _handled;
228 
229     /**
230      * @brief Conditions that specify when an associations file is valid.
231      */
232     std::vector<Condition> _conditions;
233 };
234 
235 } // namespace associations
236 } // namespace manager
237 } // namespace inventory
238 } // namespace phosphor
239