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 32 using std::string; 33 34 namespace shill { 35 36 namespace Logging { 37 static auto kModuleLogScope = ScopeLogger::kDHCP; 38 static string ObjectID(ChromeosDHCPCDListener* d) { 39 return "(dhcpcd_listener)"; 40 } 41 } 42 43 const char ChromeosDHCPCDListener::kDBusInterfaceName[] = "org.chromium.dhcpcd"; 44 const char ChromeosDHCPCDListener::kSignalEvent[] = "Event"; 45 const char ChromeosDHCPCDListener::kSignalStatusChanged[] = "StatusChanged"; 46 47 ChromeosDHCPCDListener::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 75 ChromeosDHCPCDListener::~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. 86 DBusHandlerResult 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 93 DBusHandlerResult 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 151 void 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 173 void 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