blob: 79a5055f07ffc7aa9157dfb6aa3fe5fb23430002 [file] [log] [blame]
//
// Copyright (C) 2015 The Android Open Source Project
//
// 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.
//
#include "shill/dbus/chromeos_dhcpcd_listener.h"
#include <string.h>
#include <memory>
#include <base/bind.h>
#include <base/callback.h>
#include <base/strings/stringprintf.h>
#include <brillo/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;
brillo::VariantDictionary configurations;
// ExtracMessageParameters will log the error if it failed.
if (brillo::dbus_utils::ExtractMessageParameters(&reader,
nullptr,
&pid,
&reason,
&configurations)) {
dispatcher_->PostTask(FROM_HERE,
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 (brillo::dbus_utils::ExtractMessageParameters(&reader,
nullptr,
&pid,
&status)) {
dispatcher_->PostTask(FROM_HERE,
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 brillo::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