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