xref: /openbmc/phosphor-hwmon/mainloop.cpp (revision e0b7d051)
1 /**
2  * Copyright © 2016 IBM Corporation
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 <iostream>
17 #include <memory>
18 #include <cstring>
19 #include <cstdlib>
20 #include <chrono>
21 #include <algorithm>
22 #include "sensorset.hpp"
23 #include "hwmon.hpp"
24 #include "sysfs.hpp"
25 #include "mainloop.hpp"
26 #include "util.hpp"
27 #include "env.hpp"
28 #include "thresholds.hpp"
29 
30 using namespace std::literals::chrono_literals;
31 
32 static constexpr auto typeAttrMap =
33 {
34     // 1 - hwmon class
35     // 2 - unit
36     // 3 - sysfs scaling factor
37     std::make_tuple(
38         hwmon::type::ctemp,
39         ValueInterface::Unit::DegreesC,
40         -3),
41     std::make_tuple(
42         hwmon::type::cfan,
43         ValueInterface::Unit::RPMS,
44         0),
45     std::make_tuple(
46         hwmon::type::cvolt,
47         ValueInterface::Unit::Volts,
48         -3),
49 };
50 
51 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs)
52 {
53     return std::get<0>(attrs);
54 }
55 
56 auto getUnit(decltype(typeAttrMap)::const_reference attrs)
57 {
58     return std::get<1>(attrs);
59 }
60 
61 auto getScale(decltype(typeAttrMap)::const_reference attrs)
62 {
63     return std::get<2>(attrs);
64 }
65 
66 auto addValue(const SensorSet::key_type& sensor,
67               const std::string& sysfsRoot, ObjectInfo& info)
68 {
69     // Get the initial value for the value interface.
70     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
71     auto& obj = std::get<Object>(info);
72     auto& objPath = std::get<std::string>(info);
73 
74     auto sysfsPath = make_sysfs_path(
75                          sysfsRoot,
76                          sensor.first,
77                          sensor.second,
78                          hwmon::entry::input);
79     int val = 0;
80     read_sysfs(sysfsPath, val);
81 
82     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str());
83     iface->value(val);
84 
85     // *INDENT-OFF*
86     const auto& attrs = std::find_if(
87                             typeAttrMap.begin(),
88                             typeAttrMap.end(),
89                             [&](const auto & e)
90                             {
91                                 return sensor.first == getHwmonType(e);
92                             });
93     // *INDENT-ON*
94 
95     if (attrs != typeAttrMap.end())
96     {
97         iface->unit(getUnit(*attrs));
98         iface->scale(getScale(*attrs));
99     }
100 
101     obj[InterfaceType::VALUE] = iface;
102     return iface;
103 }
104 
105 MainLoop::MainLoop(
106     sdbusplus::bus::bus&& bus,
107     const std::string& path,
108     const char* prefix,
109     const char* root)
110     : _bus(std::move(bus)),
111       _manager(sdbusplus::server::manager::manager(_bus, root)),
112       _shutdown(false),
113       _path(path),
114       _prefix(prefix),
115       _root(root),
116       state()
117 {
118     if (_path.back() == '/')
119     {
120         _path.pop_back();
121     }
122 }
123 
124 void MainLoop::shutdown() noexcept
125 {
126     _shutdown = true;
127 }
128 
129 void MainLoop::run()
130 {
131     // Check sysfs for available sensors.
132     auto sensors = std::make_unique<SensorSet>(_path);
133 
134     for (auto& i : *sensors)
135     {
136         // Get sensor configuration from the environment.
137 
138         // Ignore inputs without a label.
139         auto label = getEnv("LABEL", i.first);
140         if (label.empty())
141         {
142             continue;
143         }
144 
145         std::string objectPath{_root};
146         objectPath.append("/");
147         objectPath.append(i.first.first);
148         objectPath.append("/");
149         objectPath.append(label);
150 
151         ObjectInfo info(&_bus, std::move(objectPath), Object());
152         auto valueInterface = addValue(i.first, _path, info);
153         auto sensorValue = valueInterface->value();
154         addThreshold<WarningObject>(i.first, sensorValue, info);
155         addThreshold<CriticalObject>(i.first, sensorValue, info);
156 
157         auto value = std::make_tuple(
158                          std::move(i.second),
159                          std::move(label),
160                          std::move(info));
161 
162         state[std::move(i.first)] = std::move(value);
163     }
164 
165     {
166         auto copy = std::unique_ptr<char, phosphor::utility::Free<char>>(strdup(
167                         _path.c_str()));
168         auto busname = std::string(_prefix) + '.' + basename(copy.get());
169         _bus.request_name(busname.c_str());
170     }
171 
172     // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
173     //       ensure the objects all exist?
174 
175     // Polling loop.
176     while (!_shutdown)
177     {
178         // Iterate through all the sensors.
179         for (auto& i : state)
180         {
181             auto& attrs = std::get<0>(i.second);
182             if (attrs.find(hwmon::entry::input) != attrs.end())
183             {
184                 // Read value from sensor.
185                 int value = 0;
186                 read_sysfs(make_sysfs_path(_path,
187                                            i.first.first, i.first.second,
188                                            hwmon::entry::input),
189                            value);
190 
191                 auto& objInfo = std::get<ObjectInfo>(i.second);
192                 auto& obj = std::get<Object>(objInfo);
193 
194                 for (auto& iface : obj)
195                 {
196                     auto valueIface = std::shared_ptr<ValueObject>();
197                     auto warnIface = std::shared_ptr<WarningObject>();
198                     auto critIface = std::shared_ptr<CriticalObject>();
199 
200                     switch (iface.first)
201                     {
202                         case InterfaceType::VALUE:
203                             valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>>
204                                          (iface.second);
205                             valueIface->value(value);
206                             break;
207                         case InterfaceType::WARN:
208                             checkThresholds<WarningObject>(iface.second, value);
209                             break;
210                         case InterfaceType::CRIT:
211                             checkThresholds<CriticalObject>(iface.second, value);
212                             break;
213                         default:
214                             break;
215                     }
216                 }
217             }
218         }
219 
220         // Respond to DBus
221         _bus.process_discard();
222 
223         // Sleep until next interval.
224         // TODO: Issue#5 - Make this configurable.
225         // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
226         _bus.wait((1000000us).count());
227 
228         // TODO: Issue#7 - Should probably periodically check the SensorSet
229         //       for new entries.
230     }
231 }
232 
233 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
234