1 /**
2 * Copyright 2017 Google Inc.
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
17 #include "dbushelper.hpp"
18
19 #include "dbushelper_interface.hpp"
20 #include "dbusutil.hpp"
21
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/bus.hpp>
24 #include <sdbusplus/exception.hpp>
25
26 #include <cstdint>
27 #include <map>
28 #include <stdexcept>
29 #include <string>
30 #include <variant>
31 #include <vector>
32
33 namespace pid_control
34 {
35
36 using Property = std::string;
37 using Value = std::variant<int64_t, double, std::string, bool>;
38 using PropertyMap = std::map<Property, Value>;
39
40 using namespace phosphor::logging;
41
42 /* TODO(venture): Basically all phosphor apps need this, maybe it should be a
43 * part of sdbusplus. There is an old version in libmapper.
44 */
getService(const std::string & intf,const std::string & path)45 std::string DbusHelper::getService(const std::string& intf,
46 const std::string& path)
47 {
48 auto mapper =
49 _bus.new_method_call("xyz.openbmc_project.ObjectMapper",
50 "/xyz/openbmc_project/object_mapper",
51 "xyz.openbmc_project.ObjectMapper", "GetObject");
52
53 mapper.append(path);
54 mapper.append(std::vector<std::string>({intf}));
55
56 std::map<std::string, std::vector<std::string>> response;
57
58 try
59 {
60 auto responseMsg = _bus.call(mapper);
61
62 responseMsg.read(response);
63 }
64 catch (const sdbusplus::exception_t& ex)
65 {
66 log<level::ERR>("ObjectMapper call failure",
67 entry("WHAT=%s", ex.what()));
68 throw;
69 }
70
71 if (response.begin() == response.end())
72 {
73 throw std::runtime_error("Unable to find Object: " + path);
74 }
75
76 return response.begin()->first;
77 }
78
getProperties(const std::string & service,const std::string & path,SensorProperties * prop)79 void DbusHelper::getProperties(const std::string& service,
80 const std::string& path, SensorProperties* prop)
81 {
82 auto pimMsg = _bus.new_method_call(service.c_str(), path.c_str(),
83 propertiesintf, "GetAll");
84
85 pimMsg.append(sensorintf);
86
87 PropertyMap propMap;
88
89 try
90 {
91 auto valueResponseMsg = _bus.call(pimMsg);
92 valueResponseMsg.read(propMap);
93 }
94 catch (const sdbusplus::exception_t& ex)
95 {
96 log<level::ERR>("GetAll Properties Failed",
97 entry("WHAT=%s", ex.what()));
98 throw;
99 }
100
101 // The PropertyMap returned will look like this because it's always
102 // reading a Sensor.Value interface.
103 // a{sv} 3:
104 // "Value" x 24875
105 // "Unit" s "xyz.openbmc_project.Sensor.Value.Unit.DegreesC"
106 // "Scale" x -3
107
108 // If no error was set, the values should all be there.
109 auto findUnit = propMap.find("Unit");
110 if (findUnit != propMap.end())
111 {
112 prop->unit = std::get<std::string>(findUnit->second);
113 }
114 auto findScale = propMap.find("Scale");
115 auto findMax = propMap.find("MaxValue");
116 auto findMin = propMap.find("MinValue");
117
118 prop->min = 0;
119 prop->max = 0;
120 prop->scale = 0;
121 if (findScale != propMap.end())
122 {
123 prop->scale = std::get<int64_t>(findScale->second);
124 }
125 if (findMax != propMap.end())
126 {
127 prop->max = std::visit(VariantToDoubleVisitor(), findMax->second);
128 }
129 if (findMin != propMap.end())
130 {
131 prop->min = std::visit(VariantToDoubleVisitor(), findMin->second);
132 }
133
134 prop->value = std::visit(VariantToDoubleVisitor(), propMap["Value"]);
135
136 bool available = true;
137 try
138 {
139 getProperty(service, path, availabilityIntf, "Available", available);
140 }
141 catch (const sdbusplus::exception_t& ex)
142 {
143 // unsupported Available property, leaving reading at 'True'
144 }
145 prop->available = available;
146
147 return;
148 }
149
thresholdsAsserted(const std::string & service,const std::string & path)150 bool DbusHelper::thresholdsAsserted(const std::string& service,
151 const std::string& path)
152 {
153 auto critical = _bus.new_method_call(service.c_str(), path.c_str(),
154 propertiesintf, "GetAll");
155 critical.append(criticalThreshInf);
156 PropertyMap criticalMap;
157
158 try
159 {
160 auto msg = _bus.call(critical);
161 msg.read(criticalMap);
162 }
163 catch (const sdbusplus::exception_t&)
164 {
165 // do nothing, sensors don't have to expose critical thresholds
166 #ifndef UNC_FAILSAFE
167 return false;
168 #endif
169 }
170
171 auto findCriticalLow = criticalMap.find("CriticalAlarmLow");
172 auto findCriticalHigh = criticalMap.find("CriticalAlarmHigh");
173
174 bool asserted = false;
175 if (findCriticalLow != criticalMap.end())
176 {
177 asserted = std::get<bool>(findCriticalLow->second);
178 }
179
180 // as we are catching properties changed, a sensor could theoretically jump
181 // from one threshold to the other in one event, so check both thresholds
182 if (!asserted && findCriticalHigh != criticalMap.end())
183 {
184 asserted = std::get<bool>(findCriticalHigh->second);
185 }
186 #ifdef UNC_FAILSAFE
187 if (!asserted)
188 {
189 auto warning = _bus.new_method_call(service.c_str(), path.c_str(),
190 propertiesintf, "GetAll");
191 warning.append(warningThreshInf);
192 PropertyMap warningMap;
193
194 try
195 {
196 auto msg = _bus.call(warning);
197 msg.read(warningMap);
198 }
199 catch (const sdbusplus::exception_t&)
200 {
201 // sensors don't have to expose non-critical thresholds
202 return false;
203 }
204 auto findWarningHigh = warningMap.find("WarningAlarmHigh");
205
206 if (findWarningHigh != warningMap.end())
207 {
208 asserted = std::get<bool>(findWarningHigh->second);
209 }
210 }
211 #endif
212 return asserted;
213 }
214
215 } // namespace pid_control
216