Fcitx
bus.cpp
1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #include "../../dbus/bus.h"
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <cstdint>
11 #include <cstdlib>
12 #include <cstring>
13 #include <exception>
14 #include <memory>
15 #include <stdexcept>
16 #include <string>
17 #include <unordered_map>
18 #include <utility>
19 #include <vector>
20 #include <dbus/dbus-protocol.h>
21 #include <dbus/dbus-shared.h>
22 #include <dbus/dbus.h>
23 #include "fcitx-utils/environ.h"
24 #include "../../charutils.h"
25 #include "../../dbus/matchrule.h"
26 #include "../../dbus/message.h"
27 #include "../../dbus/objectvtable.h"
28 #include "../../event.h"
29 #include "../../eventloopinterface.h"
30 #include "../../flags.h"
31 #include "../../log.h"
32 #include "../../macros.h"
33 #include "../../misc_p.h"
34 #include "../../stringutils.h"
35 #include "../../trackableobject.h"
36 #include "bus_p.h"
37 #include "config.h"
38 #include "message_p.h"
39 #include "objectvtable_p_libdbus.h"
40 
41 namespace fcitx::dbus {
42 
43 FCITX_DEFINE_LOG_CATEGORY(libdbus_logcategory, "libdbus");
44 
45 class BusWatches : public std::enable_shared_from_this<BusWatches> {
46  struct Private {};
47 
48 public:
49  BusWatches(BusPrivate &bus, Private /*unused*/) : bus_(bus.watch()) {}
50 
51  void addWatch(DBusWatch *watch) {
52  watches_[watch] = std::make_shared<DBusWatch *>(watch);
53  refreshWatch();
54  }
55  bool removeWatch(DBusWatch *watch) {
56  watches_.erase(watch);
57  refreshWatch();
58  return watches_.empty();
59  }
60  void refreshWatch() {
61  if (watches_.empty() || !bus_.isValid()) {
62  ioEvent_.reset();
63  return;
64  }
65 
66  int fd = dbus_watch_get_unix_fd(watches_.begin()->first);
67  IOEventFlags flags;
68  for (const auto &[watch, _] : watches_) {
69  if (!dbus_watch_get_enabled(watch)) {
70  continue;
71  }
72  int dflags = dbus_watch_get_flags(watch);
73  if (dflags & DBUS_WATCH_READABLE) {
74  flags |= IOEventFlag::In;
75  }
76  if (dflags & DBUS_WATCH_WRITABLE) {
77  flags |= IOEventFlag::Out;
78  }
79  }
80 
81  FCITX_LIBDBUS_DEBUG()
82  << "IOWatch for dbus fd: " << fd << " flags: " << flags;
83  if (flags == 0) {
84  ioEvent_.reset();
85  return;
86  }
87 
88  if (!ioEvent_) {
89  ioEvent_ = bus_.get()->loop_->addIOEvent(
90  fd, flags, [this](EventSourceIO *, int, IOEventFlags flags) {
91  // Ensure this is valid.
92  // At this point, callback is always valid, so no need to
93  // keep "this".
94  auto lock = shared_from_this();
95  // Create a copy of watcher pointers, so we can safely
96  // remove the watcher during the loop.
97  std::vector<std::weak_ptr<DBusWatch *>> watchesView;
98  watchesView.reserve(watches_.size());
99  for (const auto &[_, watchRef] : watches_) {
100  watchesView.push_back(watchRef);
101  }
102 
103  for (const auto &watchRef : watchesView) {
104  auto watchStrongRef = watchRef.lock();
105  if (!watchStrongRef) {
106  continue;
107  }
108  auto *watch = *watchStrongRef;
109  if (!dbus_watch_get_enabled(watch)) {
110  continue;
111  }
112 
113  int dflags = 0;
114 
115  if ((dbus_watch_get_flags(watch) &
116  DBUS_WATCH_READABLE) &&
117  (flags & IOEventFlag::In)) {
118  dflags |= DBUS_WATCH_READABLE;
119  }
120  if ((dbus_watch_get_flags(watch) &
121  DBUS_WATCH_WRITABLE) &&
122  (flags & IOEventFlag::Out)) {
123  dflags |= DBUS_WATCH_WRITABLE;
124  }
125  if (flags & IOEventFlag::Err) {
126  dflags |= DBUS_WATCH_ERROR;
127  }
128  if (flags & IOEventFlag::Hup) {
129  dflags |= DBUS_WATCH_HANGUP;
130  }
131  if (!dflags) {
132  continue;
133  }
134  dbus_watch_handle(watch, dflags);
135  if (auto *bus = bus_.get()) {
136  bus->dispatch();
137  }
138  }
139  return true;
140  });
141  } else {
142  ioEvent_->setEvents(flags);
143  }
144  }
145 
146  static std::shared_ptr<BusWatches> create(BusPrivate &bus) {
147  return std::make_shared<BusWatches>(bus, Private());
148  }
149 
150 private:
152  // We the value as shared ptr so we know when the watch is removed during
153  // the loop.
154  std::unordered_map<DBusWatch *, std::shared_ptr<DBusWatch *>> watches_;
155  std::unique_ptr<EventSourceIO> ioEvent_;
156 };
157 
158 DBusHandlerResult DBusMessageCallback(DBusConnection * /*unused*/,
159  DBusMessage *message, void *userdata) {
160  auto *bus = static_cast<BusPrivate *>(userdata);
161  if (!bus) {
162  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
163  }
164  try {
165  auto ref = bus->watch();
166  auto msg = MessagePrivate::fromDBusMessage(ref, message, false, true);
167  for (const auto &filter : bus->filterHandlers_.view()) {
168  if (filter && filter(msg)) {
169  return DBUS_HANDLER_RESULT_HANDLED;
170  }
171  msg.rewind();
172  }
173 
174  if (msg.type() == MessageType::Signal) {
175  if (auto *bus = ref.get()) {
176  for (auto &pair : bus->matchHandlers_.view()) {
177  auto *bus = ref.get();
178  std::string alterName;
179  if (bus && bus->nameCache_ &&
180  !pair.first.service().empty()) {
181  alterName =
182  bus->nameCache_->owner(pair.first.service());
183  }
184  if (pair.first.check(msg, alterName)) {
185  if (pair.second && pair.second(msg)) {
186  return DBUS_HANDLER_RESULT_HANDLED;
187  }
188  }
189  msg.rewind();
190  }
191  }
192  }
193  } catch (const std::exception &e) {
194  // some abnormal things threw
195  FCITX_ERROR() << e.what();
196  std::abort();
197  }
198  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
199 }
200 
201 Slot::~Slot() {}
202 
203 constexpr const char xmlHeader[] =
204  "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection "
205  "1.0//EN\" "
206  "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
207  "<node>"
208  "<interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">"
209  "<method name=\"Introspect\">"
210  "<arg name=\"data\" direction=\"out\" type=\"s\"/>"
211  "</method>"
212  "</interface>";
213 constexpr const char xmlProperties[] =
214  "<interface name=\"" DBUS_INTERFACE_PROPERTIES "\">"
215  "<method name=\"Get\">"
216  "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
217  "<arg name=\"property_name\" direction=\"in\" type=\"s\"/>"
218  "<arg name=\"value\" direction=\"out\" type=\"v\"/>"
219  "</method>"
220  "<method name=\"Set\">"
221  "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
222  "<arg name=\"property_name\" direction=\"in\" type=\"s\"/>"
223  "<arg name=\"value\" direction=\"in\" type=\"v\"/>"
224  "</method>"
225  "<method name=\"GetAll\">"
226  "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
227  "<arg name=\"values\" direction=\"out\" type=\"a{sv}\"/>"
228  "</method>"
229  "<signal name=\"PropertiesChanged\">"
230  "<arg name=\"interface_name\" type=\"s\"/>"
231  "<arg name=\"changed_properties\" type=\"a{sv}\"/>"
232  "<arg name=\"invalidated_properties\" type=\"as\"/>"
233  "</signal>"
234  "</interface>";
235 
236 constexpr const char xmlInterfaceFooter[] = "</interface>";
237 
238 constexpr const char xmlFooter[] = "</node>";
239 
240 std::string DBusObjectVTableSlot::getXml() const {
241  std::string xml;
242  xml += stringutils::concat("<interface name=\"", interface_, "\">");
243  xml += objPriv_->getXml(obj_);
244  xml += xmlInterfaceFooter;
245  return xml;
246 }
247 
248 BusPrivate::BusPrivate(Bus *bus)
249  : bus_(bus),
250  matchRuleSet_(
251  [this](const MatchRule &rule) {
252  if (!conn_) {
253  return false;
254  }
255  ScopedDBusError error;
256  if (needWatchService(rule)) {
257  nameCache()->addWatch(rule.service());
258  }
259  FCITX_LIBDBUS_DEBUG() << "Add dbus match: " << rule.rule();
260  dbus_bus_add_match(conn_.get(), rule.rule().c_str(),
261  &error.error());
262  bool isError = dbus_error_is_set(&error.error());
263  return !isError;
264  },
265  [this](const MatchRule &rule) {
266  if (!conn_) {
267  return;
268  }
269  if (needWatchService(rule)) {
270  nameCache()->removeWatch(rule.service());
271  }
272  FCITX_LIBDBUS_DEBUG() << "Remove dbus match: " << rule.rule();
273  dbus_bus_remove_match(conn_.get(), rule.rule().c_str(), nullptr);
274  }),
275  objectRegistration_(
276  [this](const std::string &path) {
277  if (!conn_) {
278  return false;
279  }
280  DBusObjectPathVTable vtable;
281  memset(&vtable, 0, sizeof(vtable));
282 
283  vtable.message_function = DBusObjectPathVTableMessageCallback;
284  return dbus_connection_register_object_path(
285  conn_.get(), path.c_str(), &vtable, this) != 0;
286  },
287  [this](const std::string &path) {
288  if (!conn_) {
289  return;
290  }
291 
292  dbus_connection_unregister_object_path(conn_.get(), path.c_str());
293  }) {}
294 
295 BusPrivate::~BusPrivate() {
296  if (conn_) {
297  dbus_connection_flush(conn_.get());
298  }
299 }
300 
301 DBusObjectVTableSlot *BusPrivate::findSlot(const std::string &path,
302  const std::string &interface) {
303  // Check if interface exists.
304  for (auto &item : objectRegistration_.view(path)) {
305  if (auto *slot = item.get()) {
306  if (slot->interface_ == interface) {
307  return slot;
308  }
309  }
310  }
311  return nullptr;
312 }
313 
314 bool BusPrivate::objectVTableCallback(Message &message) {
315  if (!objectRegistration_.hasKey(message.path())) {
316  return false;
317  }
318  if (message.interface() == "org.freedesktop.DBus.Introspectable") {
319  if (message.member() != "Introspect" || !message.signature().empty()) {
320  return false;
321  }
322  std::string xml = xmlHeader;
323  bool hasProperties = false;
324  for (auto &item : objectRegistration_.view(message.path())) {
325  if (auto *slot = item.get()) {
326  hasProperties =
327  hasProperties || !slot->objPriv_->properties_.empty();
328  xml += slot->xml_;
329  }
330  }
331  if (hasProperties) {
332  xml += xmlProperties;
333  }
334  xml += xmlFooter;
335  auto reply = message.createReply();
336  reply << xml;
337  reply.send();
338  return true;
339  }
340  if (message.interface() == "org.freedesktop.DBus.Properties") {
341  if (message.member() == "Get" && message.signature() == "ss") {
342  std::string interfaceName;
343  std::string propertyName;
344  message >> interfaceName >> propertyName;
345  if (auto *slot = findSlot(message.path(), interfaceName)) {
346  auto *property = slot->obj_->findProperty(propertyName);
347  if (property) {
348  auto reply = message.createReply();
349  reply << Container(Container::Type::Variant,
350  property->signature());
351  property->getMethod()(reply);
352  reply << ContainerEnd();
353  reply.send();
354  } else {
355  auto reply = message.createError(
356  DBUS_ERROR_UNKNOWN_PROPERTY, "No such property");
357  reply.send();
358  }
359  return true;
360  }
361  } else if (message.member() == "Set" && message.signature() == "ssv") {
362  std::string interfaceName;
363  std::string propertyName;
364  message >> interfaceName >> propertyName;
365  if (auto *slot = findSlot(message.path(), interfaceName)) {
366  auto *property = slot->obj_->findProperty(propertyName);
367  if (property) {
368  if (property->writable()) {
369  message >> Container(Container::Type::Variant,
370  property->signature());
371  if (message) {
372  auto reply = message.createReply();
373  static_cast<ObjectVTableWritableProperty *>(
374  property)
375  ->setMethod()(message);
376  message >> ContainerEnd();
377  reply.send();
378  }
379  } else {
380  auto reply =
381  message.createError(DBUS_ERROR_PROPERTY_READ_ONLY,
382  "Read-only property");
383  reply.send();
384  }
385  } else {
386  auto reply = message.createError(
387  DBUS_ERROR_UNKNOWN_PROPERTY, "No such property");
388  reply.send();
389  }
390  return true;
391  }
392  } else if (message.member() == "GetAll" && message.signature() == "s") {
393  std::string interfaceName;
394  message >> interfaceName;
395  if (auto *slot = findSlot(message.path(), interfaceName)) {
396  auto reply = message.createReply();
397  reply << Container(Container::Type::Array, Signature("{sv}"));
398  for (auto &pair : slot->objPriv_->properties_) {
399  if (pair.second->options().test(PropertyOption::Hidden)) {
400  continue;
401  }
402  reply << Container(Container::Type::DictEntry,
403  Signature("sv"));
404  reply << pair.first;
405  auto *property = pair.second;
406  reply << Container(Container::Type::Variant,
407  property->signature());
408  property->getMethod()(reply);
409  reply << ContainerEnd();
410  reply << ContainerEnd();
411  }
412  reply << ContainerEnd();
413  reply.send();
414  return true;
415  }
416  }
417  } else if (auto *slot = findSlot(message.path(), message.interface())) {
418  if (auto *method = slot->obj_->findMethod(message.member())) {
419  if (method->signature() != message.signature()) {
420  return false;
421  }
422  return method->handler()(std::move(message));
423  }
424  return false;
425  }
426  return false;
427 }
428 
429 std::string escapePath(const std::string &path) {
430  std::string newPath;
431  newPath.reserve(path.size() * 3);
432  for (auto c : path) {
433  if (charutils::islower(c) || charutils::isupper(c) ||
434  charutils::isdigit(c) || c == '_' || c == '-' || c == '/' ||
435  c == '.') {
436  newPath.push_back(c);
437  } else {
438  newPath.push_back('%');
439  newPath.push_back(charutils::toHex(c >> 4));
440  newPath.push_back(charutils::toHex(c & 0xf));
441  }
442  }
443 
444  return newPath;
445 }
446 
447 std::string sessionBusAddress() {
448  auto e = getEnvironment("DBUS_SESSION_BUS_ADDRESS");
449  if (e) {
450  return *e;
451  }
452  auto xdg = getEnvironment("XDG_RUNTIME_DIR");
453  if (!xdg) {
454  return {};
455  }
456  auto escapedXdg = escapePath(*xdg);
457  return stringutils::concat("unix:path=", escapedXdg, "/bus");
458 }
459 
460 std::string addressByType(BusType type) {
461  switch (type) {
462  case BusType::Session:
463  return sessionBusAddress();
464  case BusType::System:
465  if (auto env = getEnvironment("DBUS_SYSTEM_BUS_ADDRESS")) {
466  return *env;
467  } else {
468  return DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
469  }
470  case BusType::Default:
471  if (auto starter = getEnvironment("DBUS_STARTER_BUS_TYPE")) {
472  if (stringutils::startsWith(*starter, "system")) {
473  return addressByType(BusType::System);
474  }
475  if (stringutils::startsWith(*starter, "user") ||
476  stringutils::startsWith(*starter, "session")) {
477  return addressByType(BusType::Session);
478  }
479  }
480  if (auto address = getEnvironment("DBUS_STARTER_ADDRESS")) {
481  return *address;
482  }
483 
484  {
485  uid_t uid = getuid();
486  uid_t euid = geteuid();
487  if (uid != euid || euid != 0) {
488  return addressByType(BusType::Session);
489  }
490  return addressByType(BusType::System);
491  }
492  }
493  return {};
494 }
495 
496 Bus::Bus(BusType type) : Bus(addressByType(type)) {}
497 
498 Bus::Bus(const std::string &address)
499  : d_ptr(std::make_unique<BusPrivate>(this)) {
500  FCITX_D();
501  if (address.empty()) {
502  goto fail;
503  }
504  d->address_ = address;
505  d->conn_.reset(dbus_connection_open_private(address.c_str(), nullptr));
506  if (!d->conn_) {
507  goto fail;
508  }
509 
510  dbus_connection_set_exit_on_disconnect(d->conn_.get(), false);
511 
512  if (!dbus_bus_register(d->conn_.get(), nullptr)) {
513  goto fail;
514  }
515  if (!dbus_connection_add_filter(d->conn_.get(), DBusMessageCallback, d,
516  nullptr)) {
517  goto fail;
518  }
519  return;
520 
521 fail:
522  throw std::runtime_error("Failed to create dbus connection");
523 }
524 
525 Bus::~Bus() {
526  FCITX_D();
527  if (d->loop_) {
528  detachEventLoop();
529  }
530 }
531 
532 Bus::Bus(Bus &&other) noexcept : d_ptr(std::move(other.d_ptr)) {}
533 
534 bool Bus::isOpen() const {
535  FCITX_D();
536  return d->conn_ && dbus_connection_get_is_connected(d->conn_.get());
537 }
538 
539 Message Bus::createMethodCall(const char *destination, const char *path,
540  const char *interface, const char *member) {
541  FCITX_D();
542  auto *dmsg =
543  dbus_message_new_method_call(destination, path, interface, member);
544  if (!dmsg) {
545  return {};
546  }
547  return MessagePrivate::fromDBusMessage(d->watch(), dmsg, true, false);
548 }
549 
550 Message Bus::createSignal(const char *path, const char *interface,
551  const char *member) {
552  FCITX_D();
553  auto *dmsg = dbus_message_new_signal(path, interface, member);
554  if (!dmsg) {
555  return {};
556  }
557  return MessagePrivate::fromDBusMessage(d->watch(), dmsg, true, false);
558 }
559 
560 void DBusToggleWatch(DBusWatch *watch, void *data) {
561  auto *bus = static_cast<BusPrivate *>(data);
562  if (auto *watchers =
563  findValue(bus->ioWatchers_, dbus_watch_get_unix_fd(watch))) {
564  watchers->get()->refreshWatch();
565  }
566 }
567 
568 dbus_bool_t DBusAddWatch(DBusWatch *watch, void *data) {
569  auto *bus = static_cast<BusPrivate *>(data);
570  int fd = dbus_watch_get_unix_fd(watch);
571  FCITX_LIBDBUS_DEBUG() << "DBusAddWatch fd: " << fd
572  << " flags: " << dbus_watch_get_flags(watch);
573  auto &watchers = bus->ioWatchers_[fd];
574  if (!watchers) {
575  watchers = BusWatches::create(*bus);
576  }
577  watchers->addWatch(watch);
578  return true;
579 }
580 
581 void DBusRemoveWatch(DBusWatch *watch, void *data) {
582  FCITX_LIBDBUS_DEBUG() << "DBusRemoveWatch fd: "
583  << dbus_watch_get_unix_fd(watch);
584  auto *bus = static_cast<BusPrivate *>(data);
585  auto iter = bus->ioWatchers_.find(dbus_watch_get_unix_fd(watch));
586  if (iter == bus->ioWatchers_.end()) {
587  return;
588  }
589 
590  if (iter->second->removeWatch(watch)) {
591  bus->ioWatchers_.erase(iter);
592  }
593 }
594 
595 dbus_bool_t DBusAddTimeout(DBusTimeout *timeout, void *data) {
596  auto *bus = static_cast<BusPrivate *>(data);
597  if (!dbus_timeout_get_enabled(timeout)) {
598  return false;
599  }
600  int interval = dbus_timeout_get_interval(timeout);
601  FCITX_LIBDBUS_DEBUG() << "DBusAddTimeout: " << interval;
602  auto ref = bus->watch();
603  try {
604  bus->timeWatchers_.emplace(
605  timeout,
606  bus->loop_->addTimeEvent(
607  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + interval * 1000ULL, 0,
608  [timeout, ref](EventSourceTime *event, uint64_t) {
609  // Copy is required since the lambda may be deleted.
610  // NOLINTBEGIN(performance-unnecessary-copy-initialization)
611  const auto refPivot = ref;
612  // NOLINTEND(performance-unnecessary-copy-initialization)
613  if (dbus_timeout_get_enabled(timeout)) {
614  event->setNextInterval(
615  dbus_timeout_get_interval(timeout) * 1000ULL);
616  event->setOneShot();
617  }
618  dbus_timeout_handle(timeout);
619 
620  if (auto *bus = refPivot.get()) {
621  bus->dispatch();
622  }
623  return true;
624  }));
625  } catch (const EventLoopException &) {
626  return false;
627  }
628  return true;
629 }
630 void DBusRemoveTimeout(DBusTimeout *timeout, void *data) {
631  auto *bus = static_cast<BusPrivate *>(data);
632  bus->timeWatchers_.erase(timeout);
633 }
634 
635 void DBusToggleTimeout(DBusTimeout *timeout, void *data) {
636  DBusRemoveTimeout(timeout, data);
637  DBusAddTimeout(timeout, data);
638 }
639 
640 void DBusDispatchStatusCallback(DBusConnection * /*unused*/,
641  DBusDispatchStatus status, void *userdata) {
642  auto *bus = static_cast<BusPrivate *>(userdata);
643  if (status == DBUS_DISPATCH_DATA_REMAINS) {
644  bus->deferEvent_->setOneShot();
645  }
646 }
647 
649  FCITX_D();
650  if (d->loop_) {
651  return;
652  }
653  d->loop_ = loop;
654  do {
655  if (!dbus_connection_set_watch_functions(d->conn_.get(), DBusAddWatch,
656  DBusRemoveWatch,
657  DBusToggleWatch, d, nullptr)) {
658  break;
659  }
660  if (!dbus_connection_set_timeout_functions(
661  d->conn_.get(), DBusAddTimeout, DBusRemoveTimeout,
662  DBusToggleTimeout, d, nullptr)) {
663  break;
664  }
665  if (!d->deferEvent_) {
666  d->deferEvent_ = d->loop_->addDeferEvent([d](EventSource *) {
667  d->dispatch();
668  return true;
669  });
670  d->deferEvent_->setOneShot();
671  }
672  dbus_connection_set_dispatch_status_function(
673  d->conn_.get(), DBusDispatchStatusCallback, d, nullptr);
674  d->attached_ = true;
675  return;
676  } while (0);
677 
678  detachEventLoop();
679 }
680 
682  FCITX_D();
683  dbus_connection_set_watch_functions(d->conn_.get(), nullptr, nullptr,
684  nullptr, nullptr, nullptr);
685  dbus_connection_set_timeout_functions(d->conn_.get(), nullptr, nullptr,
686  nullptr, nullptr, nullptr);
687  dbus_connection_set_dispatch_status_function(d->conn_.get(), nullptr,
688  nullptr, nullptr);
689  d->deferEvent_.reset();
690  d->loop_ = nullptr;
691 }
692 
694  FCITX_D();
695  return d->loop_;
696 }
697 
698 std::unique_ptr<Slot> Bus::addMatch(const MatchRule &rule,
699  MessageCallback callback) {
700  FCITX_D();
701  auto slot = std::make_unique<DBusMatchSlot>();
702 
703  FCITX_LIBDBUS_DEBUG() << "Add match for rule " << rule.rule()
704  << " in rule set " << d->matchRuleSet_.hasKey(rule);
705 
706  slot->ruleRef_ = d->matchRuleSet_.add(rule, 1);
707 
708  if (!slot->ruleRef_) {
709  return nullptr;
710  }
711  slot->handler_ = d->matchHandlers_.add(rule, std::move(callback));
712 
713  return slot;
714 }
715 
716 std::unique_ptr<Slot> Bus::addFilter(MessageCallback callback) {
717  FCITX_D();
718 
719  auto slot = std::make_unique<DBusFilterSlot>();
720  slot->handler_ = d->filterHandlers_.add(std::move(callback));
721 
722  return slot;
723 }
724 
725 DBusHandlerResult DBusObjectPathMessageCallback(DBusConnection * /*unused*/,
726  DBusMessage *message,
727  void *userdata) {
728  auto *slot = static_cast<DBusObjectSlot *>(userdata);
729  if (!slot) {
730  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
731  }
732  auto msg =
733  MessagePrivate::fromDBusMessage(slot->bus_, message, false, true);
734  if (slot->callback_(msg)) {
735  return DBUS_HANDLER_RESULT_HANDLED;
736  }
737  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
738 }
739 
740 std::unique_ptr<Slot> Bus::addObject(const std::string &path,
741  MessageCallback callback) {
742  FCITX_D();
743  auto slot = std::make_unique<DBusObjectSlot>(path, std::move(callback));
744  DBusObjectPathVTable vtable;
745  memset(&vtable, 0, sizeof(vtable));
746  vtable.message_function = DBusObjectPathMessageCallback;
747  if (dbus_connection_register_object_path(d->conn_.get(), path.c_str(),
748  &vtable, slot.get())) {
749  return nullptr;
750  }
751 
752  slot->bus_ = d->watch();
753  return slot;
754 }
755 
756 DBusHandlerResult
757 DBusObjectPathVTableMessageCallback(DBusConnection * /*unused*/,
758  DBusMessage *message, void *userdata) {
759  auto *bus = static_cast<BusPrivate *>(userdata);
760  if (!bus) {
761  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
762  }
763  auto msg =
764  MessagePrivate::fromDBusMessage(bus->watch(), message, false, true);
765  if (bus->objectVTableCallback(msg)) {
766  return DBUS_HANDLER_RESULT_HANDLED;
767  }
768  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
769 }
770 
771 bool Bus::addObjectVTable(const std::string &path, const std::string &interface,
772  ObjectVTableBase &vtable) {
773  FCITX_D();
774  // Check if interface exists.
775  for (auto &item : d->objectRegistration_.view(path)) {
776  if (auto *slot = item.get()) {
777  if (slot->interface_ == interface) {
778  return false;
779  }
780  }
781  }
782 
783  auto slot = std::make_unique<DBusObjectVTableSlot>(path, interface, &vtable,
784  vtable.d_func());
785 
786  auto handler = d->objectRegistration_.add(path, slot->watch());
787  if (!handler) {
788  return false;
789  }
790 
791  slot->handler_ = std::move(handler);
792  slot->bus_ = d->watch();
793 
794  vtable.setSlot(slot.release());
795  return true;
796 }
797 
798 const char *Bus::impl() { return "libdbus"; }
799 
800 void *Bus::nativeHandle() const {
801  FCITX_D();
802  return d->conn_.get();
803 }
804 
805 bool Bus::requestName(const std::string &name, Flags<RequestNameFlag> flags) {
806  FCITX_D();
807  int d_flags =
808  ((flags & RequestNameFlag::ReplaceExisting)
809  ? DBUS_NAME_FLAG_REPLACE_EXISTING
810  : 0) |
811  ((flags & RequestNameFlag::AllowReplacement)
812  ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT
813  : 0) |
814  ((flags & RequestNameFlag::Queue) ? 0 : DBUS_NAME_FLAG_DO_NOT_QUEUE);
815  auto ret =
816  dbus_bus_request_name(d->conn_.get(), name.c_str(), d_flags, nullptr);
817  return ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
818  ret == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER ||
819  ((ret == DBUS_REQUEST_NAME_REPLY_IN_QUEUE ||
820  ret == DBUS_REQUEST_NAME_REPLY_EXISTS) &&
821  (flags & RequestNameFlag::Queue));
822 }
823 
824 bool Bus::releaseName(const std::string &name) {
825  FCITX_D();
826  return dbus_bus_release_name(d->conn_.get(), name.c_str(), nullptr) >= 0;
827 }
828 
829 std::string Bus::serviceOwner(const std::string &name, uint64_t usec) {
830  auto msg = createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus",
831  "org.freedesktop.DBus", "GetNameOwner");
832  msg << name;
833  auto reply = msg.call(usec);
834 
835  if (reply.type() == dbus::MessageType::Reply) {
836  std::string ownerName;
837  reply >> ownerName;
838  return ownerName;
839  }
840  return {};
841 }
842 
843 std::unique_ptr<Slot> Bus::serviceOwnerAsync(const std::string &name,
844  uint64_t usec,
845  MessageCallback callback) {
846  auto msg = createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus",
847  "org.freedesktop.DBus", "GetNameOwner");
848  msg << name;
849  return msg.callAsync(usec, std::move(callback));
850 }
851 
852 std::string Bus::uniqueName() {
853  FCITX_D();
854  const char *name = dbus_bus_get_unique_name(d->conn_.get());
855  if (!name) {
856  return {};
857  }
858  return name;
859 }
860 
861 std::string Bus::address() {
862  FCITX_D();
863  return d->address_;
864 }
865 
866 void Bus::flush() {
867  FCITX_D();
868  dbus_connection_flush(d->conn_.get());
869 }
870 } // namespace fcitx::dbus
bool send()
Send this message.
Definition: message.cpp:261
Helper type for serialization, should not be used directly.
Definition: message.h:191
bool addObjectVTable(const std::string &path, const std::string &interface, ObjectVTableBase &vtable)
Register a new object on the dbus.
Definition: bus.cpp:771
Utility class provides a weak reference to the object.
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
std::string path() const
Return the path of the message.
Definition: message.cpp:128
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
String like type object signature &#39;g&#39;.
Definition: message.h:164
A dbus matching rule to be used with add match.
Definition: matchrule.h:36
Message createError(const char *name, const char *message) const
Create a error reply to this message.
Definition: message.cpp:62
std::string address()
Return the dbus address being connected to.
Definition: bus.cpp:861
Definition: matchrule.h:78
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
Message createReply() const
Create a reply to this message.
Definition: message.cpp:53
Helper type for serialization, should not be used directly.
Definition: message.h:175
std::string serviceOwner(const std::string &name, uint64_t usec)
Helper function to query the service owner.
Definition: bus.cpp:829
Register a DBus property to current DBus VTable.
Definition: objectvtable.h:287
FCITX_NODISCARD void * nativeHandle() const
Return the internal pointer of the implemenation.
Definition: bus.cpp:800
void flush()
Flush the bus immediately.
Definition: bus.cpp:866
std::string member() const
Return the member of the message.
Definition: message.cpp:101
std::string interface() const
Return the interface of the message.
Definition: message.cpp:110
void attachEventLoop(EventLoop *loop)
Attach this bus to an event loop.
Definition: bus.cpp:648
Message createSignal(const char *path, const char *interface, const char *member)
Create a new signal message.
Definition: bus.cpp:550
bool startsWith(std::string_view str, std::string_view prefix)
Check if a string starts with a prefix.
Definition: stringutils.cpp:86
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
std::string signature() const
Return the signature of the message.
Definition: message.cpp:119