Fcitx
bus.cpp
1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "../../dbus/bus.h"
9 #include <cerrno>
10 #include <cstdint>
11 #include <cstdlib>
12 #include <exception>
13 #include <memory>
14 #include <stdexcept>
15 #include <string>
16 #include <string_view>
17 #include <utility>
18 #include "../../dbus/matchrule.h"
19 #include "../../dbus/message.h"
20 #include "../../dbus/objectvtable.h"
21 #include "../../event.h"
22 #include "../../flags.h"
23 #include "../../log.h"
24 #include "../../macros.h"
25 #include "bus_p.h"
26 #include "message_p.h"
27 #include "objectvtable_p_sdbus.h"
28 #include "sd-bus-wrap.h"
29 
30 namespace fcitx::dbus {
31 
32 Slot::~Slot() {}
33 
34 class BusPrivate {
35 public:
36  ~BusPrivate() { sd_bus_flush_close_unref(bus_); }
37 
38  sd_bus *bus_ = nullptr;
39  EventLoop *eventLoop_ = nullptr;
40 };
41 
42 Bus::Bus(BusType type) : d_ptr(std::make_unique<BusPrivate>()) {
43  decltype(&sd_bus_open) func;
44  switch (type) {
45  case BusType::Session:
46  func = sd_bus_open_user;
47  break;
48  case BusType::System:
49  func = sd_bus_open_system;
50  break;
51  default:
52  func = sd_bus_open;
53  break;
54  }
55  if (func(&d_ptr->bus_) < 0) {
56  sd_bus_unref(d_ptr->bus_);
57  d_ptr->bus_ = nullptr;
58  throw std::runtime_error("Failed to create dbus connection");
59  }
60 }
61 
62 Bus::Bus(const std::string &address) : d_ptr(std::make_unique<BusPrivate>()) {
63  if (sd_bus_new(&d_ptr->bus_) < 0) {
64  goto fail;
65  }
66 
67  if (sd_bus_set_address(d_ptr->bus_, address.c_str()) < 0) {
68  goto fail;
69  }
70 
71  if (sd_bus_set_bus_client(d_ptr->bus_, true) < 0) {
72  goto fail;
73  }
74 
75  if (sd_bus_start(d_ptr->bus_) < 0) {
76  goto fail;
77  }
78  return;
79 
80 fail:
81  sd_bus_unref(d_ptr->bus_);
82  d_ptr->bus_ = nullptr;
83  throw std::runtime_error("Failed to create dbus connection");
84 }
85 
86 Bus::~Bus() {
87  FCITX_D();
88  if (d->eventLoop_) {
89  detachEventLoop();
90  }
91 }
92 
93 Bus::Bus(Bus &&other) noexcept : d_ptr(std::move(other.d_ptr)) {}
94 
95 bool Bus::isOpen() const {
96  FCITX_D();
97  return d->bus_ && sd_bus_is_open(d->bus_) > 0;
98 }
99 
100 Message Bus::createMethodCall(const char *destination, const char *path,
101  const char *interface, const char *member) {
102  FCITX_D();
103  Message msg;
104  auto *msgD = msg.d_func();
105  if (sd_bus_message_new_method_call(d->bus_, &msgD->msg_, destination, path,
106  interface, member) < 0) {
107  msgD->type_ = MessageType::Invalid;
108  } else {
109  msgD->type_ = MessageType::MethodCall;
110  }
111  return msg;
112 }
113 
114 Message Bus::createSignal(const char *path, const char *interface,
115  const char *member) {
116  FCITX_D();
117  Message msg;
118  auto *msgD = msg.d_func();
119  int r = sd_bus_message_new_signal(d->bus_, &msgD->msg_, path, interface,
120  member);
121  if (r < 0) {
122  msgD->type_ = MessageType::Invalid;
123  } else {
124  msgD->type_ = MessageType::Signal;
125  }
126  return msg;
127 }
128 
129 void Bus::attachEventLoop(EventLoop *loop) {
130  FCITX_D();
131  if (d->eventLoop_) {
132  return;
133  }
134  if (loop->implementation() == std::string_view("sd-event")) {
135  sd_event *event = static_cast<sd_event *>(loop->nativeHandle());
136  if (sd_bus_attach_event(d->bus_, event, 0) >= 0) {
137  d->eventLoop_ = loop;
138  }
139  } else {
140  // TODO: support sd-bus + generic event loop implementation.
141  throw std::invalid_argument(
142  "not support sd-bus with non-sdevent implementation.");
143  }
144 }
145 
146 void Bus::detachEventLoop() {
147  FCITX_D();
148  if (d->eventLoop_) {
149  sd_bus_detach_event(d->bus_);
150  d->eventLoop_ = nullptr;
151  }
152 }
153 
154 EventLoop *Bus::eventLoop() const {
155  FCITX_D();
156  return d->eventLoop_;
157 }
158 
159 int SDMessageCallback(sd_bus_message *m, void *userdata,
160  sd_bus_error * /*unused*/) {
161  auto *slot = static_cast<SDSlot *>(userdata);
162  if (!slot) {
163  return 0;
164  }
165  try {
166  auto msg = MessagePrivate::fromSDBusMessage(m);
167  auto result = slot->callback_(msg);
168  return result ? 1 : 0;
169  } catch (const std::exception &e) {
170  // some abnormal things threw
171  FCITX_ERROR() << e.what();
172  std::abort();
173  }
174  return 1;
175 }
176 
177 std::unique_ptr<Slot> Bus::addMatch(const MatchRule &rule,
178  MessageCallback callback) {
179  FCITX_D();
180  auto slot = std::make_unique<SDSlot>(std::move(callback));
181  sd_bus_slot *sdSlot;
182  int r = sd_bus_add_match(d->bus_, &sdSlot, rule.rule().c_str(),
183  SDMessageCallback, slot.get());
184  if (r < 0) {
185  return nullptr;
186  }
187 
188  slot->slot_ = sdSlot;
189 
190  return slot;
191 }
192 
193 std::unique_ptr<Slot> Bus::addFilter(MessageCallback callback) {
194  FCITX_D();
195  auto slot = std::make_unique<SDSlot>(std::move(callback));
196  sd_bus_slot *sdSlot;
197  int r = sd_bus_add_filter(d->bus_, &sdSlot, SDMessageCallback, slot.get());
198  if (r < 0) {
199  return nullptr;
200  }
201 
202  slot->slot_ = sdSlot;
203 
204  return slot;
205 }
206 
207 std::unique_ptr<Slot> Bus::addObject(const std::string &path,
208  MessageCallback callback) {
209  FCITX_D();
210  auto slot = std::make_unique<SDSlot>(std::move(callback));
211  sd_bus_slot *sdSlot;
212  int r = sd_bus_add_object(d->bus_, &sdSlot, path.c_str(), SDMessageCallback,
213  slot.get());
214  if (r < 0) {
215  return nullptr;
216  }
217 
218  slot->slot_ = sdSlot;
219 
220  return slot;
221 }
222 
223 bool Bus::addObjectVTable(const std::string &path, const std::string &interface,
224  ObjectVTableBase &vtable) {
225  FCITX_D();
226  auto slot = std::make_unique<SDVTableSlot>(this, path, interface);
227  sd_bus_slot *sdSlot;
228  int r = sd_bus_add_object_vtable(
229  d->bus_, &sdSlot, path.c_str(), interface.c_str(),
230  vtable.d_func()->toSDBusVTable(&vtable), &vtable);
231  if (r < 0) {
232  return false;
233  }
234 
235  slot->slot_ = sdSlot;
236 
237  vtable.setSlot(slot.release());
238  return true;
239 }
240 
241 const char *Bus::impl() { return "sdbus"; }
242 
243 void *Bus::nativeHandle() const {
244  FCITX_D();
245  return d->bus_;
246 }
247 
248 bool Bus::requestName(const std::string &name, Flags<RequestNameFlag> flags) {
249  FCITX_D();
250  int sd_flags = ((flags & RequestNameFlag::ReplaceExisting)
251  ? SD_BUS_NAME_REPLACE_EXISTING
252  : 0) |
253  ((flags & RequestNameFlag::AllowReplacement)
254  ? SD_BUS_NAME_ALLOW_REPLACEMENT
255  : 0) |
256  ((flags & RequestNameFlag::Queue) ? SD_BUS_NAME_QUEUE : 0);
257  int r = sd_bus_request_name(d->bus_, name.c_str(), sd_flags);
258  return r >= 0 || r == -EALREADY;
259 }
260 
261 bool Bus::releaseName(const std::string &name) {
262  FCITX_D();
263  return sd_bus_release_name(d->bus_, name.c_str()) >= 0;
264 }
265 
266 std::string Bus::serviceOwner(const std::string &name, uint64_t usec) {
267  auto msg = createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus",
268  "org.freedesktop.DBus", "GetNameOwner");
269  msg << name;
270  auto reply = msg.call(usec);
271 
272  if (reply.type() == dbus::MessageType::Reply) {
273  std::string ownerName;
274  reply >> ownerName;
275  return ownerName;
276  }
277  return {};
278 }
279 
280 std::unique_ptr<Slot> Bus::serviceOwnerAsync(const std::string &name,
281  uint64_t usec,
282  MessageCallback callback) {
283  auto msg = createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus",
284  "org.freedesktop.DBus", "GetNameOwner");
285  msg << name;
286  return msg.callAsync(usec, std::move(callback));
287 }
288 
289 std::string Bus::uniqueName() {
290  FCITX_D();
291  const char *name = nullptr;
292  if (sd_bus_get_unique_name(d->bus_, &name) < 0) {
293  return {};
294  }
295  return name;
296 }
297 
298 std::string Bus::address() {
299  FCITX_D();
300  const char *address = nullptr;
301  if (sd_bus_get_address(d->bus_, &address) < 0) {
302  return {};
303  }
304  return address;
305 }
306 
307 void Bus::flush() {
308  FCITX_D();
309  sd_bus_flush(d->bus_);
310 }
311 } // namespace fcitx::dbus
bool addObjectVTable(const std::string &path, const std::string &interface, ObjectVTableBase &vtable)
Register a new object on the dbus.
Definition: bus.cpp:771
Basic DBus type of a DBus message.
Definition: message.h:224
FCITX_NODISCARD EventLoop * eventLoop() const
Return the attached event loop.
Definition: bus.cpp:693
bool requestName(const std::string &name, Flags< RequestNameFlag > flags)
Request the dbus name on the bus.
Definition: bus.cpp:805
bool releaseName(const std::string &name)
Release the dbus name.
Definition: bus.cpp:824
Bus(const std::string &address)
Connect to given address.
Definition: bus.cpp:498
A dbus matching rule to be used with add match.
Definition: matchrule.h:36
std::string address()
Return the dbus address being connected to.
Definition: bus.cpp:861
A class that represents a connection to the Bus.
Definition: bus.h:51
Message createMethodCall(const char *destination, const char *path, const char *interface, const char *member)
Create a new method message.
Definition: bus.cpp:539
std::string uniqueName()
Return the unique name of current connection.
Definition: bus.cpp:852
std::string serviceOwner(const std::string &name, uint64_t usec)
Helper function to query the service owner.
Definition: bus.cpp:829
FCITX_NODISCARD void * nativeHandle() const
Return the internal pointer of the implemenation.
Definition: bus.cpp:800
const char * implementation() const
Return the name of implementation of event loop.
Definition: event.cpp:55
void flush()
Flush the bus immediately.
Definition: bus.cpp:866
void attachEventLoop(EventLoop *loop)
Attach this bus to an event loop.
Definition: bus.cpp:648
Class provides bit flag support for Enum.
Definition: flags.h:33
Message createSignal(const char *path, const char *interface, const char *member)
Create a new signal message.
Definition: bus.cpp:550
void detachEventLoop()
Remove this bus from an event loop.
Definition: bus.cpp:681
static const char * impl()
Return the name of the compiled implentation of fcitx dbus.
Definition: bus.cpp:798
FCITX_NODISCARD bool isOpen() const
Check if the connection is open.
Definition: bus.cpp:534