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 */ 43 std::string DbusHelper::getService(const std::string& intf, 44 const std::string& path) 45 { 46 auto mapper = _bus.new_method_call("xyz.openbmc_project.ObjectMapper", 47 "/xyz/openbmc_project/object_mapper", 48 "xyz.openbmc_project.ObjectMapper", 49 "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 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 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