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