1// 2// Copyright (C) 2015 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "shill/dbus/chromeos_dhcpcd_listener.h" 18 19#include <string.h> 20 21#include <base/bind.h> 22#include <base/callback.h> 23#include <base/strings/stringprintf.h> 24#include <brillo/dbus/dbus_method_invoker.h> 25#include <dbus/util.h> 26 27#include "shill/dhcp/dhcp_config.h" 28#include "shill/dhcp/dhcp_provider.h" 29#include "shill/event_dispatcher.h" 30#include "shill/logging.h" 31 32using std::string; 33 34namespace shill { 35 36namespace Logging { 37static auto kModuleLogScope = ScopeLogger::kDHCP; 38static string ObjectID(ChromeosDHCPCDListener* d) { 39 return "(dhcpcd_listener)"; 40} 41} 42 43const char ChromeosDHCPCDListener::kDBusInterfaceName[] = "org.chromium.dhcpcd"; 44const char ChromeosDHCPCDListener::kSignalEvent[] = "Event"; 45const char ChromeosDHCPCDListener::kSignalStatusChanged[] = "StatusChanged"; 46 47ChromeosDHCPCDListener::ChromeosDHCPCDListener( 48 const scoped_refptr<dbus::Bus>& bus, 49 EventDispatcher* dispatcher, 50 DHCPProvider* provider) 51 : bus_(bus), 52 dispatcher_(dispatcher), 53 provider_(provider), 54 match_rule_(base::StringPrintf("type='signal', interface='%s'", 55 kDBusInterfaceName)) { 56 bus_->AssertOnDBusThread(); 57 CHECK(bus_->SetUpAsyncOperations()); 58 if (!bus_->is_connected()) { 59 LOG(FATAL) << "DBus isn't connected."; 60 } 61 62 // Register filter function to the bus. It will be called when incoming 63 // messages are received. 64 bus_->AddFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this); 65 66 // Add match rule to the bus. 67 dbus::ScopedDBusError error; 68 bus_->AddMatch(match_rule_, error.get()); 69 if (error.is_set()) { 70 LOG(FATAL) << "Failed to add match rule: " << error.name() << " " 71 << error.message(); 72 } 73} 74 75ChromeosDHCPCDListener::~ChromeosDHCPCDListener() { 76 bus_->RemoveFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this); 77 dbus::ScopedDBusError error; 78 bus_->RemoveMatch(match_rule_, error.get()); 79 if (error.is_set()) { 80 LOG(FATAL) << "Failed to remove match rule: " << error.name() << " " 81 << error.message(); 82 } 83} 84 85// static. 86DBusHandlerResult ChromeosDHCPCDListener::HandleMessageThunk( 87 DBusConnection* connection, DBusMessage* raw_message, void* user_data) { 88 ChromeosDHCPCDListener* self = 89 static_cast<ChromeosDHCPCDListener*>(user_data); 90 return self->HandleMessage(connection, raw_message); 91} 92 93DBusHandlerResult ChromeosDHCPCDListener::HandleMessage( 94 DBusConnection* connection, DBusMessage* raw_message) { 95 bus_->AssertOnDBusThread(); 96 97 // Only interested in signal message. 98 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL) { 99 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 100 } 101 102 // raw_message will be unrefed in Signal's parent class's (dbus::Message) 103 // destructor. Increment the reference so we can use it in Signal. 104 dbus_message_ref(raw_message); 105 std::unique_ptr<dbus::Signal> signal( 106 dbus::Signal::FromRawMessage(raw_message)); 107 108 // Verify the signal comes from the interface that we interested in. 109 if (signal->GetInterface() != kDBusInterfaceName) { 110 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 111 } 112 113 string sender = signal->GetSender(); 114 string member_name = signal->GetMember(); 115 dbus::MessageReader reader(signal.get()); 116 if (member_name == kSignalEvent) { 117 uint32_t pid; 118 string reason; 119 brillo::VariantDictionary configurations; 120 // ExtracMessageParameters will log the error if it failed. 121 if (brillo::dbus_utils::ExtractMessageParameters(&reader, 122 nullptr, 123 &pid, 124 &reason, 125 &configurations)) { 126 dispatcher_->PostTask( 127 base::Bind(&ChromeosDHCPCDListener::EventSignal, 128 weak_factory_.GetWeakPtr(), 129 sender, pid, reason, configurations)); 130 } 131 } else if (member_name == kSignalStatusChanged) { 132 uint32_t pid; 133 string status; 134 // ExtracMessageParameters will log the error if it failed. 135 if (brillo::dbus_utils::ExtractMessageParameters(&reader, 136 nullptr, 137 &pid, 138 &status)) { 139 dispatcher_->PostTask( 140 base::Bind(&ChromeosDHCPCDListener::StatusChangedSignal, 141 weak_factory_.GetWeakPtr(), 142 sender, pid, status)); 143 } 144 } else { 145 LOG(INFO) << "Ignore signal: " << member_name; 146 } 147 148 return DBUS_HANDLER_RESULT_HANDLED; 149} 150 151void ChromeosDHCPCDListener::EventSignal( 152 const string& sender, 153 uint32_t pid, 154 const string& reason, 155 const brillo::VariantDictionary& configuration) { 156 DHCPConfigRefPtr config = provider_->GetConfig(pid); 157 if (!config.get()) { 158 if (provider_->IsRecentlyUnbound(pid)) { 159 SLOG(nullptr, 3) 160 << __func__ << ": ignoring message from recently unbound PID " << pid; 161 } else { 162 LOG(ERROR) << "Unknown DHCP client PID " << pid; 163 } 164 return; 165 } 166 config->InitProxy(sender); 167 KeyValueStore configuration_store; 168 KeyValueStore::ConvertFromVariantDictionary(configuration, 169 &configuration_store); 170 config->ProcessEventSignal(reason, configuration_store); 171} 172 173void ChromeosDHCPCDListener::StatusChangedSignal(const string& sender, 174 uint32_t pid, 175 const string& status) { 176 DHCPConfigRefPtr config = provider_->GetConfig(pid); 177 if (!config.get()) { 178 if (provider_->IsRecentlyUnbound(pid)) { 179 SLOG(nullptr, 3) 180 << __func__ << ": ignoring message from recently unbound PID " << pid; 181 } else { 182 LOG(ERROR) << "Unknown DHCP client PID " << pid; 183 } 184 return; 185 } 186 config->InitProxy(sender); 187 config->ProcessStatusChangeSignal(status); 188} 189 190} // namespace shill 191