xref: /openbmc/phosphor-hwmon/mainloop.cpp (revision 5afe21a5)
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     std::make_tuple(
50         hwmon::type::ccurr,
51         ValueInterface::Unit::Amperes,
52         -3),
53     std::make_tuple(
54         hwmon::type::cenergy,
55         ValueInterface::Unit::Joules,
56         -6),
57     std::make_tuple(
58         hwmon::type::cpower,
59         ValueInterface::Unit::Watts,
60         -6),
61 };
62 
63 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs)
64 {
65     return std::get<0>(attrs);
66 }
67 
68 auto getUnit(decltype(typeAttrMap)::const_reference attrs)
69 {
70     return std::get<1>(attrs);
71 }
72 
73 auto getScale(decltype(typeAttrMap)::const_reference attrs)
74 {
75     return std::get<2>(attrs);
76 }
77 
78 using AttributeIterator = decltype(*typeAttrMap.begin());
79 using Attributes
80     = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type;
81 
82 auto getAttributes(const std::string& type, Attributes& attributes)
83 {
84     // *INDENT-OFF*
85     auto a = std::find_if(
86                 typeAttrMap.begin(),
87                 typeAttrMap.end(),
88                 [&](const auto & e)
89                 {
90                    return type == getHwmonType(e);
91                 });
92     // *INDENT-ON*
93 
94     if (a == typeAttrMap.end())
95     {
96         return false;
97     }
98 
99     attributes = *a;
100     return true;
101 }
102 
103 auto addValue(const SensorSet::key_type& sensor,
104               const std::string& sysfsRoot, ObjectInfo& info)
105 {
106     // Get the initial value for the value interface.
107     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
108     auto& obj = std::get<Object>(info);
109     auto& objPath = std::get<std::string>(info);
110 
111     auto sysfsPath = make_sysfs_path(
112                          sysfsRoot,
113                          sensor.first,
114                          sensor.second,
115                          hwmon::entry::input);
116     int val = 0;
117     read_sysfs(sysfsPath, val);
118 
119     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str());
120     iface->value(val);
121 
122     Attributes attrs;
123     if (getAttributes(sensor.first, attrs))
124     {
125         iface->unit(getUnit(attrs));
126         iface->scale(getScale(attrs));
127     }
128 
129     obj[InterfaceType::VALUE] = iface;
130     return iface;
131 }
132 
133 MainLoop::MainLoop(
134     sdbusplus::bus::bus&& bus,
135     const std::string& path,
136     const char* prefix,
137     const char* root)
138     : _bus(std::move(bus)),
139       _manager(sdbusplus::server::manager::manager(_bus, root)),
140       _shutdown(false),
141       _path(path),
142       _prefix(prefix),
143       _root(root),
144       state()
145 {
146     if (_path.back() == '/')
147     {
148         _path.pop_back();
149     }
150 }
151 
152 void MainLoop::shutdown() noexcept
153 {
154     _shutdown = true;
155 }
156 
157 void MainLoop::run()
158 {
159     // Check sysfs for available sensors.
160     auto sensors = std::make_unique<SensorSet>(_path);
161 
162     for (auto& i : *sensors)
163     {
164         // Get sensor configuration from the environment.
165 
166         // Ignore inputs without a label.
167         auto label = getEnv("LABEL", i.first);
168         if (label.empty())
169         {
170             continue;
171         }
172 
173         std::string objectPath{_root};
174         objectPath.append("/");
175         objectPath.append(i.first.first);
176         objectPath.append("/");
177         objectPath.append(label);
178 
179         ObjectInfo info(&_bus, std::move(objectPath), Object());
180         auto valueInterface = addValue(i.first, _path, info);
181         auto sensorValue = valueInterface->value();
182         addThreshold<WarningObject>(i.first, sensorValue, info);
183         addThreshold<CriticalObject>(i.first, sensorValue, info);
184 
185         auto value = std::make_tuple(
186                          std::move(i.second),
187                          std::move(label),
188                          std::move(info));
189 
190         state[std::move(i.first)] = std::move(value);
191     }
192 
193     {
194         auto copy = std::unique_ptr<char, phosphor::utility::Free<char>>(strdup(
195                         _path.c_str()));
196         auto busname = std::string(_prefix) + '.' + basename(copy.get());
197         _bus.request_name(busname.c_str());
198     }
199 
200     // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
201     //       ensure the objects all exist?
202 
203     // Polling loop.
204     while (!_shutdown)
205     {
206         // Iterate through all the sensors.
207         for (auto& i : state)
208         {
209             auto& attrs = std::get<0>(i.second);
210             if (attrs.find(hwmon::entry::input) != attrs.end())
211             {
212                 // Read value from sensor.
213                 int value = 0;
214                 read_sysfs(make_sysfs_path(_path,
215                                            i.first.first, i.first.second,
216                                            hwmon::entry::input),
217                            value);
218 
219                 auto& objInfo = std::get<ObjectInfo>(i.second);
220                 auto& obj = std::get<Object>(objInfo);
221 
222                 for (auto& iface : obj)
223                 {
224                     auto valueIface = std::shared_ptr<ValueObject>();
225                     auto warnIface = std::shared_ptr<WarningObject>();
226                     auto critIface = std::shared_ptr<CriticalObject>();
227 
228                     switch (iface.first)
229                     {
230                         case InterfaceType::VALUE:
231                             valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>>
232                                          (iface.second);
233                             valueIface->value(value);
234                             break;
235                         case InterfaceType::WARN:
236                             checkThresholds<WarningObject>(iface.second, value);
237                             break;
238                         case InterfaceType::CRIT:
239                             checkThresholds<CriticalObject>(iface.second, value);
240                             break;
241                         default:
242                             break;
243                     }
244                 }
245             }
246         }
247 
248         // Respond to DBus
249         _bus.process_discard();
250 
251         // Sleep until next interval.
252         // TODO: Issue#5 - Make this configurable.
253         // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
254         _bus.wait((1000000us).count());
255 
256         // TODO: Issue#7 - Should probably periodically check the SensorSet
257         //       for new entries.
258     }
259 }
260 
261 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
262