• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CallPtrArray(const Proxy & proxy,const char * method,glib::ScopedPtrArray<const char * > * result)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 
GetSystemBusConnection()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 
GetPrivateBusConnection(const char * address)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 
RetrieveProperties(const Proxy & proxy,const char * interface,glib::ScopedHashTable * result)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 
Proxy()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().
Proxy(const BusConnection & connection,const char * name,const char * path,const char * interface,bool connect_to_name_owner)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).
Proxy(const BusConnection & connection,const char * name,const char * path,const char * interface)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.
Proxy(const BusConnection & connection,const char * path,const char * interface)150 Proxy::Proxy(const BusConnection& connection,
151              const char* path,
152              const char* interface)
153     : object_(GetGPeerProxy(connection, path, interface)) {
154 }
155 
Proxy(const Proxy & x)156 Proxy::Proxy(const Proxy& x)
157     : object_(x.object_) {
158   if (object_)
159     ::g_object_ref(object_);
160 }
161 
~Proxy()162 Proxy::~Proxy() {
163   if (object_)
164     ::g_object_unref(object_);
165 }
166 
167 /* static */
GetGProxy(const BusConnection & connection,const char * name,const char * path,const char * interface,bool connect_to_name_owner)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 */
GetGPeerProxy(const BusConnection & connection,const char * path,const char * interface)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 
RegisterExclusiveService(const BusConnection & connection,const char * interface_name,const char * service_name,const char * service_path,GObject * object)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 
CallMethodWithNoArguments(const char * service_name,const char * path,const char * interface_name,const char * method_name)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 
StartMonitoring(const std::string & interface,const std::string & signal)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 
~SignalWatcher()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 
GetDBusMatchString() const336 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 */
FilterDBusMessage(DBusConnection * dbus_conn,DBusMessage * message,void * data)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