xref: /openbmc/ibm-logging/policy_find.cpp (revision e3be64f1)
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(
40         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> getAdditionalDataItem(
65         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(
95         const DbusPropertyMap& properties)
96 {
97     // The modifier may be one of several things within the
98     // AdditionalData property.  Try them all until one
99     // is found.
100 
101     auto data =
102         getProperty<std::vector<std::string>>(properties, "AdditionalData");
103 
104     if (!data)
105     {
106         return std::string{};
107     }
108 
109     // AdditionalData fields where the value is the modifier
110     static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH",
111                                                    "RAIL_NAME", "INPUT_NAME"};
112 
113     optional_ns::optional<std::string> mod;
114     for (const auto& field : ADFields)
115     {
116         mod = getAdditionalDataItem(*data, field);
117         if (mod && !(*mod).empty())
118         {
119             return *mod;
120         }
121     }
122 
123     // Next are the AdditionalData fields where the value needs
124     // to be massaged to get the modifier.
125 
126     // A device path, but we only care about the type
127     mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH");
128     if (mod)
129     {
130         // The table only handles I2C and FSI
131         if ((*mod).find("i2c") != std::string::npos)
132         {
133             return std::string{"I2C"};
134         }
135         else if ((*mod).find("fsi") != std::string::npos)
136         {
137             return std::string{"FSI"};
138         }
139     }
140 
141     // A hostboot procedure ID
142     mod = getAdditionalDataItem(*data, "PROCEDURE");
143     if (mod)
144     {
145         // Convert decimal (e.g. 109) to hex (e.g. 6D)
146         std::ostringstream stream;
147         try
148         {
149             stream << std::hex << std::stoul((*mod).c_str());
150             auto value = stream.str();
151 
152             if (!value.empty())
153             {
154                 std::transform(
155                         value.begin(), value.end(), value.begin(), toupper);
156                 return value;
157             }
158         }
159         catch (std::exception& e)
160         {
161             using namespace phosphor::logging;
162             log<level::ERR>("Invalid PROCEDURE value found",
163                             entry("PROCEDURE=%s", *mod));
164         }
165     }
166 
167     return std::string{};
168 }
169 
170 PolicyProps find(
171         const policy::Table& policy,
172         const DbusPropertyMap& errorLogProperties)
173 {
174     auto errorMsg = getProperty<std::string>(
175             errorLogProperties, "Message"); //e.g. xyz.X.Error.Y
176     if (errorMsg)
177     {
178         auto modifier = getSearchModifier(errorLogProperties);
179 
180         auto result = policy.find(*errorMsg, modifier);
181 
182         if (result)
183         {
184             return {(*result).get().ceid, (*result).get().msg};
185         }
186     }
187     else
188     {
189         using namespace phosphor::logging;
190         log<level::ERR>("No Message metadata found in an error");
191     }
192 
193     return {policy.defaultEID(), policy.defaultMsg()};
194 }
195 
196 }
197 }
198 }
199