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