blob: 537ef502a9cc7e3f9c6efe0c6f7396d4da1138f2 [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
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050024#include <nlohmann/json.hpp>
25#include <phosphor-logging/log.hpp>
26#include <sdbusplus/bus/match.hpp>
27
28#include <algorithm>
Patrick Williamsfbf47032023-07-17 12:27:34 -050029#include <format>
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050030#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
Matthew Barthc024d782021-11-09 16:15:49 -060051 std::unique_ptr<std::vector<SignalPkg>> pkgs =
52 std::make_unique<std::vector<SignalPkg>>();
53 pkgs->emplace_back(std::move(signalPkg));
Patrick Williams3ea9ec22021-11-19 12:21:08 -060054 std::unique_ptr<sdbusplus::bus::match_t> ptrMatch = nullptr;
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050055 if (!match.empty())
56 {
57 // Subscribe to signal
Patrick Williams3ea9ec22021-11-19 12:21:08 -060058 ptrMatch = std::make_unique<sdbusplus::bus::match_t>(
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050059 mgr->getBus(), match.c_str(),
60 std::bind(std::mem_fn(&Manager::handleSignal), &(*mgr),
Matthew Barthc024d782021-11-09 16:15:49 -060061 std::placeholders::_1, pkgs.get()));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050062 }
Matthew Barthc024d782021-11-09 16:15:49 -060063 signalData.emplace_back(std::move(pkgs), std::move(ptrMatch));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050064 }
65 else
66 {
67 // Signal subscription already exists
68 // Only a single signal data entry tied to each match is supported
Matthew Barthc024d782021-11-09 16:15:49 -060069 auto& pkgs = std::get<std::unique_ptr<std::vector<SignalPkg>>>(
70 signalData.front());
71 auto sameSignal = false;
72 for (auto& pkg : *pkgs)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050073 {
74 if (isSameSig(pkg))
75 {
Matthew Barthc3a69082021-11-15 14:32:48 -060076 // Same SignalObject signal to trigger event actions,
77 // add actions to be run when signal for SignalObject received
Matt Spinlerd0ba86a2021-11-09 10:09:13 -060078 auto& pkgActions = std::get<TriggerActions>(signalPkg);
79 auto& actions = std::get<TriggerActions>(pkg);
Matthew Barthc3a69082021-11-15 14:32:48 -060080 actions.insert(actions.end(), pkgActions.begin(),
81 pkgActions.end());
Matthew Barthc024d782021-11-09 16:15:49 -060082 sameSignal = true;
83 break;
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050084 }
Matthew Barthc024d782021-11-09 16:15:49 -060085 }
86 if (!sameSignal)
87 {
88 // Expected signal differs, add signal package
89 pkgs->emplace_back(std::move(signalPkg));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050090 }
91 }
92}
93
Matt Spinlerd0ba86a2021-11-09 10:09:13 -060094void propertiesChanged(Manager* mgr, const Group& group,
95 TriggerActions& actions, 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
Patrick Williams61b73292023-05-10 07:50:12 -0500103 const auto match = rules::propertiesChanged(member,
104 group.getInterface());
Matthew Barth039f91e2021-10-04 15:18:07 -0500105 SignalPkg signalPkg = {Handlers::propertiesChanged,
106 SignalObject(std::cref(member),
107 std::cref(group.getInterface()),
108 std::cref(group.getProperty())),
Matthew Barthc3a69082021-11-15 14:32:48 -0600109 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500110 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
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600119void interfacesAdded(Manager* mgr, const Group& group, TriggerActions& 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
Patrick Williams61b73292023-05-10 07:50:12 -0500127 const auto match = rules::interfacesAdded() +
128 rules::argNpath(0, member);
Matthew Barth039f91e2021-10-04 15:18:07 -0500129 SignalPkg signalPkg = {Handlers::interfacesAdded,
130 SignalObject(std::cref(member),
131 std::cref(group.getInterface()),
132 std::cref(group.getProperty())),
Matthew Barthc3a69082021-11-15 14:32:48 -0600133 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500134 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
135 auto& obj = std::get<SignalObject>(pkg);
136 return intf == std::get<Intf>(obj);
137 };
Matthew Barth599afce2021-05-13 16:15:22 -0500138
Matthew Barth039f91e2021-10-04 15:18:07 -0500139 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barth599afce2021-05-13 16:15:22 -0500140 }
141}
142
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600143void interfacesRemoved(Manager* mgr, const Group& group,
144 TriggerActions& actions, const json&)
Matthew Barthc2691402021-05-13 16:20:32 -0500145{
146 // Groups are optional, but a signal triggered event with no groups
147 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500148 for (const auto& member : group.getMembers())
Matthew Barthc2691402021-05-13 16:20:32 -0500149 {
Matt Spinler78a48a52022-07-14 13:39:10 -0500150 // Setup interfaces removed signal handler on the group member
Patrick Williams61b73292023-05-10 07:50:12 -0500151 const auto match = rules::interfacesRemoved() +
152 rules::argNpath(0, member);
Matthew Barth039f91e2021-10-04 15:18:07 -0500153 SignalPkg signalPkg = {Handlers::interfacesRemoved,
154 SignalObject(std::cref(member),
155 std::cref(group.getInterface()),
156 std::cref(group.getProperty())),
Matthew Barthc3a69082021-11-15 14:32:48 -0600157 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500158 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
159 auto& obj = std::get<SignalObject>(pkg);
160 return intf == std::get<Intf>(obj);
161 };
Matthew Barthc2691402021-05-13 16:20:32 -0500162
Matthew Barth039f91e2021-10-04 15:18:07 -0500163 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barthc2691402021-05-13 16:20:32 -0500164 }
165}
166
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600167void nameOwnerChanged(Manager* mgr, const Group& group, TriggerActions& actions,
Matthew Barth737f11c2021-09-27 14:23:08 -0500168 const json&)
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500169{
Matthew Barth508bc762021-10-27 11:01:54 -0500170 std::vector<std::string> grpServices;
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500171 // Groups are optional, but a signal triggered event with no groups
172 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500173 for (const auto& member : group.getMembers())
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500174 {
Matthew Barth65f72812022-01-13 15:05:34 -0600175 auto serv = group.getService();
176 if (serv.empty())
177 {
178 serv = Manager::getService(member, group.getInterface());
179 }
Matthew Barth039f91e2021-10-04 15:18:07 -0500180 if (!serv.empty())
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500181 {
Matthew Barth508bc762021-10-27 11:01:54 -0500182 // No need to re-subscribe to the same service's nameOwnerChanged
183 // signal when a prior group member provided by the same service
184 // already did the subscription
185 if (std::find(grpServices.begin(), grpServices.end(), serv) ==
186 grpServices.end())
187 {
188 // Setup name owner changed signal handler on the group
189 // member's service
190 const auto match = rules::nameOwnerChanged(serv);
Matthew Barth6d8e2d32022-02-01 16:47:08 -0600191 SignalPkg signalPkg = {Handlers::nameOwnerChanged,
192 SignalObject(), actions};
Matthew Barth508bc762021-10-27 11:01:54 -0500193 // If signal match already exists, then the service will be the
194 // same so add action to be run
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400195 auto isSameSig = [](SignalPkg&) { return true; };
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500196
Matthew Barth508bc762021-10-27 11:01:54 -0500197 subscribe(match, std::move(signalPkg), isSameSig, mgr);
198 grpServices.emplace_back(serv);
199 }
Matthew Barth039f91e2021-10-04 15:18:07 -0500200 }
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>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500208 std::format("Events will not be triggered by name owner changed"
Matthew Barth039f91e2021-10-04 15:18:07 -0500209 "signals from service of path {}, interface {}",
210 member, group.getInterface())
211 .c_str());
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500212 }
213 }
214}
215
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600216void member(Manager* mgr, const Group& group, TriggerActions& actions,
Matthew Barth93a10ab2021-10-15 14:45:42 -0500217 const json&)
Matthew Barth16861792021-09-27 15:00:27 -0500218{
Matthew Barth16861792021-09-27 15:00:27 -0500219 // No SignalObject required to associate to this signal
Matthew Barthc3a69082021-11-15 14:32:48 -0600220 SignalPkg signalPkg = {Handlers::member, SignalObject(), actions};
Matthew Barth16861792021-09-27 15:00:27 -0500221 // If signal match already exists, then the member signal will be the
222 // same so add action to be run
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400223 auto isSameSig = [](SignalPkg&) { return true; };
Matthew Barth93a10ab2021-10-15 14:45:42 -0500224
225 // Groups are optional, but a signal triggered event with no groups
226 // will do nothing since signals require a group
227 for (const auto& member : group.getMembers())
228 {
229 // Subscribe for signal from each group member
230 const auto match =
231 rules::type::signal() + rules::member(group.getProperty()) +
232 rules::path(member) + rules::interface(group.getInterface());
233
234 subscribe(match, std::move(signalPkg), isSameSig, mgr);
235 }
Matthew Barth16861792021-09-27 15:00:27 -0500236}
237
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400238enableTrigger
239 triggerSignal(const json& jsonObj, const std::string& eventName,
240 std::vector<std::unique_ptr<ActionBase>>& /*actions*/)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500241{
242 auto subscriber = signals.end();
243 if (jsonObj.contains("signal"))
244 {
245 auto signal = jsonObj["signal"].get<std::string>();
246 std::transform(signal.begin(), signal.end(), signal.begin(), tolower);
247 subscriber = signals.find(signal);
248 }
249 if (subscriber == signals.end())
250 {
251 // Construct list of available signals
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500252 auto availSignals =
253 std::accumulate(std::next(signals.begin()), signals.end(),
254 signals.begin()->first, [](auto list, auto signal) {
Patrick Williams61b73292023-05-10 07:50:12 -0500255 return std::move(list) + ", " + signal.first;
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500256 });
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500257 auto msg =
Patrick Williamsfbf47032023-07-17 12:27:34 -0500258 std::format("Event '{}' requires a supported signal given to be "
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500259 "triggered by signal, available signals: {}",
260 eventName, availSignals);
261 log<level::ERR>(msg.c_str());
262 throw std::runtime_error(msg.c_str());
263 }
264
Matthew Barth737f11c2021-09-27 14:23:08 -0500265 return [subscriber = std::move(subscriber),
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400266 jsonObj](const std::string& /*eventName*/, Manager* mgr,
Matthew Barthcd6f3792021-09-30 15:13:25 -0500267 const std::vector<Group>& groups,
Matthew Barth737f11c2021-09-27 14:23:08 -0500268 std::vector<std::unique_ptr<ActionBase>>& actions) {
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600269 TriggerActions signalActions;
Matthew Barthc3a69082021-11-15 14:32:48 -0600270 std::for_each(actions.begin(), actions.end(),
271 [&signalActions](auto& action) {
Patrick Williams61b73292023-05-10 07:50:12 -0500272 signalActions.emplace_back(std::ref(action));
273 });
Matthew Barth039f91e2021-10-04 15:18:07 -0500274 for (const auto& group : groups)
Matthew Barth54b5a242021-05-21 11:02:52 -0500275 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500276 // Call signal subscriber for each group
Matthew Barthc3a69082021-11-15 14:32:48 -0600277 subscriber->second(mgr, group, signalActions, jsonObj);
Matthew Barth54b5a242021-05-21 11:02:52 -0500278 }
279 };
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500280}
281
282} // namespace phosphor::fan::control::json::trigger::signal