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