blob: cd057560dff2a56ad5779af999495d2c563473c3 [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{
47 // TODO - Handle signal subscriptions to objects hosted by fan control
48 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;
56 if (!match.empty())
57 {
58 // Subscribe to signal
59 ptrMatch = std::make_unique<sdbusplus::server::match::match>(
60 mgr->getBus(), match.c_str(),
61 std::bind(std::mem_fn(&Manager::handleSignal), &(*mgr),
62 std::placeholders::_1, dataPkgs.get()));
63 }
64 signalData.emplace_back(std::move(dataPkgs), std::move(ptrMatch));
65 }
66 else
67 {
68 // Signal subscription already exists
69 // Only a single signal data entry tied to each match is supported
70 auto& pkgs = std::get<std::unique_ptr<std::vector<SignalPkg>>>(
71 signalData.front());
72 for (auto& pkg : *pkgs)
73 {
74 if (isSameSig(pkg))
75 {
76 // Same signal expected, add action to be run when signal
77 // received
78 auto& pkgActions = std::get<SignalActions>(signalPkg);
79 auto& actions = std::get<SignalActions>(pkg);
80 // Only one action is given on the signal package passed in
81 actions.push_back(pkgActions.front());
82 }
83 else
84 {
85 // Expected signal differs, add signal package
86 (*pkgs).emplace_back(std::move(signalPkg));
87 }
88 }
89 }
90}
91
92void propertiesChanged(Manager* mgr, const std::string& eventName,
93 std::unique_ptr<ActionBase>& action)
94{
95 // Groups are optional, but a signal triggered event with no groups
96 // will do nothing since signals require a group
97 for (const auto& group : action->getGroups())
98 {
99 for (const auto& member : group.getMembers())
100 {
101 // 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({action})};
110 auto isSameSig = [&prop = group.getProperty()](SignalPkg& pkg) {
111 auto& obj = std::get<SignalObject>(pkg);
112 return prop == std::get<Prop>(obj);
113 };
114
115 subscribe(match, std::move(signalPkg), isSameSig, mgr);
116 }
117 }
118}
119
Matthew Barth599afce2021-05-13 16:15:22 -0500120void interfacesAdded(Manager* mgr, const std::string& eventName,
121 std::unique_ptr<ActionBase>& action)
122{
123 // Groups are optional, but a signal triggered event with no groups
124 // will do nothing since signals require a group
125 for (const auto& group : action->getGroups())
126 {
127 for (const auto& member : group.getMembers())
128 {
129 // Setup interfaces added signal handler on the group member
130 const auto match = rules::interfacesAdded(member);
131 SignalPkg signalPkg = {Handlers::interfacesAdded,
132 SignalObject(std::cref(member),
133 std::cref(group.getInterface()),
134 std::cref(group.getProperty())),
135 SignalActions({action})};
136 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
137 auto& obj = std::get<SignalObject>(pkg);
138 return intf == std::get<Intf>(obj);
139 };
140
141 subscribe(match, std::move(signalPkg), isSameSig, mgr);
142 }
143 }
144}
145
Matthew Barthc2691402021-05-13 16:20:32 -0500146void interfacesRemoved(Manager* mgr, const std::string& eventName,
147 std::unique_ptr<ActionBase>& action)
148{
149 // Groups are optional, but a signal triggered event with no groups
150 // will do nothing since signals require a group
151 for (const auto& group : action->getGroups())
152 {
153 for (const auto& member : group.getMembers())
154 {
155 // Setup interfaces added signal handler on the group member
156 const auto match = rules::interfacesRemoved(member);
157 SignalPkg signalPkg = {Handlers::interfacesRemoved,
158 SignalObject(std::cref(member),
159 std::cref(group.getInterface()),
160 std::cref(group.getProperty())),
161 SignalActions({action})};
162 auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
163 auto& obj = std::get<SignalObject>(pkg);
164 return intf == std::get<Intf>(obj);
165 };
166
167 subscribe(match, std::move(signalPkg), isSameSig, mgr);
168 }
169 }
170}
171
Matthew Barthb03f6bb2021-05-13 16:23:10 -0500172void nameOwnerChanged(Manager* mgr, const std::string& eventName,
173 std::unique_ptr<ActionBase>& action)
174{
175 // Groups are optional, but a signal triggered event with no groups
176 // will do nothing since signals require a group
177 for (const auto& group : action->getGroups())
178 {
179 for (const auto& member : group.getMembers())
180 {
181 auto serv = Manager::getService(member, group.getInterface());
182 if (!serv.empty())
183 {
184 // Setup name owner changed signal handler on the group
185 // member's service
186 const auto match = rules::nameOwnerChanged(serv);
187 SignalPkg signalPkg = {
188 Handlers::nameOwnerChanged,
189 SignalObject(std::cref(member),
190 std::cref(group.getInterface()),
191 std::cref(group.getProperty())),
192 SignalActions({action})};
193 // If signal match already exists, then the service will be the
194 // same so add action to be run
195 auto isSameSig = [](SignalPkg& pkg) { return true; };
196
197 subscribe(match, std::move(signalPkg), isSameSig, mgr);
198 }
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(
207 "Event '{}' will not be triggered by name owner "
208 "changed signals from service of path {}, interface {}",
209 eventName, member, group.getInterface())
210 .c_str());
211 }
212 }
213 }
214}
215
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500216void triggerSignal(const json& jsonObj, const std::string& eventName,
217 Manager* mgr,
218 std::vector<std::unique_ptr<ActionBase>>& actions)
219{
220 auto subscriber = signals.end();
221 if (jsonObj.contains("signal"))
222 {
223 auto signal = jsonObj["signal"].get<std::string>();
224 std::transform(signal.begin(), signal.end(), signal.begin(), tolower);
225 subscriber = signals.find(signal);
226 }
227 if (subscriber == signals.end())
228 {
229 // Construct list of available signals
230 auto availSignals =
231 std::accumulate(std::next(signals.begin()), signals.end(),
232 signals.begin()->first, [](auto list, auto signal) {
233 return std::move(list) + ", " + signal.first;
234 });
235 auto msg =
236 fmt::format("Event '{}' requires a supported signal given to be "
237 "triggered by signal, available signals: {}",
238 eventName, availSignals);
239 log<level::ERR>(msg.c_str());
240 throw std::runtime_error(msg.c_str());
241 }
242
243 for (auto& action : actions)
244 {
245 // Call signal subscriber for each group in the action
246 subscriber->second(mgr, eventName, action);
247 }
248}
249
250} // namespace phosphor::fan::control::json::trigger::signal