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