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 */ 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 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 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