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 "dbuspassive.hpp"
17 
18 #include "dbus/util.hpp"
19 
20 #include <chrono>
21 #include <cmath>
22 #include <memory>
23 #include <mutex>
24 #include <sdbusplus/bus.hpp>
25 #include <string>
26 #include <variant>
27 
28 std::unique_ptr<ReadInterface> DbusPassive::createDbusPassive(
29     sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
30     DbusHelperInterface* helper, const SensorConfig* info)
31 {
32     if (helper == nullptr)
33     {
34         return nullptr;
35     }
36     if (!validType(type))
37     {
38         return nullptr;
39     }
40 
41     /* Need to get the scale and initial value */
42     auto tempBus = sdbusplus::bus::new_system();
43 
44     /* service == busname */
45     std::string path = getSensorPath(type, id);
46 
47     struct SensorProperties settings;
48     bool failed;
49 
50     try
51     {
52         std::string service = helper->getService(tempBus, sensorintf, path);
53 
54         helper->getProperties(tempBus, service, path, &settings);
55         failed = helper->thresholdsAsserted(tempBus, service, path);
56     }
57     catch (const std::exception& e)
58     {
59         return nullptr;
60     }
61     if (info->max != conf::inheritValueFromDbus)
62     {
63         settings.max = info->max;
64     }
65 
66     if (info->max != conf::inheritValueFromDbus)
67     {
68         settings.min = info->min;
69     }
70 
71     return std::make_unique<DbusPassive>(bus, type, id, helper, settings,
72                                          failed);
73 }
74 
75 DbusPassive::DbusPassive(sdbusplus::bus::bus& bus, const std::string& type,
76                          const std::string& id, DbusHelperInterface* helper,
77                          const struct SensorProperties& settings, bool failed) :
78     ReadInterface(),
79     _bus(bus), _signal(bus, getMatch(type, id).c_str(), dbusHandleSignal, this),
80     _id(id), _helper(helper), _failed(failed)
81 {
82     _scale = settings.scale;
83     _value = settings.value * pow(10, _scale);
84     _min = settings.min * pow(10, _scale);
85     _max = settings.max * pow(10, _scale);
86     _updated = std::chrono::high_resolution_clock::now();
87 }
88 
89 ReadReturn DbusPassive::read(void)
90 {
91     std::lock_guard<std::mutex> guard(_lock);
92 
93     struct ReadReturn r = {_value, _updated};
94 
95     return r;
96 }
97 
98 void DbusPassive::setValue(double value)
99 {
100     std::lock_guard<std::mutex> guard(_lock);
101 
102     _value = value;
103     _updated = std::chrono::high_resolution_clock::now();
104 }
105 
106 bool DbusPassive::getFailed(void) const
107 {
108     return _failed;
109 }
110 
111 void DbusPassive::setFailed(bool value)
112 {
113     _failed = value;
114 }
115 
116 int64_t DbusPassive::getScale(void)
117 {
118     return _scale;
119 }
120 
121 std::string DbusPassive::getID(void)
122 {
123     return _id;
124 }
125 
126 double DbusPassive::getMax(void)
127 {
128     return _max;
129 }
130 
131 double DbusPassive::getMin(void)
132 {
133     return _min;
134 }
135 
136 int handleSensorValue(sdbusplus::message::message& msg, DbusPassive* owner)
137 {
138     std::string msgSensor;
139     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
140 
141     msg.read(msgSensor, msgData);
142 
143     if (msgSensor == "xyz.openbmc_project.Sensor.Value")
144     {
145         auto valPropMap = msgData.find("Value");
146         if (valPropMap != msgData.end())
147         {
148             double value =
149                 std::visit(VariantToDoubleVisitor(), valPropMap->second);
150 
151             value *= std::pow(10, owner->getScale());
152 
153             scaleSensorReading(owner->getMin(), owner->getMax(), value);
154 
155             owner->setValue(value);
156         }
157     }
158     else if (msgSensor == "xyz.openbmc_project.Sensor.Threshold.Critical")
159     {
160         auto criticalAlarmLow = msgData.find("CriticalAlarmLow");
161         auto criticalAlarmHigh = msgData.find("CriticalAlarmHigh");
162         if (criticalAlarmHigh == msgData.end() &&
163             criticalAlarmLow == msgData.end())
164         {
165             return 0;
166         }
167 
168         bool asserted = false;
169         if (criticalAlarmLow != msgData.end())
170         {
171             asserted = std::get<bool>(criticalAlarmLow->second);
172         }
173 
174         // checking both as in theory you could de-assert one threshold and
175         // assert the other at the same moment
176         if (!asserted && criticalAlarmHigh != msgData.end())
177         {
178             asserted = std::get<bool>(criticalAlarmHigh->second);
179         }
180         owner->setFailed(asserted);
181     }
182 
183     return 0;
184 }
185 
186 int dbusHandleSignal(sd_bus_message* msg, void* usrData, sd_bus_error* err)
187 {
188     auto sdbpMsg = sdbusplus::message::message(msg);
189     DbusPassive* obj = static_cast<DbusPassive*>(usrData);
190 
191     return handleSensorValue(sdbpMsg, obj);
192 }
193