text: // Copyright 2018 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/dhcp/dhcp_provider.h"
  
  #include <signal.h>
  
  #include <base/bind.h>
  #include <base/files/file_util.h>
  #include <base/process/process.h>
  #include <base/process/process_iterator.h>
  #include <base/stl_util.h>
  #include <base/strings/stringprintf.h>
  
  #include "shill/control_interface.h"
  #include "shill/dhcp/dhcp_properties.h"
  #include "shill/dhcp/dhcpcd_listener_interface.h"
  #include "shill/dhcp/dhcpv4_config.h"
  #ifndef DISABLE_DHCPV6
  #include "shill/dhcp/dhcpv6_config.h"
  #endif
  #include "shill/event_dispatcher.h"
  #include "shill/logging.h"
  
  using std::string;
  
  namespace shill {
  
  namespace Logging {
  static auto kModuleLogScope = ScopeLogger::kDHCP;
  static string ObjectID(DHCPProvider* d) { return "(dhcp_provider)"; }
  }
  
  namespace {
  base::LazyInstance<DHCPProvider>::DestructorAtExit g_dhcp_provider =
      LAZY_INSTANCE_INITIALIZER;
  static const int kUnbindDelayMilliseconds = 2000;
  
  const char kDHCPCDExecutableName[] = "dhcpcd";
  
  }  // namespace
  
  constexpr char DHCPProvider::kDHCPCDPathFormatLease[];
  #ifndef DISABLE_DHCPV6
  constexpr char DHCPProvider::kDHCPCDPathFormatLease6[];
  #endif  // DISABLE_DHCPV6
  
  DHCPProvider::DHCPProvider()
      : root_("/"),
        control_interface_(nullptr),
        dispatcher_(nullptr),
        metrics_(nullptr) {
    SLOG(this, 2) << __func__;
  }
  
  DHCPProvider::~DHCPProvider() {
    SLOG(this, 2) << __func__;
  }
  
  DHCPProvider* DHCPProvider::GetInstance() {
    return g_dhcp_provider.Pointer();
  }
  
  void DHCPProvider::Init(ControlInterface* control_interface,
                          EventDispatcher* dispatcher,
                          Metrics* metrics) {
    SLOG(this, 2) << __func__;
    listener_ = control_interface->CreateDHCPCDListener(this);
    control_interface_ = control_interface;
    dispatcher_ = dispatcher;
    metrics_ = metrics;
  
    // Kill the dhcpcd processes accidentally left by previous run.
    base::NamedProcessIterator iter(kDHCPCDExecutableName, nullptr);
    while (const base::ProcessEntry* entry = iter.NextProcessEntry())
      kill(entry->pid(), SIGKILL);
  }
  
  void DHCPProvider::Stop() {
    listener_.reset();
    configs_.clear();
  }
  
  DHCPConfigRefPtr DHCPProvider::CreateIPv4Config(
      const string& device_name,
      const string& lease_file_suffix,
      bool arp_gateway,
      const DhcpProperties& dhcp_props) {
    SLOG(this, 2) << __func__ << " device: " << device_name;
    return new DHCPv4Config(control_interface_,
                            dispatcher_,
                            this,
                            device_name,
                            lease_file_suffix,
                            arp_gateway,
                            dhcp_props,
                            metrics_);
  }
  
  #ifndef DISABLE_DHCPV6
  DHCPConfigRefPtr DHCPProvider::CreateIPv6Config(
      const string& device_name, const string& lease_file_suffix) {
    SLOG(this, 2) << __func__ << " device: " << device_name;
    return new DHCPv6Config(control_interface_,
                            dispatcher_,
                            this,
                            device_name,
                            lease_file_suffix);
  }
  #endif
  
  DHCPConfigRefPtr DHCPProvider::GetConfig(int pid) {
    SLOG(this, 2) << __func__ << " pid: " << pid;
    PIDConfigMap::const_iterator it = configs_.find(pid);
    if (it == configs_.end()) {
      return nullptr;
    }
    return it->second;
  }
  
  void DHCPProvider::BindPID(int pid, const DHCPConfigRefPtr& config) {
    SLOG(this, 2) << __func__ << " pid: " << pid;
    configs_[pid] = config;
  }
  
  void DHCPProvider::UnbindPID(int pid) {
    SLOG(this, 2) << __func__ << " pid: " << pid;
    configs_.erase(pid);
    recently_unbound_pids_.insert(pid);
    dispatcher_->PostDelayedTask(FROM_HERE,
                                 base::Bind(&DHCPProvider::RetireUnboundPID,
                                            base::Unretained(this),
                                            pid),
                                 kUnbindDelayMilliseconds);
  }
  
  void DHCPProvider::RetireUnboundPID(int pid) {
    recently_unbound_pids_.erase(pid);
  }
  
  bool DHCPProvider::IsRecentlyUnbound(int pid) {
    return base::ContainsKey(recently_unbound_pids_, pid);
  }
  
  void DHCPProvider::DestroyLease(const string& name) {
    SLOG(this, 2) << __func__ << " name: " << name;
    base::DeleteFile(root_.Append(
        base::StringPrintf(kDHCPCDPathFormatLease,
                           name.c_str())), false);
  #ifndef DISABLE_DHCPV6
    base::DeleteFile(root_.Append(
        base::StringPrintf(kDHCPCDPathFormatLease6,
                           name.c_str())), false);
  #endif  // DISABLE_DHCPV6
  }
  
  }  // namespace shill
  