xref: /openbmc/ibm-logging/policy_find.cpp (revision 3e71c977)
1 /**
2  * Copyright © 2018 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <phosphor-logging/log.hpp>
17 #include <sstream>
18 #include "policy_find.hpp"
19 
20 namespace ibm
21 {
22 namespace logging
23 {
24 namespace policy
25 {
26 
27 namespace optional_ns = std::experimental;
28 
29 /**
30  * Returns a property value from a map of properties.
31  *
32  * @tparam - T the property data type
33  * @param[in] properties - the property map
34  * @param[in] name - the property name
35  *
36  * @return optional<T> - the property value
37  */
38 template <typename T>
39 optional_ns::optional<T> getProperty(const DbusPropertyMap& properties,
40                                      const std::string& name)
41 {
42     auto prop = properties.find(name);
43 
44     if (prop != properties.end())
45     {
46         return prop->second.template get<T>();
47     }
48 
49     return {};
50 }
51 
52 /**
53  * Finds a value in the AdditionalData property, which is
54  * an array of strings in the form of:
55  *
56  *    NAME=VALUE
57  *
58  * @param[in] additionalData - the AdditionalData property contents
59  * @param[in] name - the name of the value to find
60  *
61  * @return optional<std::string> - the data value
62  */
63 optional_ns::optional<std::string>
64     getAdditionalDataItem(const std::vector<std::string>& additionalData,
65                           const std::string& name)
66 {
67     for (const auto& item : additionalData)
68     {
69         if (item.find(name + "=") != std::string::npos)
70         {
71             return item.substr(item.find('=') + 1);
72         }
73     }
74 
75     return {};
76 }
77 
78 /**
79  * Returns the search modifier to use.
80  *
81  * The modifier is used when the error name itself isn't granular
82  * enough to find a policy table entry.  The modifier is determined
83  * using rules provided by the IBM service team.
84  *
85  * Not all errors need a modifier, so this function isn't
86  * guaranteed to find one.
87  *
88  * @param[in] properties - the property map for the error
89  *
90  * @return string - the search modifier
91  *                  may be empty if none found
92  */
93 auto getSearchModifier(const DbusPropertyMap& properties)
94 {
95     // The modifier may be one of several things within the
96     // AdditionalData property.  Try them all until one
97     // is found.
98 
99     auto data =
100         getProperty<std::vector<std::string>>(properties, "AdditionalData");
101 
102     if (!data)
103     {
104         return std::string{};
105     }
106 
107     // AdditionalData fields where the value is the modifier
108     static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH",
109                                                    "RAIL_NAME", "INPUT_NAME"};
110 
111     optional_ns::optional<std::string> mod;
112     for (const auto& field : ADFields)
113     {
114         mod = getAdditionalDataItem(*data, field);
115         if (mod && !(*mod).empty())
116         {
117             return *mod;
118         }
119     }
120 
121     // Next are the AdditionalData fields where the value needs
122     // to be massaged to get the modifier.
123 
124     // A device path, but we only care about the type
125     mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH");
126     if (mod)
127     {
128         // The table only handles I2C and FSI
129         if ((*mod).find("i2c") != std::string::npos)
130         {
131             return std::string{"I2C"};
132         }
133         else if ((*mod).find("fsi") != std::string::npos)
134         {
135             return std::string{"FSI"};
136         }
137     }
138 
139     // A hostboot procedure ID
140     mod = getAdditionalDataItem(*data, "PROCEDURE");
141     if (mod)
142     {
143         // Convert decimal (e.g. 109) to hex (e.g. 6D)
144         std::ostringstream stream;
145         try
146         {
147             stream << std::hex << std::stoul((*mod).c_str());
148             auto value = stream.str();
149 
150             if (!value.empty())
151             {
152                 std::transform(value.begin(), value.end(), value.begin(),
153                                toupper);
154                 return value;
155             }
156         }
157         catch (std::exception& e)
158         {
159             using namespace phosphor::logging;
160             log<level::ERR>("Invalid PROCEDURE value found",
161                             entry("PROCEDURE=%s", mod->c_str()));
162         }
163     }
164 
165     return std::string{};
166 }
167 
168 PolicyProps find(const policy::Table& policy,
169                  const DbusPropertyMap& errorLogProperties)
170 {
171     auto errorMsg = getProperty<std::string>(errorLogProperties,
172                                              "Message"); // e.g. xyz.X.Error.Y
173     if (errorMsg)
174     {
175         auto modifier = getSearchModifier(errorLogProperties);
176 
177         auto result = policy.find(*errorMsg, modifier);
178 
179         if (result)
180         {
181             return {(*result).get().ceid, (*result).get().msg};
182         }
183     }
184     else
185     {
186         using namespace phosphor::logging;
187         log<level::ERR>("No Message metadata found in an error");
188     }
189 
190     return {policy.defaultEID(), policy.defaultMsg()};
191 }
192 }
193 }
194 }
195