shill: chromeos DBus proxy for dhcpcd
Implement ChromeosDHCPCDListener by registering a filter function and
adding a match rule directly to the bus, which introduces a direct
dependency on libdbus-1.
Also dhcpcd proxy does not listen for signals, since those signals are
monitored/processed by dhcpcd listener.
BUG=chromium:507869
TEST=USE="chromeos_dbus asan clang" FEATURES=test emerge-$BOARD shill
TEST=Verify ChromeosDHCPCDListener with a simple test daemon
Change-Id: I0b5ec4acdc5c273388b0b16ffb9b6b67f45b8c89
Reviewed-on: https://chromium-review.googlesource.com/288294
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Zeping Qiu <zqiu@chromium.org>
Tested-by: Zeping Qiu <zqiu@chromium.org>
diff --git a/dbus/chromeos_dhcpcd_listener.cc b/dbus/chromeos_dhcpcd_listener.cc
new file mode 100644
index 0000000..d61345a
--- /dev/null
+++ b/dbus/chromeos_dhcpcd_listener.cc
@@ -0,0 +1,178 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/dbus/chromeos_dhcpcd_listener.h"
+
+#include <string.h>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <dbus/util.h>
+
+#include "shill/dhcp/dhcp_config.h"
+#include "shill/dhcp/dhcp_provider.h"
+#include "shill/event_dispatcher.h"
+#include "shill/logging.h"
+
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kDHCP;
+static string ObjectID(ChromeosDHCPCDListener* d) {
+ return "(dhcpcd_listener)";
+}
+}
+
+const char ChromeosDHCPCDListener::kDBusInterfaceName[] = "org.chromium.dhcpcd";
+const char ChromeosDHCPCDListener::kSignalEvent[] = "Event";
+const char ChromeosDHCPCDListener::kSignalStatusChanged[] = "StatusChanged";
+
+ChromeosDHCPCDListener::ChromeosDHCPCDListener(
+ const scoped_refptr<dbus::Bus>& bus,
+ EventDispatcher* dispatcher,
+ DHCPProvider* provider)
+ : bus_(bus),
+ dispatcher_(dispatcher),
+ provider_(provider),
+ match_rule_(base::StringPrintf("type='signal', interface='%s'",
+ kDBusInterfaceName)) {
+ bus_->AssertOnDBusThread();
+ CHECK(bus_->SetUpAsyncOperations());
+ if (!bus_->is_connected()) {
+ LOG(FATAL) << "DBus isn't connected.";
+ }
+
+ // Register filter function to the bus. It will be called when incoming
+ // messages are received.
+ bus_->AddFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this);
+
+ // Add match rule to the bus.
+ dbus::ScopedDBusError error;
+ bus_->AddMatch(match_rule_, error.get());
+ if (error.is_set()) {
+ LOG(FATAL) << "Failed to add match rule: " << error.name() << " "
+ << error.message();
+ }
+}
+
+ChromeosDHCPCDListener::~ChromeosDHCPCDListener() {
+ bus_->RemoveFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this);
+ dbus::ScopedDBusError error;
+ bus_->RemoveMatch(match_rule_, error.get());
+ if (error.is_set()) {
+ LOG(FATAL) << "Failed to remove match rule: " << error.name() << " "
+ << error.message();
+ }
+}
+
+// static.
+DBusHandlerResult ChromeosDHCPCDListener::HandleMessageThunk(
+ DBusConnection* connection, DBusMessage* raw_message, void* user_data) {
+ ChromeosDHCPCDListener* self =
+ static_cast<ChromeosDHCPCDListener*>(user_data);
+ return self->HandleMessage(connection, raw_message);
+}
+
+DBusHandlerResult ChromeosDHCPCDListener::HandleMessage(
+ DBusConnection* connection, DBusMessage* raw_message) {
+ bus_->AssertOnDBusThread();
+
+ // Only interested in signal message.
+ if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ // raw_message will be unrefed in Signal's parent class's (dbus::Message)
+ // destructor. Increment the reference so we can use it in Signal.
+ dbus_message_ref(raw_message);
+ std::unique_ptr<dbus::Signal> signal(
+ dbus::Signal::FromRawMessage(raw_message));
+
+ // Verify the signal comes from the interface that we interested in.
+ if (signal->GetInterface() != kDBusInterfaceName) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ string sender = signal->GetSender();
+ string member_name = signal->GetMember();
+ dbus::MessageReader reader(signal.get());
+ if (member_name == kSignalEvent) {
+ uint32_t pid;
+ string reason;
+ chromeos::VariantDictionary configurations;
+ // ExtracMessageParameters will log the error if it failed.
+ if (chromeos::dbus_utils::ExtractMessageParameters(&reader,
+ nullptr,
+ &pid,
+ &reason,
+ &configurations)) {
+ dispatcher_->PostTask(
+ base::Bind(&ChromeosDHCPCDListener::EventSignal,
+ weak_factory_.GetWeakPtr(),
+ sender, pid, reason, configurations));
+ }
+ } else if (member_name == kSignalStatusChanged) {
+ uint32_t pid;
+ string status;
+ // ExtracMessageParameters will log the error if it failed.
+ if (chromeos::dbus_utils::ExtractMessageParameters(&reader,
+ nullptr,
+ &pid,
+ &status)) {
+ dispatcher_->PostTask(
+ base::Bind(&ChromeosDHCPCDListener::StatusChangedSignal,
+ weak_factory_.GetWeakPtr(),
+ sender, pid, status));
+ }
+ } else {
+ LOG(INFO) << "Ignore signal: " << member_name;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void ChromeosDHCPCDListener::EventSignal(
+ const string& sender,
+ uint32_t pid,
+ const string& reason,
+ const chromeos::VariantDictionary& configuration) {
+ DHCPConfigRefPtr config = provider_->GetConfig(pid);
+ if (!config.get()) {
+ if (provider_->IsRecentlyUnbound(pid)) {
+ SLOG(nullptr, 3)
+ << __func__ << ": ignoring message from recently unbound PID " << pid;
+ } else {
+ LOG(ERROR) << "Unknown DHCP client PID " << pid;
+ }
+ return;
+ }
+ config->InitProxy(sender);
+ KeyValueStore configuration_store;
+ KeyValueStore::ConvertFromVariantDictionary(configuration,
+ &configuration_store);
+ config->ProcessEventSignal(reason, configuration_store);
+}
+
+void ChromeosDHCPCDListener::StatusChangedSignal(const string& sender,
+ uint32_t pid,
+ const string& status) {
+ DHCPConfigRefPtr config = provider_->GetConfig(pid);
+ if (!config.get()) {
+ if (provider_->IsRecentlyUnbound(pid)) {
+ SLOG(nullptr, 3)
+ << __func__ << ": ignoring message from recently unbound PID " << pid;
+ } else {
+ LOG(ERROR) << "Unknown DHCP client PID " << pid;
+ }
+ return;
+ }
+ config->InitProxy(sender);
+ config->ProcessStatusChangeSignal(status);
+}
+
+} // namespace shill
diff --git a/dbus/chromeos_dhcpcd_listener.h b/dbus/chromeos_dhcpcd_listener.h
new file mode 100644
index 0000000..2316c28
--- /dev/null
+++ b/dbus/chromeos_dhcpcd_listener.h
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_DBUS_CHROMEOS_DHCPCD_LISTENER_H_
+#define SHILL_DBUS_CHROMEOS_DHCPCD_LISTENER_H_
+
+#include <string>
+
+#include <dbus/dbus.h>
+
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/weak_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+
+#include <chromeos/variant_dictionary.h>
+
+namespace shill {
+
+class DHCPProvider;
+class EventDispatcher;
+
+// The DHCPCD listener is a singleton proxy that listens to signals from all
+// DHCP clients and dispatches them through the DHCP provider to the appropriate
+// client based on the PID.
+class ChromeosDHCPCDListener final {
+ public:
+ ChromeosDHCPCDListener(const scoped_refptr<dbus::Bus>& bus,
+ EventDispatcher* dispatcher,
+ DHCPProvider* provider);
+ ~ChromeosDHCPCDListener();
+
+ private:
+ static const char kDBusInterfaceName[];
+ static const char kSignalEvent[];
+ static const char kSignalStatusChanged[];
+
+ // Redirects the function call to HandleMessage
+ static DBusHandlerResult HandleMessageThunk(DBusConnection* connection,
+ DBusMessage* raw_message,
+ void* user_data);
+
+ // Handles incoming messages.
+ DBusHandlerResult HandleMessage(DBusConnection* connection,
+ DBusMessage* raw_message);
+
+ // Signal handlers.
+ void EventSignal(const std::string& sender,
+ uint32_t pid,
+ const std::string& reason,
+ const chromeos::VariantDictionary& configurations);
+ void StatusChangedSignal(const std::string& sender,
+ uint32_t pid,
+ const std::string& status);
+
+ scoped_refptr<dbus::Bus> bus_;
+ EventDispatcher* dispatcher_;
+ DHCPProvider* provider_;
+ const std::string match_rule_;
+
+ base::WeakPtrFactory<ChromeosDHCPCDListener> weak_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(ChromeosDHCPCDListener);
+};
+
+} // namespace shill
+
+#endif // SHILL_DBUS_CHROMEOS_DHCPCD_LISTENER_H_
diff --git a/dbus/chromeos_dhcpcd_proxy.cc b/dbus/chromeos_dhcpcd_proxy.cc
new file mode 100644
index 0000000..c876aae
--- /dev/null
+++ b/dbus/chromeos_dhcpcd_proxy.cc
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/dbus/chromeos_dhcpcd_proxy.h"
+
+#include "shill/logging.h"
+
+using std::string;
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kDHCP;
+static string ObjectID(ChromeosDHCPCDProxy* d) { return "(dhcpcd_proxy)"; }
+}
+
+ChromeosDHCPCDProxy::ChromeosDHCPCDProxy(const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name)
+ : dhcpcd_proxy_(
+ new org::chromium::dhcpcdProxy(bus, service_name)) {
+ SLOG(this, 2) << "DHCPCDProxy(service=" << service_name << ").";
+ // Do not register signal handlers, signals are processed by
+ // ChromeosDHCPCDListener.
+}
+
+ChromeosDHCPCDProxy::~ChromeosDHCPCDProxy() {}
+
+void ChromeosDHCPCDProxy::Rebind(const string& interface) {
+ SLOG(DBus, nullptr, 2) << __func__;
+ chromeos::ErrorPtr error;
+ if (!dhcpcd_proxy_->Rebind(interface, &error)) {
+ LogDBusError(error, __func__, interface);
+ }
+}
+
+void ChromeosDHCPCDProxy::Release(const string& interface) {
+ SLOG(DBus, nullptr, 2) << __func__;
+ chromeos::ErrorPtr error;
+ if (!dhcpcd_proxy_->Release(interface, &error)) {
+ LogDBusError(error, __func__, interface);
+ }
+}
+
+void ChromeosDHCPCDProxy::LogDBusError(const chromeos::ErrorPtr& error,
+ const string& method,
+ const string& interface) {
+ if (error->GetCode() == DBUS_ERROR_SERVICE_UNKNOWN ||
+ error->GetCode() == DBUS_ERROR_NO_REPLY) {
+ LOG(INFO) << method << ": dhcpcd daemon appears to have exited.";
+ } else {
+ LOG(FATAL) << "DBus error: " << method << " " << interface << ": "
+ << error->GetCode() << ": " << error->GetMessage();
+ }
+}
+
+} // namespace shill
diff --git a/dbus/chromeos_dhcpcd_proxy.h b/dbus/chromeos_dhcpcd_proxy.h
new file mode 100644
index 0000000..38dccec
--- /dev/null
+++ b/dbus/chromeos_dhcpcd_proxy.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_DBUS_CHROMEOS_DHCPCD_PROXY_H_
+#define SHILL_DBUS_CHROMEOS_DHCPCD_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "dhcpcd/dbus-proxies.h"
+#include "shill/dhcp/dhcp_proxy_interface.h"
+
+namespace shill {
+
+// There's a single DHCPCD proxy per DHCP client identified by its process id.
+class ChromeosDHCPCDProxy : public DHCPProxyInterface {
+ public:
+ ChromeosDHCPCDProxy(const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name);
+ ~ChromeosDHCPCDProxy() override;
+
+ // Inherited from DHCPProxyInterface.
+ void Rebind(const std::string& interface) override;
+ void Release(const std::string& interface) override;
+
+ private:
+ void LogDBusError(const chromeos::ErrorPtr& error,
+ const std::string& method,
+ const std::string& interface);
+
+ std::unique_ptr<org::chromium::dhcpcdProxy> dhcpcd_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeosDHCPCDProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_DBUS_CHROMEOS_DHCPCD_PROXY_H_
diff --git a/shill.gyp b/shill.gyp
index 7fafc8c..05cb6ba 100644
--- a/shill.gyp
+++ b/shill.gyp
@@ -33,6 +33,13 @@
'DISABLE_CHROMEOS_DBUS',
],
}],
+ ['USE_chromeos_dbus == 1', {
+ 'variables': {
+ 'deps': [
+ 'dbus-1',
+ ],
+ },
+ }],
['USE_pppoe == 0', {
'defines': [
'DISABLE_PPPOE',
@@ -322,6 +329,8 @@
'dbus/chromeos_dbus_control.cc',
'dbus/chromeos_dbus_daemon.cc',
'dbus/chromeos_device_dbus_adaptor.cc',
+ 'dbus/chromeos_dhcpcd_listener.cc',
+ 'dbus/chromeos_dhcpcd_proxy.cc',
'dbus/chromeos_ipconfig_dbus_adaptor.cc',
'dbus/chromeos_manager_dbus_adaptor.cc',
'dbus/chromeos_profile_dbus_adaptor.cc',
@@ -332,6 +341,16 @@
],
'actions': [
{
+ 'action_name': 'generate-dhcpcd-proxies',
+ 'variables': {
+ 'proxy_output_file': 'include/dhcpcd/dbus-proxies.h',
+ },
+ 'sources': [
+ 'dbus_bindings/dhcpcd.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ {
'action_name': 'generate-upstart-proxies',
'variables': {
'proxy_output_file': 'include/upstart/dbus-proxies.h',