blob: e628d249e332b5c89f4a821adc78b27eabbcecc6 [file] [log] [blame]
Artem Senicheve8837d52020-06-07 11:59:04 +03001// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2020 YADRO
3
4#include "dbus_loop.hpp"
5
6#include <phosphor-logging/log.hpp>
7
8#include <system_error>
9
10using namespace phosphor::logging;
11
12DbusLoop::DbusLoop() : bus(nullptr), event(nullptr)
13{
14 int rc;
15
16 rc = sd_bus_default(&bus);
17 if (rc < 0)
18 {
19 std::error_code ec(-rc, std::generic_category());
20 throw std::system_error(ec, "Unable to initiate D-Bus connection");
21 }
22
23 rc = sd_event_default(&event);
24 if (rc < 0)
25 {
26 sd_bus_unref(bus);
27 std::error_code ec(-rc, std::generic_category());
28 throw std::system_error(ec, "Unable to create D-Bus event loop");
29 }
30
31 rc = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
32 if (rc < 0)
33 {
34 sd_bus_unref(bus);
35 sd_event_unref(event);
36 std::error_code ec(-rc, std::generic_category());
37 throw std::system_error(ec, "Unable to attach D-Bus event");
38 }
39}
40
41DbusLoop::~DbusLoop()
42{
43 sd_bus_unref(bus);
44 sd_event_unref(event);
45}
46
47int DbusLoop::run() const
48{
49 return sd_event_loop(event);
50}
51
52void DbusLoop::stop(int code) const
53{
54 sd_event_exit(event, code);
55}
56
57void DbusLoop::addPropertyHandler(const std::string& objPath,
58 const WatchProperties& props,
59 std::function<void()> callback)
60{
61 // Add match handler
62 const int rc = sd_bus_match_signal(bus, nullptr, nullptr, objPath.c_str(),
63 "org.freedesktop.DBus.Properties",
64 "PropertiesChanged", msgCallback, this);
65 if (rc < 0)
66 {
67 std::error_code ec(-rc, std::generic_category());
68 throw std::system_error(ec, "Unable to register property watcher");
69 }
70
71 propWatch = props;
72 propHandler = callback;
73}
74
75void DbusLoop::addIoHandler(int fd, std::function<void()> callback)
76{
77 ioHandler = callback;
78 const int rc = sd_event_add_io(event, nullptr, fd, EPOLLIN,
79 &DbusLoop::ioCallback, this);
80 if (rc < 0)
81 {
82 std::error_code ec(-rc, std::generic_category());
83 throw std::system_error(ec, "Unable to register IO handler");
84 }
85}
86
87void DbusLoop::addSignalHandler(int signal, std::function<void()> callback)
88{
89 // Block the signal
90 sigset_t ss;
91 if (sigemptyset(&ss) < 0 || sigaddset(&ss, signal) < 0 ||
92 sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
93 {
94 std::error_code ec(errno, std::generic_category());
95 std::string err = "Unable to block signal ";
96 err += strsignal(signal);
97 throw std::system_error(ec, err);
98 }
99
100 signalHandlers.insert(std::make_pair(signal, callback));
101
102 // Register handler
103 const int rc = sd_event_add_signal(event, nullptr, signal,
104 &DbusLoop::signalCallback, this);
105 if (rc < 0)
106 {
107 std::error_code ec(-rc, std::generic_category());
108 std::string err = "Unable to register handler for signal ";
109 err += strsignal(signal);
110 throw std::system_error(ec, err);
111 }
112}
113
114int DbusLoop::msgCallback(sd_bus_message* msg, void* userdata,
115 sd_bus_error* /*err*/)
116{
117 const WatchProperties& propWatch =
118 static_cast<DbusLoop*>(userdata)->propWatch;
119
120 try
121 {
122 int rc;
123
124 // Filter out by interface name
125 const char* interface;
126 rc = sd_bus_message_read(msg, "s", &interface);
127 if (rc < 0)
128 {
129 std::error_code ec(-rc, std::generic_category());
130 throw std::system_error(ec, "Unable to read interface name");
131 }
132 const auto& itIface = propWatch.find(interface);
133 if (itIface == propWatch.end())
134 {
135 return 0; // Interface is now watched
136 }
137 const Properties& props = itIface->second;
138
139 // Read message: go through list of changed properties
140 rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "{sv}");
141 if (rc < 0)
142 {
143 std::error_code ec(-rc, std::generic_category());
144 throw std::system_error(ec, "Unable to open message container");
145 }
146 while ((rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_DICT_ENTRY,
147 "sv")) > 0)
148 {
149 // Get property's name
150 const char* name;
151 rc = sd_bus_message_read(msg, "s", &name);
152 if (rc < 0)
153 {
154 sd_bus_message_exit_container(msg);
155 std::error_code ec(-rc, std::generic_category());
156 throw std::system_error(ec, "Unable to get property name");
157 }
158
159 // Get and check property's type
160 const char* type;
161 rc = sd_bus_message_peek_type(msg, nullptr, &type);
162 if (rc < 0 || strcmp(type, "s"))
163 {
164 sd_bus_message_exit_container(msg);
165 continue;
166 }
167
168 // Get property's value
169 const char* value;
170 rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_VARIANT, type);
171 if (rc < 0)
172 {
173 sd_bus_message_exit_container(msg);
174 std::error_code ec(-rc, std::generic_category());
175 throw std::system_error(ec, "Unable to open property value");
176 }
177 rc = sd_bus_message_read(msg, type, &value);
178 if (rc < 0)
179 {
180 sd_bus_message_exit_container(msg);
181 sd_bus_message_exit_container(msg);
182 std::error_code ec(-rc, std::generic_category());
183 throw std::system_error(ec, "Unable to get property value");
184 }
185 sd_bus_message_exit_container(msg);
186
187 // Check property name/value and handle the match
188 const auto& itProps = props.find(name);
189 if (itProps != props.end() &&
190 itProps->second.find(value) != itProps->second.end())
191 {
192 static_cast<DbusLoop*>(userdata)->propHandler();
193 }
194
195 sd_bus_message_exit_container(msg);
196 }
197 sd_bus_message_exit_container(msg);
198 }
199 catch (const std::exception& ex)
200 {
201 log<level::WARNING>(ex.what());
202 }
203
204 return 0;
205}
206
207int DbusLoop::signalCallback(sd_event_source* /*src*/,
208 const struct signalfd_siginfo* si, void* userdata)
209{
210 DbusLoop* instance = static_cast<DbusLoop*>(userdata);
211 const auto it = instance->signalHandlers.find(si->ssi_signo);
212 if (it != instance->signalHandlers.end())
213 {
214 it->second();
215 }
216 else
217 {
218 std::string msg = "Unhandled signal ";
219 msg += strsignal(si->ssi_signo);
220 log<level::WARNING>(msg.c_str());
221 }
222 return 0;
223}
224
225int DbusLoop::ioCallback(sd_event_source* /*src*/, int /*fd*/,
226 uint32_t /*revents*/, void* userdata)
227{
228 static_cast<DbusLoop*>(userdata)->ioHandler();
229 return 0;
230}