Home | History | Annotate | Download | only in glib
      1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "brillo/glib/dbus.h"
      6 
      7 #include <dbus/dbus.h>
      8 #include <dbus/dbus-glib-bindings.h>
      9 #include <dbus/dbus-glib-lowlevel.h>
     10 
     11 #include <base/logging.h>
     12 #include <base/strings/stringprintf.h>
     13 
     14 namespace brillo {
     15 namespace dbus {
     16 
     17 bool CallPtrArray(const Proxy& proxy,
     18                   const char* method,
     19                   glib::ScopedPtrArray<const char*>* result) {
     20   glib::ScopedError error;
     21 
     22   ::GType g_type_array = ::dbus_g_type_get_collection("GPtrArray",
     23                                                        DBUS_TYPE_G_OBJECT_PATH);
     24 
     25 
     26   if (!::dbus_g_proxy_call(proxy.gproxy(), method, &Resetter(&error).lvalue(),
     27                            G_TYPE_INVALID, g_type_array,
     28                            &Resetter(result).lvalue(), G_TYPE_INVALID)) {
     29     LOG(WARNING) << "CallPtrArray failed: "
     30         << (error->message ? error->message : "Unknown Error.");
     31     return false;
     32   }
     33 
     34   return true;
     35 }
     36 
     37 BusConnection GetSystemBusConnection() {
     38   glib::ScopedError error;
     39   ::DBusGConnection* result = ::dbus_g_bus_get(DBUS_BUS_SYSTEM,
     40                                                &Resetter(&error).lvalue());
     41   if (!result) {
     42     LOG(ERROR) << "dbus_g_bus_get(DBUS_BUS_SYSTEM) failed: "
     43                << ((error.get() && error->message) ?
     44                    error->message : "Unknown Error");
     45     return BusConnection(nullptr);
     46   }
     47   // Set to not exit when system bus is disconnected.
     48   // This fixes the problem where when the dbus daemon is stopped, exit is
     49   // called which kills Chrome.
     50   ::dbus_connection_set_exit_on_disconnect(
     51       ::dbus_g_connection_get_connection(result), FALSE);
     52   return BusConnection(result);
     53 }
     54 
     55 BusConnection GetPrivateBusConnection(const char* address) {
     56   // Since dbus-glib does not have an API like dbus_g_connection_open_private(),
     57   // we have to implement our own.
     58 
     59   // We have to call _dbus_g_value_types_init() to register standard marshalers
     60   // just like as dbus_g_bus_get() and dbus_g_connection_open() do, but the
     61   // function is not exported. So we call GetPrivateBusConnection() which calls
     62   // dbus_g_bus_get() here instead. Note that if we don't call
     63   // _dbus_g_value_types_init(), we might get "WARNING **: No demarshaller
     64   // registered for type xxxxx" error and might not be able to handle incoming
     65   // signals nor method calls.
     66   {
     67     BusConnection system_bus_connection = GetSystemBusConnection();
     68     if (!system_bus_connection.HasConnection()) {
     69       return system_bus_connection;  // returns NULL connection.
     70     }
     71   }
     72 
     73   ::DBusError error;
     74   ::dbus_error_init(&error);
     75 
     76   ::DBusGConnection* result = nullptr;
     77   ::DBusConnection* raw_connection
     78         = ::dbus_connection_open_private(address, &error);
     79   if (!raw_connection) {
     80     LOG(WARNING) << "dbus_connection_open_private failed: " << address;
     81     return BusConnection(nullptr);
     82   }
     83 
     84   if (!::dbus_bus_register(raw_connection, &error)) {
     85     LOG(ERROR) << "dbus_bus_register failed: "
     86                << (error.message ? error.message : "Unknown Error.");
     87     ::dbus_error_free(&error);
     88     // TODO(yusukes): We don't call dbus_connection_close() nor g_object_unref()
     89     // here for now since these calls might interfere with IBusBus connections
     90     // in libcros and Chrome. See the comment in ~InputMethodStatusConnection()
     91     // function in platform/cros/chromeos_input_method.cc for details.
     92     return BusConnection(nullptr);
     93   }
     94 
     95   ::dbus_connection_setup_with_g_main(
     96       raw_connection, nullptr /* default context */);
     97 
     98   // A reference count of |raw_connection| is transferred to |result|. You don't
     99   // have to (and should not) unref the |raw_connection|.
    100   result = ::dbus_connection_get_g_connection(raw_connection);
    101   CHECK(result);
    102 
    103   ::dbus_connection_set_exit_on_disconnect(
    104       ::dbus_g_connection_get_connection(result), FALSE);
    105 
    106   return BusConnection(result);
    107 }
    108 
    109 bool RetrieveProperties(const Proxy& proxy,
    110                         const char* interface,
    111                         glib::ScopedHashTable* result) {
    112   glib::ScopedError error;
    113 
    114   if (!::dbus_g_proxy_call(proxy.gproxy(), "GetAll", &Resetter(&error).lvalue(),
    115                            G_TYPE_STRING, interface, G_TYPE_INVALID,
    116                            ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
    117                                                  G_TYPE_VALUE),
    118                            &Resetter(result).lvalue(), G_TYPE_INVALID)) {
    119     LOG(WARNING) << "RetrieveProperties failed: "
    120         << (error->message ? error->message : "Unknown Error.");
    121     return false;
    122   }
    123   return true;
    124 }
    125 
    126 Proxy::Proxy()
    127     : object_(nullptr) {
    128 }
    129 
    130 // Set |connect_to_name_owner| true if you'd like to use
    131 // dbus_g_proxy_new_for_name_owner() rather than dbus_g_proxy_new_for_name().
    132 Proxy::Proxy(const BusConnection& connection,
    133              const char* name,
    134              const char* path,
    135              const char* interface,
    136              bool connect_to_name_owner)
    137     : object_(GetGProxy(
    138         connection, name, path, interface, connect_to_name_owner)) {
    139 }
    140 
    141 // Equivalent to Proxy(connection, name, path, interface, false).
    142 Proxy::Proxy(const BusConnection& connection,
    143              const char* name,
    144              const char* path,
    145              const char* interface)
    146     : object_(GetGProxy(connection, name, path, interface, false)) {
    147 }
    148 
    149 // Creates a peer proxy using dbus_g_proxy_new_for_peer.
    150 Proxy::Proxy(const BusConnection& connection,
    151              const char* path,
    152              const char* interface)
    153     : object_(GetGPeerProxy(connection, path, interface)) {
    154 }
    155 
    156 Proxy::Proxy(const Proxy& x)
    157     : object_(x.object_) {
    158   if (object_)
    159     ::g_object_ref(object_);
    160 }
    161 
    162 Proxy::~Proxy() {
    163   if (object_)
    164     ::g_object_unref(object_);
    165 }
    166 
    167 /* static */
    168 Proxy::value_type Proxy::GetGProxy(const BusConnection& connection,
    169                                    const char* name,
    170                                    const char* path,
    171                                    const char* interface,
    172                                    bool connect_to_name_owner) {
    173   value_type result = nullptr;
    174   if (connect_to_name_owner) {
    175     glib::ScopedError error;
    176     result = ::dbus_g_proxy_new_for_name_owner(connection.object_,
    177                                                name,
    178                                                path,
    179                                                interface,
    180                                                &Resetter(&error).lvalue());
    181     if (!result) {
    182       DLOG(ERROR) << "Failed to construct proxy: "
    183                   << (error->message ? error->message : "Unknown Error")
    184                   << ": " << path;
    185     }
    186   } else {
    187     result = ::dbus_g_proxy_new_for_name(connection.object_,
    188                                          name,
    189                                          path,
    190                                          interface);
    191     if (!result) {
    192       LOG(ERROR) << "Failed to construct proxy: " << path;
    193     }
    194   }
    195   return result;
    196 }
    197 
    198 /* static */
    199 Proxy::value_type Proxy::GetGPeerProxy(const BusConnection& connection,
    200                                        const char* path,
    201                                        const char* interface) {
    202   value_type result = ::dbus_g_proxy_new_for_peer(connection.object_,
    203                                                   path,
    204                                                   interface);
    205   if (!result)
    206     LOG(ERROR) << "Failed to construct peer proxy: " << path;
    207 
    208   return result;
    209 }
    210 
    211 bool RegisterExclusiveService(const BusConnection& connection,
    212                               const char* interface_name,
    213                               const char* service_name,
    214                               const char* service_path,
    215                               GObject* object) {
    216   CHECK(object);
    217   CHECK(interface_name);
    218   CHECK(service_name);
    219   // Create a proxy to DBus itself so that we can request to become a
    220   // service name owner and then register an object at the related service path.
    221   Proxy proxy = brillo::dbus::Proxy(connection,
    222                                       DBUS_SERVICE_DBUS,
    223                                       DBUS_PATH_DBUS,
    224                                       DBUS_INTERFACE_DBUS);
    225   // Exclusivity is determined by replacing any existing
    226   // service, not queuing, and ensuring we are the primary
    227   // owner after the name is ours.
    228   glib::ScopedError err;
    229   guint result = 0;
    230   // TODO(wad) determine if we are moving away from using generated functions
    231   if (!org_freedesktop_DBus_request_name(proxy.gproxy(),
    232                                          service_name,
    233                                          0,
    234                                          &result,
    235                                          &Resetter(&err).lvalue())) {
    236     LOG(ERROR) << "Unable to request service name: "
    237                << (err->message ? err->message : "Unknown Error.");
    238     return false;
    239   }
    240 
    241   // Handle the error codes, releasing the name if exclusivity conditions
    242   // are not met.
    243   bool needs_release = false;
    244   if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
    245     LOG(ERROR) << "Failed to become the primary owner. Releasing . . .";
    246     needs_release = true;
    247   }
    248   if (result == DBUS_REQUEST_NAME_REPLY_EXISTS) {
    249     LOG(ERROR) << "Service name exists: " << service_name;
    250     return false;
    251   } else if (result == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
    252     LOG(ERROR) << "Service name request enqueued despite our flags. Releasing";
    253     needs_release = true;
    254   }
    255   LOG_IF(WARNING, result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER)
    256     << "Service name already owned by this process";
    257   if (needs_release) {
    258     if (!org_freedesktop_DBus_release_name(
    259            proxy.gproxy(),
    260            service_name,
    261            &result,
    262            &Resetter(&err).lvalue())) {
    263       LOG(ERROR) << "Unabled to release service name: "
    264                  << (err->message ? err->message : "Unknown Error.");
    265     }
    266     DLOG(INFO) << "ReleaseName returned code " << result;
    267     return false;
    268   }
    269 
    270   // Determine a path from the service name and register the object.
    271   dbus_g_connection_register_g_object(connection.g_connection(),
    272                                       service_path,
    273                                       object);
    274   return true;
    275 }
    276 
    277 void CallMethodWithNoArguments(const char* service_name,
    278                                const char* path,
    279                                const char* interface_name,
    280                                const char* method_name) {
    281   Proxy proxy(dbus::GetSystemBusConnection(),
    282               service_name,
    283               path,
    284               interface_name);
    285   ::dbus_g_proxy_call_no_reply(proxy.gproxy(), method_name, G_TYPE_INVALID);
    286 }
    287 
    288 void SignalWatcher::StartMonitoring(const std::string& interface,
    289                                     const std::string& signal) {
    290   DCHECK(interface_.empty()) << "StartMonitoring() must be called only once";
    291   interface_ = interface;
    292   signal_ = signal;
    293 
    294   // Snoop on D-Bus messages so we can get notified about signals.
    295   DBusConnection* dbus_conn = dbus_g_connection_get_connection(
    296       GetSystemBusConnection().g_connection());
    297   DCHECK(dbus_conn);
    298 
    299   DBusError error;
    300   dbus_error_init(&error);
    301   dbus_bus_add_match(dbus_conn, GetDBusMatchString().c_str(), &error);
    302   if (dbus_error_is_set(&error)) {
    303     LOG(DFATAL) << "Got error while adding D-Bus match rule: " << error.name
    304                 << " (" << error.message << ")";
    305   }
    306 
    307   if (!dbus_connection_add_filter(dbus_conn,
    308                                   &SignalWatcher::FilterDBusMessage,
    309                                   this,        // user_data
    310                                   nullptr)) {  // free_data_function
    311     LOG(DFATAL) << "Unable to add D-Bus filter";
    312   }
    313 }
    314 
    315 SignalWatcher::~SignalWatcher() {
    316   if (interface_.empty())
    317     return;
    318 
    319   DBusConnection* dbus_conn = dbus_g_connection_get_connection(
    320       dbus::GetSystemBusConnection().g_connection());
    321   DCHECK(dbus_conn);
    322 
    323   dbus_connection_remove_filter(dbus_conn,
    324                                 &SignalWatcher::FilterDBusMessage,
    325                                 this);
    326 
    327   DBusError error;
    328   dbus_error_init(&error);
    329   dbus_bus_remove_match(dbus_conn, GetDBusMatchString().c_str(), &error);
    330   if (dbus_error_is_set(&error)) {
    331     LOG(DFATAL) << "Got error while removing D-Bus match rule: " << error.name
    332                 << " (" << error.message << ")";
    333   }
    334 }
    335 
    336 std::string SignalWatcher::GetDBusMatchString() const {
    337   return base::StringPrintf("type='signal', interface='%s', member='%s'",
    338                             interface_.c_str(), signal_.c_str());
    339 }
    340 
    341 /* static */
    342 DBusHandlerResult SignalWatcher::FilterDBusMessage(DBusConnection* dbus_conn,
    343                                                    DBusMessage* message,
    344                                                    void* data) {
    345   SignalWatcher* self = static_cast<SignalWatcher*>(data);
    346   if (dbus_message_is_signal(
    347           message, self->interface_.c_str(), self->signal_.c_str())) {
    348     self->OnSignal(message);
    349     return DBUS_HANDLER_RESULT_HANDLED;
    350   } else {
    351     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    352   }
    353 }
    354 
    355 }  // namespace dbus
    356 }  // namespace brillo
    357