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