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