blob: 8a8e17eea22e2ce44e3a71ccf1cc3d799cd6b30f [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};
Matthew Barthfac8a2f2021-06-10 15:50:36 -050053 std::vector<SignalPkg> dataPkgs =
54 std::vector<SignalPkg>(std::move(pkgs));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050055 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),
Matthew Barthfac8a2f2021-06-10 15:50:36 -050064 std::placeholders::_1, dataPkgs));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050065 }
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
Matthew Barthfac8a2f2021-06-10 15:50:36 -050072 auto& pkgs = std::get<std::vector<SignalPkg>>(signalData.front());
73 for (auto& pkg : pkgs)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050074 {
75 if (isSameSig(pkg))
76 {
Matthew Barth039f91e2021-10-04 15:18:07 -050077 // Same signal expected, add actions to be run when signal
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050078 // received
79 auto& pkgActions = std::get<SignalActions>(signalPkg);
80 auto& actions = std::get<SignalActions>(pkg);
Matthew Barth039f91e2021-10-04 15:18:07 -050081 actions.insert(actions.end(),
82 std::make_move_iterator(pkgActions.begin()),
83 std::make_move_iterator(pkgActions.end()));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050084 }
85 else
86 {
87 // Expected signal differs, add signal package
Matthew Barthfac8a2f2021-06-10 15:50:36 -050088 pkgs.emplace_back(std::move(signalPkg));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050089 }
90 }
91 }
92}
93
Matthew Barth039f91e2021-10-04 15:18:07 -050094void propertiesChanged(Manager* mgr, const Group& group, SignalActions actions,
Matthew Barth737f11c2021-09-27 14:23:08 -050095 const json&)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050096{
97 // Groups are optional, but a signal triggered event with no groups
98 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -050099 for (const auto& member : group.getMembers())
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500100 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500101 // Setup property changed signal handler on the group member's
102 // property
103 const auto match =
104 rules::propertiesChanged(member, group.getInterface());
105 SignalPkg signalPkg = {Handlers::propertiesChanged,
106 SignalObject(std::cref(member),
107 std::cref(group.getInterface()),
108 std::cref(group.getProperty())),
109 SignalActions(actions)};
110 auto isSameSig = [&prop = group.getProperty()](SignalPkg& pkg) {
111 auto& obj = std::get<SignalObject>(pkg);
112 return prop == std::get<Prop>(obj);
113 };
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500114
Matthew Barth039f91e2021-10-04 15:18:07 -0500115 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500116 }
117}
118
Matthew Barth039f91e2021-10-04 15:18:07 -0500119void interfacesAdded(Manager* mgr, const Group& group, SignalActions actions,
Matthew Barth737f11c2021-09-27 14:23:08 -0500120 const json&)
Matthew Barth599afce2021-05-13 16:15:22 -0500121{
122 // Groups are optional, but a signal triggered event with no groups
123 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500124 for (const auto& member : group.getMembers())
Matthew Barth599afce2021-05-13 16:15:22 -0500125 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500126 // Setup interfaces added signal handler on the group member
127 const auto match = rules::interfacesAdded(member);
128 SignalPkg signalPkg = {Handlers::interfacesAdded,
129 SignalObject(std::cref(member),
130 std::cref(group.getInterface()),
131 std::cref(group.getProperty())),
132 SignalActions(actions)};
133 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
134 auto& obj = std::get<SignalObject>(pkg);
135 return intf == std::get<Intf>(obj);
136 };
Matthew Barth599afce2021-05-13 16:15:22 -0500137
Matthew Barth039f91e2021-10-04 15:18:07 -0500138 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barth599afce2021-05-13 16:15:22 -0500139 }
140}
141
Matthew Barth039f91e2021-10-04 15:18:07 -0500142void interfacesRemoved(Manager* mgr, const Group& group, SignalActions actions,
Matthew Barth737f11c2021-09-27 14:23:08 -0500143 const json&)
Matthew Barthc2691402021-05-13 16:20:32 -0500144{
145 // Groups are optional, but a signal triggered event with no groups
146 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500147 for (const auto& member : group.getMembers())
Matthew Barthc2691402021-05-13 16:20:32 -0500148 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500149 // Setup interfaces added signal handler on the group member
150 const auto match = rules::interfacesRemoved(member);
151 SignalPkg signalPkg = {Handlers::interfacesRemoved,
152 SignalObject(std::cref(member),
153 std::cref(group.getInterface()),
154 std::cref(group.getProperty())),
155 SignalActions(actions)};
156 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
157 auto& obj = std::get<SignalObject>(pkg);
158 return intf == std::get<Intf>(obj);
159 };
Matthew Barthc2691402021-05-13 16:20:32 -0500160
Matthew Barth039f91e2021-10-04 15:18:07 -0500161 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barthc2691402021-05-13 16:20:32 -0500162 }
163}
164
Matthew Barth039f91e2021-10-04 15:18:07 -0500165void nameOwnerChanged(Manager* mgr, const Group& group, SignalActions actions,
Matthew Barth737f11c2021-09-27 14:23:08 -0500166 const json&)
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500167{
Matthew Barth508bc762021-10-27 11:01:54 -0500168 std::vector<std::string> grpServices;
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500169 // Groups are optional, but a signal triggered event with no groups
170 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500171 for (const auto& member : group.getMembers())
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500172 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500173 auto serv = Manager::getService(member, group.getInterface());
174 if (!serv.empty())
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500175 {
Matthew Barth508bc762021-10-27 11:01:54 -0500176 // No need to re-subscribe to the same service's nameOwnerChanged
177 // signal when a prior group member provided by the same service
178 // already did the subscription
179 if (std::find(grpServices.begin(), grpServices.end(), serv) ==
180 grpServices.end())
181 {
182 // Setup name owner changed signal handler on the group
183 // member's service
184 const auto match = rules::nameOwnerChanged(serv);
185 SignalPkg signalPkg = {
186 Handlers::nameOwnerChanged,
187 SignalObject(std::cref(member),
188 std::cref(group.getInterface()),
189 std::cref(group.getProperty())),
190 SignalActions(actions)};
191 // If signal match already exists, then the service will be the
192 // same so add action to be run
193 auto isSameSig = [](SignalPkg& pkg) { return true; };
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500194
Matthew Barth508bc762021-10-27 11:01:54 -0500195 subscribe(match, std::move(signalPkg), isSameSig, mgr);
196 grpServices.emplace_back(serv);
197 }
Matthew Barth039f91e2021-10-04 15:18:07 -0500198 }
199 else
200 {
201 // Unable to construct nameOwnerChanged match string
202 // Path and/or interface configured does not exist on dbus yet?
203 // TODO How to handle this? Create timer to keep checking for
204 // service to appear? When to stop checking?
205 log<level::ERR>(
206 fmt::format("Events will not be triggered by name owner changed"
207 "signals from service of path {}, interface {}",
208 member, group.getInterface())
209 .c_str());
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500210 }
211 }
212}
213
Matthew Barth93a10ab2021-10-15 14:45:42 -0500214void member(Manager* mgr, const Group& group, SignalActions actions,
215 const json&)
Matthew Barth16861792021-09-27 15:00:27 -0500216{
Matthew Barth16861792021-09-27 15:00:27 -0500217 // No SignalObject required to associate to this signal
218 SignalPkg signalPkg = {Handlers::member, SignalObject(),
Matthew Barth039f91e2021-10-04 15:18:07 -0500219 SignalActions(actions)};
Matthew Barth16861792021-09-27 15:00:27 -0500220 // If signal match already exists, then the member signal will be the
221 // same so add action to be run
222 auto isSameSig = [](SignalPkg& pkg) { return true; };
Matthew Barth93a10ab2021-10-15 14:45:42 -0500223
224 // Groups are optional, but a signal triggered event with no groups
225 // will do nothing since signals require a group
226 for (const auto& member : group.getMembers())
227 {
228 // Subscribe for signal from each group member
229 const auto match =
230 rules::type::signal() + rules::member(group.getProperty()) +
231 rules::path(member) + rules::interface(group.getInterface());
232
233 subscribe(match, std::move(signalPkg), isSameSig, mgr);
234 }
Matthew Barth16861792021-09-27 15:00:27 -0500235}
236
Matthew Barth54b5a242021-05-21 11:02:52 -0500237enableTrigger triggerSignal(const json& jsonObj, const std::string& eventName,
Matthew Barth039f91e2021-10-04 15:18:07 -0500238 SignalActions actions)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500239{
240 auto subscriber = signals.end();
241 if (jsonObj.contains("signal"))
242 {
243 auto signal = jsonObj["signal"].get<std::string>();
244 std::transform(signal.begin(), signal.end(), signal.begin(), tolower);
245 subscriber = signals.find(signal);
246 }
247 if (subscriber == signals.end())
248 {
249 // Construct list of available signals
250 auto availSignals =
251 std::accumulate(std::next(signals.begin()), signals.end(),
252 signals.begin()->first, [](auto list, auto signal) {
253 return std::move(list) + ", " + signal.first;
254 });
255 auto msg =
256 fmt::format("Event '{}' requires a supported signal given to be "
257 "triggered by signal, available signals: {}",
258 eventName, availSignals);
259 log<level::ERR>(msg.c_str());
260 throw std::runtime_error(msg.c_str());
261 }
262
Matthew Barth737f11c2021-09-27 14:23:08 -0500263 return [subscriber = std::move(subscriber),
264 jsonObj](const std::string& eventName, Manager* mgr,
Matthew Barthcd6f3792021-09-30 15:13:25 -0500265 const std::vector<Group>& groups,
Matthew Barth737f11c2021-09-27 14:23:08 -0500266 std::vector<std::unique_ptr<ActionBase>>& actions) {
Matthew Barth039f91e2021-10-04 15:18:07 -0500267 for (const auto& group : groups)
Matthew Barth54b5a242021-05-21 11:02:52 -0500268 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500269 // Call signal subscriber for each group
270 subscriber->second(mgr, group, actions, jsonObj);
Matthew Barth54b5a242021-05-21 11:02:52 -0500271 }
272 };
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500273}
274
275} // namespace phosphor::fan::control::json::trigger::signal