blob: d6236164b832356fe51c8b56de266c2f994a03f2 [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>
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000025#include <phosphor-logging/lg2.hpp>
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050026#include <sdbusplus/bus/match.hpp>
27
28#include <algorithm>
29#include <functional>
30#include <iterator>
31#include <memory>
32#include <numeric>
33#include <utility>
34#include <vector>
35
36namespace phosphor::fan::control::json::trigger::signal
37{
38
39using json = nlohmann::json;
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050040using namespace sdbusplus::bus::match;
41
42void subscribe(const std::string& match, SignalPkg&& signalPkg,
43 std::function<bool(SignalPkg&)> isSameSig, Manager* mgr)
44{
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050045 auto& signalData = mgr->getSignal(match);
46 if (signalData.empty())
47 {
48 // Signal subscription doesnt exist, add signal package and subscribe
Matthew Barthc024d782021-11-09 16:15:49 -060049 std::unique_ptr<std::vector<SignalPkg>> pkgs =
50 std::make_unique<std::vector<SignalPkg>>();
51 pkgs->emplace_back(std::move(signalPkg));
Patrick Williams3ea9ec22021-11-19 12:21:08 -060052 std::unique_ptr<sdbusplus::bus::match_t> ptrMatch = nullptr;
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050053 if (!match.empty())
54 {
55 // Subscribe to signal
Patrick Williams3ea9ec22021-11-19 12:21:08 -060056 ptrMatch = std::make_unique<sdbusplus::bus::match_t>(
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050057 mgr->getBus(), match.c_str(),
58 std::bind(std::mem_fn(&Manager::handleSignal), &(*mgr),
Matthew Barthc024d782021-11-09 16:15:49 -060059 std::placeholders::_1, pkgs.get()));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050060 }
Matthew Barthc024d782021-11-09 16:15:49 -060061 signalData.emplace_back(std::move(pkgs), std::move(ptrMatch));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050062 }
63 else
64 {
65 // Signal subscription already exists
66 // Only a single signal data entry tied to each match is supported
Matthew Barthc024d782021-11-09 16:15:49 -060067 auto& pkgs = std::get<std::unique_ptr<std::vector<SignalPkg>>>(
68 signalData.front());
69 auto sameSignal = false;
70 for (auto& pkg : *pkgs)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050071 {
72 if (isSameSig(pkg))
73 {
Matthew Barthc3a69082021-11-15 14:32:48 -060074 // Same SignalObject signal to trigger event actions,
75 // add actions to be run when signal for SignalObject received
Matt Spinlerd0ba86a2021-11-09 10:09:13 -060076 auto& pkgActions = std::get<TriggerActions>(signalPkg);
77 auto& actions = std::get<TriggerActions>(pkg);
Matthew Barthc3a69082021-11-15 14:32:48 -060078 actions.insert(actions.end(), pkgActions.begin(),
79 pkgActions.end());
Matthew Barthc024d782021-11-09 16:15:49 -060080 sameSignal = true;
81 break;
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050082 }
Matthew Barthc024d782021-11-09 16:15:49 -060083 }
84 if (!sameSignal)
85 {
86 // Expected signal differs, add signal package
87 pkgs->emplace_back(std::move(signalPkg));
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050088 }
89 }
90}
91
Matt Spinlerd0ba86a2021-11-09 10:09:13 -060092void propertiesChanged(Manager* mgr, const Group& group,
93 TriggerActions& actions, const json&)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050094{
95 // Groups are optional, but a signal triggered event with no groups
96 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -050097 for (const auto& member : group.getMembers())
Matthew Barthbaeeb8f2021-05-13 16:03:54 -050098 {
Matthew Barth039f91e2021-10-04 15:18:07 -050099 // Setup property changed signal handler on the group member's
100 // property
Patrick Williamsdfddd642024-08-16 15:21:51 -0400101 const auto match =
102 rules::propertiesChanged(member, group.getInterface());
103 SignalPkg signalPkg = {
104 Handlers::propertiesChanged,
105 SignalObject(std::cref(member), std::cref(group.getInterface()),
106 std::cref(group.getProperty())),
107 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500108 auto isSameSig = [&prop = group.getProperty()](SignalPkg& pkg) {
109 auto& obj = std::get<SignalObject>(pkg);
110 return prop == std::get<Prop>(obj);
111 };
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500112
Matthew Barth039f91e2021-10-04 15:18:07 -0500113 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500114 }
115}
116
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600117void interfacesAdded(Manager* mgr, const Group& group, TriggerActions& actions,
Matthew Barth737f11c2021-09-27 14:23:08 -0500118 const json&)
Matthew Barth599afce2021-05-13 16:15:22 -0500119{
120 // Groups are optional, but a signal triggered event with no groups
121 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500122 for (const auto& member : group.getMembers())
Matthew Barth599afce2021-05-13 16:15:22 -0500123 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500124 // Setup interfaces added signal handler on the group member
Patrick Williamsdfddd642024-08-16 15:21:51 -0400125 const auto match =
126 rules::interfacesAdded() + rules::argNpath(0, member);
127 SignalPkg signalPkg = {
128 Handlers::interfacesAdded,
129 SignalObject(std::cref(member), std::cref(group.getInterface()),
130 std::cref(group.getProperty())),
131 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500132 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
133 auto& obj = std::get<SignalObject>(pkg);
134 return intf == std::get<Intf>(obj);
135 };
Matthew Barth599afce2021-05-13 16:15:22 -0500136
Matthew Barth039f91e2021-10-04 15:18:07 -0500137 subscribe(match, std::move(signalPkg), isSameSig, mgr);
Matthew Barth599afce2021-05-13 16:15:22 -0500138 }
139}
140
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600141void interfacesRemoved(Manager* mgr, const Group& group,
142 TriggerActions& actions, const json&)
Matthew Barthc2691402021-05-13 16:20:32 -0500143{
144 // Groups are optional, but a signal triggered event with no groups
145 // will do nothing since signals require a group
Matthew Barth039f91e2021-10-04 15:18:07 -0500146 for (const auto& member : group.getMembers())
Matthew Barthc2691402021-05-13 16:20:32 -0500147 {
Matt Spinler78a48a52022-07-14 13:39:10 -0500148 // Setup interfaces removed signal handler on the group member
Patrick Williamsdfddd642024-08-16 15:21:51 -0400149 const auto match =
150 rules::interfacesRemoved() + rules::argNpath(0, member);
151 SignalPkg signalPkg = {
152 Handlers::interfacesRemoved,
153 SignalObject(std::cref(member), std::cref(group.getInterface()),
154 std::cref(group.getProperty())),
155 actions};
Matthew Barth039f91e2021-10-04 15:18:07 -0500156 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
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600165void nameOwnerChanged(Manager* mgr, const Group& group, TriggerActions& 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 Barth65f72812022-01-13 15:05:34 -0600173 auto serv = group.getService();
174 if (serv.empty())
175 {
176 serv = Manager::getService(member, group.getInterface());
177 }
Matthew Barth039f91e2021-10-04 15:18:07 -0500178 if (!serv.empty())
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500179 {
Matthew Barth508bc762021-10-27 11:01:54 -0500180 // No need to re-subscribe to the same service's nameOwnerChanged
181 // signal when a prior group member provided by the same service
182 // already did the subscription
183 if (std::find(grpServices.begin(), grpServices.end(), serv) ==
184 grpServices.end())
185 {
186 // Setup name owner changed signal handler on the group
187 // member's service
188 const auto match = rules::nameOwnerChanged(serv);
Matthew Barth6d8e2d32022-02-01 16:47:08 -0600189 SignalPkg signalPkg = {Handlers::nameOwnerChanged,
190 SignalObject(), actions};
Matthew Barth508bc762021-10-27 11:01:54 -0500191 // If signal match already exists, then the service will be the
192 // same so add action to be run
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400193 auto isSameSig = [](SignalPkg&) { 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?
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000205 lg2::error(
206 "Events will not be triggered by name owner changed"
207 "signals from service of path {MEMBER}, interface {GROUP_INTERFACE}",
208 "MEMBER", member, "GROUP_INTERFACE", group.getInterface());
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500209 }
210 }
211}
212
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600213void member(Manager* mgr, const Group& group, TriggerActions& actions,
Matthew Barth93a10ab2021-10-15 14:45:42 -0500214 const json&)
Matthew Barth16861792021-09-27 15:00:27 -0500215{
Matthew Barth16861792021-09-27 15:00:27 -0500216 // No SignalObject required to associate to this signal
Matthew Barthc3a69082021-11-15 14:32:48 -0600217 SignalPkg signalPkg = {Handlers::member, SignalObject(), actions};
Matthew Barth16861792021-09-27 15:00:27 -0500218 // If signal match already exists, then the member signal will be the
219 // same so add action to be run
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400220 auto isSameSig = [](SignalPkg&) { return true; };
Matthew Barth93a10ab2021-10-15 14:45:42 -0500221
222 // Groups are optional, but a signal triggered event with no groups
223 // will do nothing since signals require a group
224 for (const auto& member : group.getMembers())
225 {
226 // Subscribe for signal from each group member
227 const auto match =
228 rules::type::signal() + rules::member(group.getProperty()) +
229 rules::path(member) + rules::interface(group.getInterface());
230
231 subscribe(match, std::move(signalPkg), isSameSig, mgr);
232 }
Matthew Barth16861792021-09-27 15:00:27 -0500233}
234
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500235enableTrigger triggerSignal(
236 const json& jsonObj, const std::string& eventName,
237 std::vector<std::unique_ptr<ActionBase>>& /*actions*/)
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500238{
239 auto subscriber = signals.end();
240 if (jsonObj.contains("signal"))
241 {
242 auto signal = jsonObj["signal"].get<std::string>();
243 std::transform(signal.begin(), signal.end(), signal.begin(), tolower);
244 subscriber = signals.find(signal);
245 }
246 if (subscriber == signals.end())
247 {
248 // Construct list of available signals
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500249 auto availSignals =
250 std::accumulate(std::next(signals.begin()), signals.end(),
251 signals.begin()->first, [](auto list, auto signal) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400252 return std::move(list) + ", " + signal.first;
253 });
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000254 lg2::error(
255 "Event '{EVENT_NAME}' requires a supported signal given to be "
256 "triggered by signal, available signals: {AVAILABLE_SIGNALS}",
257 "EVENT_NAME", eventName, "AVAILABLE_SIGNALS", availSignals);
258 throw std::runtime_error(
259 "Event requires a supported signal given to be triggered by signal.");
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500260 }
261
Matthew Barth737f11c2021-09-27 14:23:08 -0500262 return [subscriber = std::move(subscriber),
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400263 jsonObj](const std::string& /*eventName*/, Manager* mgr,
Matthew Barthcd6f3792021-09-30 15:13:25 -0500264 const std::vector<Group>& groups,
Matthew Barth737f11c2021-09-27 14:23:08 -0500265 std::vector<std::unique_ptr<ActionBase>>& actions) {
Matt Spinlerd0ba86a2021-11-09 10:09:13 -0600266 TriggerActions signalActions;
Matthew Barthc3a69082021-11-15 14:32:48 -0600267 std::for_each(actions.begin(), actions.end(),
268 [&signalActions](auto& action) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400269 signalActions.emplace_back(std::ref(action));
270 });
Matthew Barth039f91e2021-10-04 15:18:07 -0500271 for (const auto& group : groups)
Matthew Barth54b5a242021-05-21 11:02:52 -0500272 {
Matthew Barth039f91e2021-10-04 15:18:07 -0500273 // Call signal subscriber for each group
Matthew Barthc3a69082021-11-15 14:32:48 -0600274 subscriber->second(mgr, group, signalActions, jsonObj);
Matthew Barth54b5a242021-05-21 11:02:52 -0500275 }
276 };
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500277}
278
279} // namespace phosphor::fan::control::json::trigger::signal