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