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)
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 
62     return std::make_unique<DbusPassive>(bus, type, id, helper, settings,
63                                          failed);
64 }
65 
66 DbusPassive::DbusPassive(sdbusplus::bus::bus& bus, const std::string& type,
67                          const std::string& id, DbusHelperInterface* helper,
68                          const struct SensorProperties& settings, bool failed) :
69     ReadInterface(),
70     _bus(bus), _signal(bus, getMatch(type, id).c_str(), dbusHandleSignal, this),
71     _id(id), _helper(helper), _failed(failed)
72 {
73     _scale = settings.scale;
74     _value = settings.value * pow(10, _scale);
75     _updated = std::chrono::high_resolution_clock::now();
76 }
77 
78 ReadReturn DbusPassive::read(void)
79 {
80     std::lock_guard<std::mutex> guard(_lock);
81 
82     struct ReadReturn r = {_value, _updated};
83 
84     return r;
85 }
86 
87 void DbusPassive::setValue(double value)
88 {
89     std::lock_guard<std::mutex> guard(_lock);
90 
91     _value = value;
92     _updated = std::chrono::high_resolution_clock::now();
93 }
94 
95 bool DbusPassive::getFailed(void) const
96 {
97     return _failed;
98 }
99 
100 void DbusPassive::setFailed(bool value)
101 {
102     _failed = value;
103 }
104 
105 int64_t DbusPassive::getScale(void)
106 {
107     return _scale;
108 }
109 
110 std::string DbusPassive::getID(void)
111 {
112     return _id;
113 }
114 
115 int handleSensorValue(sdbusplus::message::message& msg, DbusPassive* owner)
116 {
117     std::string msgSensor;
118     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
119 
120     msg.read(msgSensor, msgData);
121 
122     if (msgSensor == "xyz.openbmc_project.Sensor.Value")
123     {
124         auto valPropMap = msgData.find("Value");
125         if (valPropMap != msgData.end())
126         {
127             double value =
128                 std::visit(VariantToDoubleVisitor(), valPropMap->second);
129 
130             value *= std::pow(10, owner->getScale());
131 
132             owner->setValue(value);
133         }
134     }
135     else if (msgSensor == "xyz.openbmc_project.Sensor.Threshold.Critical")
136     {
137         auto criticalAlarmLow = msgData.find("CriticalAlarmLow");
138         auto criticalAlarmHigh = msgData.find("CriticalAlarmHigh");
139         if (criticalAlarmHigh == msgData.end() &&
140             criticalAlarmLow == msgData.end())
141         {
142             return 0;
143         }
144 
145         bool asserted = false;
146         if (criticalAlarmLow != msgData.end())
147         {
148             asserted = std::get<bool>(criticalAlarmLow->second);
149         }
150 
151         // checking both as in theory you could de-assert one threshold and
152         // assert the other at the same moment
153         if (!asserted && criticalAlarmHigh != msgData.end())
154         {
155             asserted = std::get<bool>(criticalAlarmHigh->second);
156         }
157         owner->setFailed(asserted);
158     }
159 
160     return 0;
161 }
162 
163 int dbusHandleSignal(sd_bus_message* msg, void* usrData, sd_bus_error* err)
164 {
165     auto sdbpMsg = sdbusplus::message::message(msg);
166     DbusPassive* obj = static_cast<DbusPassive*>(usrData);
167 
168     return handleSensorValue(sdbpMsg, obj);
169 }
170