blob: 9e793f06dd9776bfa1283f0766982b0808cebaee [file] [log] [blame]
Matthew Barthbaeeb8f2021-05-13 16:03:54 -05001/**
2 * Copyright © 2021 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 "signal.hpp"
17
18#include "../manager.hpp"
19#include "action.hpp"
20#include "group.hpp"
21#include "handlers.hpp"
22
23#include <fmt/format.h>
24
25#include <nlohmann/json.hpp>
26#include <phosphor-logging/log.hpp>
27#include <sdbusplus/bus/match.hpp>
28
29#include <algorithm>
30#include <functional>
31#include <iterator>
32#include <memory>
33#include <numeric>
34#include <utility>
35#include <vector>
36
37namespace phosphor::fan::control::json::trigger::signal
38{
39
40using json = nlohmann::json;
41using namespace phosphor::logging;
42using namespace sdbusplus::bus::match;
43
44void subscribe(const std::string& match, SignalPkg&& signalPkg,
45 std::function<bool(SignalPkg&)> isSameSig, Manager* mgr)
46{
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050047 auto& signalData = mgr->getSignal(match);
48 if (signalData.empty())
49 {
50 // Signal subscription doesnt exist, add signal package and subscribe
51 std::vector<SignalPkg> pkgs = {signalPkg};
52 std::unique_ptr<std::vector<SignalPkg>> dataPkgs =
53 std::make_unique<std::vector<SignalPkg>>(std::move(pkgs));
54 std::unique_ptr<sdbusplus::server::match::match> ptrMatch = nullptr;
Matthew Barth50219f52021-05-18 11:20:36 -050055 // TODO(ibm-openbmc/#3195) - Filter signal subscriptions to objects
56 // owned by fan control?
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050057 if (!match.empty())
58 {
59 // Subscribe to signal
60 ptrMatch = std::make_unique<sdbusplus::server::match::match>(
61 mgr->getBus(), match.c_str(),
62 std::bind(std::mem_fn(&Manager::handleSignal), &(*mgr),
63 std::placeholders::_1, dataPkgs.get()));
64 }
65 signalData.emplace_back(std::move(dataPkgs), std::move(ptrMatch));
66 }
67 else
68 {
69 // Signal subscription already exists
70 // Only a single signal data entry tied to each match is supported
71 auto& pkgs = std::get<std::unique_ptr<std::vector<SignalPkg>>>(
72 signalData.front());
73 for (auto& pkg : *pkgs)
74 {
75 if (isSameSig(pkg))
76 {
77 // Same signal expected, add action to be run when signal
78 // received
79 auto& pkgActions = std::get<SignalActions>(signalPkg);
80 auto& actions = std::get<SignalActions>(pkg);
81 // Only one action is given on the signal package passed in
82 actions.push_back(pkgActions.front());
83 }
84 else
85 {
86 // Expected signal differs, add signal package
87 (*pkgs).emplace_back(std::move(signalPkg));
88 }
89 }
90 }
91}
92
93void propertiesChanged(Manager* mgr, const std::string& eventName,
94 std::unique_ptr<ActionBase>& action)
95{
96 // Groups are optional, but a signal triggered event with no groups
97 // will do nothing since signals require a group
98 for (const auto& group : action->getGroups())
99 {
100 for (const auto& member : group.getMembers())
101 {
102 // Setup property changed signal handler on the group member's
103 // property
104 const auto match =
105 rules::propertiesChanged(member, group.getInterface());
106 SignalPkg signalPkg = {Handlers::propertiesChanged,
107 SignalObject(std::cref(member),
108 std::cref(group.getInterface()),
109 std::cref(group.getProperty())),
110 SignalActions({action})};
111 auto isSameSig = [&prop = group.getProperty()](SignalPkg& pkg) {
112 auto& obj = std::get<SignalObject>(pkg);
113 return prop == std::get<Prop>(obj);
114 };
115
116 subscribe(match, std::move(signalPkg), isSameSig, mgr);
117 }
118 }
119}
120
Matthew Barth599afce2021-05-13 16:15:22 -0500121void interfacesAdded(Manager* mgr, const std::string& eventName,
122 std::unique_ptr<ActionBase>& action)
123{
124 // Groups are optional, but a signal triggered event with no groups
125 // will do nothing since signals require a group
126 for (const auto& group : action->getGroups())
127 {
128 for (const auto& member : group.getMembers())
129 {
130 // Setup interfaces added signal handler on the group member
131 const auto match = rules::interfacesAdded(member);
132 SignalPkg signalPkg = {Handlers::interfacesAdded,
133 SignalObject(std::cref(member),
134 std::cref(group.getInterface()),
135 std::cref(group.getProperty())),
136 SignalActions({action})};
137 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
138 auto& obj = std::get<SignalObject>(pkg);
139 return intf == std::get<Intf>(obj);
140 };
141
142 subscribe(match, std::move(signalPkg), isSameSig, mgr);
143 }
144 }
145}
146
Matthew Barthc2691402021-05-13 16:20:32 -0500147void interfacesRemoved(Manager* mgr, const std::string& eventName,
148 std::unique_ptr<ActionBase>& action)
149{
150 // Groups are optional, but a signal triggered event with no groups
151 // will do nothing since signals require a group
152 for (const auto& group : action->getGroups())
153 {
154 for (const auto& member : group.getMembers())
155 {
156 // Setup interfaces added signal handler on the group member
157 const auto match = rules::interfacesRemoved(member);
158 SignalPkg signalPkg = {Handlers::interfacesRemoved,
159 SignalObject(std::cref(member),
160 std::cref(group.getInterface()),
161 std::cref(group.getProperty())),
162 SignalActions({action})};
163 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
164 auto& obj = std::get<SignalObject>(pkg);
165 return intf == std::get<Intf>(obj);
166 };
167
168 subscribe(match, std::move(signalPkg), isSameSig, mgr);
169 }
170 }
171}
172
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500173void nameOwnerChanged(Manager* mgr, const std::string& eventName,
174 std::unique_ptr<ActionBase>& action)
175{
176 // Groups are optional, but a signal triggered event with no groups
177 // will do nothing since signals require a group
178 for (const auto& group : action->getGroups())
179 {
180 for (const auto& member : group.getMembers())
181 {
182 auto serv = Manager::getService(member, group.getInterface());
183 if (!serv.empty())
184 {
185 // Setup name owner changed signal handler on the group
186 // member's service
187 const auto match = rules::nameOwnerChanged(serv);
188 SignalPkg signalPkg = {
189 Handlers::nameOwnerChanged,
190 SignalObject(std::cref(member),
191 std::cref(group.getInterface()),
192 std::cref(group.getProperty())),
193 SignalActions({action})};
194 // If signal match already exists, then the service will be the
195 // same so add action to be run
196 auto isSameSig = [](SignalPkg& pkg) { return true; };
197
198 subscribe(match, std::move(signalPkg), isSameSig, mgr);
199 }
200 else
201 {
202 // Unable to construct nameOwnerChanged match string
203 // Path and/or interface configured does not exist on dbus yet?
204 // TODO How to handle this? Create timer to keep checking for
205 // service to appear? When to stop checking?
206 log<level::ERR>(
207 fmt::format(
208 "Event '{}' will not be triggered by name owner "
209 "changed signals from service of path {}, interface {}",
210 eventName, member, group.getInterface())
211 .c_str());
212 }
213 }
214 }
215}
216
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500217void triggerSignal(const json& jsonObj, const std::string& eventName,
218 Manager* mgr,
219 std::vector<std::unique_ptr<ActionBase>>& actions)
220{
221 auto subscriber = signals.end();
222 if (jsonObj.contains("signal"))
223 {
224 auto signal = jsonObj["signal"].get<std::string>();
225 std::transform(signal.begin(), signal.end(), signal.begin(), tolower);
226 subscriber = signals.find(signal);
227 }
228 if (subscriber == signals.end())
229 {
230 // Construct list of available signals
231 auto availSignals =
232 std::accumulate(std::next(signals.begin()), signals.end(),
233 signals.begin()->first, [](auto list, auto signal) {
234 return std::move(list) + ", " + signal.first;
235 });
236 auto msg =
237 fmt::format("Event '{}' requires a supported signal given to be "
238 "triggered by signal, available signals: {}",
239 eventName, availSignals);
240 log<level::ERR>(msg.c_str());
241 throw std::runtime_error(msg.c_str());
242 }
243
244 for (auto& action : actions)
245 {
246 // Call signal subscriber for each group in the action
247 subscriber->second(mgr, eventName, action);
248 }
249}
250
251} // namespace phosphor::fan::control::json::trigger::signal