1c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
2c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Copyright (C) 2015 The Android Open Source Project
3c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
4c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Licensed under the Apache License, Version 2.0 (the "License");
5c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// you may not use this file except in compliance with the License.
6c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// You may obtain a copy of the License at
7c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
8c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//      http://www.apache.org/licenses/LICENSE-2.0
9c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
10c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Unless required by applicable law or agreed to in writing, software
11c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// distributed under the License is distributed on an "AS IS" BASIS,
12c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// See the License for the specific language governing permissions and
14c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// limitations under the License.
15c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
1665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
1765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include "shill/dbus/chromeos_dhcpcd_listener.h"
1865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
1965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include <string.h>
2065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
2165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include <base/bind.h>
2265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include <base/callback.h>
2365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include <base/strings/stringprintf.h>
2403e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko#include <brillo/dbus/dbus_method_invoker.h>
2565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include <dbus/util.h>
2665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
2765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include "shill/dhcp/dhcp_config.h"
2865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include "shill/dhcp/dhcp_provider.h"
2965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include "shill/event_dispatcher.h"
3065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu#include "shill/logging.h"
3165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
3265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuusing std::string;
3365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
3465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiunamespace shill {
3565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
3665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiunamespace Logging {
3765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiustatic auto kModuleLogScope = ScopeLogger::kDHCP;
3865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiustatic string ObjectID(ChromeosDHCPCDListener* d) {
3965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  return "(dhcpcd_listener)";
4065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
4165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
4265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
4365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuconst char ChromeosDHCPCDListener::kDBusInterfaceName[] = "org.chromium.dhcpcd";
4465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuconst char ChromeosDHCPCDListener::kSignalEvent[] = "Event";
4565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuconst char ChromeosDHCPCDListener::kSignalStatusChanged[] = "StatusChanged";
4665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
4765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter QiuChromeosDHCPCDListener::ChromeosDHCPCDListener(
4865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    const scoped_refptr<dbus::Bus>& bus,
4965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    EventDispatcher* dispatcher,
5065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    DHCPProvider* provider)
5165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    : bus_(bus),
5265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      dispatcher_(dispatcher),
5365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      provider_(provider),
5465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      match_rule_(base::StringPrintf("type='signal', interface='%s'",
5565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                                     kDBusInterfaceName)) {
5665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->AssertOnDBusThread();
5765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  CHECK(bus_->SetUpAsyncOperations());
5865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (!bus_->is_connected()) {
5965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    LOG(FATAL) << "DBus isn't connected.";
6065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
6165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
6265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // Register filter function to the bus.  It will be called when incoming
6365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // messages are received.
6465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->AddFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this);
6565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
6665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // Add match rule to the bus.
6765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  dbus::ScopedDBusError error;
6865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->AddMatch(match_rule_, error.get());
6965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (error.is_set()) {
7065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    LOG(FATAL) << "Failed to add match rule: " << error.name() << " "
7165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu               << error.message();
7265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
7365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
7465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
7565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter QiuChromeosDHCPCDListener::~ChromeosDHCPCDListener() {
7665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->RemoveFilterFunction(&ChromeosDHCPCDListener::HandleMessageThunk, this);
7765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  dbus::ScopedDBusError error;
7865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->RemoveMatch(match_rule_, error.get());
7965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (error.is_set()) {
8065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    LOG(FATAL) << "Failed to remove match rule: " << error.name() << " "
8165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu               << error.message();
8265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
8365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
8465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
8565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu// static.
8665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter QiuDBusHandlerResult ChromeosDHCPCDListener::HandleMessageThunk(
8765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    DBusConnection* connection, DBusMessage* raw_message, void* user_data) {
8865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  ChromeosDHCPCDListener* self =
8965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      static_cast<ChromeosDHCPCDListener*>(user_data);
9065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  return self->HandleMessage(connection, raw_message);
9165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
9265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
9365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter QiuDBusHandlerResult ChromeosDHCPCDListener::HandleMessage(
9465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    DBusConnection* connection, DBusMessage* raw_message) {
9565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  bus_->AssertOnDBusThread();
9665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
9765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // Only interested in signal message.
9865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL) {
9965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
10065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
10165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
10265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // raw_message will be unrefed in Signal's parent class's (dbus::Message)
10365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // destructor. Increment the reference so we can use it in Signal.
10465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  dbus_message_ref(raw_message);
10565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  std::unique_ptr<dbus::Signal> signal(
10665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      dbus::Signal::FromRawMessage(raw_message));
10765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
10865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  // Verify the signal comes from the interface that we interested in.
10965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (signal->GetInterface() != kDBusInterfaceName) {
11065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
11165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
11265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
11365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  string sender = signal->GetSender();
11465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  string member_name = signal->GetMember();
11565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  dbus::MessageReader reader(signal.get());
11665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (member_name == kSignalEvent) {
11765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    uint32_t pid;
11865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    string reason;
11903e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko    brillo::VariantDictionary configurations;
12065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    // ExtracMessageParameters will log the error if it failed.
12103e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko    if (brillo::dbus_utils::ExtractMessageParameters(&reader,
12203e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     nullptr,
12303e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     &pid,
12403e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     &reason,
12503e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     &configurations)) {
12665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      dispatcher_->PostTask(
12765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu          base::Bind(&ChromeosDHCPCDListener::EventSignal,
12865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                     weak_factory_.GetWeakPtr(),
12965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                     sender, pid, reason, configurations));
13065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    }
13165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  } else if (member_name == kSignalStatusChanged) {
13265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    uint32_t pid;
13365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    string status;
13465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    // ExtracMessageParameters will log the error if it failed.
13503e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko    if (brillo::dbus_utils::ExtractMessageParameters(&reader,
13603e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     nullptr,
13703e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     &pid,
13803e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko                                                     &status)) {
13965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      dispatcher_->PostTask(
14065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu          base::Bind(&ChromeosDHCPCDListener::StatusChangedSignal,
14165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                     weak_factory_.GetWeakPtr(),
14265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                     sender, pid, status));
14365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    }
14465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  } else {
14565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    LOG(INFO) << "Ignore signal: " << member_name;
14665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
14765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
14865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  return DBUS_HANDLER_RESULT_HANDLED;
14965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
15065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
15165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuvoid ChromeosDHCPCDListener::EventSignal(
15265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    const string& sender,
15365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    uint32_t pid,
15465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    const string& reason,
15503e6719bae1e0903d94853b896673a033196bcf5Alex Vakulenko    const brillo::VariantDictionary& configuration) {
15665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  DHCPConfigRefPtr config = provider_->GetConfig(pid);
15765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (!config.get()) {
15865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    if (provider_->IsRecentlyUnbound(pid)) {
15965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      SLOG(nullptr, 3)
16065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu          << __func__ << ": ignoring message from recently unbound PID " << pid;
16165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    } else {
16265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      LOG(ERROR) << "Unknown DHCP client PID " << pid;
16365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    }
16465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    return;
16565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
16665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  config->InitProxy(sender);
16765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  KeyValueStore configuration_store;
16865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  KeyValueStore::ConvertFromVariantDictionary(configuration,
16965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                                              &configuration_store);
17065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  config->ProcessEventSignal(reason, configuration_store);
17165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
17265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
17365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiuvoid ChromeosDHCPCDListener::StatusChangedSignal(const string& sender,
17465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                                                 uint32_t pid,
17565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu                                                 const string& status) {
17665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  DHCPConfigRefPtr config = provider_->GetConfig(pid);
17765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  if (!config.get()) {
17865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    if (provider_->IsRecentlyUnbound(pid)) {
17965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      SLOG(nullptr, 3)
18065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu          << __func__ << ": ignoring message from recently unbound PID " << pid;
18165a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    } else {
18265a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu      LOG(ERROR) << "Unknown DHCP client PID " << pid;
18365a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    }
18465a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu    return;
18565a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  }
18665a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  config->InitProxy(sender);
18765a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu  config->ProcessStatusChangeSignal(status);
18865a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}
18965a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu
19065a049b4b284b5c189e0b6f901b448edd6c3d86dPeter Qiu}  // namespace shill
191