| /* |
| * |
| * Copyright (c) 2016 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * Description: |
| * Implementation of the DBus IPCServer subclass. |
| * |
| */ |
| |
| #if HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #ifndef ASSERT_MACROS_USE_SYSLOG |
| #define ASSERT_MACROS_USE_SYSLOG 1 |
| #endif |
| |
| #include <stdio.h> |
| #include "DBUSIPCServer.h" |
| #include <dbus/dbus.h> |
| #if USING_GLIB |
| #include <glib.h> |
| #include <glib-object.h> |
| #endif |
| #include <string.h> |
| #include "NCPControlInterface.h" |
| #include "assert-macros.h" |
| |
| #include <boost/bind.hpp> |
| #include "DBUSHelpers.h" |
| #include <errno.h> |
| #include <algorithm> |
| #include "any-to.h" |
| #include "wpan-dbus-v0.h" |
| |
| using namespace DBUSHelpers; |
| using namespace nl; |
| using namespace nl::wpantund; |
| |
| static const char gDBusObjectManagerMatchString[] = |
| "type='signal'" |
| ",interface='" WPAN_TUNNEL_DBUS_INTERFACE "'" |
| ; |
| |
| static DBusConnection * |
| get_dbus_connection() |
| { |
| static DBusConnection* connection = NULL; |
| DBusError error; |
| dbus_error_init(&error); |
| |
| if (connection == NULL) { |
| syslog(LOG_DEBUG, "Getting DBus connection"); |
| |
| connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); |
| |
| require_string(connection != NULL, bail, error.message); |
| |
| syslog(LOG_DEBUG, "Registering DBus connection"); |
| |
| dbus_bus_register(connection, &error); |
| |
| require_string(error.name == NULL, bail, error.message); |
| |
| syslog(LOG_DEBUG, "Requesting DBus connection name %s", WPAN_TUNNEL_DBUS_NAME); |
| |
| dbus_bus_request_name( |
| connection, |
| WPAN_TUNNEL_DBUS_NAME, |
| 0, |
| &error |
| ); |
| |
| require_string(error.name == NULL, bail, error.message); |
| } |
| |
| bail: |
| if (error.message) { |
| throw std::runtime_error(error.message); |
| } |
| dbus_error_free(&error); |
| |
| return connection; |
| } |
| |
| DBUSIPCServer::DBUSIPCServer(): |
| mConnection(get_dbus_connection()), |
| mAPI_v0(mConnection), |
| mAPI_v1(mConnection) |
| { |
| DBusError error; |
| |
| dbus_error_init(&error); |
| |
| static const DBusObjectPathVTable ipc_interface_vtable = { |
| NULL, |
| &DBUSIPCServer::dbus_message_handler, |
| }; |
| |
| require( |
| dbus_connection_register_object_path( |
| mConnection, |
| WPAN_TUNNEL_DBUS_PATH, |
| &ipc_interface_vtable, |
| (void*)this |
| ), |
| bail |
| ); |
| |
| dbus_bus_add_match( |
| mConnection, |
| gDBusObjectManagerMatchString, |
| &error |
| ); |
| require_string(error.name == NULL, bail, error.message); |
| |
| dbus_connection_add_filter(mConnection, &DBUSIPCServer::dbus_message_handler, (void*)this, NULL); |
| |
| syslog(LOG_NOTICE, "Ready. Using DBUS bus \"%s\"", dbus_bus_get_unique_name(mConnection)); |
| |
| bail: |
| if (error.message) { |
| throw std::runtime_error(error.message); |
| } |
| dbus_error_free(&error); |
| } |
| |
| DBUSIPCServer::~DBUSIPCServer() |
| { |
| dbus_bus_remove_match( |
| mConnection, |
| gDBusObjectManagerMatchString, |
| NULL |
| ); |
| dbus_connection_unref(mConnection); |
| } |
| |
| cms_t |
| DBUSIPCServer::get_ms_to_next_event() |
| { |
| cms_t ret = CMS_DISTANT_FUTURE; |
| |
| /* We could set up some sort of complicated mechanism |
| * using the dbus_timer objects to actually calculate this |
| * correctly, however we aren't really using any of the |
| * timer-dependent stuff in DBus. As such, the following |
| * seems to suffice. If we want wpantund to do things |
| * like handle response timeouts in a timely manner then |
| * we will need to go ahead and fully implement such a |
| * mechanism. */ |
| |
| if (dbus_connection_get_dispatch_status(mConnection) == DBUS_DISPATCH_DATA_REMAINS) { |
| ret = 0; |
| } |
| |
| if (dbus_connection_has_messages_to_send(mConnection)) { |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| void |
| DBUSIPCServer::process(void) |
| { |
| dbus_connection_read_write_dispatch(mConnection, 0); |
| } |
| |
| int |
| DBUSIPCServer::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout) |
| { |
| int ret = -1; |
| int unix_fd = -1; |
| |
| require(dbus_connection_get_unix_fd(mConnection, &unix_fd), bail); |
| |
| if (read_fd_set != NULL) { |
| FD_SET(unix_fd, read_fd_set); |
| } |
| |
| if (error_fd_set != NULL) { |
| FD_SET(unix_fd, error_fd_set); |
| } |
| |
| if ((write_fd_set != NULL) && dbus_connection_has_messages_to_send(mConnection)) { |
| FD_SET(unix_fd, write_fd_set); |
| } |
| |
| if ((max_fd != NULL)) { |
| *max_fd = std::max(*max_fd, unix_fd); |
| } |
| |
| if (timeout != NULL) { |
| *timeout = std::min(*timeout, get_ms_to_next_event()); |
| } |
| |
| ret = 0; |
| bail: |
| return ret; |
| } |
| |
| |
| |
| void |
| DBUSIPCServer::interface_added(const std::string& interface_name) |
| { |
| DBusMessage* signal; |
| const char *interface_name_cstr = interface_name.c_str(); |
| |
| syslog(LOG_DEBUG, |
| "DBus Sending Interface Added Signal for %s", |
| interface_name_cstr); |
| signal = dbus_message_new_signal( |
| WPAN_TUNNEL_DBUS_PATH, |
| WPAN_TUNNEL_DBUS_INTERFACE, |
| WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED |
| ); |
| dbus_message_append_args( |
| signal, |
| DBUS_TYPE_STRING, &interface_name_cstr, |
| DBUS_TYPE_INVALID |
| ); |
| |
| dbus_connection_send(mConnection, signal, NULL); |
| dbus_message_unref(signal); |
| } |
| |
| void |
| DBUSIPCServer::interface_removed(const std::string& interface_name) |
| { |
| DBusMessage* signal; |
| const char *interface_name_cstr = interface_name.c_str(); |
| |
| syslog(LOG_DEBUG, |
| "DBus Sending Interface Added Signal for %s", |
| interface_name_cstr); |
| signal = dbus_message_new_signal( |
| WPAN_TUNNEL_DBUS_PATH, |
| WPAN_TUNNEL_DBUS_INTERFACE, |
| "InterfaceRemoved" |
| ); |
| dbus_message_append_args( |
| signal, |
| DBUS_TYPE_STRING, &interface_name_cstr, |
| DBUS_TYPE_INVALID |
| ); |
| dbus_connection_send(mConnection, signal, NULL); |
| dbus_message_unref(signal); |
| } |
| |
| int |
| DBUSIPCServer::add_interface(NCPControlInterface* instance) |
| { |
| std::string name = instance->get_name(); |
| |
| mInterfaceMap[name] = instance; |
| |
| mAPI_v0.add_interface(instance); |
| mAPI_v1.add_interface(instance); |
| |
| interface_added(name); |
| |
| return 0; |
| } |
| |
| DBusHandlerResult |
| DBUSIPCServer::message_handler( |
| DBusConnection *connection, |
| DBusMessage *message |
| ) |
| { |
| DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| if (dbus_message_is_method_call(message, WPAN_TUNNEL_DBUS_INTERFACE, |
| WPAN_TUNNEL_CMD_GET_INTERFACES)) { |
| DBusMessage *reply = dbus_message_new_method_return(message); |
| DBusMessageIter iter, array_iter; |
| dbus_message_iter_init_append(reply, &iter); |
| |
| dbus_message_iter_open_container( |
| &iter, |
| DBUS_TYPE_ARRAY, |
| DBUS_TYPE_ARRAY_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING, |
| &array_iter |
| ); |
| { |
| std::map<std::string, NCPControlInterface*>::const_iterator i; |
| std::map<std::string, |
| NCPControlInterface*>::const_iterator end = |
| mInterfaceMap.end(); |
| for (i = mInterfaceMap.begin(); i != end; ++i) { |
| DBusMessageIter item_iter; |
| const char* name = i->first.c_str(); |
| const char* bus_name = dbus_bus_get_unique_name(connection); |
| dbus_message_iter_open_container( |
| &array_iter, |
| DBUS_TYPE_ARRAY, |
| DBUS_TYPE_STRING_AS_STRING, |
| &item_iter |
| ); |
| dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING, |
| &name); |
| dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING, |
| &bus_name); |
| dbus_message_iter_close_container(&array_iter, &item_iter); |
| } |
| } |
| { |
| std::map<std::string, std::string>::const_iterator i; |
| std::map<std::string, std::string>::const_iterator end = |
| mExternalInterfaceMap.end(); |
| for (i = mExternalInterfaceMap.begin(); i != end; ++i) { |
| DBusMessageIter item_iter; |
| const char* name = i->first.c_str(); |
| const char* bus_name = i->second.c_str(); |
| dbus_message_iter_open_container( |
| &array_iter, |
| DBUS_TYPE_ARRAY, |
| DBUS_TYPE_STRING_AS_STRING, |
| &item_iter |
| ); |
| dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING, |
| &name); |
| dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING, |
| &bus_name); |
| dbus_message_iter_close_container(&array_iter, &item_iter); |
| } |
| } |
| |
| dbus_message_iter_close_container(&iter, &array_iter); |
| |
| dbus_connection_send(connection, reply, NULL); |
| dbus_message_unref(reply); |
| |
| ret = DBUS_HANDLER_RESULT_HANDLED; |
| } else if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE, WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED)) { |
| ret = DBUS_HANDLER_RESULT_HANDLED; |
| if (!strequal(dbus_message_get_sender(message), dbus_bus_get_unique_name(connection))) { |
| const char* interface_name = NULL; |
| dbus_message_get_args( |
| message, NULL, |
| DBUS_TYPE_STRING, &interface_name, |
| DBUS_TYPE_INVALID |
| ); |
| if (NULL != interface_name && (mInterfaceMap.count(interface_name) == 0)) { |
| mExternalInterfaceMap[interface_name] = dbus_message_get_sender(message); |
| } |
| } |
| } else if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE, WPAN_TUNNEL_SIGNAL_INTERFACE_REMOVED)) { |
| ret = DBUS_HANDLER_RESULT_HANDLED; |
| if (!strequal(dbus_message_get_sender(message), dbus_bus_get_unique_name(connection))) { |
| const char* interface_name = NULL; |
| dbus_message_get_args( |
| message, NULL, |
| DBUS_TYPE_STRING, &interface_name, |
| DBUS_TYPE_INVALID |
| ); |
| if ((NULL != interface_name) |
| && (mInterfaceMap.count(interface_name) == 0) |
| && (mExternalInterfaceMap.count(interface_name) == 1) |
| && (mExternalInterfaceMap[interface_name] == dbus_message_get_sender(message)) |
| ) { |
| mExternalInterfaceMap.erase(interface_name); |
| } |
| } |
| } else if (dbus_message_is_method_call(message, WPAN_TUNNEL_DBUS_INTERFACE, |
| WPAN_TUNNEL_CMD_GET_VERSION)) { |
| DBusMessage *reply = dbus_message_new_method_return(message); |
| uint32_t version = WPAN_TUNNEL_DBUS_VERSION; |
| dbus_message_append_args( |
| reply, |
| DBUS_TYPE_UINT32, &version, |
| DBUS_TYPE_INVALID |
| ); |
| |
| dbus_connection_send(connection, reply, NULL); |
| dbus_message_unref(reply); |
| |
| ret = DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| return ret; |
| } |
| |
| DBusHandlerResult |
| DBUSIPCServer::dbus_message_handler( |
| DBusConnection *connection, |
| DBusMessage * message, |
| void * user_data |
| ) |
| { |
| return ((DBUSIPCServer*)user_data)->message_handler(connection, message); |
| } |